summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/adsl/meson.build6
-rw-r--r--src/devices/adsl/nm-device-adsl.c32
-rw-r--r--src/devices/bluetooth/meson.build6
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c73
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c4
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c8
-rw-r--r--src/devices/bluetooth/nm-device-bt.c52
-rw-r--r--src/devices/nm-device-6lowpan.c345
-rw-r--r--src/devices/nm-device-6lowpan.h37
-rw-r--r--src/devices/nm-device-bond.c67
-rw-r--r--src/devices/nm-device-bridge.c96
-rw-r--r--src/devices/nm-device-dummy.c23
-rw-r--r--src/devices/nm-device-ethernet-utils.c25
-rw-r--r--src/devices/nm-device-ethernet-utils.h2
-rw-r--r--src/devices/nm-device-ethernet.c130
-rw-r--r--src/devices/nm-device-factory.c5
-rw-r--r--src/devices/nm-device-generic.c30
-rw-r--r--src/devices/nm-device-infiniband.c48
-rw-r--r--src/devices/nm-device-ip-tunnel.c247
-rw-r--r--src/devices/nm-device-macsec.c44
-rw-r--r--src/devices/nm-device-macvlan.c70
-rw-r--r--src/devices/nm-device-ppp.c37
-rw-r--r--src/devices/nm-device-private.h27
-rw-r--r--src/devices/nm-device-tun.c50
-rw-r--r--src/devices/nm-device-veth.c5
-rw-r--r--src/devices/nm-device-vlan.c96
-rw-r--r--src/devices/nm-device-vxlan.c117
-rw-r--r--src/devices/nm-device-wireguard.c212
-rw-r--r--src/devices/nm-device-wireguard.h41
-rw-r--r--src/devices/nm-device-wpan.c253
-rw-r--r--src/devices/nm-device-wpan.h35
-rw-r--r--src/devices/nm-device.c999
-rw-r--r--src/devices/nm-device.h30
-rw-r--r--src/devices/ovs/meson.build7
-rw-r--r--src/devices/ovs/nm-device-ovs-bridge.c21
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.c26
-rw-r--r--src/devices/ovs/nm-device-ovs-port.c26
-rw-r--r--src/devices/ovs/nm-ovsdb.c2
-rw-r--r--src/devices/team/meson.build6
-rw-r--r--src/devices/team/nm-device-team.c75
-rw-r--r--src/devices/wifi/meson.build6
-rw-r--r--src/devices/wifi/nm-device-iwd.c425
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c54
-rw-r--r--src/devices/wifi/nm-device-wifi.c419
-rw-r--r--src/devices/wifi/nm-iwd-manager.c540
-rw-r--r--src/devices/wifi/nm-iwd-manager.h17
-rw-r--r--src/devices/wifi/nm-wifi-ap.c122
-rw-r--r--src/devices/wifi/nm-wifi-ap.h8
-rw-r--r--src/devices/wifi/nm-wifi-common.c2
-rw-r--r--src/devices/wifi/nm-wifi-utils.c56
-rw-r--r--src/devices/wifi/nm-wifi-utils.h16
-rw-r--r--src/devices/wifi/tests/test-general.c34
-rw-r--r--src/devices/wwan/meson.build6
-rw-r--r--src/devices/wwan/nm-device-modem.c47
-rw-r--r--src/devices/wwan/nm-modem-broadband.c52
-rw-r--r--src/devices/wwan/nm-modem-manager.c14
-rw-r--r--src/devices/wwan/nm-modem-ofono.c47
-rw-r--r--src/devices/wwan/nm-modem.c52
-rw-r--r--src/devices/wwan/nm-modem.h11
59 files changed, 3618 insertions, 1725 deletions
diff --git a/src/devices/adsl/meson.build b/src/devices/adsl/meson.build
index ee804d23..7ac0e123 100644
--- a/src/devices/adsl/meson.build
+++ b/src/devices/adsl/meson.build
@@ -20,10 +20,10 @@ libnm_device_plugin_adsl = shared_module(
 
 core_plugins += libnm_device_plugin_adsl
 
-run_target(
+test(
   'check-local-devices-adsl',
-  command: [check_exports, libnm_device_plugin_adsl.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_adsl
+  check_exports,
+  args: [libnm_device_plugin_adsl.full_path(), linker_script_devices],
 )
 
 # FIXME: check_so_symbols replacement
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index 1450a836..c9984f51 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -87,25 +87,23 @@ get_generic_capabilities (NMDevice *dev)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMSettingAdsl *s_adsl;
 	const char *protocol;
 
-	if (!NM_DEVICE_CLASS (nm_device_adsl_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	if (!nm_connection_is_type (connection, NM_SETTING_ADSL_SETTING_NAME))
+	if (!NM_DEVICE_CLASS (nm_device_adsl_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	s_adsl = nm_connection_get_setting_adsl (connection);
-	if (!s_adsl)
-		return FALSE;
 
-	/* FIXME: we don't yet support IPoATM */
 	protocol = nm_setting_adsl_get_protocol (s_adsl);
-	if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_IPOATM) == 0)
+	if (nm_streq0 (protocol, NM_SETTING_ADSL_PROTOCOL_IPOATM)) {
+		/* FIXME: we don't yet support IPoATM */
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "IPoATM protocol is not yet supported");
 		return FALSE;
+	}
 
 	return TRUE;
 }
@@ -669,7 +667,7 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed  = constructed;
 	object_class->dispose      = dispose;
@@ -678,14 +676,16 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_adsl);
 
-	parent_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->connection_type_check_compatible = NM_SETTING_ADSL_SETTING_NAME;
+
+	device_class->get_generic_capabilities = get_generic_capabilities;
 
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->complete_connection = complete_connection;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->complete_connection = complete_connection;
 
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
-	parent_class->deactivate = deactivate;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	device_class->deactivate = deactivate;
 
 	obj_properties[PROP_ATM_INDEX] =
 	     g_param_spec_int (NM_DEVICE_ADSL_ATM_INDEX, "", "",
diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build
index 30284f9e..019f04f6 100644
--- a/src/devices/bluetooth/meson.build
+++ b/src/devices/bluetooth/meson.build
@@ -31,10 +31,10 @@ libnm_device_plugin_bluetooth = shared_module(
 
 core_plugins += libnm_device_plugin_bluetooth
 
-run_target(
+test(
   'check-local-devices-bluetooth',
-  command: [check_exports, libnm_device_plugin_bluetooth.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_bluetooth
+  check_exports,
+  args: [libnm_device_plugin_bluetooth.full_path(), linker_script_devices],
 )
 
 # FIXME: check_so_symbols replacement
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index d8e40d6f..b69b4320 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -94,7 +94,7 @@ typedef struct {
 	NMSettings *settings;
 	GSList *connections;
 
-	NMConnection *pan_connection;
+	NMSettingsConnection *pan_connection;
 	gboolean pan_connection_no_autocreate;
 } NMBluezDevicePrivate;
 
@@ -114,8 +114,9 @@ G_DEFINE_TYPE (NMBluezDevice, nm_bluez_device, G_TYPE_OBJECT)
 /*****************************************************************************/
 
 static void cp_connection_added (NMSettings *settings,
-                                 NMConnection *connection, NMBluezDevice *self);
-static gboolean connection_compatible (NMBluezDevice *self, NMConnection *connection);
+                                 NMSettingsConnection *sett_conn,
+                                 NMBluezDevice *self);
+static gboolean connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn);
 
 /*****************************************************************************/
 
@@ -181,10 +182,10 @@ nm_bluez_device_get_connected (NMBluezDevice *self)
 static void
 pan_connection_check_create (NMBluezDevice *self)
 {
-	NMConnection *connection;
-	NMConnection *added;
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingsConnection *added;
 	NMSetting *setting;
-	char *id;
+	gs_free char *id = NULL;
 	char uuid[37];
 	GError *error = NULL;
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
@@ -246,15 +247,14 @@ pan_connection_check_create (NMBluezDevice *self)
 	 * which then already finds the suitable connection in priv->connections. This is confusing,
 	 * so block the signal. check_emit_usable will succeed after this function call returns. */
 	g_signal_handlers_block_by_func (priv->settings, cp_connection_added, self);
-	added = NM_CONNECTION (nm_settings_add_connection (priv->settings, connection, FALSE, &error));
+	added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
 	g_signal_handlers_unblock_by_func (priv->settings, cp_connection_added, self);
 
 	if (added) {
-		g_assert (!g_slist_find (priv->connections, added));
-		g_assert (connection_compatible (self, added));
-		g_assert (nm_connection_compare (added, connection, NM_SETTING_COMPARE_FLAG_EXACT));
+		nm_assert (!g_slist_find (priv->connections, added));
+		nm_assert (connection_compatible (self, added));
 
-		nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added), NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, TRUE);
+		nm_settings_connection_set_flags (added, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED, TRUE);
 
 		priv->connections = g_slist_prepend (priv->connections, g_object_ref (added));
 		priv->pan_connection = added;
@@ -263,11 +263,7 @@ pan_connection_check_create (NMBluezDevice *self)
 		nm_log_warn (LOGD_BT, "bluez[%s] couldn't add new Bluetooth connection for NAP device: '%s' (%s): %s",
 		             priv->path, id, uuid, error->message);
 		g_clear_error (&error);
-
 	}
-	g_object_unref (connection);
-
-	g_free (id);
 }
 
 static gboolean
@@ -321,9 +317,10 @@ check_emit_usable_schedule (NMBluezDevice *self)
 /*****************************************************************************/
 
 static gboolean
-connection_compatible (NMBluezDevice *self, NMConnection *connection)
+connection_compatible (NMBluezDevice *self, NMSettingsConnection *sett_conn)
 {
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
+	NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
 	NMSettingBluetooth *s_bt;
 	const char *bt_type;
 	const char *bdaddr;
@@ -361,22 +358,24 @@ connection_compatible (NMBluezDevice *self, NMConnection *connection)
 }
 
 static gboolean
-_internal_track_connection (NMBluezDevice *self, NMConnection *connection, gboolean tracked)
+_internal_track_connection (NMBluezDevice *self,
+                            NMSettingsConnection *sett_conn,
+                            gboolean tracked)
 {
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 	gboolean was_tracked;
 
-	was_tracked = !!g_slist_find (priv->connections, connection);
+	was_tracked = !!g_slist_find (priv->connections, sett_conn);
 	if (was_tracked == !!tracked)
 		return FALSE;
 
 	if (tracked)
-		priv->connections = g_slist_prepend (priv->connections, g_object_ref (connection));
+		priv->connections = g_slist_prepend (priv->connections, g_object_ref (sett_conn));
 	else {
-		priv->connections = g_slist_remove (priv->connections, connection);
-		if (priv->pan_connection == connection)
+		priv->connections = g_slist_remove (priv->connections, sett_conn);
+		if (priv->pan_connection == sett_conn)
 			priv->pan_connection = NULL;
-		g_object_unref (connection);
+		g_object_unref (sett_conn);
 	}
 
 	return TRUE;
@@ -384,32 +383,32 @@ _internal_track_connection (NMBluezDevice *self, NMConnection *connection, gbool
 
 static void
 cp_connection_added (NMSettings *settings,
-                     NMConnection *connection,
+                     NMSettingsConnection *sett_conn,
                      NMBluezDevice *self)
 {
-	if (connection_compatible (self, connection)) {
-		if (_internal_track_connection (self, connection, TRUE))
+	if (connection_compatible (self, sett_conn)) {
+		if (_internal_track_connection (self, sett_conn, TRUE))
 			check_emit_usable (self);
 	}
 }
 
 static void
 cp_connection_removed (NMSettings *settings,
-                       NMConnection *connection,
+                       NMSettingsConnection *sett_conn,
                        NMBluezDevice *self)
 {
-	if (_internal_track_connection (self, connection, FALSE))
+	if (_internal_track_connection (self, sett_conn, FALSE))
 		check_emit_usable (self);
 }
 
 static void
 cp_connection_updated (NMSettings *settings,
-                       NMConnection *connection,
+                       NMSettingsConnection *sett_conn,
                        gboolean by_user,
                        NMBluezDevice *self)
 {
-	if (_internal_track_connection (self, connection,
-	                                connection_compatible (self, connection)))
+	if (_internal_track_connection (self, sett_conn,
+	                                connection_compatible (self, sett_conn)))
 		check_emit_usable_schedule (self);
 }
 
@@ -423,10 +422,8 @@ load_connections (NMBluezDevice *self)
 
 	connections = nm_settings_get_connections (priv->settings, NULL);
 	for (i = 0; connections[i]; i++) {
-		NMConnection *connection = (NMConnection *) connections[i];
-
-		if (connection_compatible (self, connection))
-			changed |= _internal_track_connection (self, connection, TRUE);
+		if (connection_compatible (self, connections[i]))
+			changed |= _internal_track_connection (self, connections[i], TRUE);
 	}
 	if (changed)
 		check_emit_usable (self);
@@ -1178,14 +1175,14 @@ dispose (GObject *object)
 {
 	NMBluezDevice *self = NM_BLUEZ_DEVICE (object);
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
-	NMConnection *to_delete = NULL;
+	NMSettingsConnection *to_delete = NULL;
 
 	nm_clear_g_source (&priv->check_emit_usable_id);
 
 	if (priv->pan_connection) {
 		/* Check whether we want to remove the created connection. If so, we take a reference
 		 * and delete it at the end of dispose(). */
-		if (NM_FLAGS_HAS (nm_settings_connection_get_flags (NM_SETTINGS_CONNECTION (priv->pan_connection)),
+		if (NM_FLAGS_HAS (nm_settings_connection_get_flags (priv->pan_connection),
 		                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
 			to_delete = g_object_ref (priv->pan_connection);
 
@@ -1219,8 +1216,8 @@ dispose (GObject *object)
 
 	if (to_delete) {
 		nm_log_dbg (LOGD_BT, "bluez[%s] removing Bluetooth connection for NAP device: '%s' (%s)", priv->path,
-		            nm_connection_get_id (to_delete), nm_connection_get_uuid (to_delete));
-		nm_settings_connection_delete (NM_SETTINGS_CONNECTION (to_delete), NULL);
+		            nm_settings_connection_get_id (to_delete), nm_settings_connection_get_uuid (to_delete));
+		nm_settings_connection_delete (to_delete, NULL);
 		g_object_unref (to_delete);
 	}
 
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index c74ecd81..711f2e57 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -244,8 +244,8 @@ setup_bluez5 (NMBluezManager *self)
 
 static void
 watch_name_on_appeared (GDBusConnection *connection,
-                        const gchar *name,
-                        const gchar *name_owner,
+                        const char *name,
+                        const char *name_owner,
                         gpointer user_data)
 {
 	check_bluez_and_try_setup (NM_BLUEZ_MANAGER (user_data));
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 5d3bd23a..e984212b 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -224,7 +224,7 @@ network_server_unregister_bridge (const NMBtVTableNetworkServer *vtable,
 }
 
 static void
-network_server_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
+network_server_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
 {
 	NetworkServer *network_server;
 
@@ -241,7 +241,7 @@ network_server_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *s
 }
 
 static void
-network_server_added (GDBusProxy *proxy, const gchar *path, const char *addr, NMBluez5Manager *self)
+network_server_added (GDBusProxy *proxy, const char *path, const char *addr, NMBluez5Manager *self)
 {
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 	NetworkServer *network_server;
@@ -342,7 +342,7 @@ device_initialized (NMBluezDevice *device, gboolean success, NMBluez5Manager *se
 }
 
 static void
-device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
+device_added (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
 {
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 	NMBluezDevice *device;
@@ -356,7 +356,7 @@ device_added (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
 }
 
 static void
-device_removed (GDBusProxy *proxy, const gchar *path, NMBluez5Manager *self)
+device_removed (GDBusProxy *proxy, const char *path, NMBluez5Manager *self)
 {
 	NMBluez5ManagerPrivate *priv = NM_BLUEZ5_MANAGER_GET_PRIVATE (self);
 	NMBluezDevice *device;
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 92ad0b31..1f650d33 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -36,6 +36,7 @@
 #include "nm-setting-serial.h"
 #include "nm-setting-ppp.h"
 #include "NetworkManagerUtils.h"
+#include "settings/nm-settings-connection.h"
 #include "nm-utils.h"
 #include "nm-bt-error.h"
 #include "platform/nm-platform.h"
@@ -137,7 +138,7 @@ get_generic_capabilities (NMDevice *device)
 
 static gboolean
 can_auto_connect (NMDevice *device,
-                  NMConnection *connection,
+                  NMSettingsConnection *sett_conn,
                   char **specific_object)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
@@ -145,11 +146,11 @@ can_auto_connect (NMDevice *device,
 
 	nm_assert (!specific_object || !*specific_object);
 
-	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->can_auto_connect (device, connection, NULL))
+	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->can_auto_connect (device, sett_conn, NULL))
 		return FALSE;
 
 	/* Can't auto-activate a DUN connection without ModemManager */
-	bt_type = get_connection_bt_type (connection);
+	bt_type = get_connection_bt_type (nm_settings_connection_get_connection (sett_conn));
 	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
 		return FALSE;
 
@@ -157,36 +158,36 @@ can_auto_connect (NMDevice *device,
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
-	NMSettingConnection *s_con;
 	NMSettingBluetooth *s_bt;
 	const char *bdaddr;
 	guint32 bt_type;
 
-	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->check_connection_compatible (device, connection))
+	if (!NM_DEVICE_CLASS (nm_device_bt_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-
-	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME))
+	bt_type = get_connection_bt_type (connection);
+	if (!NM_FLAGS_ALL (priv->capabilities, bt_type)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "device does not support bluetooth type of profile");
 		return FALSE;
+	}
 
 	s_bt = nm_connection_get_setting_bluetooth (connection);
-	if (!s_bt)
-		return FALSE;
-
-	bt_type = get_connection_bt_type (connection);
-	if (!(bt_type & priv->capabilities))
-		return FALSE;
 
 	bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
-	if (!bdaddr)
+	if (!bdaddr) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "profile lacks bdaddr setting");
 		return FALSE;
-	if (!nm_utils_hwaddr_matches (priv->bdaddr, -1, bdaddr, -1))
+	}
+	if (!nm_utils_hwaddr_matches (priv->bdaddr, -1, bdaddr, -1)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "devices bdaddr setting mismatches");
 		return FALSE;
+	}
 
 	return TRUE;
 }
@@ -195,18 +196,25 @@ static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
+                            const char *specific_object,
+                            GError **error)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
 	guint32 bt_type;
 
 	bt_type = get_connection_bt_type (connection);
-	if (!(bt_type & priv->capabilities))
+	if (!(bt_type & priv->capabilities)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "device does not support bluetooth type");
 		return FALSE;
+	}
 
 	/* DUN connections aren't available without ModemManager */
-	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE)
+	if (bt_type == NM_BT_CAPABILITY_DUN && priv->mm_running == FALSE) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "ModemManager missing for DUN profile");
 		return FALSE;
+	}
 
 	return TRUE;
 }
@@ -1177,6 +1185,8 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bluetooth);
 
+	device_class->connection_type_check_compatible = NM_SETTING_BLUETOOTH_SETTING_NAME;
+
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->can_auto_connect = can_auto_connect;
 	device_class->deactivate = deactivate;
diff --git a/src/devices/nm-device-6lowpan.c b/src/devices/nm-device-6lowpan.c
new file mode 100644
index 00000000..600d1b80
--- /dev/null
+++ b/src/devices/nm-device-6lowpan.c
@@ -0,0 +1,345 @@
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-6lowpan.h"
+
+#include "nm-device-private.h"
+#include "settings/nm-settings.h"
+#include "platform/nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-setting-6lowpan.h"
+#include "nm-utils.h"
+
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDevice6Lowpan);
+
+/*****************************************************************************/
+
+typedef struct {
+	gulong parent_state_id;
+} NMDevice6LowpanPrivate;
+
+struct _NMDevice6Lowpan {
+	NMDevice parent;
+	NMDevice6LowpanPrivate _priv;
+};
+
+struct _NMDevice6LowpanClass {
+	NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDevice6Lowpan, nm_device_6lowpan, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_6LOWPAN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDevice6Lowpan, NM_IS_DEVICE_6LOWPAN)
+
+/*****************************************************************************/
+
+static void
+parent_state_changed (NMDevice *parent,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason,
+                      gpointer user_data)
+{
+	NMDevice6Lowpan *self = NM_DEVICE_6LOWPAN (user_data);
+
+	nm_device_set_unmanaged_by_flags (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent, FALSE), reason);
+}
+
+static void
+parent_changed_notify (NMDevice *device,
+                       int old_ifindex,
+                       NMDevice *old_parent,
+                       int new_ifindex,
+                       NMDevice *new_parent)
+{
+	NMDevice6Lowpan *self = NM_DEVICE_6LOWPAN (device);
+	NMDevice6LowpanPrivate *priv = NM_DEVICE_6LOWPAN_GET_PRIVATE (self);
+
+	NM_DEVICE_CLASS (nm_device_6lowpan_parent_class)->parent_changed_notify (device, old_ifindex, old_parent, new_ifindex, new_parent);
+
+	/*  note that @self doesn't have to clear @parent_state_id on dispose,
+	 *  because NMDevice's dispose() will unset the parent, which in turn calls
+	 *  parent_changed_notify(). */
+	nm_clear_g_signal_handler (old_parent, &priv->parent_state_id);
+
+	if (new_parent) {
+		priv->parent_state_id = g_signal_connect (new_parent,
+		                                          NM_DEVICE_STATE_CHANGED,
+		                                          G_CALLBACK (parent_state_changed),
+		                                          device);
+
+		/* Set parent-dependent unmanaged flag */
+		nm_device_set_unmanaged_by_flags (device,
+		                                  NM_UNMANAGED_PARENT,
+		                                  !nm_device_get_managed (new_parent, FALSE),
+		                                  NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED);
+	}
+
+	if (new_ifindex > 0) {
+		/* Recheck availability now that the parent has changed */
+		nm_device_queue_recheck_available (device,
+		                                   NM_DEVICE_STATE_REASON_PARENT_CHANGED,
+		                                   NM_DEVICE_STATE_REASON_PARENT_CHANGED);
+	}
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	const char *iface = nm_device_get_iface (device);
+	NMPlatformError plerr;
+	NMSetting6Lowpan *s_6lowpan;
+	int parent_ifindex;
+
+	s_6lowpan = nm_connection_get_setting_6lowpan (connection);
+	g_return_val_if_fail (s_6lowpan, FALSE);
+
+	parent_ifindex = parent ? nm_device_get_ifindex (parent) : 0;
+
+	if (parent_ifindex <= 0) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES,
+		             "6LoWPAN devices can not be created without a parent interface");
+		g_return_val_if_fail (!parent, FALSE);
+		return FALSE;
+	}
+
+	plerr = nm_platform_link_6lowpan_add (nm_device_get_platform (device), iface, parent_ifindex, out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create 6lowpan interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string_a (plerr));
+		return FALSE;
+	}
+
+	nm_device_parent_set_ifindex (device, parent_ifindex);
+
+	return TRUE;
+}
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *dev)
+{
+	return NM_DEVICE_CAP_CARRIER_DETECT | NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+static void
+link_changed (NMDevice *device,
+	      const NMPlatformLink *pllink)
+{
+	NMDevice6Lowpan *self = NM_DEVICE_6LOWPAN (device);
+	int parent = 0;
+	int ifindex;
+
+	NM_DEVICE_CLASS (nm_device_6lowpan_parent_class)->link_changed (device, pllink);
+
+	ifindex = nm_device_get_ifindex (device);
+	if (!nm_platform_link_6lowpan_get_properties (nm_device_get_platform (device), ifindex, &parent)) {
+		_LOGW (LOGD_DEVICE, "could not get 6lowpan properties");
+		return;
+	}
+
+	nm_device_parent_set_ifindex (device, parent);
+}
+
+static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+	if (!nm_device_parent_get_device (device))
+		return FALSE;
+	return NM_DEVICE_CLASS (nm_device_6lowpan_parent_class)->is_available (device, flags);
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     NMConnection *const*existing_connections,
+                     GError **error)
+{
+	NMSetting6Lowpan *s_6lowpan;
+
+	nm_utils_complete_generic (nm_device_get_platform (device),
+	                           connection,
+	                           NM_SETTING_6LOWPAN_SETTING_NAME,
+	                           existing_connections,
+	                           NULL,
+	                           _("6LOWPAN connection"),
+	                           NULL,
+	                           TRUE);
+
+	s_6lowpan = nm_connection_get_setting_6lowpan (connection);
+	if (!s_6lowpan) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "A '6lowpan' setting is required.");
+		return FALSE;
+	}
+
+	/* If there's no 6LoWPAN interface, no parent, and no hardware address in the
+	 * settings, then there's not enough information to complete the setting.
+	 */
+	if (   !nm_setting_6lowpan_get_parent (s_6lowpan)
+	    && !nm_device_match_parent_hwaddr (device, connection, TRUE)) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "The '6lowpan' setting had no interface name, parent, or hardware address.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+update_connection (NMDevice *device, NMConnection *connection)
+{
+	NMSetting6Lowpan *s_6lowpan = nm_connection_get_setting_6lowpan (connection);
+
+	if (!s_6lowpan) {
+		s_6lowpan = (NMSetting6Lowpan *) nm_setting_6lowpan_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_6lowpan);
+	}
+
+	g_object_set (s_6lowpan,
+	              NM_SETTING_6LOWPAN_PARENT,
+	              nm_device_parent_find_for_connection (device,
+	                                                    nm_setting_6lowpan_get_parent (s_6lowpan)),
+	              NULL);
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
+{
+	NMActStageReturn ret;
+
+	ret = NM_DEVICE_CLASS (nm_device_6lowpan_parent_class)->act_stage1_prepare (dev, out_failure_reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	if (!nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_6lowpan_init (NMDevice6Lowpan *self)
+{
+}
+
+static const NMDBusInterfaceInfoExtended interface_info_device_6lowpan = {
+	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
+		NM_DBUS_INTERFACE_DEVICE_6LOWPAN,
+		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Parent", "o", NM_DEVICE_PARENT),
+		),
+	),
+};
+
+static void
+nm_device_6lowpan_class_init (NMDevice6LowpanClass *klass)
+{
+	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_6lowpan);
+
+	device_class->connection_type_supported = NM_SETTING_6LOWPAN_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_6LOWPAN_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_6LOWPAN);
+
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->complete_connection = complete_connection;
+	device_class->create_and_realize = create_and_realize;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+	device_class->link_changed = link_changed;
+	device_class->is_available = is_available;
+	device_class->parent_changed_notify = parent_changed_notify;
+	device_class->update_connection = update_connection;
+}
+
+/*****************************************************************************/
+
+#define NM_TYPE_6LOWPAN_DEVICE_FACTORY (nm_6lowpan_device_factory_get_type ())
+#define NM_6LOWPAN_DEVICE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_6LOWPAN_DEVICE_FACTORY, NM6LowpanDeviceFactory))
+
+static NMDevice *
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
+{
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_6LOWPAN,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_TYPE_DESC, "6LoWPAN",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_6LOWPAN,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_6LOWPAN,
+	                                  NULL);
+}
+
+static const char *
+get_connection_parent (NMDeviceFactory *factory, NMConnection *connection)
+{
+	NMSetting6Lowpan *s_6lowpan;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_6LOWPAN_SETTING_NAME), NULL);
+
+	s_6lowpan = nm_connection_get_setting_6lowpan (connection);
+	g_assert (s_6lowpan);
+
+	return nm_setting_6lowpan_get_parent (s_6lowpan);
+}
+
+static char *
+get_connection_iface (NMDeviceFactory *factory,
+                      NMConnection *connection,
+                      const char *parent_iface)
+{
+	NMSetting6Lowpan *s_6lowpan;
+	const char *ifname;
+
+	g_return_val_if_fail (nm_connection_is_type (connection, NM_SETTING_6LOWPAN_SETTING_NAME), NULL);
+
+	s_6lowpan = nm_connection_get_setting_6lowpan (connection);
+	g_assert (s_6lowpan);
+
+	if (!parent_iface)
+		return NULL;
+
+	ifname = nm_connection_get_interface_name (connection);
+	return g_strdup (ifname);
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (6LOWPAN, 6Lowpan, 6lowpan,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_6LOWPAN)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_6LOWPAN_SETTING_NAME),
+	factory_class->create_device = create_device;
+	factory_class->get_connection_parent = get_connection_parent;
+	factory_class->get_connection_iface = get_connection_iface;
+);
diff --git a/src/devices/nm-device-6lowpan.h b/src/devices/nm-device-6lowpan.h
new file mode 100644
index 00000000..86edf4af
--- /dev/null
+++ b/src/devices/nm-device-6lowpan.h
@@ -0,0 +1,37 @@
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_6LOWPAN_H__
+#define __NETWORKMANAGER_DEVICE_6LOWPAN_H__
+
+#include "nm-device.h"
+
+#define NM_TYPE_DEVICE_6LOWPAN            (nm_device_6lowpan_get_type ())
+#define NM_DEVICE_6LOWPAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_6LOWPAN, NMDevice6Lowpan))
+#define NM_DEVICE_6LOWPAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_6LOWPAN, NMDevice6LowpanClass))
+#define NM_IS_DEVICE_6LOWPAN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_6LOWPAN))
+#define NM_IS_DEVICE_6LOWPAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_6LOWPAN))
+#define NM_DEVICE_6LOWPAN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_6LOWPAN, NMDevice6LowpanClass))
+
+typedef struct _NMDevice6Lowpan NMDevice6Lowpan;
+typedef struct _NMDevice6LowpanClass NMDevice6LowpanClass;
+
+GType nm_device_6lowpan_get_type (void);
+
+#endif /* __NETWORKMANAGER_DEVICE_6LOWPAN_H__ */
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index f59ec9ff..6e7e6ffc 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2011 - 2016 Red Hat, Inc.
+ * Copyright 2011 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -56,23 +56,6 @@ get_generic_capabilities (NMDevice *dev)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingBond *s_bond;
-
-	if (!NM_DEVICE_CLASS (nm_device_bond_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_bond = nm_connection_get_setting_bond (connection);
-	if (!s_bond || !nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME))
-		return FALSE;
-
-	/* FIXME: match bond properties like mode, etc? */
-
-	return TRUE;
-}
-
-static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
@@ -363,7 +346,6 @@ static NMActStageReturn
 act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
-	gboolean no_firmware = FALSE;
 
 	ret = NM_DEVICE_CLASS (nm_device_bond_parent_class)->act_stage1_prepare (dev, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
@@ -374,7 +356,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
 	ret = apply_bonding_config (dev);
 	if (ret != NM_ACT_STAGE_RETURN_FAILURE)
 		ret = nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE);
-	nm_device_bring_up (dev, TRUE, &no_firmware);
+	nm_device_bring_up (dev, TRUE, NULL);
 
 	return ret;
 }
@@ -386,7 +368,7 @@ enslave_slave (NMDevice *device,
                gboolean configure)
 {
 	NMDeviceBond *self = NM_DEVICE_BOND (device);
-	gboolean success = TRUE, no_firmware = FALSE;
+	gboolean success = TRUE;
 	const char *slave_iface = nm_device_get_ip_iface (slave);
 	NMConnection *master_con;
 
@@ -397,7 +379,7 @@ enslave_slave (NMDevice *device,
 		success = nm_platform_link_enslave (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
-		nm_device_bring_up (slave, TRUE, &no_firmware);
+		nm_device_bring_up (slave, TRUE, NULL);
 
 		if (!success)
 			return FALSE;
@@ -434,7 +416,7 @@ release_slave (NMDevice *device,
                gboolean configure)
 {
 	NMDeviceBond *self = NM_DEVICE_BOND (device);
-	gboolean success, no_firmware = FALSE;
+	gboolean success;
 	gs_free char *address = NULL;
 
 	if (configure) {
@@ -463,7 +445,7 @@ release_slave (NMDevice *device,
 		 * IFF_UP), so we must bring it back up here to ensure carrier changes and
 		 * other state is noticed by the now-released slave.
 		 */
-		if (!nm_device_bring_up (slave, TRUE, &no_firmware))
+		if (!nm_device_bring_up (slave, TRUE, NULL))
 			_LOGW (LOGD_BOND, "released bond slave could not be brought up.");
 	} else {
 		_LOGI (LOGD_BOND, "bond slave %s was released",
@@ -632,27 +614,28 @@ static void
 nm_device_bond_class_init (NMDeviceBondClass *klass)
 {
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BOND_SETTING_NAME, NM_LINK_TYPE_BOND)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bond);
 
-	parent_class->is_master = TRUE;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->complete_connection = complete_connection;
-
-	parent_class->update_connection = update_connection;
-	parent_class->master_update_slave_connection = master_update_slave_connection;
-
-	parent_class->create_and_realize = create_and_realize;
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
-	parent_class->enslave_slave = enslave_slave;
-	parent_class->release_slave = release_slave;
-	parent_class->can_reapply_change = can_reapply_change;
-	parent_class->reapply_connection = reapply_connection;
+	device_class->connection_type_supported = NM_SETTING_BOND_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_BOND_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_BOND);
+
+	device_class->is_master = TRUE;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->complete_connection = complete_connection;
+
+	device_class->update_connection = update_connection;
+	device_class->master_update_slave_connection = master_update_slave_connection;
+
+	device_class->create_and_realize = create_and_realize;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+	device_class->enslave_slave = enslave_slave;
+	device_class->release_slave = release_slave;
+	device_class->can_reapply_change = can_reapply_change;
+	device_class->reapply_connection = reapply_connection;
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index c81a0253..e79de95c 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -61,41 +61,63 @@ static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
+                            const char *specific_object,
+                            GError **error)
 {
 	NMSettingBluetooth *s_bt;
 
-	if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_available (device, connection, flags, specific_object))
+	if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_available (device, connection, flags, specific_object, error))
 		return FALSE;
 
 	s_bt = _nm_connection_get_setting_bluetooth_for_nap (connection);
 	if (s_bt) {
-		return    nm_bt_vtable_network_server
-		       && nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server,
-		                                                     nm_setting_bluetooth_get_bdaddr (s_bt));
+		const char *bdaddr;
+
+		if (!nm_bt_vtable_network_server) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "bluetooth plugin not available to activate NAP profile");
+			return FALSE;
+		}
+
+		bdaddr = nm_setting_bluetooth_get_bdaddr (s_bt);
+		if (!nm_bt_vtable_network_server->is_available (nm_bt_vtable_network_server, bdaddr)) {
+			if (bdaddr)
+				nm_utils_error_set (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                    "not suitable NAP device \"%s\" available", bdaddr);
+			else
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "not suitable NAP device available");
+			return FALSE;
+		}
 	}
 
 	return TRUE;
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMSettingBridge *s_bridge;
 	const char *mac_address;
 
-	if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_compatible (device, connection))
+	if (!NM_DEVICE_CLASS (nm_device_bridge_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
-	s_bridge = nm_connection_get_setting_bridge (connection);
-	if (!s_bridge)
-		return FALSE;
+	if (   nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME)
+	    && _nm_connection_get_setting_bluetooth_for_nap (connection)) {
+		s_bridge = nm_connection_get_setting_bridge (connection);
+		if (!s_bridge) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "missing bridge setting for bluetooth NAP profile");
+			return FALSE;
+		}
 
-	if (!nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) {
-		if (   nm_connection_is_type (connection, NM_SETTING_BLUETOOTH_SETTING_NAME)
-		    && _nm_connection_get_setting_bluetooth_for_nap (connection)) {
-			/* a bluetooth NAP connection is handled by the bridge */
-		} else
+		/* a bluetooth NAP connection is handled by the bridge.
+		 *
+		 * Proceed... */
+	} else {
+		s_bridge = _nm_connection_check_main_setting (connection, NM_SETTING_BRIDGE_SETTING_NAME, error);
+		if (!s_bridge)
 			return FALSE;
 	}
 
@@ -104,8 +126,11 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		const char *hw_addr;
 
 		hw_addr = nm_device_get_hw_address (device);
-		if (!hw_addr || !nm_utils_hwaddr_matches (hw_addr, -1, mac_address, -1))
+		if (!hw_addr || !nm_utils_hwaddr_matches (hw_addr, -1, mac_address, -1)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "mac address mismatches");
 			return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -505,28 +530,29 @@ static void
 nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
 {
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_BRIDGE_SETTING_NAME, NM_LINK_TYPE_BRIDGE)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_bridge);
 
-	parent_class->is_master = TRUE;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->check_connection_available = check_connection_available;
-	parent_class->complete_connection = complete_connection;
-
-	parent_class->update_connection = update_connection;
-	parent_class->master_update_slave_connection = master_update_slave_connection;
-
-	parent_class->create_and_realize = create_and_realize;
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->deactivate = deactivate;
-	parent_class->enslave_slave = enslave_slave;
-	parent_class->release_slave = release_slave;
-	parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+	device_class->connection_type_supported = NM_SETTING_BRIDGE_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_BRIDGE);
+
+	device_class->is_master = TRUE;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->check_connection_available = check_connection_available;
+	device_class->complete_connection = complete_connection;
+
+	device_class->update_connection = update_connection;
+	device_class->master_update_slave_connection = master_update_slave_connection;
+
+	device_class->create_and_realize = create_and_realize;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->deactivate = deactivate;
+	device_class->enslave_slave = enslave_slave;
+	device_class->release_slave = release_slave;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c
index 9be4d29f..a9059383 100644
--- a/src/devices/nm-device-dummy.c
+++ b/src/devices/nm-device-dummy.c
@@ -117,21 +117,6 @@ create_and_realize (NMDevice *device,
 	return TRUE;
 }
 
-static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingDummy *s_dummy;
-
-	if (!NM_DEVICE_CLASS (nm_device_dummy_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_dummy = nm_connection_get_setting_dummy (connection);
-	if (!s_dummy)
-		return FALSE;
-
-	return TRUE;
-}
-
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
@@ -173,13 +158,13 @@ nm_device_dummy_class_init (NMDeviceDummyClass *klass)
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_DUMMY)
-
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_dummy);
 
-	device_class->connection_type = NM_SETTING_DUMMY_SETTING_NAME;
+	device_class->connection_type_supported = NM_SETTING_DUMMY_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_DUMMY_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_DUMMY);
+
 	device_class->complete_connection = complete_connection;
-	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->update_connection = update_connection;
diff --git a/src/devices/nm-device-ethernet-utils.c b/src/devices/nm-device-ethernet-utils.c
index 298e6dff..8bdb1991 100644
--- a/src/devices/nm-device-ethernet-utils.c
+++ b/src/devices/nm-device-ethernet-utils.c
@@ -18,33 +18,24 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
-#include "nm-connection.h"
-
 #include "nm-device-ethernet-utils.h"
 
+#include "settings/nm-settings-connection.h"
+
 char *
-nm_device_ethernet_utils_get_default_wired_name (NMConnection *const *connections)
+nm_device_ethernet_utils_get_default_wired_name (GHashTable *existing_ids)
 {
 	char *temp;
-	guint j;
 	int i;
 
 	/* Find the next available unique connection name */
-	for (i = 1; i <= 10000; i++) {
+	for (i = 1; i <= G_MAXINT; i++) {
 		temp = g_strdup_printf (_("Wired connection %d"), i);
-		for (j = 0; connections[j]; j++) {
-			if (nm_streq0 (nm_connection_get_id (connections[j]), temp)) {
-				g_free (temp);
-				goto next;
-			}
-		}
-		return temp;
-next:
-		;
+		if (   !existing_ids
+		    || !g_hash_table_contains (existing_ids, temp))
+			return temp;
+		g_free (temp);
 	}
-
 	return NULL;
 }
 
diff --git a/src/devices/nm-device-ethernet-utils.h b/src/devices/nm-device-ethernet-utils.h
index 197d0a9e..7e5c8b31 100644
--- a/src/devices/nm-device-ethernet-utils.h
+++ b/src/devices/nm-device-ethernet-utils.h
@@ -19,6 +19,6 @@
 #ifndef __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
 #define __NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H__
 
-char *nm_device_ethernet_utils_get_default_wired_name (NMConnection *const *connections);
+char *nm_device_ethernet_utils_get_default_wired_name (GHashTable *existing_ids);
 
 #endif  /* NETWORKMANAGER_DEVICE_ETHERNET_UTILS_H */
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index a9a9f968..0fb8fea4 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -343,23 +343,21 @@ match_subchans (NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_m
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
 	NMSettingWired *s_wired;
 
-	if (!NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->check_connection_compatible (device, connection))
+	if (!NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
-	s_wired = nm_connection_get_setting_wired (connection);
-
 	if (nm_connection_is_type (connection, NM_SETTING_PPPOE_SETTING_NAME)) {
-		/* NOP */
-	} else if (nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME)) {
+		s_wired = nm_connection_get_setting_wired (connection);
+	} else {
+		s_wired = _nm_connection_check_main_setting (connection, NM_SETTING_WIRED_SETTING_NAME, error);
 		if (!s_wired)
 			return FALSE;
-	} else
-		return FALSE;
+	}
 
 	if (s_wired) {
 		const char *mac, *perm_hw_addr;
@@ -367,28 +365,43 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		const char * const *mac_blacklist;
 		int i;
 
-		if (!match_subchans (self, s_wired, &try_mac))
+		if (!match_subchans (self, s_wired, &try_mac)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "s390 subchannels don't match");
 			return FALSE;
+		}
 
 		perm_hw_addr = nm_device_get_permanent_hw_address (device);
 		mac = nm_setting_wired_get_mac_address (s_wired);
 		if (perm_hw_addr) {
-			if (try_mac && mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
+			if (   try_mac
+			    && mac
+			    && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "permanent MAC address doesn't match");
 				return FALSE;
+			}
 
 			/* Check for MAC address blacklist */
 			mac_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
 			for (i = 0; mac_blacklist[i]; i++) {
 				if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
-					g_warn_if_reached ();
+					nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+					                            "invalid MAC in blacklist");
 					return FALSE;
 				}
 
-				if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
+				if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1)) {
+					nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+					                            "permanent MAC address of device blacklisted");
 					return FALSE;
+				}
 			}
-		} else if (mac)
+		} else if (mac) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "device has no permanent MAC address to match");
 			return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -1185,7 +1198,6 @@ wake_on_lan_enable (NMDevice *device)
 	NMSettingWiredWakeOnLan wol;
 	NMSettingWired *s_wired;
 	const char *password = NULL;
-	gs_free char *value = NULL;
 
 	s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED);
 	if (s_wired) {
@@ -1195,27 +1207,25 @@ wake_on_lan_enable (NMDevice *device)
 			goto found;
 	}
 
-	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               "ethernet.wake-on-lan",
-	                                               device);
-
-	if (value) {
-		wol = _nm_utils_ascii_str_to_int64 (value, 10,
-		                                    NM_SETTING_WIRED_WAKE_ON_LAN_NONE,
-		                                    G_MAXINT32,
-		                                    NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT);
-
-		if (   NM_FLAGS_ANY (wol, NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS)
-		    && !nm_utils_is_power_of_two (wol)) {
-			nm_log_dbg (LOGD_ETHER, "invalid default value %u for wake-on-lan", (guint) wol);
-			wol = NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT;
-		}
-		if (wol != NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)
-			goto found;
+	wol = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                   "ethernet.wake-on-lan",
+	                                                   device,
+	                                                   NM_SETTING_WIRED_WAKE_ON_LAN_NONE,
+	                                                   G_MAXINT32,
+	                                                   NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT);
+
+	if (   NM_FLAGS_ANY (wol, NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS)
+	    && !nm_utils_is_power_of_two (wol)) {
+		nm_log_dbg (LOGD_ETHER, "invalid default value %u for wake-on-lan", (guint) wol);
+		wol = NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT;
 	}
+	if (wol != NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)
+		goto found;
 	wol = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
 found:
-	return nm_platform_ethtool_set_wake_on_lan (nm_device_get_platform (device), nm_device_get_ifindex (device), wol, password);
+	return nm_platform_ethtool_set_wake_on_lan (nm_device_get_platform (device),
+	                                            nm_device_get_ifindex (device),
+	                                            wol, password);
 }
 
 /*****************************************************************************/
@@ -1430,12 +1440,14 @@ new_default_connection (NMDevice *self)
 	NMConnection *connection;
 	NMSettingsConnection *const*connections;
 	NMSetting *setting;
+	gs_unref_hashtable GHashTable *existing_ids = NULL;
 	struct udev_device *dev;
 	const char *perm_hw_addr;
 	const char *uprop = "0";
 	gs_free char *defname = NULL;
 	gs_free char *uuid = NULL;
 	gs_free char *machine_id = NULL;
+	guint i, n_connections;
 
 	if (nm_config_get_no_auto_default_for_device (nm_config_get (), self))
 		return NULL;
@@ -1448,8 +1460,13 @@ new_default_connection (NMDevice *self)
 	setting = nm_setting_connection_new ();
 	nm_connection_add_setting (connection, setting);
 
-	connections = nm_settings_get_connections (nm_device_get_settings (self), NULL);
-	defname = nm_device_ethernet_utils_get_default_wired_name ((NMConnection *const*) connections);
+	connections = nm_settings_get_connections (nm_device_get_settings (self), &n_connections);
+	if (n_connections > 0) {
+		existing_ids = g_hash_table_new (nm_str_hash, g_str_equal);
+		for (i = 0; i < n_connections; i++)
+			g_hash_table_add (existing_ids, (char *) nm_settings_connection_get_id (connections[i]));
+	}
+	defname = nm_device_ethernet_utils_get_default_wired_name (existing_ids);
 	if (!defname)
 		return NULL;
 
@@ -1750,12 +1767,10 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	g_type_class_add_private (object_class, sizeof (NMDeviceEthernetPrivate));
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRED_SETTING_NAME, NM_LINK_TYPE_ETHERNET)
-
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 	object_class->get_property = get_property;
@@ -1763,25 +1778,28 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wired);
 
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->complete_connection = complete_connection;
-	parent_class->new_default_connection = new_default_connection;
-
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
-	parent_class->get_configured_mtu = get_configured_mtu;
-	parent_class->deactivate = deactivate;
-	parent_class->get_s390_subchannels = get_s390_subchannels;
-	parent_class->update_connection = update_connection;
-	parent_class->carrier_changed_notify = carrier_changed_notify;
-	parent_class->link_changed = link_changed;
-	parent_class->is_available = is_available;
-	parent_class->can_reapply_change = can_reapply_change;
-	parent_class->reapply_connection = reapply_connection;
-
-	parent_class->state_changed = device_state_changed;
+	device_class->connection_type_supported = NM_SETTING_WIRED_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_ETHERNET);
+
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->complete_connection = complete_connection;
+	device_class->new_default_connection = new_default_connection;
+
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	device_class->get_configured_mtu = get_configured_mtu;
+	device_class->deactivate = deactivate;
+	device_class->get_s390_subchannels = get_s390_subchannels;
+	device_class->update_connection = update_connection;
+	device_class->carrier_changed_notify = carrier_changed_notify;
+	device_class->link_changed = link_changed;
+	device_class->is_available = is_available;
+	device_class->can_reapply_change = can_reapply_change;
+	device_class->reapply_connection = reapply_connection;
+
+	device_class->state_changed = device_state_changed;
 
 	obj_properties[PROP_SPEED] =
 	    g_param_spec_uint (NM_DEVICE_ETHERNET_SPEED, "", "",
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index bc717bb3..a11ead6e 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (C) 2014 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -417,6 +417,7 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call
 		                        callback, user_data); \
 	} G_STMT_END
 
+	_ADD_INTERNAL (nm_6lowpan_device_factory_get_type);
 	_ADD_INTERNAL (nm_bond_device_factory_get_type);
 	_ADD_INTERNAL (nm_bridge_device_factory_get_type);
 	_ADD_INTERNAL (nm_dummy_device_factory_get_type);
@@ -430,6 +431,8 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call
 	_ADD_INTERNAL (nm_veth_device_factory_get_type);
 	_ADD_INTERNAL (nm_vlan_device_factory_get_type);
 	_ADD_INTERNAL (nm_vxlan_device_factory_get_type);
+	_ADD_INTERNAL (nm_wireguard_device_factory_get_type);
+	_ADD_INTERNAL (nm_wpan_device_factory_get_type);
 
 	load_factories_from_dir (NMPLUGINDIR, callback, user_data);
 }
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index 27eaf8d6..d00aa93b 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -86,19 +86,19 @@ realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMSettingConnection *s_con;
 
-	if (!NM_DEVICE_CLASS (nm_device_generic_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	if (!nm_connection_is_type (connection, NM_SETTING_GENERIC_SETTING_NAME))
+	if (!NM_DEVICE_CLASS (nm_device_generic_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	s_con = nm_connection_get_setting_connection (connection);
-	if (!nm_setting_connection_get_interface_name (s_con))
+	if (!nm_setting_connection_get_interface_name (s_con)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "generic profiles need an interface name");
 		return FALSE;
+	}
 
 	return TRUE;
 }
@@ -220,9 +220,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_GENERIC_SETTING_NAME, NM_LINK_TYPE_ANY)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructor = constructor;
 	object_class->dispose = dispose;
@@ -231,11 +229,15 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_generic);
 
-	parent_class->realize_start_notify = realize_start_notify;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->get_type_description = get_type_description;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->update_connection = update_connection;
+	device_class->connection_type_supported = NM_SETTING_GENERIC_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_GENERIC_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_ANY);
+
+	device_class->realize_start_notify = realize_start_notify;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->get_type_description = get_type_description;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->update_connection = update_connection;
 
 	obj_properties[PROP_TYPE_DESCRIPTION] =
 	     g_param_spec_string (NM_DEVICE_GENERIC_TYPE_DESCRIPTION, "", "",
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 99bcef00..41fac157 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright 2011 Red Hat, Inc.
+ * Copyright 2011 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -80,7 +80,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	NMSettingInfiniband *s_infiniband;
 	char ifname_verified[IFNAMSIZ];
 	const char *transport_mode;
-	gboolean ok, no_firmware = FALSE;
+	gboolean ok;
 
 	ret = NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
@@ -104,7 +104,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	/* With some drivers the interface must be down to set transport mode */
 	nm_device_take_down (device, TRUE);
 	ok = nm_platform_sysctl_set (nm_device_get_platform (device), NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode"), transport_mode);
-	nm_device_bring_up (device, TRUE, &no_firmware);
+	nm_device_bring_up (device, TRUE, NULL);
 
 	if (!ok) {
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
@@ -123,30 +123,28 @@ get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMSettingInfiniband *s_infiniband;
 
-	if (!NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	if (!nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME))
-		return FALSE;
-
-	s_infiniband = nm_connection_get_setting_infiniband (connection);
-	if (!s_infiniband)
+	if (!NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	if (nm_device_is_real (device)) {
 		const char *mac;
 		const char *hw_addr;
 
+		s_infiniband = nm_connection_get_setting_infiniband (connection);
+
 		mac = nm_setting_infiniband_get_mac_address (s_infiniband);
 		if (mac) {
 			hw_addr = nm_device_get_permanent_hw_address (device);
 			if (   !hw_addr
-			    || !nm_utils_hwaddr_matches (mac, -1, hw_addr, -1))
+			    || !nm_utils_hwaddr_matches (mac, -1, hw_addr, -1)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "MAC address mismatches");
 				return FALSE;
+			}
 		}
 	}
 
@@ -367,24 +365,26 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_INFINIBAND_SETTING_NAME, NM_LINK_TYPE_INFINIBAND)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_infiniband);
 
-	parent_class->create_and_realize = create_and_realize;
-	parent_class->unrealize = unrealize;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->complete_connection = complete_connection;
-	parent_class->update_connection = update_connection;
+	device_class->connection_type_supported = NM_SETTING_INFINIBAND_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_INFINIBAND_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_INFINIBAND);
+
+	device_class->create_and_realize = create_and_realize;
+	device_class->unrealize = unrealize;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->complete_connection = complete_connection;
+	device_class->update_connection = update_connection;
 
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->get_configured_mtu = get_configured_mtu;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->get_configured_mtu = get_configured_mtu;
 
 	obj_properties[PROP_IS_PARTITION] =
 	     g_param_spec_boolean (NM_DEVICE_INFINIBAND_IS_PARTITION, "", "",
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index 2e266bb9..568403c6 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -179,10 +179,13 @@ clear:
 		goto out;
 	}
 
-	if (priv->mode == NM_IP_TUNNEL_MODE_GRE) {
+	if (NM_IN_SET (priv->mode, NM_IP_TUNNEL_MODE_GRE, NM_IP_TUNNEL_MODE_GRETAP)) {
 		const NMPlatformLnkGre *lnk;
 
-		lnk = nm_platform_link_get_lnk_gre (nm_device_get_platform (device), ifindex, NULL);
+		if (priv->mode == NM_IP_TUNNEL_MODE_GRE)
+			lnk = nm_platform_link_get_lnk_gre (nm_device_get_platform (device), ifindex, NULL);
+		else
+			lnk = nm_platform_link_get_lnk_gretap (nm_device_get_platform (device), ifindex, NULL);
 		if (!lnk) {
 			_LOGW (LOGD_PLATFORM, "could not read %s properties", "gre");
 			goto clear;
@@ -254,11 +257,21 @@ clear:
 		ttl = lnk->ttl;
 		tos = lnk->tos;
 		pmtud = lnk->path_mtu_discovery;
-	} else if (   priv->mode == NM_IP_TUNNEL_MODE_IPIP6
-	           || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6) {
+	} else if (NM_IN_SET (priv->mode,
+	                      NM_IP_TUNNEL_MODE_IPIP6,
+	                      NM_IP_TUNNEL_MODE_IP6IP6,
+	                      NM_IP_TUNNEL_MODE_IP6GRE,
+	                      NM_IP_TUNNEL_MODE_IP6GRETAP)) {
 		const NMPlatformLnkIp6Tnl *lnk;
+		NMPlatform *plat = nm_device_get_platform (device);
+
+		if (priv->mode == NM_IP_TUNNEL_MODE_IP6GRE)
+			lnk = nm_platform_link_get_lnk_ip6gre (plat, ifindex, NULL);
+		else if (priv->mode == NM_IP_TUNNEL_MODE_IP6GRETAP)
+			lnk = nm_platform_link_get_lnk_ip6gretap (plat, ifindex, NULL);
+		else
+			lnk = nm_platform_link_get_lnk_ip6tnl (plat, ifindex, NULL);
 
-		lnk = nm_platform_link_get_lnk_ip6tnl (nm_device_get_platform (device), ifindex, NULL);
 		if (!lnk) {
 			_LOGW (LOGD_PLATFORM, "could not read %s properties", "ip6tnl");
 			goto clear;
@@ -272,6 +285,40 @@ clear:
 		encap_limit = lnk->encap_limit;
 		flow_label = lnk->flow_label;
 		flags = ip6tnl_flags_plat_to_setting (lnk->flags);
+
+		if (NM_IN_SET (priv->mode,
+		               NM_IP_TUNNEL_MODE_IP6GRE,
+		               NM_IP_TUNNEL_MODE_IP6GRETAP)) {
+			if (NM_FLAGS_HAS (lnk->input_flags, NM_GRE_KEY)) {
+				key = g_strdup_printf ("%u", lnk->input_key);
+				if (g_strcmp0 (priv->input_key, key)) {
+					g_free (priv->input_key);
+					priv->input_key = key;
+					_notify (self, PROP_INPUT_KEY);
+				} else
+					g_free (key);
+			} else {
+				if (priv->input_key) {
+					g_clear_pointer (&priv->input_key, g_free);
+					_notify (self, PROP_INPUT_KEY);
+				}
+			}
+
+			if (NM_FLAGS_HAS (lnk->output_flags, NM_GRE_KEY)) {
+				key = g_strdup_printf ("%u", lnk->output_key);
+				if (g_strcmp0 (priv->output_key, key)) {
+					g_free (priv->output_key);
+					priv->output_key = key;
+					_notify (self, PROP_OUTPUT_KEY);
+				} else
+					g_free (key);
+			} else {
+				if (priv->output_key) {
+					g_clear_pointer (&priv->output_key, g_free);
+					_notify (self, PROP_OUTPUT_KEY);
+				}
+			}
+		}
 	} else
 		g_return_if_reached ();
 
@@ -388,8 +435,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 	NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device);
 	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self);
 	NMSettingIPTunnel *s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
-	NMDevice *parent = NULL;
-	const char *setting_parent, *new_parent;
 
 	if (!s_ip_tunnel) {
 		s_ip_tunnel = (NMSettingIPTunnel *) nm_setting_ip_tunnel_new ();
@@ -399,25 +444,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 	if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode)
 		g_object_set (G_OBJECT (s_ip_tunnel), NM_SETTING_IP_TUNNEL_MODE, priv->mode, NULL);
 
-	parent = nm_device_parent_get_device (device);
-
-	/* Update parent in the connection; default to parent's interface name */
-	if (parent) {
-		new_parent = nm_device_get_iface (parent);
-		setting_parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel);
-		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
-			NMConnection *parent_connection;
-
-			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device),
-			                                                                         setting_parent);
-			if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection))
-				new_parent = NULL;
-		}
-		if (new_parent)
-			g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, new_parent, NULL);
-	} else
-		g_object_set (s_ip_tunnel, NM_SETTING_IP_TUNNEL_PARENT, NULL, NULL);
+	g_object_set (s_ip_tunnel,
+	              NM_SETTING_IP_TUNNEL_PARENT,
+	              nm_device_parent_find_for_connection (device,
+	                                                    nm_setting_ip_tunnel_get_parent (s_ip_tunnel)),
+	              NULL);
 
 	if (!address_equal_pp (priv->addr_family,
 	                       nm_setting_ip_tunnel_get_local (s_ip_tunnel),
@@ -456,7 +487,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 		                        NULL);
 	}
 
-	if (priv->mode == NM_IP_TUNNEL_MODE_GRE || priv->mode == NM_IP_TUNNEL_MODE_IP6GRE) {
+	if (NM_IN_SET (priv->mode,
+	               NM_IP_TUNNEL_MODE_GRE,
+	               NM_IP_TUNNEL_MODE_GRETAP,
+	               NM_IP_TUNNEL_MODE_IP6GRE,
+	               NM_IP_TUNNEL_MODE_IP6GRETAP)) {
 		if (g_strcmp0 (nm_setting_ip_tunnel_get_input_key (s_ip_tunnel), priv->input_key)) {
 			g_object_set (G_OBJECT (s_ip_tunnel),
 			              NM_SETTING_IP_TUNNEL_INPUT_KEY,
@@ -473,54 +508,79 @@ update_connection (NMDevice *device, NMConnection *connection)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device);
 	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self);
 	NMSettingIPTunnel *s_ip_tunnel;
 	const char *parent;
 
-	if (!NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->check_connection_compatible (device, connection))
+	if (!NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
-	if (!s_ip_tunnel)
-		return FALSE;
 
-	if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode)
+	if (nm_setting_ip_tunnel_get_mode (s_ip_tunnel) != priv->mode) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "incompatible IP tunnel mode");
 		return FALSE;
+	}
 
 	if (nm_device_is_real (device)) {
 		/* Check parent interface; could be an interface name or a UUID */
 		parent = nm_setting_ip_tunnel_get_parent (s_ip_tunnel);
-		if (parent && !nm_device_match_parent (device, parent))
+		if (parent && !nm_device_match_parent (device, parent)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "IP tunnel parent mismatches");
 			return FALSE;
+		}
 
 		if (!address_equal_pp (priv->addr_family,
 		                       nm_setting_ip_tunnel_get_local (s_ip_tunnel),
-		                       priv->local))
+		                       priv->local)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "local IP tunnel address mismatches");
 			return FALSE;
+		}
 
 		if (!address_equal_pp (priv->addr_family,
 		                       nm_setting_ip_tunnel_get_remote (s_ip_tunnel),
-		                       priv->remote))
+		                       priv->remote)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "remote IP tunnel address mismatches");
 			return FALSE;
+		}
 
-		if (nm_setting_ip_tunnel_get_ttl (s_ip_tunnel) != priv->ttl)
+		if (nm_setting_ip_tunnel_get_ttl (s_ip_tunnel) != priv->ttl) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "TTL of IP tunnel mismatches");
 			return FALSE;
+		}
 
-		if (nm_setting_ip_tunnel_get_tos (s_ip_tunnel) != priv->tos)
+		if (nm_setting_ip_tunnel_get_tos (s_ip_tunnel) != priv->tos) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "TOS of IP tunnel mismatches");
 			return FALSE;
+		}
 
 		if (priv->addr_family == AF_INET) {
-			if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery)
+			if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "MTU discovery setting of IP tunnel mismatches");
 				return FALSE;
+			}
 		} else {
-			if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit)
+			if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "encapsulation limit of IP tunnel mismatches");
 				return FALSE;
+			}
 
-			if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label)
+			if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "flow-label of IP tunnel mismatches");
 				return FALSE;
+			}
 		}
 	}
 
@@ -535,6 +595,8 @@ platform_link_to_tunnel_mode (const NMPlatformLink *link)
 	switch (link->type) {
 	case NM_LINK_TYPE_GRE:
 		return NM_IP_TUNNEL_MODE_GRE;
+	case NM_LINK_TYPE_GRETAP:
+		return NM_IP_TUNNEL_MODE_GRETAP;
 	case NM_LINK_TYPE_IP6TNL:
 		lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, link->ifindex, NULL);
 		if (lnk) {
@@ -544,6 +606,10 @@ platform_link_to_tunnel_mode (const NMPlatformLink *link)
 				return NM_IP_TUNNEL_MODE_IP6IP6;
 		}
 		return NM_IP_TUNNEL_MODE_UNKNOWN;
+	case NM_LINK_TYPE_IP6GRE:
+		return NM_IP_TUNNEL_MODE_IP6GRE;
+	case NM_LINK_TYPE_IP6GRETAP:
+		return NM_IP_TUNNEL_MODE_IP6GRETAP;
 	case NM_LINK_TYPE_IPIP:
 		return NM_IP_TUNNEL_MODE_IPIP;
 	case NM_LINK_TYPE_SIT:
@@ -559,15 +625,20 @@ tunnel_mode_to_link_type (NMIPTunnelMode tunnel_mode)
 	switch (tunnel_mode) {
 	case NM_IP_TUNNEL_MODE_GRE:
 		return NM_LINK_TYPE_GRE;
+	case NM_IP_TUNNEL_MODE_GRETAP:
+		return NM_LINK_TYPE_GRETAP;
 	case NM_IP_TUNNEL_MODE_IPIP6:
 	case NM_IP_TUNNEL_MODE_IP6IP6:
 		return NM_LINK_TYPE_IP6TNL;
+	case NM_IP_TUNNEL_MODE_IP6GRE:
+		return NM_LINK_TYPE_IP6GRE;
+	case NM_IP_TUNNEL_MODE_IP6GRETAP:
+		return NM_LINK_TYPE_IP6GRETAP;
 	case NM_IP_TUNNEL_MODE_IPIP:
 		return NM_LINK_TYPE_IPIP;
 	case NM_IP_TUNNEL_MODE_SIT:
 		return NM_LINK_TYPE_SIT;
 	case NM_IP_TUNNEL_MODE_VTI:
-	case NM_IP_TUNNEL_MODE_IP6GRE:
 	case NM_IP_TUNNEL_MODE_VTI6:
 	case NM_IP_TUNNEL_MODE_ISATAP:
 		return NM_LINK_TYPE_UNKNOWN;
@@ -595,11 +666,16 @@ create_and_realize (NMDevice *device,
 	NMPlatformLnkIp6Tnl lnk_ip6tnl = { };
 	const char *str;
 	gint64 val;
+	NMIPTunnelMode mode;
 
 	s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection);
 	g_assert (s_ip_tunnel);
 
-	switch (nm_setting_ip_tunnel_get_mode (s_ip_tunnel)) {
+	mode = nm_setting_ip_tunnel_get_mode (s_ip_tunnel);
+	switch (mode) {
+	case NM_IP_TUNNEL_MODE_GRETAP:
+		lnk_gre.is_tap = TRUE;
+		/* fall-through */
 	case NM_IP_TUNNEL_MODE_GRE:
 		if (parent)
 			lnk_gre.parent_ifindex = nm_device_get_ifindex (parent);
@@ -700,6 +776,8 @@ create_and_realize (NMDevice *device,
 		break;
 	case NM_IP_TUNNEL_MODE_IPIP6:
 	case NM_IP_TUNNEL_MODE_IP6IP6:
+	case NM_IP_TUNNEL_MODE_IP6GRE:
+	case NM_IP_TUNNEL_MODE_IP6GRETAP:
 		if (parent)
 			lnk_ip6tnl.parent_ifindex = nm_device_get_ifindex (parent);
 
@@ -715,13 +793,44 @@ create_and_realize (NMDevice *device,
 		lnk_ip6tnl.tclass = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
 		lnk_ip6tnl.encap_limit = nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel);
 		lnk_ip6tnl.flow_label = nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel);
-		lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6;
 		lnk_ip6tnl.flags = ip6tnl_flags_setting_to_plat (nm_setting_ip_tunnel_get_flags (s_ip_tunnel));
 
-		plerr = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), iface, &lnk_ip6tnl, out_plink);
+		if (NM_IN_SET (mode, NM_IP_TUNNEL_MODE_IP6GRE, NM_IP_TUNNEL_MODE_IP6GRETAP)) {
+			val = _nm_utils_ascii_str_to_int64 (nm_setting_ip_tunnel_get_input_key (s_ip_tunnel),
+			                                    10,
+			                                    0,
+			                                    G_MAXUINT32,
+			                                    -1);
+			if (val != -1) {
+				lnk_ip6tnl.input_key = val;
+				lnk_ip6tnl.input_flags = NM_GRE_KEY;
+			}
+
+			val = _nm_utils_ascii_str_to_int64 (nm_setting_ip_tunnel_get_output_key (s_ip_tunnel),
+			                                    10,
+			                                    0,
+			                                    G_MAXUINT32,
+			                                    -1);
+			if (val != -1) {
+				lnk_ip6tnl.output_key = val;
+				lnk_ip6tnl.output_flags = NM_GRE_KEY;
+			}
+
+			lnk_ip6tnl.is_gre = TRUE;
+			lnk_ip6tnl.is_tap = (mode == NM_IP_TUNNEL_MODE_IP6GRETAP);
+
+			plerr = nm_platform_link_ip6gre_add (nm_device_get_platform (device),
+			                                     iface, &lnk_ip6tnl, out_plink);
+		} else {
+			lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6
+			                       ? IPPROTO_IPIP
+			                       : IPPROTO_IPV6;
+			plerr = nm_platform_link_ip6tnl_add (nm_device_get_platform (device),
+			                                     iface, &lnk_ip6tnl, out_plink);
+		}
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
-			             "Failed to create IPIP interface '%s' for '%s': %s",
+			             "Failed to create IPv6 tunnel interface '%s' for '%s': %s",
 			             iface,
 			             nm_connection_get_id (connection),
 			             nm_platform_error_to_string_a (plerr));
@@ -852,6 +961,21 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+{
+	NMActStageReturn ret;
+
+	ret = NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class)->act_stage1_prepare (device, out_failure_reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
 /*****************************************************************************/
 
 static void
@@ -864,8 +988,11 @@ constructed (GObject *object)
 {
 	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE ((NMDeviceIPTunnel *) object);
 
-	if (   priv->mode == NM_IP_TUNNEL_MODE_IPIP6
-	    || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6)
+	if (NM_IN_SET (priv->mode,
+	               NM_IP_TUNNEL_MODE_IPIP6,
+	               NM_IP_TUNNEL_MODE_IP6IP6,
+	               NM_IP_TUNNEL_MODE_IP6GRE,
+	               NM_IP_TUNNEL_MODE_IP6GRETAP))
 		priv->addr_family = AF_INET6;
 	else
 		priv->addr_family = AF_INET;
@@ -925,6 +1052,17 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ip_tunnel);
 
+	device_class->connection_type_supported = NM_SETTING_IP_TUNNEL_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_IP_TUNNEL_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_GRE,
+	                                                        NM_LINK_TYPE_GRETAP,
+	                                                        NM_LINK_TYPE_IP6TNL,
+	                                                        NM_LINK_TYPE_IP6GRE,
+	                                                        NM_LINK_TYPE_IP6GRETAP,
+	                                                        NM_LINK_TYPE_IPIP,
+	                                                        NM_LINK_TYPE_SIT);
+
+	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->link_changed = link_changed;
 	device_class->can_reapply_change = can_reapply_change;
 	device_class->complete_connection = complete_connection;
@@ -935,13 +1073,6 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 	device_class->get_configured_mtu = get_configured_mtu;
 	device_class->unrealize_notify = unrealize_notify;
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass,
-	                               NM_SETTING_IP_TUNNEL_SETTING_NAME,
-	                               NM_LINK_TYPE_GRE,
-	                               NM_LINK_TYPE_IP6TNL,
-	                               NM_LINK_TYPE_IPIP,
-	                               NM_LINK_TYPE_SIT);
-
 	obj_properties[PROP_MODE] =
 	     g_param_spec_uint (NM_DEVICE_IP_TUNNEL_MODE, "", "",
 	                        0, G_MAXUINT, 0,
@@ -1084,7 +1215,13 @@ get_connection_iface (NMDeviceFactory *factory,
 }
 
 NM_DEVICE_FACTORY_DEFINE_INTERNAL (IP_TUNNEL, IPTunnel, ip_tunnel,
-	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_GRE, NM_LINK_TYPE_SIT, NM_LINK_TYPE_IPIP)
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_GRE,
+	                                      NM_LINK_TYPE_GRETAP,
+	                                      NM_LINK_TYPE_SIT,
+	                                      NM_LINK_TYPE_IPIP,
+	                                      NM_LINK_TYPE_IP6TNL,
+	                                      NM_LINK_TYPE_IP6GRE,
+	                                      NM_LINK_TYPE_IP6GRETAP)
 	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_IP_TUNNEL_SETTING_NAME),
 	factory_class->create_device = create_device;
 	factory_class->get_connection_parent = get_connection_parent;
diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
index 166bfb57..8ea4c8b5 100644
--- a/src/devices/nm-device-macsec.c
+++ b/src/devices/nm-device-macsec.c
@@ -627,21 +627,6 @@ deactivate (NMDevice *device)
 	supplicant_interface_release (self);
 }
 
-static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingMacsec *s_macsec;
-
-	if (!NM_DEVICE_CLASS (nm_device_macsec_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_macsec = nm_connection_get_setting_macsec (connection);
-	if (!s_macsec)
-		return FALSE;
-
-	return TRUE;
-}
-
 /******************************************************************/
 
 static NMDeviceCapabilities
@@ -841,27 +826,26 @@ nm_device_macsec_class_init (NMDeviceMacsecClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACSEC)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_macsec);
 
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->create_and_realize = create_and_realize;
-	parent_class->deactivate = deactivate;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->link_changed = link_changed;
-	parent_class->is_available = is_available;
-	parent_class->parent_changed_notify = parent_changed_notify;
-	parent_class->state_changed = device_state_changed;
-	parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
-
-	parent_class->connection_type = NM_SETTING_MACSEC_SETTING_NAME;
+	device_class->connection_type_supported = NM_SETTING_MACSEC_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_MACSEC_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_MACSEC);
+
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->create_and_realize = create_and_realize;
+	device_class->deactivate = deactivate;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->link_changed = link_changed;
+	device_class->is_available = is_available;
+	device_class->parent_changed_notify = parent_changed_notify;
+	device_class->state_changed = device_state_changed;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
 
 	obj_properties[PROP_SCI] =
 	    g_param_spec_uint64 (NM_DEVICE_MACSEC_SCI, "", "",
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index dc4fb3e8..ff386c82 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -290,40 +290,58 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 /*****************************************************************************/
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE ((NMDeviceMacvlan *) device);
 	NMSettingMacvlan *s_macvlan;
 	const char *parent = NULL;
 
-	if (!NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->check_connection_compatible (device, connection))
+	if (!NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	s_macvlan = nm_connection_get_setting_macvlan (connection);
-	if (!s_macvlan)
-		return FALSE;
 
-	if (nm_setting_macvlan_get_tap (s_macvlan) != priv->props.tap)
+	if (nm_setting_macvlan_get_tap (s_macvlan) != priv->props.tap) {
+		if (priv->props.tap) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "macvtap device does not match macvlan profile");
+		} else {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "macvlan device does not match macvtap profile");
+		}
 		return FALSE;
+	}
 
 	/* Before the device is realized some properties will not be set */
 	if (nm_device_is_real (device)) {
 
-		if (setting_mode_to_platform (nm_setting_macvlan_get_mode (s_macvlan)) != priv->props.mode)
+		if (setting_mode_to_platform (nm_setting_macvlan_get_mode (s_macvlan)) != priv->props.mode) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "macvlan mode setting differs");
 			return FALSE;
+		}
 
-		if (nm_setting_macvlan_get_promiscuous (s_macvlan) ==  priv->props.no_promisc)
+		if (nm_setting_macvlan_get_promiscuous (s_macvlan) ==  priv->props.no_promisc) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "macvlan promiscuous setting differs");
 			return FALSE;
+		}
 
 		/* Check parent interface; could be an interface name or a UUID */
 		parent = nm_setting_macvlan_get_parent (s_macvlan);
 		if (parent) {
-			if (!nm_device_match_parent (device, parent))
+			if (!nm_device_match_parent (device, parent)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "macvlan parent setting differs");
 				return FALSE;
+			}
 		} else {
 			/* Parent could be a MAC address in an NMSettingWired */
-			if (!nm_device_match_hwaddr (device, connection, TRUE))
+			if (!nm_device_match_parent_hwaddr (device, connection, TRUE)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "macvlan parent mac setting differs");
 				return FALSE;
+			}
 		}
 	}
 
@@ -359,7 +377,7 @@ complete_connection (NMDevice *device,
 	 * settings, then there's not enough information to complete the setting.
 	 */
 	if (   !nm_setting_macvlan_get_parent (s_macvlan)
-	    && !nm_device_match_hwaddr (device, connection, TRUE)) {
+	    && !nm_device_match_parent_hwaddr (device, connection, TRUE)) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
 		                     "The 'macvlan' setting had no interface name, parent, or hardware address.");
 		return FALSE;
@@ -373,8 +391,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE ((NMDeviceMacvlan *) device);
 	NMSettingMacvlan *s_macvlan = nm_connection_get_setting_macvlan (connection);
-	NMDevice *parent_device;
-	const char *setting_parent, *new_parent;
 	int new_mode;
 
 	if (!s_macvlan) {
@@ -392,24 +408,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 	if (priv->props.tap != nm_setting_macvlan_get_tap (s_macvlan))
 		g_object_set (s_macvlan, NM_SETTING_MACVLAN_TAP, !!priv->props.tap, NULL);
 
-	/* Update parent in the connection; default to parent's interface name */
-	parent_device = nm_device_parent_get_device (device);
-	if (parent_device) {
-		new_parent = nm_device_get_iface (parent_device);
-		setting_parent = nm_setting_macvlan_get_parent (s_macvlan);
-		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
-			NMConnection *parent_connection;
-
-			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device), setting_parent);
-			if (parent_connection && nm_device_check_connection_compatible (parent_device, parent_connection))
-				new_parent = NULL;
-		}
-		if (new_parent)
-			g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, new_parent, NULL);
-	} else
-		g_object_set (s_macvlan, NM_SETTING_MACVLAN_PARENT, NULL, NULL);
-
+	g_object_set (s_macvlan,
+	              NM_SETTING_MACVLAN_PARENT,
+	              nm_device_parent_find_for_connection (device,
+	                                                    nm_setting_macvlan_get_parent (s_macvlan)),
+	              NULL);
 }
 
 static NMActStageReturn
@@ -495,17 +498,18 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP)
-
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_macvlan);
 
+	device_class->connection_type_supported = NM_SETTING_MACVLAN_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_MACVLAN_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP);
+
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->complete_connection = complete_connection;
-	device_class->connection_type = NM_SETTING_MACVLAN_SETTING_NAME;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c
index a6abb228..74b4d710 100644
--- a/src/devices/nm-device-ppp.c
+++ b/src/devices/nm-device-ppp.c
@@ -49,24 +49,6 @@ G_DEFINE_TYPE (NMDevicePpp, nm_device_ppp, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_PPP_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDevicePpp, NM_IS_DEVICE_PPP)
 
-static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingPppoe *s_pppoe;
-
-	if (!NM_DEVICE_CLASS (nm_device_ppp_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	if (!nm_streq0 (nm_connection_get_connection_type (connection),
-	                NM_SETTING_PPPOE_SETTING_NAME))
-		return FALSE;
-
-	s_pppoe = nm_connection_get_setting_pppoe (connection);
-	nm_assert (s_pppoe);
-
-	return !!nm_setting_pppoe_get_parent (s_pppoe);
-}
-
 static NMDeviceCapabilities
 get_generic_capabilities (NMDevice *device)
 {
@@ -275,20 +257,21 @@ nm_device_ppp_class_init (NMDevicePppClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_PPPOE_SETTING_NAME, NM_LINK_TYPE_PPP)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->dispose = dispose;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ppp);
 
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->create_and_realize = create_and_realize;
-	parent_class->deactivate = deactivate;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->connection_type_supported = NM_SETTING_PPPOE_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_PPPOE_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_PPP);
+
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	device_class->create_and_realize = create_and_realize;
+	device_class->deactivate = deactivate;
+	device_class->get_generic_capabilities = get_generic_capabilities;
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index ba28e9e4..66c715de 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -130,12 +130,21 @@ void nm_device_commit_mtu (NMDevice *self);
 
 /*****************************************************************************/
 
-#define NM_DEVICE_CLASS_DECLARE_TYPES(klass, conn_type, ...) \
-	NM_DEVICE_CLASS (klass)->connection_type = conn_type; \
-	{ \
-		static const NMLinkType link_types[] = { __VA_ARGS__, NM_LINK_TYPE_NONE }; \
-		NM_DEVICE_CLASS (klass)->link_types = link_types; \
-	}
+#define NM_DEVICE_DEFINE_LINK_TYPES(...) \
+	((NM_NARG (__VA_ARGS__) == 0) \
+	  ? NULL \
+	  : ({ \
+	      static const struct { \
+	          const NMLinkType types[NM_NARG (__VA_ARGS__)]; \
+	          const NMLinkType sentinel; \
+	      } _link_types = { \
+	          .types = { __VA_ARGS__ }, \
+	          .sentinel = NM_LINK_TYPE_NONE, \
+	      }; \
+	      \
+	      _link_types.types; \
+	    })\
+	)
 
 gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name,
                                              GError **error, const char **whitelist);
@@ -143,8 +152,8 @@ gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setti
 	_nm_device_hash_check_invalid_keys (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL }))
 
 gboolean nm_device_match_parent (NMDevice *device, const char *parent);
-gboolean nm_device_match_hwaddr (NMDevice *device,
-                                 NMConnection *connection,
-                                 gboolean fail_if_no_hwaddr);
+gboolean nm_device_match_parent_hwaddr (NMDevice *device,
+                                        NMConnection *connection,
+                                        gboolean fail_if_no_hwaddr);
 
 #endif /* NM_DEVICE_PRIVATE_H */
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index 3846676c..0f76b23a 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -289,18 +289,14 @@ _same_og (const char *str, gboolean og_valid, guint32 og_num)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceTun *self = NM_DEVICE_TUN (device);
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
 	NMSettingTunMode mode;
 	NMSettingTun *s_tun;
 
-	if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_tun = nm_connection_get_setting_tun (connection);
-	if (!s_tun)
+	if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	if (nm_device_is_real (device)) {
@@ -308,22 +304,43 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		case IFF_TUN: mode = NM_SETTING_TUN_MODE_TUN; break;
 		case IFF_TAP: mode = NM_SETTING_TUN_MODE_TAP; break;
 		default:
-			/* Huh? */
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "invalid tun type on device");
 			return FALSE;
 		}
 
-		if (mode != nm_setting_tun_get_mode (s_tun))
+		s_tun = nm_connection_get_setting_tun (connection);
+
+		if (mode != nm_setting_tun_get_mode (s_tun)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "tun mode setting mismatches");
 			return FALSE;
-		if (!_same_og (nm_setting_tun_get_owner (s_tun), priv->props.owner_valid, priv->props.owner))
+		}
+		if (!_same_og (nm_setting_tun_get_owner (s_tun), priv->props.owner_valid, priv->props.owner)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "tun owner setting mismatches");
 			return FALSE;
-		if (!_same_og (nm_setting_tun_get_group (s_tun), priv->props.group_valid, priv->props.group))
+		}
+		if (!_same_og (nm_setting_tun_get_group (s_tun), priv->props.group_valid, priv->props.group)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "tun group setting mismatches");
 			return FALSE;
-		if (nm_setting_tun_get_pi (s_tun) != priv->props.pi)
+		}
+		if (nm_setting_tun_get_pi (s_tun) != priv->props.pi) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "tun pi setting mismatches");
 			return FALSE;
-		if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr)
+		}
+		if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "tun vnet-hdr setting mismatches");
 			return FALSE;
-		if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue)
+		}
+		if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "tun multi-queue setting mismatches");
 			return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -430,13 +447,14 @@ nm_device_tun_class_init (NMDeviceTunClass *klass)
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN)
-
 	object_class->get_property = get_property;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_tun);
 
-	device_class->connection_type = NM_SETTING_TUN_SETTING_NAME;
+	device_class->connection_type_supported = NM_SETTING_TUN_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_TUN_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_TUN);
+
 	device_class->link_changed = link_changed;
 	device_class->complete_connection = complete_connection;
 	device_class->check_connection_compatible = check_connection_compatible;
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index 186173eb..6f90758c 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -153,12 +153,13 @@ nm_device_veth_class_init (NMDeviceVethClass *klass)
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VETH)
-
 	object_class->get_property = get_property;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_veth);
 
+	device_class->connection_type_supported = NULL;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_VETH);
+
 	device_class->can_unmanaged_external_down = can_unmanaged_external_down;
 	device_class->link_changed = link_changed;
 	device_class->parent_changed_notify = parent_changed_notify;
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index b0e746a8..b7f0c4e7 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -331,33 +331,39 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 /*****************************************************************************/
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE ((NMDeviceVlan *) device);
 	NMSettingVlan *s_vlan;
-	const char *parent = NULL;
+	const char *parent;
 
-	if (!NM_DEVICE_CLASS (nm_device_vlan_parent_class)->check_connection_compatible (device, connection))
+	if (!NM_DEVICE_CLASS (nm_device_vlan_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
-	s_vlan = nm_connection_get_setting_vlan (connection);
-	if (!s_vlan)
-		return FALSE;
-
-	/* Before the device is realized some properties will not be set */
 	if (nm_device_is_real (device)) {
-		if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id)
+		s_vlan = nm_connection_get_setting_vlan (connection);
+
+		if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vlan id setting mismatches");
 			return FALSE;
+		}
 
 		/* Check parent interface; could be an interface name or a UUID */
 		parent = nm_setting_vlan_get_parent (s_vlan);
 		if (parent) {
-			if (!nm_device_match_parent (device, parent))
+			if (!nm_device_match_parent (device, parent)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "vlan parent setting differs");
 				return FALSE;
+			}
 		} else {
 			/* Parent could be a MAC address in an NMSettingWired */
-			if (!nm_device_match_hwaddr (device, connection, TRUE))
+			if (!nm_device_match_parent_hwaddr (device, connection, TRUE)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "vlan parent mac setting differs");
 				return FALSE;
+			}
 		}
 	}
 
@@ -368,12 +374,13 @@ static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
+                            const char *specific_object,
+                            GError **error)
 {
 	if (!nm_device_is_real (device))
 		return TRUE;
 
-	return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->check_connection_available (device, connection, flags, specific_object);
+	return NM_DEVICE_CLASS (nm_device_vlan_parent_class)->check_connection_available (device, connection, flags, specific_object, error);
 }
 
 static gboolean
@@ -405,7 +412,7 @@ complete_connection (NMDevice *device,
 	 * settings, then there's not enough information to complete the setting.
 	 */
 	if (   !nm_setting_vlan_get_parent (s_vlan)
-	    && !nm_device_match_hwaddr (device, connection, TRUE)) {
+	    && !nm_device_match_parent_hwaddr (device, connection, TRUE)) {
 		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
 		                     "The 'vlan' setting had no interface name, parent, or hardware address.");
 		return FALSE;
@@ -420,10 +427,8 @@ update_connection (NMDevice *device, NMConnection *connection)
 	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
 	NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection);
 	int ifindex = nm_device_get_ifindex (device);
-	const char *setting_parent, *new_parent;
 	const NMPlatformLink *plink;
 	const NMPObject *polnk;
-	NMDevice *parent_device;
 	guint vlan_id;
 	guint vlan_flags;
 
@@ -441,26 +446,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 	if (vlan_id != nm_setting_vlan_get_id (s_vlan))
 		g_object_set (s_vlan, NM_SETTING_VLAN_ID, vlan_id, NULL);
 
-	/* Update parent in the connection; default to parent's interface name */
-	parent_device = nm_device_parent_get_device (device);
-	if (   parent_device
-	    && polnk
-	    && plink->parent > 0
-	    && nm_device_get_ifindex (parent_device) == plink->parent) {
-		new_parent = nm_device_get_iface (parent_device);
-		setting_parent = nm_setting_vlan_get_parent (s_vlan);
-		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
-			NMConnection *parent_connection;
-
-			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device), setting_parent);
-			if (parent_connection && nm_device_check_connection_compatible (parent_device, parent_connection))
-				new_parent = NULL;
-		}
-		if (new_parent)
-			g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL);
-	} else
-		g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, NULL, NULL);
+	g_object_set (s_vlan,
+	              NM_SETTING_VLAN_PARENT,
+	              nm_device_parent_find_for_connection (device,
+	                                                    nm_setting_vlan_get_parent (s_vlan)),
+	              NULL);
 
 	if (polnk)
 		vlan_flags = polnk->lnk_vlan.flags;
@@ -599,27 +589,29 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_VLAN_SETTING_NAME, NM_LINK_TYPE_VLAN)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->get_property = get_property;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_vlan);
 
-	parent_class->create_and_realize = create_and_realize;
-	parent_class->link_changed = link_changed;
-	parent_class->unrealize_notify = unrealize_notify;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->get_configured_mtu = get_configured_mtu;
-	parent_class->is_available = is_available;
-	parent_class->parent_changed_notify = parent_changed_notify;
-
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->check_connection_available = check_connection_available;
-	parent_class->complete_connection = complete_connection;
-	parent_class->update_connection = update_connection;
+	device_class->connection_type_supported = NM_SETTING_VLAN_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_VLAN_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_VLAN);
+
+	device_class->create_and_realize = create_and_realize;
+	device_class->link_changed = link_changed;
+	device_class->unrealize_notify = unrealize_notify;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->get_configured_mtu = get_configured_mtu;
+	device_class->is_available = is_available;
+	device_class->parent_changed_notify = parent_changed_notify;
+
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->check_connection_available = check_connection_available;
+	device_class->complete_connection = complete_connection;
+	device_class->update_connection = update_connection;
 
 	obj_properties[PROP_VLAN_ID] =
 	     g_param_spec_uint (NM_DEVICE_VLAN_ID, "", "",
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index e1252223..c34f4142 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -246,65 +246,108 @@ address_matches (const char *str, in_addr_t addr4, struct in6_addr *addr6)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE ((NMDeviceVxlan *) device);
 	NMSettingVxlan *s_vxlan;
 	const char *parent;
 
-	if (!NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_vxlan = nm_connection_get_setting_vxlan (connection);
-	if (!s_vxlan)
+	if (!NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	if (nm_device_is_real (device)) {
+		s_vxlan = nm_connection_get_setting_vxlan (connection);
+
 		parent = nm_setting_vxlan_get_parent (s_vxlan);
-		if (parent && !nm_device_match_parent (device, parent))
+		if (parent && !nm_device_match_parent (device, parent)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan parent mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan))
+		if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan id mismatches");
 			return FALSE;
+		}
 
-		if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6))
+		if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan local address mismatches");
 			return FALSE;
+		}
 
-		if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6))
+		if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan remote address mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.src_port_min != nm_setting_vxlan_get_source_port_min (s_vxlan))
+		if (priv->props.src_port_min != nm_setting_vxlan_get_source_port_min (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan source port min mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.src_port_max != nm_setting_vxlan_get_source_port_max (s_vxlan))
+		if (priv->props.src_port_max != nm_setting_vxlan_get_source_port_max (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan source port max mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.dst_port != nm_setting_vxlan_get_destination_port (s_vxlan))
+		if (priv->props.dst_port != nm_setting_vxlan_get_destination_port (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan destination port mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.tos != nm_setting_vxlan_get_tos (s_vxlan))
+		if (priv->props.tos != nm_setting_vxlan_get_tos (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan TOS mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.ttl != nm_setting_vxlan_get_ttl (s_vxlan))
+		if (priv->props.ttl != nm_setting_vxlan_get_ttl (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan TTL mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.learning != nm_setting_vxlan_get_learning (s_vxlan))
+		if (priv->props.learning != nm_setting_vxlan_get_learning (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan learning mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.ageing != nm_setting_vxlan_get_ageing (s_vxlan))
+		if (priv->props.ageing != nm_setting_vxlan_get_ageing (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan ageing mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.proxy != nm_setting_vxlan_get_proxy (s_vxlan))
+		if (priv->props.proxy != nm_setting_vxlan_get_proxy (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan proxy mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.rsc != nm_setting_vxlan_get_rsc (s_vxlan))
+		if (priv->props.rsc != nm_setting_vxlan_get_rsc (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan rsc mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.l2miss != nm_setting_vxlan_get_l2_miss (s_vxlan))
+		if (priv->props.l2miss != nm_setting_vxlan_get_l2_miss (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan l2miss mismatches");
 			return FALSE;
+		}
 
-		if (priv->props.l3miss != nm_setting_vxlan_get_l3_miss (s_vxlan))
+		if (priv->props.l3miss != nm_setting_vxlan_get_l3_miss (s_vxlan)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "vxlan l3miss mismatches");
 			return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -343,9 +386,6 @@ update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE ((NMDeviceVxlan *) device);
 	NMSettingVxlan *s_vxlan = nm_connection_get_setting_vxlan (connection);
-	NMDevice *parent_device;
-	const char *setting_parent;
-	const char *new_parent = NULL;
 
 	if (!s_vxlan) {
 		s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new ();
@@ -355,23 +395,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 	if (priv->props.id != nm_setting_vxlan_get_id (s_vxlan))
 		g_object_set (G_OBJECT (s_vxlan), NM_SETTING_VXLAN_ID, priv->props.id, NULL);
 
-	parent_device = nm_device_parent_get_device (device);
-
-	/* Update parent in the connection; default to parent's interface name */
-	if (parent_device) {
-		new_parent = nm_device_get_iface (parent_device);
-		setting_parent = nm_setting_vxlan_get_parent (s_vxlan);
-		if (setting_parent && nm_utils_is_uuid (setting_parent)) {
-			NMConnection *parent_connection;
-
-			/* Don't change a parent specified by UUID if it's still valid */
-			parent_connection = (NMConnection *) nm_settings_get_connection_by_uuid (nm_device_get_settings (device),
-			                                                                         setting_parent);
-			if (parent_connection && nm_device_check_connection_compatible (parent_device, parent_connection))
-				new_parent = NULL;
-		}
-	}
-	g_object_set (s_vxlan, NM_SETTING_VXLAN_PARENT, new_parent, NULL);
+	g_object_set (s_vxlan,
+	              NM_SETTING_VXLAN_PARENT,
+	              nm_device_parent_find_for_connection (device,
+	                                                    nm_setting_vxlan_get_parent (s_vxlan)),
+	              NULL);
 
 	if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6)) {
 		if (priv->props.group) {
@@ -577,15 +605,16 @@ nm_device_vxlan_class_init (NMDeviceVxlanClass *klass)
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_VXLAN)
-
 	object_class->get_property = get_property;
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_vxlan);
 
+	device_class->connection_type_supported = NM_SETTING_VXLAN_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_VXLAN_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_VXLAN);
+
 	device_class->link_changed = link_changed;
 	device_class->unrealize_notify = unrealize_notify;
-	device_class->connection_type = NM_SETTING_VXLAN_SETTING_NAME;
 	device_class->create_and_realize = create_and_realize;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->complete_connection = complete_connection;
diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c
new file mode 100644
index 00000000..62ec0274
--- /dev/null
+++ b/src/devices/nm-device-wireguard.c
@@ -0,0 +1,212 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Javier Arteaga <jarteaga@jbeta.is>
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-wireguard.h"
+
+#include "nm-device-private.h"
+#include "platform/nm-platform.h"
+#include "nm-device-factory.h"
+
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceWireGuard);
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWireGuard,
+	PROP_PUBLIC_KEY,
+	PROP_LISTEN_PORT,
+	PROP_FWMARK,
+);
+
+struct _NMDeviceWireGuard {
+	NMDevice parent;
+	NMPlatformLnkWireGuard props;
+};
+
+struct _NMDeviceWireGuardClass {
+	NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceWireGuard, nm_device_wireguard, NM_TYPE_DEVICE)
+
+/******************************************************************/
+
+static GVariant *
+get_public_key_as_variant (const NMDeviceWireGuard *self)
+{
+	return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+	                                  self->props.public_key, sizeof (self->props.public_key), 1);
+}
+
+static void
+update_properties (NMDevice *device)
+{
+	NMDeviceWireGuard *self;
+	const NMPlatformLink *plink;
+	const NMPlatformLnkWireGuard *props = NULL;
+	int ifindex;
+
+	g_return_if_fail (NM_IS_DEVICE_WIREGUARD (device));
+	self = NM_DEVICE_WIREGUARD (device);
+
+	ifindex = nm_device_get_ifindex (device);
+	props = nm_platform_link_get_lnk_wireguard (nm_device_get_platform (device), ifindex, &plink);
+	if (!props) {
+		_LOGW (LOGD_PLATFORM, "could not get wireguard properties");
+		return;
+	}
+
+	g_object_freeze_notify (G_OBJECT (device));
+
+#define CHECK_PROPERTY_CHANGED(field, prop) \
+	G_STMT_START { \
+		if (self->props.field != props->field) { \
+			self->props.field = props->field; \
+			_notify (self, prop); \
+		} \
+	} G_STMT_END
+
+#define CHECK_PROPERTY_CHANGED_ARRAY(field, prop) \
+	G_STMT_START { \
+		if (memcmp (&self->props.field, &props->field, sizeof (props->field)) != 0) { \
+			memcpy (&self->props.field, &props->field, sizeof (props->field)); \
+			_notify (self, prop); \
+		} \
+	} G_STMT_END
+
+	CHECK_PROPERTY_CHANGED_ARRAY (public_key, PROP_PUBLIC_KEY);
+	CHECK_PROPERTY_CHANGED (listen_port, PROP_LISTEN_PORT);
+	CHECK_PROPERTY_CHANGED (fwmark, PROP_FWMARK);
+
+	g_object_thaw_notify (G_OBJECT (device));
+}
+
+static void
+link_changed (NMDevice *device,
+              const NMPlatformLink *pllink)
+{
+	NM_DEVICE_CLASS (nm_device_wireguard_parent_class)->link_changed (device, pllink);
+	update_properties (device);
+}
+
+
+/******************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (object);
+
+	switch (prop_id) {
+	case PROP_PUBLIC_KEY:
+		g_value_take_variant (value, get_public_key_as_variant (self));
+		break;
+	case PROP_LISTEN_PORT:
+		g_value_set_uint (value, self->props.listen_port);
+		break;
+	case PROP_FWMARK:
+		g_value_set_uint (value, self->props.fwmark);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_device_wireguard_init (NMDeviceWireGuard *self)
+{
+}
+
+static const NMDBusInterfaceInfoExtended interface_info_device_wireguard = {
+	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
+		NM_DBUS_INTERFACE_DEVICE_WIREGUARD,
+		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("PublicKey",  "ay", NM_DEVICE_WIREGUARD_PUBLIC_KEY),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("ListenPort", "q", NM_DEVICE_WIREGUARD_LISTEN_PORT),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("FwMark",     "u", NM_DEVICE_WIREGUARD_FWMARK),
+		),
+	),
+};
+
+static void
+nm_device_wireguard_class_init (NMDeviceWireGuardClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+	object_class->get_property = get_property;
+
+	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wireguard);
+
+	device_class->connection_type_supported = NULL;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIREGUARD);
+
+	device_class->link_changed = link_changed;
+
+	obj_properties[PROP_PUBLIC_KEY] =
+	    g_param_spec_variant (NM_DEVICE_WIREGUARD_PUBLIC_KEY,
+	                          "", "",
+	                          G_VARIANT_TYPE ("ay"),
+	                          NULL,
+	                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_LISTEN_PORT] =
+	    g_param_spec_uint (NM_DEVICE_WIREGUARD_LISTEN_PORT,
+	                       "", "",
+	                       0, G_MAXUINT16, 0,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	obj_properties[PROP_FWMARK] =
+	    g_param_spec_uint (NM_DEVICE_WIREGUARD_FWMARK,
+	                       "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
+
+/*************************************************************/
+
+#define NM_TYPE_WIREGUARD_DEVICE_FACTORY (nm_wireguard_device_factory_get_type ())
+#define NM_WIREGUARD_DEVICE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIREGUARD_DEVICE_FACTORY, NMWireGuardDeviceFactory))
+
+static NMDevice *
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
+{
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIREGUARD,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_TYPE_DESC, "WireGuard",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIREGUARD,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIREGUARD,
+	                                  NULL);
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (WIREGUARD, WireGuard, wireguard,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_WIREGUARD),
+	factory_class->create_device = create_device;
+)
diff --git a/src/devices/nm-device-wireguard.h b/src/devices/nm-device-wireguard.h
new file mode 100644
index 00000000..3ad41f9b
--- /dev/null
+++ b/src/devices/nm-device-wireguard.h
@@ -0,0 +1,41 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Javier Arteaga <jarteaga@jbeta.is>
+ */
+
+#ifndef __NM_DEVICE_WIREGUARD_H__
+#define __NM_DEVICE_WIREGUARD_H__
+
+#include "nm-device.h"
+
+#define NM_TYPE_DEVICE_WIREGUARD            (nm_device_wireguard_get_type ())
+#define NM_DEVICE_WIREGUARD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIREGUARD, NMDeviceWireGuard))
+#define NM_DEVICE_WIREGUARD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_WIREGUARD, NMDeviceWireGuardClass))
+#define NM_IS_DEVICE_WIREGUARD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIREGUARD))
+#define NM_IS_DEVICE_WIREGUARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WIREGUARD))
+#define NM_DEVICE_WIREGUARD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WIREGUARD, NMDeviceWireGuardClass))
+
+#define NM_DEVICE_WIREGUARD_PUBLIC_KEY      "public-key"
+#define NM_DEVICE_WIREGUARD_LISTEN_PORT     "listen-port"
+#define NM_DEVICE_WIREGUARD_FWMARK          "fwmark"
+
+typedef struct _NMDeviceWireGuard NMDeviceWireGuard;
+typedef struct _NMDeviceWireGuardClass NMDeviceWireGuardClass;
+
+GType nm_device_wireguard_get_type (void);
+
+#endif /* __NM_DEVICE_WIREGUARD_H__ */
diff --git a/src/devices/nm-device-wpan.c b/src/devices/nm-device-wpan.c
new file mode 100644
index 00000000..dd2ebac1
--- /dev/null
+++ b/src/devices/nm-device-wpan.c
@@ -0,0 +1,253 @@
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
+ */
+
+#include "nm-default.h"
+
+#include "nm-manager.h"
+#include "nm-device-wpan.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "nm-act-request.h"
+#include "nm-device-private.h"
+#include "nm-ip4-config.h"
+#include "platform/nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-setting-wpan.h"
+#include "nm-core-internal.h"
+
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceWpan);
+
+/*****************************************************************************/
+
+struct _NMDeviceWpan {
+	NMDevice parent;
+};
+
+struct _NMDeviceWpanClass {
+	NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceWpan, nm_device_wpan, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     NMConnection *const*existing_connections,
+                     GError **error)
+{
+	NMSettingWpan *s_wpan;
+
+	nm_utils_complete_generic (nm_device_get_platform (device),
+	                           connection,
+	                           NM_SETTING_WPAN_SETTING_NAME,
+	                           existing_connections,
+	                           NULL,
+	                           _("WPAN connection"),
+	                           NULL,
+	                           TRUE);
+
+	s_wpan = nm_connection_get_setting_wpan (connection);
+	if (!s_wpan) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "A 'wpan' setting is required.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+update_connection (NMDevice *device, NMConnection *connection)
+{
+	NMSettingWpan *s_wpan = nm_connection_get_setting_wpan (connection);
+
+	if (!s_wpan) {
+		s_wpan = (NMSettingWpan *) nm_setting_wpan_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_wpan);
+	}
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+	NMSettingWpan *s_wpan;
+	const char *mac, *hw_addr;
+
+	if (!NM_DEVICE_CLASS (nm_device_wpan_parent_class)->check_connection_compatible (device, connection, error))
+		return FALSE;
+
+	s_wpan = nm_connection_get_setting_wpan (connection);
+
+	mac = nm_setting_wpan_get_mac_address (s_wpan);
+	if (mac) {
+		hw_addr = nm_device_get_hw_address (device);
+		if (!nm_utils_hwaddr_matches (mac, -1, hw_addr, -1)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "MAC address mismatches");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+{
+	NMDeviceWpan *self = NM_DEVICE_WPAN (device);
+	NMConnection *connection;
+	NMSettingWpan *s_wpan;
+	NMPlatform *platform;
+	guint16 pan_id;
+	guint16 short_address;
+	int ifindex;
+	const guint8 *hwaddr;
+	gsize hwaddr_len = 0;
+	const NMPlatformLink *lowpan_plink;
+	NMDevice *lowpan_device = NULL;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+
+	ret = NM_DEVICE_CLASS (nm_device_wpan_parent_class)->act_stage1_prepare (device, out_failure_reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	platform = nm_device_get_platform (device);
+	g_return_val_if_fail (platform, NM_ACT_STAGE_RETURN_FAILURE);
+
+	ifindex = nm_device_get_ifindex (device);
+	g_return_val_if_fail (ifindex > 0, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_device_get_applied_connection (device);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_wpan = nm_connection_get_setting_wpan (connection);
+	g_return_val_if_fail (s_wpan, NM_ACT_STAGE_RETURN_FAILURE);
+
+	hwaddr = nm_platform_link_get_address (platform, ifindex, &hwaddr_len);
+	g_return_val_if_fail (hwaddr, NM_ACT_STAGE_RETURN_FAILURE);
+
+	/* As of kernel 4.16, the 6LoWPAN devices layered on top of WPANs
+	 * need to be DOWN as well as the WPAN device itself in order to
+	 * modify the WPAN properties. */
+	lowpan_plink = nm_platform_link_get_by_address (platform,
+	                                                NM_LINK_TYPE_6LOWPAN,
+	                                                hwaddr,
+	                                                hwaddr_len);
+	if (lowpan_plink && NM_FLAGS_HAS (lowpan_plink->n_ifi_flags, IFF_UP)) {
+		lowpan_device = nm_manager_get_device_by_ifindex (nm_manager_get (),
+		                                                  lowpan_plink->ifindex);
+	}
+
+	if (lowpan_device)
+		nm_device_take_down (lowpan_device, TRUE);
+
+	nm_device_take_down (device, TRUE);
+
+	pan_id = nm_setting_wpan_get_pan_id (s_wpan);
+	if (pan_id != G_MAXUINT16) {
+		if (!nm_platform_wpan_set_pan_id (platform, ifindex, pan_id)) {
+			_LOGW (LOGD_DEVICE, "unable to set the PAN ID");
+			goto out;
+		}
+	}
+
+	short_address = nm_setting_wpan_get_short_address (s_wpan);
+	if (short_address != G_MAXUINT16) {
+		if (!nm_platform_wpan_set_short_addr (platform, ifindex, short_address)) {
+			_LOGW (LOGD_DEVICE, "unable to set the short address");
+			goto out;
+		}
+	}
+
+	ret = NM_ACT_STAGE_RETURN_SUCCESS;
+out:
+        nm_device_bring_up (device, TRUE, NULL);
+
+	if (lowpan_device)
+		nm_device_bring_up (lowpan_device, TRUE, NULL);
+
+	return ret;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_wpan_init (NMDeviceWpan *self)
+{
+}
+
+static const NMDBusInterfaceInfoExtended interface_info_device_wpan = {
+	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
+		NM_DBUS_INTERFACE_DEVICE_WPAN,
+		.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("HwAddress", "s", NM_DEVICE_HW_ADDRESS),
+		),
+	),
+};
+
+static void
+nm_device_wpan_class_init (NMDeviceWpanClass *klass)
+{
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
+
+	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wpan);
+
+	device_class->connection_type_supported = NM_SETTING_WPAN_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_WPAN_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WPAN);
+
+	device_class->complete_connection = complete_connection;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->update_connection = update_connection;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+}
+
+/*****************************************************************************/
+
+#define NM_TYPE_WPAN_DEVICE_FACTORY (nm_wpan_device_factory_get_type ())
+#define NM_WPAN_DEVICE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WPAN_DEVICE_FACTORY, NMWpanDeviceFactory))
+
+static NMDevice *
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
+{
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WPAN,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_TYPE_DESC, "WPAN",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WPAN,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WPAN,
+	                                  NULL);
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (WPAN, Wpan, wpan,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_WPAN)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WPAN_SETTING_NAME),
+	factory_class->create_device = create_device;
+);
diff --git a/src/devices/nm-device-wpan.h b/src/devices/nm-device-wpan.h
new file mode 100644
index 00000000..33f24776
--- /dev/null
+++ b/src/devices/nm-device-wpan.h
@@ -0,0 +1,35 @@
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_WPAN_H__
+#define __NETWORKMANAGER_DEVICE_WPAN_H__
+
+#define NM_TYPE_DEVICE_WPAN            (nm_device_wpan_get_type ())
+#define NM_DEVICE_WPAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WPAN, NMDeviceWpan))
+#define NM_DEVICE_WPAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_WPAN, NMDeviceWpanClass))
+#define NM_IS_DEVICE_WPAN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WPAN))
+#define NM_IS_DEVICE_WPAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WPAN))
+#define NM_DEVICE_WPAN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WPAN, NMDeviceWpanClass))
+
+typedef struct _NMDeviceWpan NMDeviceWpan;
+typedef struct _NMDeviceWpanClass NMDeviceWpanClass;
+
+GType nm_device_wpan_get_type (void);
+
+#endif /* __NETWORKMANAGER_DEVICE_WPAN_H__ */
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 19e43762..515e6334 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2005 - 2017 Red Hat, Inc.
+ * Copyright (C) 2005 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
@@ -43,6 +43,7 @@
 #include "nm-utils/nm-random-utils.h"
 #include "nm-utils/unaligned.h"
 
+#include "nm-ethtool-utils.h"
 #include "nm-common-macros.h"
 #include "nm-device-private.h"
 #include "NetworkManagerUtils.h"
@@ -65,6 +66,7 @@
 #include "nm-firewall-manager.h"
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
+#include "nm-setting-ethtool.h"
 #include "nm-auth-utils.h"
 #include "nm-netns.h"
 #include "nm-dispatcher.h"
@@ -172,6 +174,12 @@ struct _NMDeviceConnectivityHandle {
 	bool is_periodic_bump_on_complete:1;
 };
 
+typedef struct {
+	int ifindex;
+	NMEthtoolFeatureStates *features;
+	NMTernary requested[_NM_ETHTOOL_ID_FEATURE_NUM];
+} EthtoolState;
+
 /*****************************************************************************/
 
 enum {
@@ -508,6 +516,8 @@ typedef struct _NMDevicePrivate {
 
 	GHashTable *   ip6_saved_properties;
 
+	EthtoolState  *ethtool_state;
+
 	struct {
 		NMDhcpClient *   client;
 		NMNDiscDHCPLevel mode;
@@ -614,7 +624,7 @@ static void _set_state_full (NMDevice *self,
 static void queued_state_clear (NMDevice *device);
 static gboolean queued_ip4_config_change (gpointer user_data);
 static gboolean queued_ip6_config_change (gpointer user_data);
-static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data);
+static void ip_check_ping_watch_cb (GPid pid, int status, gpointer user_data);
 static gboolean ip_config_valid (NMDeviceState state);
 static NMActStageReturn dhcp4_start (NMDevice *self);
 static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll);
@@ -724,6 +734,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_device_state_reason_to_str, NMDeviceStateReason,
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED,                   "ovsdb-failed"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE,           "ip-address-duplicate"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED,          "ip-method-unsupported"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED,     "sriov-configuration-failed"),
 );
 
 #define reason_to_string(reason) \
@@ -739,6 +750,79 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (mtu_source_to_str, NMDeviceMtuSource,
 
 /*****************************************************************************/
 
+static void
+_ethtool_state_reset (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->ethtool_state) {
+		gs_free NMEthtoolFeatureStates *features = priv->ethtool_state->features;
+		gs_free EthtoolState *ethtool_state = g_steal_pointer (&priv->ethtool_state);
+
+		if (!nm_platform_ethtool_set_features (nm_device_get_platform (self),
+		                                       ethtool_state->ifindex,
+		                                       features,
+		                                       ethtool_state->requested,
+		                                       FALSE))
+			_LOGW (LOGD_DEVICE, "ethtool: failure resetting one or more offload features");
+		else
+			_LOGD (LOGD_DEVICE, "ethtool: offload features successfully reset");
+	}
+}
+
+static void
+_ethtool_state_set (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ifindex;
+	NMConnection *connection;
+	NMSettingEthtool *s_ethtool;
+	NMPlatform *platform;
+	gs_free EthtoolState *ethtool_state = NULL;
+	gs_free NMEthtoolFeatureStates *features = NULL;
+
+	_ethtool_state_reset (self);
+
+	connection = nm_device_get_applied_connection (self);
+	if (!connection)
+		return;
+
+	ifindex = nm_device_get_ip_ifindex (self);
+	if (ifindex <= 0)
+		return;
+
+	s_ethtool = NM_SETTING_ETHTOOL (nm_connection_get_setting (connection, NM_TYPE_SETTING_ETHTOOL));
+	if (!s_ethtool)
+		return;
+
+	ethtool_state = g_new (EthtoolState, 1);
+	if (nm_setting_ethtool_init_features (s_ethtool, ethtool_state->requested) == 0)
+		return;
+
+	platform = nm_device_get_platform (self);
+
+	features = nm_platform_ethtool_get_link_features (platform, ifindex);
+	if (!features) {
+		_LOGW (LOGD_DEVICE, "ethtool: failure setting offload features (cannot read features)");
+		return;
+	}
+
+	if (!nm_platform_ethtool_set_features (platform,
+	                                       ifindex,
+	                                       features,
+	                                       ethtool_state->requested,
+	                                       TRUE))
+		_LOGW (LOGD_DEVICE, "ethtool: failure setting one or more offload features");
+	else
+		_LOGD (LOGD_DEVICE, "ethtool: offload features successfully set");
+
+	ethtool_state->ifindex = ifindex;
+	ethtool_state->features = g_steal_pointer (&features);
+	priv->ethtool_state = g_steal_pointer (&ethtool_state);
+}
+
+/*****************************************************************************/
+
 NMSettings *
 nm_device_get_settings (NMDevice *self)
 {
@@ -1017,15 +1101,19 @@ nm_device_assume_state_reset (NMDevice *self)
 static void
 init_ip_config_dns_priority (NMDevice *self, NMIPConfig *config)
 {
-	gs_free char *value = NULL;
-	gint priority;
-
-	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               (nm_ip_config_get_addr_family (config) == AF_INET)
-	                                                 ? "ipv4.dns-priority"
-	                                                 : "ipv6.dns-priority",
-	                                               self);
-	priority = _nm_utils_ascii_str_to_int64 (value, 10, G_MININT, G_MAXINT, 0);
+	const char *property;
+	int priority;
+
+	property = (nm_ip_config_get_addr_family (config) == AF_INET)
+	             ? "ipv4.dns-priority"
+	             : "ipv6.dns-priority";
+
+	priority = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                        property,
+	                                                        self,
+	                                                        G_MININT,
+	                                                        G_MAXINT,
+	                                                        0);
 	nm_ip_config_set_dns_priority (config, priority ?: NM_DNS_PRIORITY_DEFAULT_NORMAL);
 }
 
@@ -1627,6 +1715,41 @@ nm_device_parent_notify_changed (NMDevice *self,
 
 /*****************************************************************************/
 
+const char *
+nm_device_parent_find_for_connection (NMDevice *self,
+                                      const char *current_setting_parent)
+{
+	const char *new_parent;
+	NMDevice *parent_device;
+
+	parent_device = nm_device_parent_get_device (self);
+	if (!parent_device)
+		return NULL;
+
+	new_parent = nm_device_get_iface (parent_device);
+	if (!new_parent)
+		return NULL;
+
+	if (   current_setting_parent
+	    && !nm_streq (current_setting_parent, new_parent)
+	    && nm_utils_is_uuid (current_setting_parent)) {
+		NMSettingsConnection *parent_connection;
+
+		/* Don't change a parent specified by UUID if it's still valid */
+		parent_connection = nm_settings_get_connection_by_uuid (nm_device_get_settings (self),
+		                                                        current_setting_parent);
+		if (   parent_connection
+		    && nm_device_check_connection_compatible (parent_device,
+		                                              nm_settings_connection_get_connection (parent_connection),
+		                                              NULL))
+			return current_setting_parent;
+	}
+
+	return new_parent;
+}
+
+/*****************************************************************************/
+
 static void
 _stats_update_counters (NMDevice *self,
                         guint64 tx_bytes,
@@ -1731,7 +1854,11 @@ static gboolean
 get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMPlatform *platform = nm_device_get_platform (self);
 	const NMPlatformLink *pllink;
+	const guint8 *hwaddr;
+	guint8 pseudo_hwaddr[ETH_ALEN];
+	guint hwaddr_len;
 	int ifindex;
 	gboolean success;
 
@@ -1739,7 +1866,7 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 	ifindex = nm_device_get_ip_ifindex (self);
 	g_return_val_if_fail (ifindex > 0, FALSE);
 
-	pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex);
+	pllink = nm_platform_link_get (platform, ifindex);
 	if (   !pllink
 	    || NM_IN_SET (pllink->type, NM_LINK_TYPE_NONE, NM_LINK_TYPE_UNKNOWN))
 		return FALSE;
@@ -1749,9 +1876,35 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 	if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX)
 		g_return_val_if_reached (FALSE);
 
+	hwaddr = pllink->addr.data;
+	hwaddr_len = pllink->addr.len;
+
+	if (pllink->type == NM_LINK_TYPE_6LOWPAN) {
+		/* If the underlying IEEE 802.15.4 device has a short address we generate
+		 * a "pseudo 48-bit address" that's to be used in the same fashion as a
+		 * wired Ethernet address. The mechanism is specified in Section 6. of
+		 * RFC 4944 */
+		guint16 pan_id;
+		guint16 short_addr;
+
+		short_addr = nm_platform_wpan_get_short_addr (platform, pllink->parent);
+		if (short_addr != G_MAXUINT16) {
+			pan_id = nm_platform_wpan_get_pan_id (platform, pllink->parent);
+			pseudo_hwaddr[0] = short_addr & 0xff;
+			pseudo_hwaddr[1] = (short_addr >> 8) & 0xff;
+			pseudo_hwaddr[2] = 0;
+			pseudo_hwaddr[3] = 0;
+			pseudo_hwaddr[4] = pan_id & 0xff;
+			pseudo_hwaddr[5] = (pan_id >> 8) & 0xff;
+
+			hwaddr = pseudo_hwaddr;
+			hwaddr_len = G_N_ELEMENTS (pseudo_hwaddr);
+		}
+	}
+
 	success = nm_utils_get_ipv6_interface_identifier (pllink->type,
-	                                                  pllink->addr.data,
-	                                                  pllink->addr.len,
+	                                                  hwaddr,
+	                                                  hwaddr_len,
 	                                                  priv->dev_id,
 	                                                  out_iid);
 	if (!success) {
@@ -1865,7 +2018,9 @@ nm_device_get_route_metric_default (NMDeviceType device_type)
 	 */
 
 	switch (device_type) {
-	/* 50 is reserved for VPN (NM_VPN_ROUTE_METRIC_DEFAULT) */
+	/* 50 is also used for VPN plugins (NM_VPN_ROUTE_METRIC_DEFAULT) */
+	case NM_DEVICE_TYPE_WIREGUARD:
+		return 50;
 	case NM_DEVICE_TYPE_ETHERNET:
 	case NM_DEVICE_TYPE_VETH:
 		return 100;
@@ -1905,10 +2060,14 @@ nm_device_get_route_metric_default (NMDeviceType device_type)
 		return 700;
 	case NM_DEVICE_TYPE_BT:
 		return 750;
+	case NM_DEVICE_TYPE_6LOWPAN:
+		return 775;
 	case NM_DEVICE_TYPE_OVS_BRIDGE:
 	case NM_DEVICE_TYPE_OVS_INTERFACE:
 	case NM_DEVICE_TYPE_OVS_PORT:
 		return 800;
+	case NM_DEVICE_TYPE_WPAN:
+		return 850;
 	case NM_DEVICE_TYPE_GENERIC:
 		return 950;
 	case NM_DEVICE_TYPE_UNKNOWN:
@@ -1953,10 +2112,10 @@ guint32
 nm_device_get_route_metric (NMDevice *self,
                             int addr_family)
 {
-	char *value;
 	gint64 route_metric;
 	NMSettingIPConfig *s_ip;
 	NMConnection *connection;
+	const char *property;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), G_MAXUINT32);
 	g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), G_MAXUINT32);
@@ -1981,15 +2140,13 @@ nm_device_get_route_metric (NMDevice *self,
 	/* use the current NMConfigData, which makes this configuration reloadable.
 	 * Note that that means that the route-metric might change between SIGHUP.
 	 * You must cache the returned value if that is a problem. */
-	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               addr_family == AF_INET ? "ipv4.route-metric" : "ipv6.route-metric", self);
-	if (value) {
-		route_metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
-		g_free (value);
-
-		if (route_metric >= 0)
-			goto out;
-	}
+	property = addr_family == AF_INET ? "ipv4.route-metric" : "ipv6.route-metric";
+	route_metric = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                            property,
+	                                                            self,
+	                                                            0, G_MAXUINT32, -1);
+	if (route_metric >= 0)
+		goto out;
 
 	route_metric = nm_manager_device_route_metric_reserve (nm_manager_get (),
 	                                                       nm_device_get_ip_ifindex (self),
@@ -2009,21 +2166,37 @@ _get_mdns (NMDevice *self)
 	connection = nm_device_get_applied_connection (self);
 	if (connection)
 		mdns = nm_setting_connection_get_mdns (nm_connection_get_setting_connection (connection));
+	if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT)
+		return mdns;
 
-	if (mdns == NM_SETTING_CONNECTION_MDNS_DEFAULT) {
-		gs_free char *value = NULL;
+	return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                    "connection.mdns",
+	                                                    self,
+	                                                    NM_SETTING_CONNECTION_MDNS_NO,
+	                                                    NM_SETTING_CONNECTION_MDNS_YES,
+	                                                    NM_SETTING_CONNECTION_MDNS_DEFAULT);
+}
 
-		value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-		                                               "connection.mdns",
-		                                               self);
-		mdns = _nm_utils_ascii_str_to_int64 (value,
-		                                     10,
-		                                     NM_SETTING_CONNECTION_MDNS_NO,
-		                                     NM_SETTING_CONNECTION_MDNS_YES,
-		                                     NM_SETTING_CONNECTION_MDNS_DEFAULT);
-	}
+static NMSettingConnectionLlmnr
+_get_llmnr (NMDevice *self)
+{
+	NMConnection *connection;
+	NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_SETTING_CONNECTION_LLMNR_DEFAULT);
+
+	connection = nm_device_get_applied_connection (self);
+	if (connection)
+		llmnr = nm_setting_connection_get_llmnr (nm_connection_get_setting_connection (connection));
+	if (llmnr != NM_SETTING_CONNECTION_LLMNR_DEFAULT)
+		return llmnr;
 
-	return mdns;
+	return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                    "connection.llmnr",
+	                                                    self,
+	                                                    NM_SETTING_CONNECTION_LLMNR_NO,
+	                                                    NM_SETTING_CONNECTION_LLMNR_YES,
+	                                                    NM_SETTING_CONNECTION_LLMNR_DEFAULT);
 }
 
 guint32
@@ -2066,14 +2239,13 @@ nm_device_get_route_table (NMDevice *self,
 		 * connection. Otherwise, the connection is not active, and the
 		 * connection default doesn't matter. */
 		if (route_table == 0) {
-			gs_free char *value = NULL;
+			const char *property;
 
-			value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-			                                               addr_family == AF_INET
-			                                                 ? "ipv4.route-table"
-			                                                 : "ipv6.route-table",
-			                                               self);
-			route_table = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, 0);
+			property = addr_family == AF_INET ? "ipv4.route-table" : "ipv6.route-table";
+			route_table = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+			                                                           property,
+			                                                           self,
+			                                                           0, G_MAXUINT32, 0);
 		}
 	}
 
@@ -2191,6 +2363,22 @@ nm_device_get_settings_connection (NMDevice *self)
 }
 
 NMConnection *
+nm_device_get_settings_connection_get_connection (NMDevice *self)
+{
+	NMSettingsConnection *sett_con;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->act_request.obj)
+		return NULL;
+
+	sett_con = nm_act_request_get_settings_connection (priv->act_request.obj);
+	if (!sett_con)
+		return NULL;
+
+	return nm_settings_connection_get_connection (sett_con);
+}
+
+NMConnection *
 nm_device_get_applied_connection (NMDevice *self)
 {
 	NMDevicePrivate *priv;
@@ -3903,7 +4091,7 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink
 }
 
 static void
-device_init_sriov_num_vfs (NMDevice *self)
+device_init_static_sriov_num_vfs (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gs_free char *value = NULL;
@@ -3917,8 +4105,8 @@ device_init_sriov_num_vfs (NMDevice *self)
 		                                          NULL);
 		num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1);
 		if (num_vfs >= 0) {
-			nm_platform_link_set_sriov_num_vfs (nm_device_get_platform (self),
-			                                    priv->ifindex, num_vfs);
+			nm_platform_link_set_sriov_params (nm_device_get_platform (self),
+			                                   priv->ifindex, num_vfs, -1);
 		}
 	}
 }
@@ -3937,7 +4125,7 @@ config_changed (NMConfig *config,
 		priv->ignore_carrier = nm_config_data_get_ignore_carrier (config_data, self);
 
 	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_VALUES))
-		device_init_sriov_num_vfs (self);
+		device_init_static_sriov_num_vfs (self);
 }
 
 static void
@@ -4081,7 +4269,7 @@ realize_start_setup (NMDevice *self,
 
 	nm_device_set_carrier_from_platform (self);
 
-	device_init_sriov_num_vfs (self);
+	device_init_static_sriov_num_vfs (self);
 
 	nm_assert (!priv->stats.timeout_id);
 	real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms);
@@ -4236,6 +4424,9 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 		}
 	}
 
+	nm_clear_g_source (&priv->queued_ip_config_id_4);
+	nm_clear_g_source (&priv->queued_ip_config_id_6);
+
 	g_object_freeze_notify (G_OBJECT (self));
 	NM_DEVICE_GET_CLASS (self)->unrealize_notify (self);
 
@@ -5038,7 +5229,7 @@ nm_device_autoconnect_allowed (NMDevice *self)
 
 static gboolean
 can_auto_connect (NMDevice *self,
-                  NMConnection *connection,
+                  NMSettingsConnection *sett_conn,
                   char **specific_object)
 {
 	nm_assert (!specific_object || !*specific_object);
@@ -5048,27 +5239,27 @@ can_auto_connect (NMDevice *self,
 /**
  * nm_device_can_auto_connect:
  * @self: an #NMDevice
- * @connection: a #NMConnection
+ * @sett_conn: a #NMSettingsConnection
  * @specific_object: (out) (transfer full): on output, the path of an
  *   object associated with the returned connection, to be passed to
  *   nm_manager_activate_connection(), or %NULL.
  *
- * Checks if @connection can be auto-activated on @self right now.
+ * Checks if @sett_conn can be auto-activated on @self right now.
  * This requires, at a minimum, that the connection be compatible with
  * @self, and that it have the #NMSettingConnection:autoconnect property
  * set, and that the device allow auto connections. Some devices impose
  * additional requirements. (Eg, a Wi-Fi connection can only be activated
  * if its SSID was seen in the last scan.)
  *
- * Returns: %TRUE, if the @connection can be auto-activated.
+ * Returns: %TRUE, if the @sett_conn can be auto-activated.
  **/
 gboolean
 nm_device_can_auto_connect (NMDevice *self,
-                            NMConnection *connection,
+                            NMSettingsConnection *sett_conn,
                             char **specific_object)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn), FALSE);
 	g_return_val_if_fail (!specific_object || !*specific_object, FALSE);
 
 	/* the caller must ensure that nm_device_autoconnect_allowed() returns
@@ -5080,10 +5271,14 @@ nm_device_can_auto_connect (NMDevice *self,
 	 * over and over again. The caller is supposed to do that. */
 	nm_assert (nm_device_autoconnect_allowed (self));
 
-	if (!nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_NONE, NULL))
+	if (!nm_device_check_connection_available (self,
+	                                           nm_settings_connection_get_connection (sett_conn),
+	                                           NM_DEVICE_CHECK_CON_AVAILABLE_NONE,
+	                                           NULL,
+	                                           NULL))
 		return FALSE;
 
-	if (!NM_DEVICE_GET_CLASS (self)->can_auto_connect (self, connection, specific_object))
+	if (!NM_DEVICE_GET_CLASS (self)->can_auto_connect (self, sett_conn, specific_object))
 		return FALSE;
 
 	return TRUE;
@@ -5204,8 +5399,10 @@ nm_device_generate_connection (NMDevice *self,
 	              NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
 	              NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
 	              NULL);
-	if (klass->connection_type)
-		g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, klass->connection_type, NULL);
+
+	if (klass->connection_type_supported)
+		g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, klass->connection_type_supported, NULL);
+
 	nm_connection_add_setting (connection, s_con);
 
 	/* If the device is a slave, update various slave settings */
@@ -5357,9 +5554,9 @@ nm_device_match_parent (NMDevice *self, const char *parent)
 }
 
 gboolean
-nm_device_match_hwaddr (NMDevice *device,
-                        NMConnection *connection,
-                        gboolean fail_if_no_hwaddr)
+nm_device_match_parent_hwaddr (NMDevice *device,
+                               NMConnection *connection,
+                               gboolean fail_if_no_hwaddr)
 {
 	NMSettingWired *s_wired;
 	NMDevice *parent_device;
@@ -5383,21 +5580,54 @@ nm_device_match_hwaddr (NMDevice *device,
 }
 
 static gboolean
-check_connection_compatible (NMDevice *self, NMConnection *connection)
+check_connection_compatible (NMDevice *self, NMConnection *connection, GError **error)
 {
 	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);
+	gs_free_error GError *local = NULL;
+	gs_free char *conn_iface = NULL;
+	NMDeviceClass *klass;
+	const char *const *patterns;
+	NMSettingMatch *s_match;
+	guint num_patterns;
+
+	klass = NM_DEVICE_GET_CLASS (self);
+	if (klass->connection_type_check_compatible) {
+		if (!_nm_connection_check_main_setting (connection,
+		                                        klass->connection_type_check_compatible,
+		                                        error))
+			return FALSE;
+	}
+
+	conn_iface = nm_manager_get_connection_iface (nm_manager_get (),
+	                                              connection,
+	                                              NULL,
+	                                              &local);
 
 	/* 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);
-
-	if (strcmp (conn_iface, device_iface) != 0)
+	if (!conn_iface) {
+		if (nm_connection_is_virtual (connection)) {
+			nm_utils_error_set (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                    "cannot get interface name due to %s", local->message);
+			return FALSE;
+		}
+	} else if (!nm_streq0 (conn_iface, device_iface)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "mismatching interface name");
 		return FALSE;
+	}
+
+	s_match = (NMSettingMatch *) nm_connection_get_setting (connection,
+	                                                        NM_TYPE_SETTING_MATCH);
+	if (s_match) {
+		patterns = nm_setting_match_get_interface_names (s_match, &num_patterns);
+		if (!nm_wildcard_match_check (device_iface, patterns, num_patterns)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "device does not satisfy match.interface-name property");
+			return FALSE;
+		}
+	}
 
 	return TRUE;
 }
@@ -5406,6 +5636,11 @@ check_connection_compatible (NMDevice *self, NMConnection *connection)
  * nm_device_check_connection_compatible:
  * @self: an #NMDevice
  * @connection: an #NMConnection
+ * @error: optional reason why it is incompatible. Note that the
+ *   error code is set to %NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
+ *   if the profile is fundamentally incompatible with the device
+ *   (most commonly, because the device-type does not support the
+ *   connection-type).
  *
  * Checks if @connection could potentially be activated on @self.
  * This means only that @self has the proper capabilities, and that
@@ -5418,12 +5653,12 @@ check_connection_compatible (NMDevice *self, NMConnection *connection)
  *   @self.
  */
 gboolean
-nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection)
+nm_device_check_connection_compatible (NMDevice *self, NMConnection *connection, GError **error)
 {
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
 
-	return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection);
+	return NM_DEVICE_GET_CLASS (self)->check_connection_compatible (self, connection, error);
 }
 
 gboolean
@@ -5439,7 +5674,7 @@ nm_device_check_slave_connection_compatible (NMDevice *self, NMConnection *slave
 		return FALSE;
 
 	/* All masters should have connection type set */
-	connection_type = NM_DEVICE_GET_CLASS (self)->connection_type;
+	connection_type = NM_DEVICE_GET_CLASS (self)->connection_type_supported;
 	g_return_val_if_fail (connection_type, FALSE);
 
 	s_con = nm_connection_get_setting_connection (slave);
@@ -5810,24 +6045,151 @@ lldp_rx_enabled (NMDevice *self)
 
 	lldp = nm_setting_connection_get_lldp (s_con);
 	if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) {
-		gs_free char *value = NULL;
-
-		value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-		                                               "connection.lldp",
-		                                               self);
-		lldp = _nm_utils_ascii_str_to_int64 (value, 10,
-		                                     NM_SETTING_CONNECTION_LLDP_DEFAULT,
-		                                     NM_SETTING_CONNECTION_LLDP_ENABLE_RX,
-		                                     NM_SETTING_CONNECTION_LLDP_DEFAULT);
+		lldp = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+		                                                    "connection.lldp",
+		                                                    self,
+		                                                    NM_SETTING_CONNECTION_LLDP_DEFAULT,
+		                                                    NM_SETTING_CONNECTION_LLDP_ENABLE_RX,
+		                                                    NM_SETTING_CONNECTION_LLDP_DEFAULT);
 		if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT)
 			lldp = NM_SETTING_CONNECTION_LLDP_DISABLE;
 	}
 	return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX;
 }
 
+static NMPlatformVF *
+sriov_vf_config_to_platform (NMDevice *self,
+                             NMSriovVF *vf,
+                             GError **error)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gs_free NMPlatformVF *plat_vf = NULL;
+	const guint *vlan_ids;
+	GVariant *variant;
+	guint i, num_vlans;
+	gsize length;
+
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
+	plat_vf = g_malloc0 (  sizeof (NMPlatformVF)
+	                     + sizeof (NMPlatformVFVlan) * num_vlans);
+
+	plat_vf->index = nm_sriov_vf_get_index (vf);
+
+	variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK);
+	if (variant)
+		plat_vf->spoofchk = g_variant_get_boolean (variant);
+	else
+		plat_vf->spoofchk = -1;
+
+	variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_TRUST);
+	if (variant)
+		plat_vf->trust = g_variant_get_boolean (variant);
+	else
+		plat_vf->trust = -1;
+
+	variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC);
+	if (variant) {
+		if (!_nm_utils_hwaddr_aton (g_variant_get_string (variant, NULL),
+		                            plat_vf->mac.data,
+		                            sizeof (plat_vf->mac.data),
+		                            &length)) {
+			g_set_error (error,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_FAILED,
+			             "invalid MAC %s",
+			             g_variant_get_string (variant, NULL));
+			return NULL;
+		}
+		if (length != priv->hw_addr_len) {
+			g_set_error (error,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_FAILED,
+			             "wrong MAC length %" G_GSIZE_FORMAT ", should be %u",
+			             length, priv->hw_addr_len);
+			return NULL;
+		}
+		plat_vf->mac.len = length;
+	}
+
+	variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE);
+	if (variant)
+		plat_vf->min_tx_rate = g_variant_get_uint32 (variant);
+
+	variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE);
+	if (variant)
+		plat_vf->max_tx_rate = g_variant_get_uint32 (variant);
+
+	plat_vf->num_vlans = num_vlans;
+	plat_vf->vlans = (NMPlatformVFVlan *) (&plat_vf[1]);
+	for (i = 0; i < num_vlans; i++) {
+		plat_vf->vlans[i].id = vlan_ids[i];
+		plat_vf->vlans[i].qos = nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]);
+		plat_vf->vlans[i].proto_ad = nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]) == NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD;
+	}
+
+	return g_steal_pointer (&plat_vf);
+}
+
 static NMActStageReturn
 act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason)
 {
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMSettingSriov *s_sriov;
+	guint i, num;
+
+	if (   priv->ifindex > 0
+	    && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV)
+	    && (s_sriov = (NMSettingSriov *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) {
+		nm_auto_freev NMPlatformVF **plat_vfs = NULL;
+		gs_free_error GError *error = NULL;
+		NMSriovVF *vf;
+		int autoprobe;
+
+		autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov);
+		if (autoprobe == NM_TERNARY_DEFAULT) {
+			autoprobe = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+			                                                         "sriov.autoprobe-drivers",
+			                                                         self,
+			                                                         NM_TERNARY_FALSE,
+			                                                         NM_TERNARY_TRUE,
+			                                                         NM_TERNARY_TRUE);
+		}
+
+		num = nm_setting_sriov_get_num_vfs (s_sriov);
+		plat_vfs = g_new0 (NMPlatformVF *, num + 1);
+		for (i = 0; i < num; i++) {
+			vf = nm_setting_sriov_get_vf (s_sriov, i);
+			plat_vfs[i] = sriov_vf_config_to_platform (self, vf, &error);
+			if (!plat_vfs[i]) {
+				_LOGE (LOGD_DEVICE,
+				       "failed to apply SR-IOV VF '%s': %s",
+				       nm_utils_sriov_vf_to_str (vf, FALSE, NULL),
+				       error->message);
+				NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+				return NM_ACT_STAGE_RETURN_FAILURE;
+			}
+		}
+
+		if (!nm_platform_link_set_sriov_params (nm_device_get_platform (self),
+		                                        priv->ifindex,
+		                                        nm_setting_sriov_get_total_vfs (s_sriov),
+		                                        autoprobe)) {
+			_LOGE (LOGD_DEVICE, "failed to apply SR-IOV parameters");
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		}
+
+		if (!nm_platform_link_set_sriov_vfs (nm_device_get_platform (self),
+		                                     priv->ifindex,
+		                                     (const NMPlatformVF *const *) plat_vfs)) {
+			_LOGE (LOGD_DEVICE, "failed to apply SR-IOV VFs");
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED);
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		}
+	}
+
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
@@ -6031,6 +6393,8 @@ activate_stage2_device_config (NMDevice *self)
 	if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
 		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
+		_ethtool_state_set (self);
+
 		if (!tc_commit (self)) {
 			_LOGW (LOGD_IP6, "failed applying traffic control rules");
 			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
@@ -6145,27 +6509,22 @@ get_ipv4_dad_timeout (NMDevice *self)
 {
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip4 = NULL;
-	gs_free char *value = NULL;
-	gint ret = 0;
+	int timeout = -1;
 
 	connection = nm_device_get_applied_connection (self);
 	if (connection)
 		s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (s_ip4)
+		timeout = nm_setting_ip_config_get_dad_timeout (s_ip4);
+	if (timeout >= 0)
+		return timeout;
 
-	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;
+	return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                    "ipv4.dad-timeout",
+	                                                    self,
+	                                                    0,
+	                                                    NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX,
+	                                                    0);
 }
 
 static void
@@ -6185,15 +6544,30 @@ acd_data_destroy (gpointer ptr, GClosure *closure)
 static void
 ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean success)
 {
+	NMConnection *connection;
+	const char *method;
 	NMIP4Config *empty;
 
-	if (success) {
+	connection = nm_device_get_applied_connection (self);
+	nm_assert (connection);
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	nm_assert (NM_IN_STRSET (method,
+	                         NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+	                         NM_SETTING_IP4_CONFIG_METHOD_AUTO));
+
+	if (!success) {
+		nm_device_ip_method_failed (self, AF_INET,
+		                            NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE);
+		return;
+	}
+
+	if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
 		empty = _ip4_config_new (self);
 		nm_device_activate_schedule_ip4_config_result (self, empty);
 		g_object_unref (empty);
 	} else {
-		nm_device_ip_method_failed (self, AF_INET,
-		                            NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE);
+		if (NM_DEVICE_GET_PRIVATE (self)->ip4_state != IP_DONE)
+			ip_config_merge_and_apply (self, AF_INET, TRUE);
 	}
 }
 
@@ -6531,6 +6905,7 @@ ensure_con_ip_config (NMDevice *self, int addr_family)
 		nm_ip4_config_merge_setting (NM_IP4_CONFIG (con_ip_config),
 		                             nm_connection_get_setting_ip4_config (connection),
 		                             _get_mdns (self),
+		                             _get_llmnr (self),
 		                             nm_device_get_route_table (self, addr_family, TRUE),
 		                             nm_device_get_route_metric (self, addr_family));
 	} else {
@@ -6943,6 +7318,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 			nm_ip4_config_merge_setting (manual,
 			                             nm_connection_get_setting_ip4_config (connection),
 			                             NM_SETTING_CONNECTION_MDNS_DEFAULT,
+			                             NM_SETTING_CONNECTION_LLMNR_DEFAULT,
 			                             nm_device_get_route_table (self, AF_INET, TRUE),
 			                             nm_device_get_route_metric (self, AF_INET));
 
@@ -6997,19 +7373,14 @@ get_dhcp_timeout (NMDevice *self, int addr_family)
 	if (timeout)
 		return timeout;
 
-	{
-		gs_free char *value = NULL;
-
-		value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-		                                               addr_family == AF_INET
-		                                                 ? "ipv4.dhcp-timeout"
-		                                                 : "ipv6.dhcp-timeout",
-		                                               self);
-		timeout = _nm_utils_ascii_str_to_int64 (value, 10,
-		                                        0, G_MAXINT32, 0);
-		if (timeout)
-			return timeout;
-	}
+	timeout = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                       addr_family == AF_INET
+	                                                         ? "ipv4.dhcp-timeout"
+	                                                         : "ipv6.dhcp-timeout",
+	                                                       self,
+	                                                       0, G_MAXINT32, 0);
+	if (timeout)
+		return timeout;
 
 	klass = NM_DEVICE_GET_CLASS (self);
 	if (klass->get_dhcp_timeout)
@@ -7443,30 +7814,45 @@ act_stage3_ip4_config_start (NMDevice *self,
 
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
 
-	/* Start IPv4 addressing based on the method requested */
-	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) {
-		ret = dhcp4_start (self);
-		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
-			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED);
-	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0) {
-		ret = ipv4ll_start (self);
-		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
-			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED);
-	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
+	if (NM_IN_STRSET (method,
+	                  NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	                  NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+		NMSettingIPConfig *s_ip4;
 		NMIP4Config **configs, *config;
+		guint num_addresses;
 
-		config = _ip4_config_new (self);
-		nm_ip4_config_merge_setting (config,
-		                             nm_connection_get_setting_ip4_config (connection),
-		                             NM_SETTING_CONNECTION_MDNS_DEFAULT,
-		                             nm_device_get_route_table (self, AF_INET, TRUE),
-		                             nm_device_get_route_metric (self, AF_INET));
+		s_ip4 = nm_connection_get_setting_ip4_config (connection);
+		g_return_val_if_fail (s_ip4, NM_ACT_STAGE_RETURN_FAILURE);
+		num_addresses = nm_setting_ip_config_get_num_addresses (s_ip4);
+
+		if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
+			ret = dhcp4_start (self);
+			if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+				NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED);
+				return ret;
+			}
+		} else {
+			g_return_val_if_fail (num_addresses != 0, NM_ACT_STAGE_RETURN_FAILURE);
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
+		}
 
-		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) {
+		if (num_addresses) {
+			config = _ip4_config_new (self);
+			nm_ip4_config_merge_setting (config,
+			                             nm_connection_get_setting_ip4_config (connection),
+			                             NM_SETTING_CONNECTION_MDNS_DEFAULT,
+			                             NM_SETTING_CONNECTION_LLMNR_DEFAULT,
+			                             nm_device_get_route_table (self, AF_INET, TRUE),
+			                             nm_device_get_route_metric (self, AF_INET));
+			configs = g_new0 (NMIP4Config *, 2);
+			configs[0] = config;
+			ipv4_dad_start (self, configs, ipv4_manual_method_apply);
+		}
+	} else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
+		ret = ipv4ll_start (self);
+		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED);
+	} else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
 		if (out_config) {
 			*out_config = shared4_new_config (self, connection);
 			if (*out_config) {
@@ -7478,7 +7864,7 @@ act_stage3_ip4_config_start (NMDevice *self,
 			}
 		} else
 			g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE);
-	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
+	} else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	else
 		_LOGW (LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method);
@@ -7743,35 +8129,35 @@ static GBytes *
 generate_duid_llt (const guint8 *hwaddr /* ETH_ALEN bytes */,
                    gint64 time)
 {
-	GByteArray *duid_arr;
+	guint8 *arr;
 	const guint16 duid_type = htons (1);
 	const guint16 hw_type = htons (ARPHRD_ETHER);
 	const guint32 duid_time = htonl (NM_MAX (0, time - EPOCH_DATETIME_200001010000));
 
-	duid_arr = g_byte_array_sized_new (2 + 4 + 2 + ETH_ALEN);
+	arr = g_new (guint8, 2 + 2 + 4 + ETH_ALEN);
 
-	g_byte_array_append (duid_arr, (const guint8 *) &duid_type, 2);
-	g_byte_array_append (duid_arr, (const guint8 *) &hw_type, 2);
-	g_byte_array_append (duid_arr, (const guint8 *) &duid_time, 4);
-	g_byte_array_append (duid_arr, hwaddr, ETH_ALEN);
+	memcpy (&arr[0], &duid_type, 2);
+	memcpy (&arr[2], &hw_type, 2);
+	memcpy (&arr[4], &duid_time, 4);
+	memcpy (&arr[8], hwaddr, ETH_ALEN);
 
-	return g_byte_array_free_to_bytes (duid_arr);
+	return g_bytes_new_take (arr, 2 + 2 + 4 + ETH_ALEN);
 }
 
 static GBytes *
 generate_duid_ll (const guint8 *hwaddr /* ETH_ALEN bytes */)
 {
-	GByteArray *duid_arr;
+	guint8 *arr;
 	const guint16 duid_type = htons (3);
 	const guint16 hw_type = htons (ARPHRD_ETHER);
 
-	duid_arr = g_byte_array_sized_new (2 + 2 + ETH_ALEN);
+	arr = g_new (guint8, 2 + 2 + ETH_ALEN);
 
-	g_byte_array_append (duid_arr, (const guint8 *) &duid_type, 2);
-	g_byte_array_append (duid_arr, (const guint8 *) &hw_type, 2);
-	g_byte_array_append (duid_arr, hwaddr, ETH_ALEN);
+	memcpy (&arr[0], &duid_type, 2);
+	memcpy (&arr[2], &hw_type, 2);
+	memcpy (&arr[4], hwaddr, ETH_ALEN);
 
-	return g_byte_array_free_to_bytes (duid_arr);
+	return g_bytes_new_take (arr, 2 + 2 + ETH_ALEN);
 }
 
 static GBytes *
@@ -8367,10 +8753,10 @@ gint64
 nm_device_get_configured_mtu_from_connection_default (NMDevice *self,
                                                       const char *property_name)
 {
-	gs_free char *str = NULL;
-
-	str = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, property_name, self);
-	return _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1);
+	return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                    property_name,
+	                                                    self,
+	                                                    0, G_MAXUINT32, -1);
 }
 
 guint32
@@ -9023,7 +9409,8 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 		_LOGD (LOGD_IP6, "will %s userland IPv6LL", detail);
 		plerr = nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), ifindex, enable);
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
-			_NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN,
+			_NMLOG ((   plerr == NM_PLATFORM_ERROR_NOT_FOUND
+			         || plerr == NM_PLATFORM_ERROR_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN,
 			        LOGD_IP6,
 			        "failed to %s userspace IPv6LL address handling (%s)",
 			        detail,
@@ -9066,7 +9453,6 @@ static NMSettingIP6ConfigPrivacy
 _ip6_privacy_get (NMDevice *self)
 {
 	NMSettingIP6ConfigPrivacy ip6_privacy;
-	gs_free char *value = NULL;
 	NMConnection *connection;
 
 	g_return_val_if_fail (self, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
@@ -9085,14 +9471,13 @@ _ip6_privacy_get (NMDevice *self)
 		}
 	}
 
-	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               "ipv6.ip6-privacy", self);
-
 	/* 2.) use the default value from the configuration. */
-	ip6_privacy = _nm_utils_ascii_str_to_int64 (value, 10,
-	                                            NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
-	                                            NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
-	                                            NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	ip6_privacy = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                           "ipv6.ip6-privacy",
+	                                                           self,
+	                                                           NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
+	                                                           NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
+	                                                           NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 	if (ip6_privacy != NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
 		return ip6_privacy;
 
@@ -10322,6 +10707,7 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 		nm_ip4_config_merge_setting (priv->con_ip_config_4,
 		                             s_ip4_new,
 		                             _get_mdns (self),
+		                             _get_llmnr (self),
 		                             nm_device_get_route_table (self, AF_INET, TRUE),
 		                             nm_device_get_route_metric (self, AF_INET));
 
@@ -10337,10 +10723,37 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 			_set_ip_state (self, AF_INET, IP_WAIT);
 			if (!nm_device_activate_stage3_ip4_start (self))
 				_LOGW (LOGD_IP4, "Failed to apply IPv4 configuration");
-		} else {
-			if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
-				_LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration");
+			return;
 		}
+
+		if (s_ip4_old && s_ip4_new) {
+			gint64 metric_old, metric_new;
+
+			/* For dynamic IP methods (DHCP, IPv4LL, WWAN) the route metric is
+			 * set at activation/renewal time using the value from static
+			 * configuration. To support runtime change we need to update the
+			 * dynamic configuration in place and tell the DHCP client the new
+			 * value to use for future renewals.
+			 */
+			metric_old = nm_setting_ip_config_get_route_metric (s_ip4_old);
+			metric_new = nm_setting_ip_config_get_route_metric (s_ip4_new);
+
+			if (metric_old != metric_new) {
+				if (priv->dev_ip4_config.orig) {
+					nm_ip4_config_update_routes_metric ((NMIP4Config *) priv->dev_ip4_config.orig,
+					                                    nm_device_get_route_metric (self, AF_INET));
+				}
+				if (priv->wwan_ip_config_4.orig) {
+					nm_ip4_config_update_routes_metric ((NMIP4Config *) priv->wwan_ip_config_4.orig,
+					                                    nm_device_get_route_metric (self, AF_INET));
+				}
+				if (priv->dhcp4.client)
+					nm_dhcp_client_set_route_metric (priv->dhcp4.client, metric_new);
+			}
+		}
+
+		if (!ip_config_merge_and_apply (self, AF_INET, TRUE))
+			_LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration");
 	}
 }
 
@@ -10382,10 +10795,36 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 			_set_ip_state (self, AF_INET6, IP_WAIT);
 			if (!nm_device_activate_stage3_ip6_start (self))
 				_LOGW (LOGD_IP6, "Failed to apply IPv6 configuration");
-		} else {
-			if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
-				_LOGW (LOGD_IP4, "Failed to reapply IPv6 configuration");
+			return;
+		}
+
+		if (s_ip6_old && s_ip6_new) {
+			gint64 metric_old, metric_new;
+
+			/* See comment in nm_device_reactivate_ip6_config() */
+			metric_old = nm_setting_ip_config_get_route_metric (s_ip6_old);
+			metric_new = nm_setting_ip_config_get_route_metric (s_ip6_new);
+
+			if (metric_old != metric_new) {
+				if (priv->ac_ip6_config.orig) {
+					nm_ip6_config_update_routes_metric ((NMIP6Config *) priv->ac_ip6_config.orig,
+					                                    nm_device_get_route_metric (self, AF_INET6));
+				}
+				if (priv->dhcp6.ip6_config.orig) {
+					nm_ip6_config_update_routes_metric ((NMIP6Config *) priv->dhcp6.ip6_config.orig,
+					                                    nm_device_get_route_metric (self, AF_INET6));
+				}
+				if (priv->wwan_ip_config_6.orig) {
+					nm_ip6_config_update_routes_metric ((NMIP6Config *) priv->wwan_ip_config_6.orig,
+					                                    nm_device_get_route_metric (self, AF_INET6));
+				}
+				if (priv->dhcp6.client)
+					nm_dhcp_client_set_route_metric (priv->dhcp6.client, metric_new);
+			}
 		}
+
+		if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
+			_LOGW (LOGD_IP4, "Failed to reapply IPv6 configuration");
 	}
 }
 
@@ -10440,7 +10879,9 @@ can_reapply_change (NMDevice *self, const char *setting_name,
 		                                          NM_SETTING_CONNECTION_AUTOCONNECT,
 		                                          NM_SETTING_CONNECTION_ZONE,
 		                                          NM_SETTING_CONNECTION_METERED,
-		                                          NM_SETTING_CONNECTION_LLDP);
+		                                          NM_SETTING_CONNECTION_LLDP,
+		                                          NM_SETTING_CONNECTION_MDNS,
+		                                          NM_SETTING_CONNECTION_LLMNR);
 	} else if (NM_IN_STRSET (setting_name,
 	                         NM_SETTING_PROXY_SETTING_NAME)) {
 		return TRUE;
@@ -10684,7 +11125,7 @@ reapply_cb (NMDevice *self,
 		nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
 
 	if (!check_and_reapply_connection (self,
-	                                   connection ? : (NMConnection *) nm_device_get_settings_connection (self),
+	                                   connection ?: nm_device_get_settings_connection_get_connection (self),
 	                                   version_id,
 	                                   &audit_args,
 	                                   &local)) {
@@ -11171,7 +11612,7 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req)
 	if (!connection_requires_carrier (connection))
 		return FALSE;
 
-	if (!nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_ALL, NULL)) {
+	if (!nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_ALL, NULL, NULL)) {
 		/* We passed all @flags we have, and no @specific_object.
 		 * This equals maximal availability, if a connection is not available
 		 * in this case, it is not waiting for carrier.
@@ -11182,7 +11623,9 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req)
 		return FALSE;
 	}
 
-	if (nm_device_check_connection_available (self, connection, NM_DEVICE_CHECK_CON_AVAILABLE_ALL & ~_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER, NULL)) {
+	if (nm_device_check_connection_available (self, connection,
+	                                          NM_DEVICE_CHECK_CON_AVAILABLE_ALL & ~_NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER,
+	                                          NULL, NULL)) {
 		/* The connection was available with flags ALL, and it is still available
 		 * if we pretend not to wait for carrier. That means that the
 		 * connection is available now, and does not wait for carrier.
@@ -11470,7 +11913,8 @@ nm_device_set_ip_config (NMDevice *self,
 		                     NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED)
 		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request.obj)) == NM_ACTIVATION_TYPE_EXTERNAL) {
 			g_object_freeze_notify (G_OBJECT (settings_connection));
-			nm_connection_add_setting (NM_CONNECTION (settings_connection),
+			/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
+			nm_connection_add_setting (nm_settings_connection_get_connection (settings_connection),
 			                           IS_IPv4
 			                             ? nm_ip4_config_create_setting (priv->ip_config_4)
 			                             : nm_ip6_config_create_setting (priv->ip_config_6));
@@ -11679,11 +12123,11 @@ spawn_ping (NMDevice *self)
 	gboolean ret;
 
 	args[6] = str_timeout = g_strdup_printf ("%u", priv->gw_ping.deadline);
-	tmp_str = g_strjoinv (" ", (gchar **) args);
+	tmp_str = g_strjoinv (" ", (char **) args);
 	_LOGD (priv->gw_ping.log_domain, "ping: running '%s'", tmp_str);
 
 	ret = g_spawn_async ("/",
-	                     (gchar **) args,
+	                     (char **) args,
 	                      NULL,
 	                      G_SPAWN_DO_NOT_REAP_CHILD,
 	                      NULL,
@@ -11719,7 +12163,7 @@ respawn_ping_cb (gpointer user_data)
 }
 
 static void
-ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data)
+ip_check_ping_watch_cb (GPid pid, int status, gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -12929,7 +13373,7 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 	                                                               NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY))
 		return;
 
-	s_con_settings = nm_connection_get_setting_connection ((NMConnection *) settings_connection);
+	s_con_settings = nm_connection_get_setting_connection (nm_settings_connection_get_connection (settings_connection));
 	s_con_applied = nm_connection_get_setting_connection (applied_connection);
 
 	if (g_strcmp0 ((zone = nm_setting_connection_get_zone (s_con_settings)),
@@ -13043,38 +13487,89 @@ static gboolean
 _nm_device_check_connection_available (NMDevice *self,
                                        NMConnection *connection,
                                        NMDeviceCheckConAvailableFlags flags,
-                                       const char *specific_object)
+                                       const char *specific_object,
+                                       GError **error)
 {
 	NMDeviceState state;
+	GError *local = NULL;
 
 	/* 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);
+		if (nm_device_is_software (self)) {
+			if (!nm_device_check_connection_compatible (self, connection,
+			                                            error ? &local : NULL)) {
+				if (error) {
+					g_return_val_if_fail (local, FALSE);
+					nm_utils_error_set (error,
+					                    local->domain == NM_UTILS_ERROR
+					                      ? local->code
+					                      : NM_UTILS_ERROR_UNKNOWN,
+					                    "profile is not compatible with software device (%s)",
+					                    local->message);
+					g_error_free (local);
+				}
+				return FALSE;
+			}
+			return TRUE;
+		}
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
+		                            "hardware device is not realized");
 		return FALSE;
 	}
 
 	state = nm_device_get_state (self);
-	if (state < NM_DEVICE_STATE_UNMANAGED)
-		return FALSE;
-	if (   state < NM_DEVICE_STATE_UNAVAILABLE
-	    && (   (   !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))))
+	if (state < NM_DEVICE_STATE_UNMANAGED) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
+		                            "device is in unknown state");
 		return FALSE;
+	}
+	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+		if (NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)) {
+			if (!nm_device_get_managed (self, TRUE)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
+				                            "device is unmanaged");
+				return FALSE;
+			}
+		} else {
+			if (!nm_device_get_managed (self, FALSE)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
+				                            "device is unmanaged for interal request");
+				return FALSE;
+			}
+		}
+	}
 	if (   state < NM_DEVICE_STATE_DISCONNECTED
-	    && !nm_device_is_software (self)
-	    && (   (   !NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)
-	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE))
-	        || (    NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)
-	            && !nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST))))
-		return FALSE;
+	    && !nm_device_is_software (self)) {
+		if (NM_FLAGS_ANY (flags, NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST)) {
+			if (!nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "device is not available");
+				return FALSE;
+			}
+		} else {
+			if (!nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "device is not available for internal request");
+				return FALSE;
+			}
+		}
+	}
 
-	if (!nm_device_check_connection_compatible (self, connection))
+	if (!nm_device_check_connection_compatible (self, connection,
+	                                            error ? &local : NULL)) {
+		if (error) {
+			nm_utils_error_set (error,
+			                    local->domain == NM_UTILS_ERROR
+			                      ? local->code
+			                      : NM_UTILS_ERROR_UNKNOWN,
+			                    "profile is not compatible with device (%s)",
+			                    local->message);
+			g_error_free (local);
+		}
 		return FALSE;
+	}
 
-	return NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, flags, specific_object);
+	return NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, flags, specific_object, error);
 }
 
 /**
@@ -13087,6 +13582,7 @@ _nm_device_check_connection_available (NMDevice *self,
  * @specific_object: a device type dependent argument to further
  *   filter the result. Passing a non %NULL specific object can only reduce
  *   the availability of a connection.
+ * @error: optionally give reason why not available.
  *
  * Check if @connection is available to be activated on @self.
  *
@@ -13096,11 +13592,12 @@ gboolean
 nm_device_check_connection_available (NMDevice *self,
                                       NMConnection *connection,
                                       NMDeviceCheckConAvailableFlags flags,
-                                      const char *specific_object)
+                                      const char *specific_object,
+                                      GError **error)
 {
 	gboolean available;
 
-	available = _nm_device_check_connection_available (self, connection, flags, specific_object);
+	available = _nm_device_check_connection_available (self, connection, flags, specific_object, error);
 
 #if NM_MORE_ASSERTS >= 2
 	{
@@ -13110,7 +13607,7 @@ nm_device_check_connection_available (NMDevice *self,
 		gboolean available_all[NM_DEVICE_CHECK_CON_AVAILABLE_ALL + 1] = { FALSE };
 
 		for (i = 0; i <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; i++)
-			available_all[i] = _nm_device_check_connection_available (self, connection, i, specific_object);
+			available_all[i] = _nm_device_check_connection_available (self, connection, i, specific_object, NULL);
 
 		for (i = 0; i <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; i++) {
 			for (j = 1; j <= NM_DEVICE_CHECK_CON_AVAILABLE_ALL; j <<= 1) {
@@ -13137,22 +13634,23 @@ available_connections_del_all (NMDevice *self)
 }
 
 static gboolean
-available_connections_add (NMDevice *self, NMConnection *connection)
+available_connections_add (NMDevice *self, NMSettingsConnection *sett_conn)
 {
-	return g_hash_table_add (self->_priv->available_connections, g_object_ref (connection));
+	return g_hash_table_add (self->_priv->available_connections, g_object_ref (sett_conn));
 }
 
 static gboolean
-available_connections_del (NMDevice *self, NMConnection *connection)
+available_connections_del (NMDevice *self, NMSettingsConnection *sett_conn)
 {
-	return g_hash_table_remove (self->_priv->available_connections, connection);
+	return g_hash_table_remove (self->_priv->available_connections, sett_conn);
 }
 
 static gboolean
 check_connection_available (NMDevice *self,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
+                            const char *specific_object,
+                            GError **error)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
@@ -13178,6 +13676,8 @@ check_connection_available (NMDevice *self,
 	if (nm_device_is_master (self))
 		return TRUE;
 
+	nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+	                            "device has no carrier");
 	return FALSE;
 }
 
@@ -13188,7 +13688,7 @@ nm_device_recheck_available_connections (NMDevice *self)
 	NMSettingsConnection *const*connections;
 	gboolean changed = FALSE;
 	GHashTableIter h_iter;
-	NMConnection *connection;
+	NMSettingsConnection *sett_conn;
 	guint i;
 	gs_unref_hashtable GHashTable *prune_list = NULL;
 
@@ -13199,29 +13699,30 @@ nm_device_recheck_available_connections (NMDevice *self)
 	if (g_hash_table_size (priv->available_connections) > 0) {
 		prune_list = g_hash_table_new (nm_direct_hash, NULL);
 		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);
+		while (g_hash_table_iter_next (&h_iter, (gpointer *) &sett_conn, NULL))
+			g_hash_table_add (prune_list, sett_conn);
 	}
 
 	connections = nm_settings_get_connections (priv->settings, NULL);
 	for (i = 0; connections[i]; i++) {
-		connection = (NMConnection *) connections[i];
+		sett_conn = connections[i];
 
 		if (nm_device_check_connection_available (self,
-		                                          connection,
+		                                          nm_settings_connection_get_connection (sett_conn),
 		                                          NM_DEVICE_CHECK_CON_AVAILABLE_NONE,
+		                                          NULL,
 		                                          NULL)) {
-			if (available_connections_add (self, connection))
+			if (available_connections_add (self, sett_conn))
 				changed = TRUE;
 			if (prune_list)
-				g_hash_table_remove (prune_list, connection);
+				g_hash_table_remove (prune_list, sett_conn);
 		}
 	}
 
 	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))
+		while (g_hash_table_iter_next (&h_iter, (gpointer *) &sett_conn, NULL)) {
+			if (available_connections_del (self, sett_conn))
 				changed = TRUE;
 		}
 	}
@@ -13248,7 +13749,7 @@ nm_device_get_best_connection (NMDevice *self,
                                GError **error)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMSettingsConnection *connection = NULL;
+	NMSettingsConnection *sett_conn = NULL;
 	NMSettingsConnection *candidate;
 	guint64 best_timestamp = 0;
 	GHashTableIter iter;
@@ -13262,42 +13763,44 @@ nm_device_get_best_connection (NMDevice *self,
 		 */
 		if (    specific_object /* << Optimization: we know that the connection is available without @specific_object.  */
 		    && !nm_device_check_connection_available (self,
-		                                              NM_CONNECTION (candidate),
+		                                              nm_settings_connection_get_connection (candidate),
 		                                              _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST,
-		                                              specific_object))
+		                                              specific_object,
+		                                              NULL))
 			continue;
 
 		nm_settings_connection_get_timestamp (candidate, &candidate_timestamp);
-		if (!connection || (candidate_timestamp > best_timestamp)) {
-			connection = candidate;
+		if (!sett_conn || (candidate_timestamp > best_timestamp)) {
+			sett_conn = candidate;
 			best_timestamp = candidate_timestamp;
 		}
 	}
 
-	if (!connection) {
+	if (!sett_conn) {
 		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
 		             "The device '%s' has no connections available for activation.",
 		              nm_device_get_iface (self));
 	}
 
-	return connection;
+	return sett_conn;
 }
 
 static void
-cp_connection_added_or_updated (NMDevice *self, NMConnection *connection)
+cp_connection_added_or_updated (NMDevice *self, NMSettingsConnection *sett_conn)
 {
 	gboolean changed;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
 
 	if (nm_device_check_connection_available (self,
-	                                          connection,
+	                                          nm_settings_connection_get_connection (sett_conn),
 	                                          _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST,
+	                                          NULL,
 	                                          NULL))
-		changed = available_connections_add (self, connection);
+		changed = available_connections_add (self, sett_conn);
 	else
-		changed = available_connections_del (self, connection);
+		changed = available_connections_del (self, sett_conn);
 
 	if (changed) {
 		_notify (self, PROP_AVAILABLE_CONNECTIONS);
@@ -13306,25 +13809,25 @@ cp_connection_added_or_updated (NMDevice *self, NMConnection *connection)
 }
 
 static void
-cp_connection_added (NMSettings *settings, NMConnection *connection, gpointer user_data)
+cp_connection_added (NMSettings *settings, NMSettingsConnection *sett_conn, gpointer user_data)
 {
-	cp_connection_added_or_updated (user_data, connection);
+	cp_connection_added_or_updated (user_data, sett_conn);
 }
 
 static void
-cp_connection_updated (NMSettings *settings, NMConnection *connection, gboolean by_user, gpointer user_data)
+cp_connection_updated (NMSettings *settings, NMSettingsConnection *sett_conn, gboolean by_user, gpointer user_data)
 {
-	cp_connection_added_or_updated (user_data, connection);
+	cp_connection_added_or_updated (user_data, sett_conn);
 }
 
 static void
-cp_connection_removed (NMSettings *settings, NMConnection *connection, gpointer user_data)
+cp_connection_removed (NMSettings *settings, NMSettingsConnection *sett_conn, gpointer user_data)
 {
 	NMDevice *self = user_data;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	if (available_connections_del (self, connection)) {
+	if (available_connections_del (self, sett_conn)) {
 		_notify (self, PROP_AVAILABLE_CONNECTIONS);
 		available_connections_check_delete_unrealized (self);
 	}
@@ -13683,6 +14186,8 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 		priv->ip6_mtu_initial = 0;
 	}
 
+	_ethtool_state_reset (self);
+
 	_cleanup_generic_post (self, cleanup_type);
 }
 
@@ -13962,7 +14467,7 @@ _set_state_full (NMDevice *self,
 	NMDeviceState old_state;
 	NMActRequest *req;
 	gboolean no_firmware = FALSE;
-	NMSettingsConnection *connection;
+	NMSettingsConnection *sett_conn;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -14221,10 +14726,10 @@ _set_state_full (NMDevice *self,
 			break;
 		}
 
-		connection = nm_device_get_settings_connection (self);
+		sett_conn = nm_device_get_settings_connection (self);
 		_LOGW (LOGD_DEVICE | LOGD_WIFI,
 		       "Activation: failed for connection '%s'",
-		       connection ? nm_settings_connection_get_id (connection) : "<unknown>");
+		       sett_conn ? nm_settings_connection_get_id (sett_conn) : "<unknown>");
 
 		/* Notify any slaves of the unexpected failure */
 		nm_device_master_release_slaves (self);
@@ -14234,8 +14739,8 @@ _set_state_full (NMDevice *self,
 		 * failed (zero timestamp), connections that succeeded (non-zero timestamp),
 		 * and those we haven't tried yet (no timestamp).
 		 */
-		if (connection && !nm_settings_connection_get_timestamp (connection, NULL))
-			nm_settings_connection_update_timestamp (connection, (guint64) 0, TRUE);
+		if (sett_conn && !nm_settings_connection_get_timestamp (sett_conn, NULL))
+			nm_settings_connection_update_timestamp (sett_conn, (guint64) 0, TRUE);
 
 		/* Schedule the transition to DISCONNECTED.  The device can't transition
 		 * immediately because we can't change states again from the state
@@ -15149,8 +15654,7 @@ nm_device_get_supplicant_timeout (NMDevice *self)
 {
 	NMConnection *connection;
 	NMSetting8021x *s_8021x;
-	gs_free char *value = NULL;
-	gint timeout;
+	int timeout;
 #define SUPPLICANT_DEFAULT_TIMEOUT 25
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), SUPPLICANT_DEFAULT_TIMEOUT);
@@ -15164,11 +15668,12 @@ nm_device_get_supplicant_timeout (NMDevice *self)
 			return timeout;
 	}
 
-	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               "802-1x.auth-timeout",
-	                                               self);
-	return _nm_utils_ascii_str_to_int64 (value, 10, 1, G_MAXINT32,
-	                                     SUPPLICANT_DEFAULT_TIMEOUT);
+	return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                    "802-1x.auth-timeout",
+	                                                    self,
+	                                                    1,
+	                                                    G_MAXINT32,
+	                                                    SUPPLICANT_DEFAULT_TIMEOUT);
 }
 
 gboolean
@@ -15191,12 +15696,10 @@ nm_device_auth_retries_try_next (NMDevice *self)
 			auth_retries = nm_setting_connection_get_auth_retries (s_con);
 
 		if (auth_retries == -1) {
-			gs_free char *value = NULL;
-
-			value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-			                                               "connection.auth-retries",
-			                                               self);
-			auth_retries = _nm_utils_ascii_str_to_int64 (value, 10, -1, G_MAXINT32, -1);
+			auth_retries = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+			                                                            "connection.auth-retries",
+			                                                            self,
+			                                                            -1, G_MAXINT32, -1);
 		}
 
 		if (auth_retries == 0)
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index d145f8fc..7d74e5b9 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -202,7 +202,12 @@ typedef struct _NMDeviceClass {
 	struct _NMDeviceClass *default_type_description_klass;
 	const char *default_type_description;
 
-	const char *connection_type;
+	const char *connection_type_supported;
+
+	/* most device types, can only handle profiles of a particular type. This
+	 * is the connection.type setting, as checked by nm_device_check_connection_compatible() */
+	const char *connection_type_check_compatible;
+
 	const NMLinkType *link_types;
 
 	/* Whether the device type is a master-type. This depends purely on the
@@ -292,7 +297,7 @@ typedef struct _NMDeviceClass {
 	gboolean    (* get_autoconnect_allowed) (NMDevice *self);
 
 	gboolean    (* can_auto_connect) (NMDevice *self,
-	                                  NMConnection *connection,
+	                                  NMSettingsConnection *sett_conn,
 	                                  char **specific_object);
 
 	guint32     (*get_configured_mtu) (NMDevice *self, NMDeviceMtuSource *out_source);
@@ -301,7 +306,9 @@ typedef struct _NMDeviceClass {
 	 * only the devices type and characteristics.  Does not use any live
 	 * network information like WiFi scan lists etc.
 	 */
-	gboolean    (* check_connection_compatible) (NMDevice *self, NMConnection *connection);
+	gboolean    (* check_connection_compatible) (NMDevice *self,
+	                                             NMConnection *connection,
+	                                             GError **error);
 
 	/* Checks whether the connection is likely available to be activated,
 	 * including any live network information like scan lists.  The connection
@@ -317,7 +324,8 @@ typedef struct _NMDeviceClass {
 	gboolean    (* check_connection_available) (NMDevice *self,
 	                                            NMConnection *connection,
 	                                            NMDeviceCheckConAvailableFlags flags,
-	                                            const char *specific_object);
+	                                            const char *specific_object,
+	                                            GError **error);
 
 	gboolean    (* complete_connection)         (NMDevice *self,
 	                                             NMConnection *connection,
@@ -493,6 +501,9 @@ gboolean        nm_device_parent_notify_changed (NMDevice *self,
                                                  NMDevice *change_candidate,
                                                  gboolean device_removed);
 
+const char     *nm_device_parent_find_for_connection (NMDevice *self,
+                                                      const char *current_setting_parent);
+
 /* Master */
 gboolean        nm_device_is_master             (NMDevice *dev);
 
@@ -501,6 +512,7 @@ NMDevice *      nm_device_get_master            (NMDevice *dev);
 
 NMActRequest *  nm_device_get_act_request       (NMDevice *dev);
 NMSettingsConnection *nm_device_get_settings_connection (NMDevice *dev);
+NMConnection *  nm_device_get_settings_connection_get_connection (NMDevice *self);
 NMConnection *  nm_device_get_applied_connection (NMDevice *dev);
 gboolean        nm_device_has_unmodified_applied_connection (NMDevice *self,
                                                              NMSettingCompareFlags compare_flags);
@@ -524,7 +536,7 @@ gboolean nm_device_master_update_slave_connection (NMDevice *master,
                                                    GError **error);
 
 gboolean nm_device_can_auto_connect (NMDevice *self,
-                                     NMConnection *connection,
+                                     NMSettingsConnection *sett_conn,
                                      char **specific_object);
 
 gboolean nm_device_complete_connection (NMDevice *device,
@@ -533,7 +545,10 @@ gboolean nm_device_complete_connection (NMDevice *device,
                                         NMConnection *const*existing_connections,
                                         GError **error);
 
-gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection);
+gboolean nm_device_check_connection_compatible (NMDevice *device,
+                                                NMConnection *connection,
+                                                GError **error);
+
 gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnection *connection);
 
 gboolean nm_device_unmanage_on_quit (NMDevice *self);
@@ -742,7 +757,8 @@ NMSettingsConnection *nm_device_get_best_connection (NMDevice *device,
 gboolean   nm_device_check_connection_available (NMDevice *device,
                                                  NMConnection *connection,
                                                  NMDeviceCheckConAvailableFlags flags,
-                                                 const char *specific_object);
+                                                 const char *specific_object,
+                                                 GError **error);
 
 gboolean nm_device_notify_component_added (NMDevice *device, GObject *component);
 
diff --git a/src/devices/ovs/meson.build b/src/devices/ovs/meson.build
index f4edd57c..9d637fe9 100644
--- a/src/devices/ovs/meson.build
+++ b/src/devices/ovs/meson.build
@@ -15,7 +15,6 @@ libnm_device_plugin_ovs = shared_module(
   'nm-device-plugin-ovs',
   sources: sources,
   dependencies: deps,
-  c_args: '-DRUNSTATEDIR="@0@"'.format(nm_runstatedir),
   link_args: ldflags_linker_script_devices,
   link_depends: linker_script_devices,
   install: true,
@@ -24,10 +23,10 @@ libnm_device_plugin_ovs = shared_module(
 
 core_plugins += libnm_device_plugin_ovs
 
-run_target(
+test(
   'check-local-devices-ovs',
-  command: [check_exports, libnm_device_plugin_ovs.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_ovs
+  check_exports,
+  args: [libnm_device_plugin_ovs.full_path(), linker_script_devices],
 )
 
 # FIXME: check_so_symbols replacement
diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c
index 2e4d1cbd..eff355a3 100644
--- a/src/devices/ovs/nm-device-ovs-bridge.c
+++ b/src/devices/ovs/nm-device-ovs-bridge.c
@@ -76,21 +76,6 @@ get_generic_capabilities (NMDevice *device)
 	return NM_DEVICE_CAP_IS_SOFTWARE;
 }
 
-static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	const char *connection_type;
-
-	if (!NM_DEVICE_CLASS (nm_device_ovs_bridge_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	connection_type = nm_connection_get_connection_type (connection);
-	if (!nm_streq0 (connection_type, NM_SETTING_OVS_BRIDGE_SETTING_NAME))
-		return FALSE;
-
-	return TRUE;
-}
-
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *device,
                              NMIP4Config **out_config,
@@ -152,13 +137,15 @@ nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_bridge);
 
-	device_class->connection_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
+	device_class->connection_type_supported = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES ();
+
 	device_class->is_master = TRUE;
 	device_class->get_type_description = get_type_description;
 	device_class->create_and_realize = create_and_realize;
 	device_class->unrealize = unrealize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
-	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
 	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
 	device_class->enslave_slave = enslave_slave;
diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c
index c59af7a6..2b48fae6 100644
--- a/src/devices/ovs/nm-device-ovs-interface.c
+++ b/src/devices/ovs/nm-device-ovs-interface.c
@@ -85,25 +85,20 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
-	NMSettingConnection *s_con;
 	NMSettingOvsInterface *s_ovs_iface;
 
-	if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->check_connection_compatible (device, connection))
+	if (!NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	s_ovs_iface = nm_connection_get_setting_ovs_interface (connection);
-	if (!s_ovs_iface)
-		return FALSE;
-	if (!NM_IN_STRSET (nm_setting_ovs_interface_get_interface_type (s_ovs_iface),
-	                   "internal", "patch")) {
-		return FALSE;
-	}
 
-	s_con = nm_connection_get_setting_connection (connection);
-	if (g_strcmp0 (nm_setting_connection_get_connection_type (s_con),
-	               NM_SETTING_OVS_INTERFACE_SETTING_NAME) != 0) {
+	if (!NM_IN_STRSET (nm_setting_ovs_interface_get_interface_type (s_ovs_iface),
+	                   "internal",
+	                   "patch")) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "unsupported OVS interface type in profile");
 		return FALSE;
 	}
 
@@ -199,11 +194,12 @@ nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_OPENVSWITCH);
-
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_interface);
 
-	device_class->connection_type = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
+	device_class->connection_type_supported = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH);
+
 	device_class->get_type_description = get_type_description;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c
index a9a2b24a..1f9afbab 100644
--- a/src/devices/ovs/nm-device-ovs-port.c
+++ b/src/devices/ovs/nm-device-ovs-port.c
@@ -70,26 +70,6 @@ get_generic_capabilities (NMDevice *device)
 	return NM_DEVICE_CAP_IS_SOFTWARE;
 }
 
-static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingConnection *s_con;
-	const char *connection_type;
-
-	if (!NM_DEVICE_CLASS (nm_device_ovs_port_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	connection_type = nm_setting_connection_get_connection_type (s_con);
-	if (!connection_type)
-		return FALSE;
-
-	if (strcmp (connection_type, NM_SETTING_OVS_PORT_SETTING_NAME) == 0)
-		return TRUE;
-
-	return FALSE;
-}
-
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *device,
                              NMIP4Config **out_config,
@@ -198,12 +178,14 @@ nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_ovs_port);
 
-	device_class->connection_type = NM_SETTING_OVS_PORT_SETTING_NAME;
+	device_class->connection_type_supported = NM_SETTING_OVS_PORT_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_OVS_PORT_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES ();
+
 	device_class->is_master = TRUE;
 	device_class->get_type_description = get_type_description;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
-	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
 	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
 	device_class->enslave_slave = enslave_slave;
diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c
index a9cb5703..b00397cf 100644
--- a/src/devices/ovs/nm-ovsdb.c
+++ b/src/devices/ovs/nm-ovsdb.c
@@ -139,7 +139,7 @@ typedef struct {
 static void
 _call_trace (const char *comment, OvsdbMethodCall *call, json_t *msg)
 {
-#ifdef NM_MORE_LOGGING
+#if NM_MORE_LOGGING
 	char *str = NULL;
 
 	if (msg)
diff --git a/src/devices/team/meson.build b/src/devices/team/meson.build
index 159c182a..0f0763bd 100644
--- a/src/devices/team/meson.build
+++ b/src/devices/team/meson.build
@@ -21,10 +21,10 @@ libnm_device_plugin_team = shared_module(
 
 core_plugins += libnm_device_plugin_team
 
-run_target(
+test(
   'check-local-devices-team',
-  command: [check_exports, libnm_device_plugin_team.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_team
+  check_exports,
+  args: [libnm_device_plugin_team.full_path(), linker_script_devices],
 )
 
 # FIXME: check_so_symbols replacement
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index f83ba22b..899932fd 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -1,8 +1,6 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
- * Copyright (C) 2013 Jiri Pirko <jiri@resnulli.us>
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -16,6 +14,9 @@
  * You should have received a copy of the GNU General Public License along
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Jiri Pirko <jiri@resnulli.us>
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -84,23 +85,6 @@ get_generic_capabilities (NMDevice *device)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingTeam *s_team;
-
-	if (!NM_DEVICE_CLASS (nm_device_team_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_team = nm_connection_get_setting_team (connection);
-	if (!s_team || !nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME))
-		return FALSE;
-
-	/* FIXME: match team properties like mode, etc? */
-
-	return TRUE;
-}
-
-static gboolean
 complete_connection (NMDevice *device,
                      NMConnection *connection,
                      const char *specific_object,
@@ -367,8 +351,8 @@ teamd_timeout_cb (gpointer user_data)
 
 static void
 teamd_dbus_appeared (GDBusConnection *connection,
-                     const gchar *name,
-                     const gchar *name_owner,
+                     const char *name,
+                     const char *name_owner,
                      gpointer user_data)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
@@ -430,7 +414,7 @@ teamd_dbus_appeared (GDBusConnection *connection,
 
 static void
 teamd_dbus_vanished (GDBusConnection *dbus_connection,
-                     const gchar *name,
+                     const char *name,
                      gpointer user_data)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
@@ -463,7 +447,7 @@ teamd_dbus_vanished (GDBusConnection *dbus_connection,
 }
 
 static void
-teamd_process_watch_cb (GPid pid, gint status, gpointer user_data)
+teamd_process_watch_cb (GPid pid, int status, gpointer user_data)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
@@ -516,7 +500,7 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
 	g_ptr_array_add (argv, (gpointer) nm_device_get_iface (NM_DEVICE (self)));
 	g_ptr_array_add (argv, NULL);
 
-	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (char **) argv->pdata)));
 	return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, teamd_child_setup, NULL, NULL, NULL, NULL, error);
 }
 
@@ -604,7 +588,7 @@ teamd_start (NMDevice *device, NMConnection *connection)
 		g_ptr_array_add (argv, (gpointer) "-gg");
 	g_ptr_array_add (argv, NULL);
 
-	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (char **) argv->pdata)));
 	if (!g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
 	                    teamd_child_setup, NULL, &priv->teamd_pid, &error)) {
 		_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message);
@@ -703,7 +687,7 @@ enslave_slave (NMDevice *device,
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
-	gboolean success = TRUE, no_firmware = FALSE;
+	gboolean success = TRUE;
 	const char *slave_iface = nm_device_get_ip_iface (slave);
 	NMSettingTeamPort *s_team_port;
 
@@ -738,7 +722,7 @@ enslave_slave (NMDevice *device,
 		success = nm_platform_link_enslave (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
-		nm_device_bring_up (slave, TRUE, &no_firmware);
+		nm_device_bring_up (slave, TRUE, NULL);
 
 		if (!success)
 			return FALSE;
@@ -762,7 +746,7 @@ release_slave (NMDevice *device,
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
-	gboolean success, no_firmware = FALSE;
+	gboolean success;
 
 	if (configure) {
 		success = nm_platform_link_release (nm_device_get_platform (device),
@@ -778,7 +762,7 @@ release_slave (NMDevice *device,
 		 * IFF_UP), so we must bring it back up here to ensure carrier changes and
 		 * other state is noticed by the now-released port.
 		 */
-		if (!nm_device_bring_up (slave, TRUE, &no_firmware))
+		if (!nm_device_bring_up (slave, TRUE, NULL))
 			_LOGW (LOGD_TEAM, "released team port %s could not be brought up",
 			       nm_device_get_ip_iface (slave));
 
@@ -910,9 +894,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_TEAM_SETTING_NAME, NM_LINK_TYPE_TEAM)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
@@ -920,19 +902,22 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_team);
 
-	parent_class->is_master = TRUE;
-	parent_class->create_and_realize = create_and_realize;
-	parent_class->get_generic_capabilities = get_generic_capabilities;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->complete_connection = complete_connection;
-	parent_class->update_connection = update_connection;
-	parent_class->master_update_slave_connection = master_update_slave_connection;
-
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
-	parent_class->deactivate = deactivate;
-	parent_class->enslave_slave = enslave_slave;
-	parent_class->release_slave = release_slave;
+	device_class->connection_type_supported = NM_SETTING_TEAM_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_TEAM_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_TEAM);
+
+	device_class->is_master = TRUE;
+	device_class->create_and_realize = create_and_realize;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->complete_connection = complete_connection;
+	device_class->update_connection = update_connection;
+	device_class->master_update_slave_connection = master_update_slave_connection;
+
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+	device_class->deactivate = deactivate;
+	device_class->enslave_slave = enslave_slave;
+	device_class->release_slave = release_slave;
 
 	obj_properties[PROP_CONFIG] =
 	    g_param_spec_string (NM_DEVICE_TEAM_CONFIG, "", "",
diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build
index a27f8e6a..2745040b 100644
--- a/src/devices/wifi/meson.build
+++ b/src/devices/wifi/meson.build
@@ -33,10 +33,10 @@ libnm_device_plugin_wifi = shared_module(
 
 core_plugins += libnm_device_plugin_wifi
 
-run_target(
+test(
   'check-local-devices-wifi',
-  command: [check_exports, libnm_device_plugin_wifi.full_path(), linker_script_devices],
-  depends: libnm_device_plugin_wifi
+  check_exports,
+  args: [libnm_device_plugin_wifi.full_path(), linker_script_devices],
 )
 
 # FIXME: check_so_symbols replacement
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index 7667816a..56b1f853 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -67,7 +67,8 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
 	GDBusObject *   dbus_obj;
-	GDBusProxy *    dbus_proxy;
+	GDBusProxy *    dbus_device_proxy;
+	GDBusProxy *    dbus_station_proxy;
 	CList           aps_lst_head;
 	NMWifiAP *      current_ap;
 	GCancellable *  cancellable;
@@ -204,11 +205,11 @@ remove_all_aps (NMDeviceIwd *self)
 }
 
 static GVariant *
-vardict_from_network_type (const gchar *type)
+vardict_from_network_type (const char *type)
 {
 	GVariantBuilder builder;
-	const gchar *key_mgmt = "";
-	const gchar *pairwise = "ccmp";
+	const char *key_mgmt = "";
+	const char *pairwise = "ccmp";
 
 	if (!strcmp (type, "psk"))
 		key_mgmt = "wpa-psk";
@@ -235,7 +236,7 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *variant = NULL;
 	GVariantIter *networks;
-	const gchar *path, *name, *type;
+	const char *path, *name, *type;
 	int16_t signal;
 	NMWifiAP *ap, *ap_safe, *new_ap;
 	gboolean changed = FALSE;
@@ -246,7 +247,7 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	                                      G_VARIANT_TYPE ("(a(osns))"),
 	                                      &error);
 	if (!variant) {
-		_LOGE (LOGD_WIFI, "Device.GetOrderedNetworks failed: %s",
+		_LOGE (LOGD_WIFI, "Station.GetOrderedNetworks failed: %s",
 		       error->message);
 		return;
 	}
@@ -296,8 +297,11 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		props = g_variant_new ("a{sv}", &builder);
 
 		ap = nm_wifi_ap_new_from_properties (path, props);
-		if (name[0] != '\0')
-			nm_wifi_ap_set_ssid (ap, (const guint8 *) name, strlen (name));
+
+		nm_wifi_ap_set_ssid_arr (ap,
+		                         (const guint8 *) name,
+		                         NM_MIN (32, strlen (name)));
+
 		nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (signal / 100));
 		nm_wifi_ap_set_freq (ap, 2417);
 		nm_wifi_ap_set_max_bitrate (ap, 65000);
@@ -355,7 +359,7 @@ update_aps (NMDeviceIwd *self)
 	if (!priv->cancellable)
 		priv->cancellable = g_cancellable_new ();
 
-	g_dbus_proxy_call (priv->dbus_proxy, "GetOrderedNetworks",
+	g_dbus_proxy_call (priv->dbus_station_proxy, "GetOrderedNetworks",
 	                   g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE,
 	                   2000, priv->cancellable,
 	                   get_ordered_networks_cb, self);
@@ -366,7 +370,7 @@ send_disconnect (NMDeviceIwd *self)
 {
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 
-	g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"),
+	g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"),
 	                   G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
 }
 
@@ -405,7 +409,7 @@ deactivate_async_finish (NMDevice *device, GAsyncResult *res, GError **error)
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device));
 	gs_unref_variant GVariant *variant = NULL;
 
-	variant = g_dbus_proxy_call_finish (priv->dbus_proxy, res, error);
+	variant = g_dbus_proxy_call_finish (priv->dbus_station_proxy, res, error);
 	return variant != NULL;
 }
 
@@ -441,39 +445,18 @@ deactivate_async (NMDevice *device,
 	ctx->callback = callback;
 	ctx->user_data = user_data;
 
-	g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"),
+	g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"),
 	                   G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx);
 }
 
-static NMIwdNetworkSecurity
-get_connection_iwd_security (NMConnection *connection)
-{
-	NMSettingWirelessSecurity *s_wireless_sec;
-	const char *key_mgmt = NULL;
-
-	s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
-	if (!s_wireless_sec)
-		return NM_IWD_NETWORK_SECURITY_NONE;
-
-	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
-	nm_assert (key_mgmt);
-
-	if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x"))
-		return NM_IWD_NETWORK_SECURITY_WEP;
-
-	if (!strcmp (key_mgmt, "wpa-psk"))
-		return NM_IWD_NETWORK_SECURITY_PSK;
-
-	nm_assert (!strcmp (key_mgmt, "wpa-eap"));
-	return NM_IWD_NETWORK_SECURITY_8021X;
-}
-
 static gboolean
 is_connection_known_network (NMConnection *connection)
 {
 	NMSettingWireless *s_wireless;
+	NMIwdNetworkSecurity security;
+	gboolean security_ok;
 	GBytes *ssid;
-	gs_free gchar *str_ssid = NULL;
+	gs_free char *ssid_utf8 = NULL;
 
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	if (!s_wireless)
@@ -483,68 +466,73 @@ is_connection_known_network (NMConnection *connection)
 	if (!ssid)
 		return FALSE;
 
-	str_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
-	                                  g_bytes_get_size (ssid));
+	ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
+
+	security = nm_wifi_connection_get_iwd_security (connection, &security_ok);
+	if (!security_ok)
+		return FALSE;
 
 	return nm_iwd_manager_is_known_network (nm_iwd_manager_get (),
-	                                        str_ssid,
-	                                        get_connection_iwd_security (connection));
+	                                        ssid_utf8, security);
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
-	NMSettingConnection *s_con;
 	NMSettingWireless *s_wireless;
 	const char *mac;
 	const char * const *mac_blacklist;
 	int i;
-	const char *mode;
 	const char *perm_hw_addr;
 
-	if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-
-	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME))
+	if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	s_wireless = nm_connection_get_setting_wireless (connection);
-	if (!s_wireless)
-		return FALSE;
 
 	perm_hw_addr = nm_device_get_permanent_hw_address (device);
 	mac = nm_setting_wireless_get_mac_address (s_wireless);
 	if (perm_hw_addr) {
-		if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
+		if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "device MAC address does not match the profile");
 			return FALSE;
+		}
 
 		/* Check for MAC address blacklist */
 		mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
 		for (i = 0; mac_blacklist[i]; i++) {
-			if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
-				g_warn_if_reached ();
-				return FALSE;
-			}
+			nm_assert (nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN));
 
-			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
+			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "MAC address blacklisted");
 				return FALSE;
+			}
 		}
-	} else if (mac)
+	} else if (mac) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "device has no valid MAC address as required by profile");
 		return FALSE;
+	}
 
-	mode = nm_setting_wireless_get_mode (s_wireless);
-	if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+	if (!NM_IN_STRSET (nm_setting_wireless_get_mode (s_wireless),
+	                   NULL,
+	                   NM_SETTING_WIRELESS_MODE_INFRA)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "IWD only support infrastructure type profiles");
 		return FALSE;
+	}
 
 	/* 8021x networks can only be used if they've been provisioned on the IWD side and
 	 * thus are Known Networks.
 	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
-		if (!is_connection_known_network (connection))
+	if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) {
+		if (!is_connection_known_network (connection)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "802.1x profile is not a known network");
 			return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -554,7 +542,8 @@ static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
+                            const char *specific_object,
+                            GError **error)
 {
 	NMDeviceIwd *self = NM_DEVICE_IWD (device);
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
@@ -566,19 +555,28 @@ check_connection_available (NMDevice *device,
 
 	/* Only Infrastrusture mode at this time */
 	mode = nm_setting_wireless_get_mode (s_wifi);
-	if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+	if (!NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "iwd only supports infrastructure mode connections");
 		return FALSE;
+	}
 
 	/* Hidden SSIDs not supported yet */
-	if (nm_setting_wireless_get_hidden (s_wifi))
+	if (nm_setting_wireless_get_hidden (s_wifi)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "hidden networks not supported by iwd");
 		return FALSE;
+	}
 
 	/* 8021x networks can only be used if they've been provisioned on the IWD side and
 	 * thus are Known Networks.
 	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
-		if (!is_connection_known_network (connection))
+	if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) {
+		if (!is_connection_known_network (connection)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "network is not known to iwd");
 			return FALSE;
+		}
 	}
 
 	/* a connection that is available for a certain @specific_object, MUST
@@ -588,14 +586,29 @@ check_connection_available (NMDevice *device,
 		NMWifiAP *ap;
 
 		ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
-		return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE;
+		if (!ap) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "requested access point not found");
+			return FALSE;
+		}
+		if (!nm_wifi_ap_check_compatible (ap, connection)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "requested access point is not compatible with profile");
+			return FALSE;
+		}
+		return TRUE;
 	}
 
 	if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
 		return TRUE;
 
-	/* Check at least one AP is compatible with this connection */
-	return !!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+	if (!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "no compatible access point found");
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
 static gboolean
@@ -609,10 +622,9 @@ complete_connection (NMDevice *device,
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	NMSettingWireless *s_wifi;
 	const char *setting_mac;
-	char *str_ssid = NULL;
+	gs_free char *ssid_utf8 = NULL;
 	NMWifiAP *ap;
-	const GByteArray *ssid = NULL;
-	GByteArray *tmp_ssid = NULL;
+	GBytes *ssid;
 	GBytes *setting_ssid = NULL;
 	const char *perm_hw_addr;
 	const char *mode;
@@ -676,8 +688,7 @@ complete_connection (NMDevice *device,
 	}
 
 	ssid = nm_wifi_ap_get_ssid (ap);
-
-	if (ssid == NULL) {
+	if (!ssid) {
 		g_set_error_literal (error,
 		                     NM_DEVICE_ERROR,
 		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
@@ -688,30 +699,23 @@ complete_connection (NMDevice *device,
 	if (!nm_wifi_ap_complete_connection (ap,
 	                                     connection,
 	                                     nm_wifi_utils_is_manf_default_ssid (ssid),
-	                                     error)) {
-		if (tmp_ssid)
-			g_byte_array_unref (tmp_ssid);
+	                                     error))
 		return FALSE;
-	}
-
-	str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
 
+	ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
 	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_WIRELESS_SETTING_NAME,
 	                           existing_connections,
-	                           str_ssid,
-	                           str_ssid,
+	                           ssid_utf8,
+	                           ssid_utf8,
 	                           NULL,
 	                           TRUE);
-	g_free (str_ssid);
-	if (tmp_ssid)
-		g_byte_array_unref (tmp_ssid);
 
 	/* 8021x networks can only be used if they've been provisioned on the IWD side and
 	 * thus are Known Networks.
 	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
+	if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) {
 		if (!is_connection_known_network (connection)) {
 			g_set_error_literal (error,
 			                     NM_CONNECTION_ERROR,
@@ -753,6 +757,32 @@ complete_connection (NMDevice *device,
 }
 
 static gboolean
+get_variant_boolean (GVariant *v, const char *property)
+{
+	if (!v || !g_variant_is_of_type (v, G_VARIANT_TYPE_BOOLEAN)) {
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Property %s not cached or not boolean type", property);
+
+		return FALSE;
+	}
+
+	return g_variant_get_boolean (v);
+}
+
+static const char *
+get_variant_state (GVariant *v)
+{
+	if (!v || !g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) {
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "State property not cached or not a string");
+
+		return "unknown";
+	}
+
+	return g_variant_get_string (v, NULL);
+}
+
+static gboolean
 is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceIwd *self = NM_DEVICE_IWD (device);
@@ -762,8 +792,8 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 	if (!priv->enabled || !priv->dbus_obj)
 		return FALSE;
 
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Powered");
-	return g_variant_get_boolean (value);
+	value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered");
+	return get_variant_boolean (value, "Powered");
 }
 
 static gboolean
@@ -777,11 +807,12 @@ get_autoconnect_allowed (NMDevice *device)
 
 static gboolean
 can_auto_connect (NMDevice *device,
-                  NMConnection *connection,
+                  NMSettingsConnection *sett_conn,
                   char **specific_object)
 {
 	NMDeviceIwd *self = NM_DEVICE_IWD (device);
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+	NMConnection *connection;
 	NMSettingWireless *s_wifi;
 	NMWifiAP *ap;
 	const char *mode;
@@ -789,9 +820,11 @@ can_auto_connect (NMDevice *device,
 
 	nm_assert (!specific_object || !*specific_object);
 
-	if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->can_auto_connect (device, connection, NULL))
+	if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->can_auto_connect (device, sett_conn, NULL))
 		return FALSE;
 
+	connection = nm_settings_connection_get_connection (sett_conn);
+
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wifi, FALSE);
 
@@ -804,7 +837,7 @@ can_auto_connect (NMDevice *device,
 	 * but haven't been successful, since these are often accidental choices
 	 * from the menu and the user may not know the password.
 	 */
-	if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp)) {
+	if (nm_settings_connection_get_timestamp (sett_conn, &timestamp)) {
 		if (timestamp == 0)
 			return FALSE;
 	}
@@ -812,7 +845,7 @@ can_auto_connect (NMDevice *device,
 	/* 8021x networks can only be used if they've been provisioned on the IWD side and
 	 * thus are Known Networks.
 	 */
-	if (get_connection_iwd_security (connection) == NM_IWD_NETWORK_SECURITY_8021X) {
+	if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) {
 		if (!is_connection_known_network (connection))
 			return FALSE;
 	}
@@ -921,7 +954,7 @@ dbus_request_scan_cb (NMDevice *device,
 	}
 
 	if (!priv->scanning && !priv->scan_requested) {
-		g_dbus_proxy_call (priv->dbus_proxy, "Scan",
+		g_dbus_proxy_call (priv->dbus_station_proxy, "Scan",
 		                   g_variant_new ("()"),
 		                   G_DBUS_CALL_FLAGS_NONE, -1,
 		                   priv->cancellable, scan_cb, self);
@@ -1005,11 +1038,11 @@ static gboolean
 try_reply_agent_request (NMDeviceIwd *self,
                          NMConnection *connection,
                          GDBusMethodInvocation *invocation,
-                         const gchar **setting_name,
-                         const gchar **setting_key,
+                         const char **setting_name,
+                         const char **setting_key,
                          gboolean *replied)
 {
-	const gchar *method_name = g_dbus_method_invocation_get_method_name (invocation);
+	const char *method_name = g_dbus_method_invocation_get_method_name (invocation);
 	NMSettingWirelessSecurity *s_wireless_sec;
 	NMSetting8021x *s_8021x;
 
@@ -1019,7 +1052,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 	*replied = FALSE;
 
 	if (!strcmp (method_name, "RequestPassphrase")) {
-		const gchar *psk;
+		const char *psk;
 
 		if (!s_wireless_sec)
 			return FALSE;
@@ -1039,7 +1072,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 		*setting_key = NM_SETTING_WIRELESS_SECURITY_PSK;
 		return TRUE;
 	} else if (!strcmp (method_name, "RequestPrivateKeyPassphrase")) {
-		const gchar *password;
+		const char *password;
 
 		if (!s_8021x)
 			return FALSE;
@@ -1059,7 +1092,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 		*setting_key = NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD;
 		return TRUE;
 	} else if (!strcmp (method_name, "RequestUserNameAndPassword")) {
-		const gchar *identity, *password;
+		const char *identity, *password;
 
 		if (!s_8021x)
 			return FALSE;
@@ -1083,7 +1116,7 @@ try_reply_agent_request (NMDeviceIwd *self,
 			*setting_key = NM_SETTING_802_1X_PASSWORD;
 		return TRUE;
 	} else if (!strcmp (method_name, "RequestUserPassword")) {
-		const gchar *password;
+		const char *password;
 
 		if (!s_8021x)
 			return FALSE;
@@ -1124,8 +1157,8 @@ wifi_secrets_cb (NMActRequest *req,
 	NMDeviceIwdPrivate *priv;
 	NMDevice *device;
 	GDBusMethodInvocation *invocation;
-	const gchar *setting_name;
-	const gchar *setting_key;
+	const char *setting_name;
+	const char *setting_key;
 	gboolean replied;
 	NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
@@ -1223,12 +1256,12 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	NMConnection *connection;
 	NMSettingWireless *s_wifi;
 	GBytes *ssid;
-	gs_free gchar *str_ssid = NULL;
+	gs_free char *ssid_utf8 = NULL;
 
 	if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
 	                                 G_VARIANT_TYPE ("()"),
 	                                 &error)) {
-		gs_free gchar *dbus_error = NULL;
+		gs_free char *dbus_error = NULL;
 
 		/* Connection failed; radio problems or if the network wasn't
 		 * open, the passwords or certificates may be wrong.
@@ -1275,17 +1308,13 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	if (!ssid)
 		goto failed;
 
-	str_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
-	                                  g_bytes_get_size (ssid));
+	ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
 
 	_LOGI (LOGD_DEVICE | LOGD_WIFI,
 	       "Activation: (wifi) Stage 2 of 5 (Device Configure) successful.  Connected to '%s'.",
-	       str_ssid);
+	       ssid_utf8);
 	nm_device_activate_schedule_stage3_ip_config_start (device);
 
-	nm_iwd_manager_network_connected (nm_iwd_manager_get (), str_ssid,
-	                                  get_connection_iwd_security (connection));
-
 	return;
 
 failed:
@@ -1299,7 +1328,7 @@ set_powered (NMDeviceIwd *self, gboolean powered)
 {
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 
-	g_dbus_proxy_call (priv->dbus_proxy,
+	g_dbus_proxy_call (priv->dbus_device_proxy,
 	                   "org.freedesktop.DBus.Properties.Set",
 	                   g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE,
 	                                  "Powered",
@@ -1452,7 +1481,7 @@ periodic_scan_timeout_cb (gpointer user_data)
 	if (priv->scanning || priv->scan_requested)
 		return FALSE;
 
-	g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new ("()"),
+	g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", g_variant_new ("()"),
 	                   G_DBUS_CALL_FLAGS_NONE, -1,
 	                   priv->cancellable, scan_cb, self);
 	priv->scan_requested = TRUE;
@@ -1556,7 +1585,7 @@ set_enabled (NMDevice *device, gboolean enabled)
 		return;
 	}
 
-	if (priv->dbus_proxy)
+	if (priv->dbus_obj)
 		set_powered (self, enabled);
 
 	if (enabled) {
@@ -1667,7 +1696,7 @@ set_property (GObject *object, guint prop_id,
 /*****************************************************************************/
 
 static void
-state_changed (NMDeviceIwd *self, const gchar *new_state)
+state_changed (NMDeviceIwd *self, const char *new_state)
 {
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
@@ -1734,7 +1763,8 @@ state_changed (NMDeviceIwd *self, const gchar *new_state)
 	                                 NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
 
 		return;
-	}
+	} else if (nm_streq (new_state, "unknown"))
+		return;
 
 	_LOGE (LOGD_WIFI, "State %s unknown", new_state);
 }
@@ -1761,6 +1791,29 @@ scanning_changed (NMDeviceIwd *self, gboolean new_scanning)
 }
 
 static void
+station_properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
+                            GStrv invalidate_properties, gpointer user_data)
+{
+	NMDeviceIwd *self = user_data;
+	GVariantIter *iter;
+	const char *key;
+	GVariant *value;
+
+	g_variant_get (changed_properties, "a{sv}", &iter);
+	while (g_variant_iter_next (iter, "{&sv}", &key, &value)) {
+		if (!strcmp (key, "State"))
+			state_changed (self, get_variant_state (value));
+
+		if (!strcmp (key, "Scanning"))
+			scanning_changed (self, get_variant_boolean (value, "Scanning"));
+
+		g_variant_unref (value);
+	}
+
+	g_variant_iter_free (iter);
+}
+
+static void
 powered_changed (NMDeviceIwd *self, gboolean new_powered)
 {
 	nm_device_queue_recheck_available (NM_DEVICE (self),
@@ -1769,24 +1822,18 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered)
 }
 
 static void
-properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
-                    GStrv invalidate_properties, gpointer user_data)
+device_properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
+                           GStrv invalidate_properties, gpointer user_data)
 {
 	NMDeviceIwd *self = user_data;
 	GVariantIter *iter;
-	const gchar *key;
+	const char *key;
 	GVariant *value;
 
 	g_variant_get (changed_properties, "a{sv}", &iter);
 	while (g_variant_iter_next (iter, "{&sv}", &key, &value)) {
-		if (!strcmp (key, "State"))
-			state_changed (self, g_variant_get_string (value, NULL));
-
-		if (!strcmp (key, "Scanning"))
-			scanning_changed (self, g_variant_get_boolean (value));
-
 		if (!strcmp (key, "Powered"))
-			powered_changed (self, g_variant_get_boolean (value));
+			powered_changed (self, get_variant_boolean (value, "Powered"));
 
 		g_variant_unref (value);
 	}
@@ -1804,11 +1851,13 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
 	if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object))
 		return;
 
-	if (priv->dbus_proxy) {
-		g_signal_handlers_disconnect_by_func (priv->dbus_proxy,
-		                                      properties_changed, self);
-
-		g_clear_object (&priv->dbus_proxy);
+	if (priv->dbus_device_proxy) {
+		g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy,
+		                                      device_properties_changed, self);
+		g_clear_object (&priv->dbus_device_proxy);
+		g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy,
+		                                      station_properties_changed, self);
+		g_clear_object (&priv->dbus_station_proxy);
 	}
 
 	if (priv->enabled)
@@ -1824,19 +1873,55 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
 	}
 
 	interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE);
-	priv->dbus_proxy = G_DBUS_PROXY (interface);
+	if (!interface) {
+		_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
+		       NM_IWD_DEVICE_INTERFACE,
+		       g_dbus_object_get_object_path (object));
+		goto error;
+	}
+
+	priv->dbus_device_proxy = G_DBUS_PROXY (interface);
+
+	g_signal_connect (priv->dbus_device_proxy, "g-properties-changed",
+	                  G_CALLBACK (device_properties_changed), self);
 
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Scanning");
-	priv->scanning = g_variant_get_boolean (value);
+	interface = g_dbus_object_get_interface (object, NM_IWD_STATION_INTERFACE);
+	if (!interface) {
+		/* No Station interface on the device object.  Check if the
+		 * "State" property is present on the Device interface, that
+		 * would mean we're dealing with an IWD version from before the
+		 * Device/Station split (0.7 or earlier) and we can easily
+		 * handle that by making priv->dbus_device_proxy and
+		 * priv->dbus_station_proxy both point at the Device interface.
+		 *
+		 * TODO: handle device in a mode initially other than station
+		 * -- also means the Station interface won't be there.
+		 */
+		value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State");
+		g_variant_unref (value);
+		if (!value) {
+			_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
+			       NM_IWD_STATION_INTERFACE,
+			       g_dbus_object_get_object_path (object));
+			goto error;
+		}
+
+		interface = g_object_ref (priv->dbus_device_proxy);
+	}
+
+	priv->dbus_station_proxy = G_DBUS_PROXY (interface);
+
+	value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "Scanning");
+	priv->scanning = get_variant_boolean (value, "Scanning");
 	g_variant_unref (value);
 	priv->scan_requested = FALSE;
 
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "State");
-	state_changed (self, g_variant_get_string (value, NULL));
+	value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State");
+	state_changed (self, get_variant_state (value));
 	g_variant_unref (value);
 
-	g_signal_connect (priv->dbus_proxy, "g-properties-changed",
-	                  G_CALLBACK (properties_changed), self);
+	g_signal_connect (priv->dbus_station_proxy, "g-properties-changed",
+	                  G_CALLBACK (station_properties_changed), self);
 
 	set_powered (self, priv->enabled);
 
@@ -1845,6 +1930,15 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
 	 */
 	if (priv->enabled)
 		send_disconnect (self);
+
+	return;
+error:
+	g_clear_object (&priv->dbus_obj);
+	if (priv->dbus_device_proxy) {
+		g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy,
+		                                      device_properties_changed, self);
+		g_clear_object (&priv->dbus_device_proxy);
+	}
 }
 
 gboolean
@@ -1852,8 +1946,8 @@ nm_device_iwd_agent_query (NMDeviceIwd *self,
                            GDBusMethodInvocation *invocation)
 {
 	NMActRequest *req;
-	const gchar *setting_name;
-	const gchar *setting_key;
+	const char *setting_name;
+	const char *setting_key;
 	gboolean replied;
 	NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
@@ -1937,7 +2031,8 @@ dispose (GObject *object)
 
 	cleanup_association_attempt (self, TRUE);
 
-	g_clear_object (&priv->dbus_proxy);
+	g_clear_object (&priv->dbus_device_proxy);
+	g_clear_object (&priv->dbus_station_proxy);
 	g_clear_object (&priv->dbus_obj);
 
 	remove_all_aps (self);
@@ -1952,9 +2047,7 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
@@ -1962,25 +2055,29 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless);
 
-	parent_class->can_auto_connect = can_auto_connect;
-	parent_class->is_available = is_available;
-	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->check_connection_available = check_connection_available;
-	parent_class->complete_connection = complete_connection;
-	parent_class->get_enabled = get_enabled;
-	parent_class->set_enabled = set_enabled;
-	parent_class->get_type_description = get_type_description;
-
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->get_configured_mtu = get_configured_mtu;
-	parent_class->deactivate = deactivate;
-	parent_class->deactivate_async = deactivate_async;
-	parent_class->deactivate_async_finish = deactivate_async_finish;
-	parent_class->can_reapply_change = can_reapply_change;
-
-	parent_class->state_changed = device_state_changed;
+	device_class->connection_type_supported = NM_SETTING_WIRELESS_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_WIRELESS_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIFI);
+
+	device_class->can_auto_connect = can_auto_connect;
+	device_class->is_available = is_available;
+	device_class->get_autoconnect_allowed = get_autoconnect_allowed;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->check_connection_available = check_connection_available;
+	device_class->complete_connection = complete_connection;
+	device_class->get_enabled = get_enabled;
+	device_class->set_enabled = set_enabled;
+	device_class->get_type_description = get_type_description;
+
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->get_configured_mtu = get_configured_mtu;
+	device_class->deactivate = deactivate;
+	device_class->deactivate_async = deactivate_async;
+	device_class->deactivate_async_finish = deactivate_async_finish;
+	device_class->can_reapply_change = can_reapply_change;
+
+	device_class->state_changed = device_state_changed;
 
 	klass->scanning_prohibited = scanning_prohibited;
 
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index fd7bf3f7..6f833635 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -80,28 +80,6 @@ G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE)
 /*****************************************************************************/
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
-{
-	NMSettingConnection *s_con;
-	NMSettingOlpcMesh *s_mesh;
-
-	if (!NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-
-	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_OLPC_MESH_SETTING_NAME))
-		return FALSE;
-
-	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
-	if (!s_mesh)
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean
 get_autoconnect_allowed (NMDevice *device)
 {
 	return FALSE;
@@ -117,7 +95,6 @@ complete_connection (NMDevice *device,
                      GError **error)
 {
 	NMSettingOlpcMesh *s_mesh;
-	GByteArray *tmp;
 
 	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
 	if (!s_mesh) {
@@ -126,10 +103,10 @@ complete_connection (NMDevice *device,
 	}
 
 	if (!nm_setting_olpc_mesh_get_ssid (s_mesh)) {
-		tmp = g_byte_array_sized_new (strlen (DEFAULT_SSID));
-		g_byte_array_append (tmp, (const guint8 *) DEFAULT_SSID, strlen (DEFAULT_SSID));
-		g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_SSID, tmp, NULL);
-		g_byte_array_free (tmp, TRUE);
+		gs_unref_bytes GBytes *ssid = NULL;
+
+		ssid = g_bytes_new_static (DEFAULT_SSID, NM_STRLEN (DEFAULT_SSID));
+		g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_SSID, ssid, NULL);
 	}
 
 	if (!nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh)) {
@@ -515,9 +492,7 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_OLPC_MESH_SETTING_NAME, NM_LINK_TYPE_OLPC_MESH)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed = constructed;
 	object_class->get_property = get_property;
@@ -525,14 +500,17 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_olpc_mesh);
 
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
-	parent_class->complete_connection = complete_connection;
-	parent_class->is_available = is_available;
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->state_changed = state_changed;
-	parent_class->get_dhcp_timeout = get_dhcp_timeout;
+	device_class->connection_type_supported = NM_SETTING_OLPC_MESH_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_OLPC_MESH_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_OLPC_MESH);
+
+	device_class->get_autoconnect_allowed = get_autoconnect_allowed;
+	device_class->complete_connection = complete_connection;
+	device_class->is_available = is_available;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->state_changed = state_changed;
+	device_class->get_dhcp_timeout = get_dhcp_timeout;
 
 	obj_properties[PROP_COMPANION] =
 	     g_param_spec_string (NM_DEVICE_OLPC_MESH_COMPANION, "", "",
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 6bd06524..64ab82cb 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -121,6 +121,8 @@ typedef struct {
 	gint32 hw_addr_scan_expire;
 
 	guint             wps_timeout_id;
+
+	NMSettingWirelessWakeOnWLan wowlan_restore;
 } NMDeviceWifiPrivate;
 
 struct _NMDeviceWifi
@@ -436,7 +438,7 @@ periodic_update (NMDeviceWifi *self)
 		percent = nm_platform_wifi_get_quality (nm_device_get_platform (NM_DEVICE (self)), ifindex);
 		if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
 			if (nm_wifi_ap_set_strength (priv->current_ap, (gint8) percent)) {
-#ifdef NM_MORE_LOGGING
+#if NM_MORE_LOGGING
 				_ap_dump (self, LOGL_TRACE, priv->current_ap, "updated", 0);
 #endif
 			}
@@ -508,6 +510,22 @@ remove_all_aps (NMDeviceWifi *self)
 	nm_device_recheck_available_connections (NM_DEVICE (self));
 }
 
+static gboolean
+wake_on_wlan_restore (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSettingWirelessWakeOnWLan w;
+
+	w = priv->wowlan_restore;
+	if (w == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE)
+		return TRUE;
+
+	priv->wowlan_restore = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
+	return nm_platform_wifi_set_wake_on_wlan (NM_PLATFORM_GET,
+	                                          nm_device_get_ifindex (NM_DEVICE (self)),
+	                                          w);
+}
+
 static void
 deactivate (NMDevice *device)
 {
@@ -524,6 +542,9 @@ deactivate (NMDevice *device)
 
 	set_current_ap (self, NULL, TRUE);
 
+	if (!wake_on_wlan_restore (self))
+		_LOGW (LOGD_DEVICE | LOGD_WIFI, "Cannot unconfigure WoWLAN.");
+
 	/* Clear any critical protocol notification in the Wi-Fi stack */
 	nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE);
 
@@ -583,11 +604,10 @@ is_adhoc_wpa (NMConnection *connection)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	NMSettingConnection *s_con;
 	NMSettingWireless *s_wireless;
 	const char *mac;
 	const char * const *mac_blacklist;
@@ -595,24 +615,19 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 	const char *mode;
 	const char *perm_hw_addr;
 
-	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection))
-		return FALSE;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-
-	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME))
+	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
 	s_wireless = nm_connection_get_setting_wireless (connection);
-	if (!s_wireless)
-		return FALSE;
 
 	perm_hw_addr = nm_device_get_permanent_hw_address (device);
 	mac = nm_setting_wireless_get_mac_address (s_wireless);
 	if (perm_hw_addr) {
-		if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
+		if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "device MAC address does not match the profile");
 			return FALSE;
+		}
 
 		/* Check for MAC address blacklist */
 		mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
@@ -622,27 +637,45 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 				return FALSE;
 			}
 
-			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
+			if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "MAC address blacklisted");
 				return FALSE;
+			}
 		}
-	} else if (mac)
+	} else if (mac) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "device has no valid MAC address as required by profile");
 		return FALSE;
+	}
 
-	if (is_adhoc_wpa (connection))
+	if (is_adhoc_wpa (connection)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "Ad-Hoc WPA networks are not supported");
 		return FALSE;
+	}
 
 	/* Early exit if supplicant or device doesn't support requested mode */
 	mode = nm_setting_wireless_get_mode (s_wireless);
 	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) {
-		if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC))
+		if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "the device does not support Ad-Hoc networks");
 			return FALSE;
+		}
 	} else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) {
-		if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP))
+		if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "the device does not support Access Point mode");
 			return FALSE;
+		}
 
 		if (priv->sup_iface) {
-			if (nm_supplicant_interface_get_ap_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_NO)
+			if (nm_supplicant_interface_get_ap_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_NO) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "wpa_supplicant does not support Access Point mode");
 				return FALSE;
+			}
 		}
 	}
 
@@ -657,7 +690,8 @@ static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
+                            const char *specific_object,
+                            GError **error)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
@@ -674,7 +708,17 @@ check_connection_available (NMDevice *device,
 		NMWifiAP *ap;
 
 		ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
-		return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE;
+		if (!ap) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "requested access point not found");
+			return FALSE;
+		}
+		if (!nm_wifi_ap_check_compatible (ap, connection)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "requested access point is not compatible with profile");
+			return FALSE;
+		}
+		return TRUE;
 	}
 
 	/* Ad-Hoc and AP connections are always available because they may be
@@ -693,11 +737,17 @@ check_connection_available (NMDevice *device,
 	 * activating but the network isn't available let the device recheck
 	 * availability.
 	 */
-	if (nm_setting_wireless_get_hidden (s_wifi) || NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
+	if (   nm_setting_wireless_get_hidden (s_wifi)
+	    || NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
 		return TRUE;
 
-	/* check at least one AP is compatible with this connection */
-	return !!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+	if (!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "no compatible access point found");
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
 static gboolean
@@ -711,10 +761,9 @@ complete_connection (NMDevice *device,
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSettingWireless *s_wifi;
 	const char *setting_mac;
-	char *str_ssid = NULL;
+	gs_free char *ssid_utf8 = NULL;
 	NMWifiAP *ap;
-	const GByteArray *ssid = NULL;
-	GByteArray *tmp_ssid = NULL;
+	GBytes *ssid = NULL;
 	GBytes *setting_ssid = NULL;
 	gboolean hidden = FALSE;
 	const char *perm_hw_addr;
@@ -786,19 +835,14 @@ complete_connection (NMDevice *device,
 
 	if (ap)
 		ssid = nm_wifi_ap_get_ssid (ap);
+
 	if (ssid == NULL) {
 		/* The AP must be hidden.  Connecting to a WiFi AP requires the SSID
 		 * as part of the initial handshake, so check the connection details
 		 * for the SSID.  The AP object will still be used for encryption
 		 * settings and such.
 		 */
-		setting_ssid = nm_setting_wireless_get_ssid (s_wifi);
-		if (setting_ssid) {
-			ssid = tmp_ssid = g_byte_array_new ();
-			g_byte_array_append (tmp_ssid,
-			                     g_bytes_get_data (setting_ssid, NULL),
-			                     g_bytes_get_size (setting_ssid));
-		}
+		ssid = nm_setting_wireless_get_ssid (s_wifi);
 	}
 
 	if (ssid == NULL) {
@@ -821,11 +865,8 @@ complete_connection (NMDevice *device,
 		if (!nm_wifi_ap_complete_connection (ap,
 		                                     connection,
 		                                     nm_wifi_utils_is_manf_default_ssid (ssid),
-		                                     error)) {
-			if (tmp_ssid)
-				g_byte_array_unref (tmp_ssid);
+		                                     error))
 			return FALSE;
-		}
 	}
 
 	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
@@ -838,24 +879,18 @@ complete_connection (NMDevice *device,
 		                     NM_CONNECTION_ERROR_INVALID_SETTING,
 		                     _("WPA Ad-Hoc disabled due to kernel bugs"));
 		g_prefix_error (error, "%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
-		if (tmp_ssid)
-			g_byte_array_unref (tmp_ssid);
 		return FALSE;
 	}
 
-	str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
-
+	ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
 	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_WIRELESS_SETTING_NAME,
 	                           existing_connections,
-	                           str_ssid,
-	                           str_ssid,
+	                           ssid_utf8,
+	                           ssid_utf8,
 	                           NULL,
 	                           TRUE);
-	g_free (str_ssid);
-	if (tmp_ssid)
-		g_byte_array_unref (tmp_ssid);
 
 	if (hidden)
 		g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
@@ -923,11 +958,12 @@ get_autoconnect_allowed (NMDevice *device)
 
 static gboolean
 can_auto_connect (NMDevice *device,
-                  NMConnection *connection,
+                  NMSettingsConnection *sett_conn,
                   char **specific_object)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMConnection *connection;
 	NMSettingWireless *s_wifi;
 	NMWifiAP *ap;
 	const char *method, *mode;
@@ -935,26 +971,28 @@ can_auto_connect (NMDevice *device,
 
 	nm_assert (!specific_object || !*specific_object);
 
-	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (device, connection, NULL))
+	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (device, sett_conn, NULL))
 		return FALSE;
 
+	connection = nm_settings_connection_get_connection (sett_conn);
+
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wifi, FALSE);
 
 	/* Always allow autoconnect for AP and non-autoconf Ad-Hoc */
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
 	mode = nm_setting_wireless_get_mode (s_wifi);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0)
+	if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP))
 		return TRUE;
-	else if (   g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0
-	         && g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0)
+	else if (   nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)
+	         && !nm_streq0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
 		return TRUE;
 
 	/* Don't autoconnect to networks that have been tried at least once
 	 * but haven't been successful, since these are often accidental choices
 	 * from the menu and the user may not know the password.
 	 */
-	if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp)) {
+	if (nm_settings_connection_get_timestamp (sett_conn, &timestamp)) {
 		if (timestamp == 0)
 			return FALSE;
 	}
@@ -1033,7 +1071,6 @@ static GPtrArray *
 ssids_options_to_ptrarray (GVariant *value, GError **error)
 {
 	GPtrArray *ssids = NULL;
-	GByteArray *ssid_array;
 	GVariant *v;
 	const guint8 *bytes;
 	gsize len;
@@ -1049,7 +1086,7 @@ ssids_options_to_ptrarray (GVariant *value, GError **error)
 	}
 
 	if (num_ssids) {
-		ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_byte_array_unref);
+		ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_bytes_unref);
 		for (i = 0; i < num_ssids; i++) {
 			v = g_variant_get_child_value (value, i);
 			bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8));
@@ -1062,9 +1099,7 @@ ssids_options_to_ptrarray (GVariant *value, GError **error)
 				return NULL;
 			}
 
-			ssid_array = g_byte_array_new ();
-			g_byte_array_append (ssid_array, bytes, len);
-			g_ptr_array_add (ssids, ssid_array);
+			g_ptr_array_add (ssids, g_bytes_new (bytes, len));
 		}
 	}
 	return ssids;
@@ -1232,14 +1267,15 @@ check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic)
 
 static gboolean
 hidden_filter_func (NMSettings *settings,
-                    NMSettingsConnection *connection,
+                    NMSettingsConnection *set_con,
                     gpointer user_data)
 {
+	NMConnection *connection = nm_settings_connection_get_connection (set_con);
 	NMSettingWireless *s_wifi;
 
-	if (!nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_WIRELESS_SETTING_NAME))
+	if (!nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME))
 		return FALSE;
-	s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection));
+	s_wifi = nm_connection_get_setting_wireless (connection);
 	if (!s_wifi)
 		return FALSE;
 	if (nm_streq0 (nm_setting_wireless_get_mode (s_wifi), NM_SETTING_WIRELESS_MODE_AP))
@@ -1255,7 +1291,7 @@ build_hidden_probe_list (NMDeviceWifi *self)
 	gs_free NMSettingsConnection **connections = NULL;
 	guint i, len;
 	GPtrArray *ssids = NULL;
-	static GByteArray *nullssid = NULL;
+	static GBytes *nullssid = NULL;
 
 	/* Need at least two: wildcard SSID and one or more hidden SSIDs */
 	if (max_scan_ssids < 2)
@@ -1270,30 +1306,23 @@ build_hidden_probe_list (NMDeviceWifi *self)
 
 	g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_timestamp_p_with_data, NULL);
 
-	ssids = g_ptr_array_new_full (max_scan_ssids, (GDestroyNotify) g_byte_array_unref);
+	ssids = g_ptr_array_new_full (max_scan_ssids, (GDestroyNotify) g_bytes_unref);
 
 	/* Add wildcard SSID using a static wildcard SSID used for every scan */
 	if (G_UNLIKELY (nullssid == NULL))
-		nullssid = g_byte_array_new ();
-	g_ptr_array_add (ssids, g_byte_array_ref (nullssid));
+		nullssid = g_bytes_new_static ("", 0);
+	g_ptr_array_add (ssids, g_bytes_ref (nullssid));
 
 	for (i = 0; connections[i]; i++) {
 		NMSettingWireless *s_wifi;
 		GBytes *ssid;
-		GByteArray *ssid_array;
 
 		if (i >= max_scan_ssids - 1)
 			break;
 
-		s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (NM_CONNECTION (connections[i]));
-		g_assert (s_wifi);
+		s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (nm_settings_connection_get_connection (connections[i]));
 		ssid = nm_setting_wireless_get_ssid (s_wifi);
-		g_assert (ssid);
-		ssid_array = g_byte_array_new ();
-		g_byte_array_append (ssid_array,
-		                     g_bytes_get_data (ssid, NULL),
-		                     g_bytes_get_size (ssid));
-		g_ptr_array_add (ssids, ssid_array);
+		g_ptr_array_add (ssids, g_bytes_ref (ssid));
 	}
 
 	return ssids;
@@ -1320,24 +1349,22 @@ request_wireless_scan (NMDeviceWifi *self,
 
 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested");
 
-		if (!ssids) {
+		if (!ssids)
 			ssids = hidden_ssids = build_hidden_probe_list (self);
-		}
 
 		if (_LOGD_ENABLED (LOGD_WIFI)) {
 			if (ssids) {
-				const GByteArray *ssid;
 				guint i;
-				char *foo;
 
 				for (i = 0; i < ssids->len; i++) {
-					ssid = g_ptr_array_index (ssids, i);
-					foo = ssid->len > 0
-					      ? nm_utils_ssid_to_utf8 (ssid->data, ssid->len)
-					      : NULL;
-					_LOGD (LOGD_WIFI, "wifi-scan: (%u) probe scanning SSID %s%s%s",
-					       i, NM_PRINT_FMT_QUOTED (foo, "\"", foo, "\"", "*any*"));
-					g_free (foo);
+					gs_free char *ssid_str = NULL;
+					GBytes *ssid = ssids->pdata[i];
+
+					ssid_str = g_bytes_get_size (ssid) > 0
+					           ? _nm_utils_ssid_to_string (ssid)
+					           : NULL;
+					_LOGD (LOGD_WIFI, "wifi-scan: (%u) probe scanning SSID %s",
+					       i, ssid_str ?: "*any*");
 				}
 			} else
 				_LOGD (LOGD_WIFI, "wifi-scan: no SSIDs to probe scan");
@@ -1345,7 +1372,9 @@ request_wireless_scan (NMDeviceWifi *self,
 
 		_hw_addr_set_scanning (self, FALSE);
 
-		nm_supplicant_interface_request_scan (priv->sup_iface, ssids);
+		nm_supplicant_interface_request_scan (priv->sup_iface,
+		                                      ssids ? (GBytes *const*) ssids->pdata : NULL,
+		                                      ssids ? ssids->len : 0u);
 		request_started = TRUE;
 	} else
 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested but not allowed at this time");
@@ -1484,17 +1513,13 @@ try_fill_ssid_for_hidden_ap (NMDeviceWifi *self,
 	 * and if a match is found, copy over the SSID */
 	connections = nm_settings_get_connections (nm_device_get_settings ((NMDevice *) self), NULL);
 	for (i = 0; connections[i]; i++) {
-		NMConnection *connection = (NMConnection *) connections[i];
+		NMSettingsConnection *sett_conn = connections[i];
 		NMSettingWireless *s_wifi;
 
-		s_wifi = nm_connection_get_setting_wireless (connection);
+		s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (sett_conn));
 		if (s_wifi) {
-			if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid)) {
-				GBytes *ssid = nm_setting_wireless_get_ssid (s_wifi);
-
-				nm_wifi_ap_set_ssid (ap,
-				                     g_bytes_get_data (ssid, NULL),
-				                     g_bytes_get_size (ssid));
+			if (nm_settings_connection_has_seen_bssid (sett_conn, bssid)) {
+				nm_wifi_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
 				break;
 			}
 		}
@@ -1510,7 +1535,7 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMDeviceState state;
 	NMWifiAP *found_ap = NULL;
-	const GByteArray *ssid;
+	GBytes *ssid;
 
 	g_return_if_fail (self != NULL);
 	g_return_if_fail (properties != NULL);
@@ -1539,15 +1564,19 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
 
 		/* Let the manager try to fill in the SSID from seen-bssids lists */
 		ssid = nm_wifi_ap_get_ssid (ap);
-		if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
+		if (!ssid || _nm_utils_is_empty_ssid (ssid)) {
 			/* Try to fill the SSID from the AP database */
 			try_fill_ssid_for_hidden_ap (self, ap);
 
 			ssid = nm_wifi_ap_get_ssid (ap);
-			if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
+			if (   ssid
+			    && !_nm_utils_is_empty_ssid (ssid)) {
+				gs_free char *s = NULL;
+
 				/* Yay, matched it, no longer treat as hidden */
-				_LOGD (LOGD_WIFI, "matched hidden AP %s => '%s'",
-				       nm_wifi_ap_get_address (ap), nm_utils_escape_ssid (ssid->data, ssid->len));
+				_LOGD (LOGD_WIFI, "matched hidden AP %s => %s",
+				       nm_wifi_ap_get_address (ap),
+				       (s = _nm_utils_ssid_to_string (ssid)));
 			} else {
 				/* Didn't have an entry for this AP in the database */
 				_LOGD (LOGD_WIFI, "failed to match hidden AP %s",
@@ -1859,7 +1888,7 @@ need_new_8021x_secrets (NMDeviceWifi *self,
 static gboolean
 need_new_wpa_psk (NMDeviceWifi *self,
                   NMSupplicantInterfaceState old_state,
-                  gint disconnect_reason,
+                  int disconnect_reason,
                   const char **setting_name)
 {
 	NMSettingWirelessSecurity *s_wsec;
@@ -2004,6 +2033,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			NMConnection *connection;
 			NMSettingWireless *s_wifi;
 			GBytes *ssid;
+			gs_free char *ssid_str = NULL;
 
 			connection = nm_device_get_applied_connection (NM_DEVICE (self));
 			g_return_if_fail (connection);
@@ -2015,11 +2045,11 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			g_return_if_fail (ssid);
 
 			_LOGI (LOGD_DEVICE | LOGD_WIFI,
-			       "Activation: (wifi) Stage 2 of 5 (Device Configure) successful.  %s '%s'.",
-			       priv->mode == NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot" :
-			       "Connected to wireless network",
-			       ssid ? nm_utils_escape_ssid (g_bytes_get_data (ssid, NULL),
-			                                    g_bytes_get_size (ssid)) : "(none)");
+			       "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. %s %s",
+			       priv->mode == NM_802_11_MODE_AP
+			       ? "Started Wi-Fi Hotspot"
+			       : "Connected to wireless network",
+			       (ssid_str = _nm_utils_ssid_to_string (ssid)));
 			nm_device_activate_schedule_stage3_ip_config_start (device);
 		} else if (devstate == NM_DEVICE_STATE_ACTIVATED)
 			periodic_update (self);
@@ -2127,9 +2157,11 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
 
 	if (new_ap != priv->current_ap) {
 		const char *new_bssid = NULL;
-		const GByteArray *new_ssid = NULL;
+		GBytes *new_ssid = NULL;
 		const char *old_bssid = NULL;
-		const GByteArray *old_ssid = NULL;
+		GBytes *old_ssid = NULL;
+		gs_free char *new_ssid_s = NULL;
+		gs_free char *old_ssid_s = NULL;
 
 		/* Don't ever replace a "fake" current AP if we don't know about the
 		 * supplicant's current BSS yet.  It'll get replaced when we receive
@@ -2150,9 +2182,9 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
 
 		_LOGD (LOGD_WIFI, "roamed from BSSID %s (%s) to %s (%s)",
 		       old_bssid ?: "(none)",
-		       old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)",
+		       (old_ssid_s = _nm_utils_ssid_to_string (old_ssid)),
 		       new_bssid ?: "(none)",
-		       new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)");
+		       (new_ssid_s = _nm_utils_ssid_to_string (new_ssid)));
 
 		set_current_ap (self, new_ap, TRUE);
 	}
@@ -2332,7 +2364,6 @@ build_supplicant_config (NMDeviceWifi *self,
 	NMSettingWirelessSecurity *s_wireless_sec;
 	NMSettingWirelessSecurityPmf pmf;
 	NMSettingWirelessSecurityFils fils;
-	gs_free char *value = NULL;
 
 	g_return_val_if_fail (priv->sup_iface, NULL);
 
@@ -2374,25 +2405,23 @@ build_supplicant_config (NMDeviceWifi *self,
 		/* Configure PMF (802.11w) */
 		pmf = nm_setting_wireless_security_get_pmf (s_wireless_sec);
 		if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) {
-			value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-			                                               "wifi-sec.pmf",
-			                                               NM_DEVICE (self));
-			pmf = _nm_utils_ascii_str_to_int64 (value, 10,
-			                                    NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE,
-			                                    NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED,
-			                                    NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL);
+			pmf = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+			                                                   "wifi-sec.pmf",
+			                                                   NM_DEVICE (self),
+			                                                   NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE,
+			                                                   NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED,
+			                                                   NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL);
 		}
 
 		/* Configure FILS (802.11ai) */
 		fils = nm_setting_wireless_security_get_fils (s_wireless_sec);
 		if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT) {
-			value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-			                                               "wifi-sec.fils",
-			                                               NM_DEVICE (self));
-			fils = _nm_utils_ascii_str_to_int64 (value, 10,
-			                                     NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE,
-			                                     NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED,
-			                                     NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL);
+			fils = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+			                                                    "wifi-sec.fils",
+			                                                    NM_DEVICE (self),
+			                                                    NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE,
+			                                                    NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED,
+			                                                    NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL);
 		}
 
 		s_8021x = nm_connection_get_setting_802_1x (connection);
@@ -2426,9 +2455,9 @@ error:
 static gboolean
 wake_on_wlan_enable (NMDeviceWifi *self)
 {
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSettingWirelessWakeOnWLan wowl;
 	NMSettingWireless *s_wireless;
-	gs_free char *value = NULL;
 
 	s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS);
 	if (s_wireless) {
@@ -2437,32 +2466,39 @@ wake_on_wlan_enable (NMDeviceWifi *self)
 			goto found;
 	}
 
-	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-	                                               "wifi.wake-on-wlan",
-	                                               NM_DEVICE (self));
-
-	if (value) {
-		wowl = _nm_utils_ascii_str_to_int64 (value, 10,
-		                                     NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE,
-		                                     G_MAXINT32,
-		                                     NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT);
+	wowl = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                    "wifi.wake-on-wlan",
+	                                                    NM_DEVICE (self),
+	                                                    NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE,
+	                                                    G_MAXINT32,
+	                                                    NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT);
 
-		if (NM_FLAGS_ANY (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_EXCLUSIVE_FLAGS)) {
-			if (!nm_utils_is_power_of_two (wowl)) {
-				_LOGD (LOGD_WIFI, "invalid default value %u for wake-on-wlan: "
-				       "'default' and 'ignore' are exclusive flags", (guint) wowl);
-				wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT;
-			}
-		} else if (NM_FLAGS_ANY (wowl, ~NM_SETTING_WIRELESS_WAKE_ON_WLAN_ALL)) {
-			_LOGD (LOGD_WIFI, "invalid default value %u for wake-on-wlan", (guint) wowl);
+	if (NM_FLAGS_ANY (wowl, NM_SETTING_WIRELESS_WAKE_ON_WLAN_EXCLUSIVE_FLAGS)) {
+		if (!nm_utils_is_power_of_two (wowl)) {
+			_LOGD (LOGD_WIFI, "invalid default value %u for wake-on-wlan: "
+			       "'default' and 'ignore' are exclusive flags", (guint) wowl);
 			wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT;
 		}
-		if (wowl != NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT)
-			goto found;
+	} else if (NM_FLAGS_ANY (wowl, ~NM_SETTING_WIRELESS_WAKE_ON_WLAN_ALL)) {
+		_LOGD (LOGD_WIFI, "invalid default value %u for wake-on-wlan", (guint) wowl);
+		wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT;
 	}
+	if (wowl != NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT)
+		goto found;
+
 	wowl = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
 found:
-	return nm_platform_wifi_set_wake_on_wlan (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), wowl);
+	if (wowl == NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE) {
+		priv->wowlan_restore = wowl;
+		return TRUE;
+	}
+
+	priv->wowlan_restore = nm_platform_wifi_get_wake_on_wlan (NM_PLATFORM_GET,
+	                                                          nm_device_get_ifindex (NM_DEVICE (self)));
+
+	return nm_platform_wifi_set_wake_on_wlan (NM_PLATFORM_GET,
+	                                          nm_device_get_ifindex (NM_DEVICE (self)),
+	                                          wowl);
 }
 
 static NMActStageReturn
@@ -2597,31 +2633,29 @@ set_powersave (NMDevice *device)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMSettingWireless *s_wireless;
-	NMSettingWirelessPowersave powersave;
-	gs_free char *value = NULL;
+	NMSettingWirelessPowersave val;
 
 	s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS);
 	g_return_if_fail (s_wireless);
 
-	powersave = nm_setting_wireless_get_powersave (s_wireless);
-	if (powersave == NM_SETTING_WIRELESS_POWERSAVE_DEFAULT) {
-		value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
-		                                               "wifi.powersave",
-		                                               device);
-		powersave = _nm_utils_ascii_str_to_int64 (value, 10,
-		                                          NM_SETTING_WIRELESS_POWERSAVE_IGNORE,
-		                                          NM_SETTING_WIRELESS_POWERSAVE_ENABLE,
-		                                          NM_SETTING_WIRELESS_POWERSAVE_IGNORE);
+	val = nm_setting_wireless_get_powersave (s_wireless);
+	if (val == NM_SETTING_WIRELESS_POWERSAVE_DEFAULT) {
+		val = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+		                                                   "wifi.powersave",
+		                                                   device,
+		                                                   NM_SETTING_WIRELESS_POWERSAVE_IGNORE,
+		                                                   NM_SETTING_WIRELESS_POWERSAVE_ENABLE,
+		                                                   NM_SETTING_WIRELESS_POWERSAVE_IGNORE);
 	}
 
-	_LOGT (LOGD_WIFI, "powersave is set to %u", (unsigned) powersave);
+	_LOGT (LOGD_WIFI, "powersave is set to %u", (unsigned) val);
 
-	if (powersave == NM_SETTING_WIRELESS_POWERSAVE_IGNORE)
+	if (val == NM_SETTING_WIRELESS_POWERSAVE_IGNORE)
 		return;
 
 	nm_platform_wifi_set_powersave (nm_device_get_platform (device),
 	                                nm_device_get_ifindex (device),
-	                                powersave == NM_SETTING_WIRELESS_POWERSAVE_ENABLE);
+	                                val == NM_SETTING_WIRELESS_POWERSAVE_ENABLE);
 }
 
 static NMActStageReturn
@@ -2658,8 +2692,6 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wireless);
 
-	wake_on_wlan_enable (self);
-
 	/* If we need secrets, get them */
 	setting_name = nm_connection_need_secrets (connection, NULL);
 	if (setting_name) {
@@ -2676,6 +2708,9 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		goto out;
 	}
 
+	if (!wake_on_wlan_enable (self))
+		_LOGW (LOGD_DEVICE | LOGD_WIFI, "Cannot configure WoWLAN.");
+
 	/* have secrets, or no secrets required */
 	if (nm_connection_get_setting_wireless_security (connection)) {
 		_LOGI (LOGD_DEVICE | LOGD_WIFI,
@@ -2726,8 +2761,10 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	ret = NM_ACT_STAGE_RETURN_POSTPONE;
 
 out:
-	if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+	if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
 		cleanup_association_attempt (self, TRUE);
+		wake_on_wlan_restore (self);
+	}
 
 	if (config) {
 		/* Supplicant interface object refs the config; we no longer care about
@@ -3133,7 +3170,8 @@ reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_n
 
 	_LOGD (LOGD_DEVICE, "reapplying wireless settings");
 
-	wake_on_wlan_enable (self);
+	if (!wake_on_wlan_enable (self))
+		_LOGW (LOGD_DEVICE | LOGD_WIFI, "Cannot configure WoWLAN.");
 }
 
 /*****************************************************************************/
@@ -3206,6 +3244,7 @@ nm_device_wifi_init (NMDeviceWifi *self)
 	c_list_init (&priv->aps_lst_head);
 
 	priv->mode = NM_802_11_MODE_INFRA;
+	priv->wowlan_restore = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
 }
 
 static void
@@ -3273,9 +3312,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
-	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
-
-	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed = constructed;
 	object_class->get_property = get_property;
@@ -3285,29 +3322,33 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless);
 
-	parent_class->can_auto_connect = can_auto_connect;
-	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
-	parent_class->is_available = is_available;
-	parent_class->check_connection_compatible = check_connection_compatible;
-	parent_class->check_connection_available = check_connection_available;
-	parent_class->complete_connection = complete_connection;
-	parent_class->get_enabled = get_enabled;
-	parent_class->set_enabled = set_enabled;
-
-	parent_class->act_stage1_prepare = act_stage1_prepare;
-	parent_class->act_stage2_config = act_stage2_config;
-	parent_class->get_configured_mtu = get_configured_mtu;
-	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
-	parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
-	parent_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout;
-	parent_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
-	parent_class->deactivate = deactivate;
-	parent_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
-	parent_class->unmanaged_on_quit = unmanaged_on_quit;
-	parent_class->can_reapply_change = can_reapply_change;
-	parent_class->reapply_connection = reapply_connection;
-
-	parent_class->state_changed = device_state_changed;
+	device_class->connection_type_supported = NM_SETTING_WIRELESS_SETTING_NAME;
+	device_class->connection_type_check_compatible = NM_SETTING_WIRELESS_SETTING_NAME;
+	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIFI);
+
+	device_class->can_auto_connect = can_auto_connect;
+	device_class->get_autoconnect_allowed = get_autoconnect_allowed;
+	device_class->is_available = is_available;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->check_connection_available = check_connection_available;
+	device_class->complete_connection = complete_connection;
+	device_class->get_enabled = get_enabled;
+	device_class->set_enabled = set_enabled;
+
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->act_stage2_config = act_stage2_config;
+	device_class->get_configured_mtu = get_configured_mtu;
+	device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+	device_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout;
+	device_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
+	device_class->deactivate = deactivate;
+	device_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
+	device_class->unmanaged_on_quit = unmanaged_on_quit;
+	device_class->can_reapply_change = can_reapply_change;
+	device_class->reapply_connection = reapply_connection;
+
+	device_class->state_changed = device_state_changed;
 
 	klass->scanning_prohibited = scanning_prohibited;
 
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index d6511296..1f28d263 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -29,13 +29,21 @@
 #include "nm-core-internal.h"
 #include "nm-manager.h"
 #include "nm-device-iwd.h"
+#include "nm-wifi-utils.h"
 #include "nm-utils/nm-random-utils.h"
+#include "settings/nm-settings.h"
 
 /*****************************************************************************/
 
 typedef struct {
-	gchar *name;
+	const char *name;
 	NMIwdNetworkSecurity security;
+	char buf[0];
+} KnownNetworkId;
+
+typedef struct {
+	GDBusProxy *known_network;
+	NMSettingsConnection *mirror_connection;
 } KnownNetworkData;
 
 typedef struct {
@@ -44,8 +52,8 @@ typedef struct {
 	gboolean running;
 	GDBusObjectManager *object_manager;
 	guint agent_id;
-	gchar *agent_path;
-	GSList *known_networks;
+	char *agent_path;
+	GHashTable *known_networks;
 } NMIwdManagerPrivate;
 
 struct _NMIwdManager {
@@ -83,20 +91,49 @@ G_DEFINE_TYPE (NMIwdManager, nm_iwd_manager, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+static void mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn);
+
+/*****************************************************************************/
+
+static const char *
+get_variant_string_or_null (GVariant *v)
+{
+	if (!v)
+		return NULL;
+
+	if (   !g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)
+	    && !g_variant_is_of_type (v, G_VARIANT_TYPE_OBJECT_PATH))
+		return NULL;
+
+	return g_variant_get_string (v, NULL);
+}
+
+static const char *
+get_property_string_or_null (GDBusProxy *proxy, const char *property)
+{
+	gs_unref_variant GVariant *value = NULL;
+
+	if (!proxy || !property)
+		return NULL;
+
+	value = g_dbus_proxy_get_cached_property (proxy, property);
+
+	return get_variant_string_or_null (value);
+}
+
 static void
 agent_dbus_method_cb (GDBusConnection *connection,
-                      const gchar *sender, const gchar *object_path,
-                      const gchar *interface_name, const gchar *method_name,
+                      const char *sender, const char *object_path,
+                      const char *interface_name, const char *method_name,
                       GVariant *parameters,
                       GDBusMethodInvocation *invocation,
                       gpointer user_data)
 {
 	NMIwdManager *self = user_data;
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	const gchar *network_path, *device_path, *ifname;
+	const char *network_path, *device_path, *ifname;
 	gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL;
-	gs_unref_variant GVariant *value = NULL;
-	gint ifindex;
+	int ifindex;
 	NMDevice *device;
 	gs_free char *name_owner = NULL;
 
@@ -113,9 +150,8 @@ agent_dbus_method_cb (GDBusConnection *connection,
 	network = g_dbus_object_manager_get_interface (priv->object_manager,
 	                                               network_path,
 	                                               NM_IWD_NETWORK_INTERFACE);
-	value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (network), "Device");
-	device_path = g_variant_get_string (value, NULL);
 
+	device_path = get_property_string_or_null (G_DBUS_PROXY (network), "Device");
 	if (!device_path) {
 		_LOGD ("agent-request: device not cached for network %s in IWD Agent request",
 		       network_path);
@@ -125,10 +161,8 @@ agent_dbus_method_cb (GDBusConnection *connection,
 	device_obj = g_dbus_object_manager_get_interface (priv->object_manager,
 	                                                  device_path,
 	                                                  NM_IWD_DEVICE_INTERFACE);
-	g_variant_unref (value);
-	value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (device_obj), "Name");
-	ifname = g_variant_get_string (value, NULL);
 
+	ifname = get_property_string_or_null (G_DBUS_PROXY (device_obj), "Name");
 	if (!ifname) {
 		_LOGD ("agent-request: name not cached for device %s in IWD Agent request",
 		       device_path);
@@ -207,12 +241,12 @@ static const GDBusInterfaceInfo iwd_agent_iface_info = NM_DEFINE_GDBUS_INTERFACE
 
 static guint
 iwd_agent_export (GDBusConnection *connection, gpointer user_data,
-                  gchar **agent_path, GError **error)
+                  char **agent_path, GError **error)
 {
 	static const GDBusInterfaceVTable vtable = {
 		.method_call = agent_dbus_method_cb,
 	};
-	gchar path[50];
+	char path[50];
 	unsigned int rnd;
 	guint id;
 
@@ -251,38 +285,68 @@ register_agent (NMIwdManager *self)
 
 /*****************************************************************************/
 
-static void
-set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface,
-                        GDBusObject *object)
+static KnownNetworkId *
+known_network_id_new (const char *name, NMIwdNetworkSecurity security)
 {
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusProxy *proxy;
-	GVariant *value;
-	const char *ifname;
-	gint ifindex;
-	NMDevice *device;
+	KnownNetworkId *id;
+	gsize strsize = strlen (name) + 1;
 
-	if (!priv->running)
-		return;
+	id = g_malloc (sizeof (KnownNetworkId) + strsize);
+	id->name = id->buf;
+	id->security = security;
+	memcpy (id->buf, name, strsize);
 
-	g_return_if_fail (G_IS_DBUS_PROXY (interface));
+	return id;
+}
 
-	proxy = G_DBUS_PROXY (interface);
+static guint
+known_network_id_hash (KnownNetworkId *id)
+{
+	NMHashState h;
 
-	if (strcmp (g_dbus_proxy_get_interface_name (proxy),
-	            NM_IWD_DEVICE_INTERFACE))
+	nm_hash_init (&h, 1947951703u);
+	nm_hash_update_val (&h, id->security);
+	nm_hash_update_str (&h, id->name);
+	return nm_hash_complete (&h);
+}
+
+static gboolean
+known_network_id_equal (KnownNetworkId *a, KnownNetworkId *b)
+{
+	return    a->security == b->security
+	       && nm_streq (a->name, b->name);
+}
+
+static void
+known_network_data_free (KnownNetworkData *network)
+{
+	if (!network)
 		return;
 
-	value = g_dbus_proxy_get_cached_property (proxy, "Name");
-	if (!value) {
+	g_object_unref (network->known_network);
+	mirror_8021x_connection_take_and_delete (network->mirror_connection);
+	g_slice_free (KnownNetworkData, network);
+}
+
+/*****************************************************************************/
+
+static void
+set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy,
+                        GDBusObject *object)
+{
+	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+	const char *ifname;
+	int ifindex;
+	NMDevice *device;
+
+	ifname = get_property_string_or_null (proxy, "Name");
+	if (!ifname) {
 		_LOGE ("Name not cached for Device at %s",
 		       g_dbus_proxy_get_object_path (proxy));
 		return;
 	}
 
-	ifname = g_variant_get_string (value, NULL);
 	ifindex = if_nametoindex (ifname);
-	g_variant_unref (value);
 
 	if (!ifindex) {
 		_LOGE ("if_nametoindex failed for Name %s for Device at %s: %i",
@@ -299,13 +363,183 @@ set_device_dbus_object (NMIwdManager *self, GDBusInterface *interface,
 	nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object);
 }
 
+/* Create an in-memory NMConnection for a WPA2-Enterprise network that
+ * has been preprovisioned with an IWD config file so that NM autoconnect
+ * mechanism and the clients know this networks needs no additional EAP
+ * configuration from the user.  Only do this if no existing connection
+ * SSID and security type match that network yet.
+ */
+static NMSettingsConnection *
+mirror_8021x_connection (NMIwdManager *self,
+                         const char *name)
+{
+	NMSettings *settings = NM_SETTINGS_GET;
+	NMSettingsConnection *const*iter;
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingsConnection *settings_connection;
+	char uuid[37];
+	NMSetting *setting;
+	GError *error = NULL;
+	gs_unref_bytes GBytes *new_ssid = NULL;
+
+	for (iter = nm_settings_get_connections (settings, NULL); *iter; iter++) {
+		NMSettingsConnection *sett_conn = *iter;
+		NMConnection *conn = nm_settings_connection_get_connection (sett_conn);
+		NMIwdNetworkSecurity security;
+		gs_free char *ssid_name = NULL;
+		NMSettingWireless *s_wifi;
+
+		security = nm_wifi_connection_get_iwd_security (conn, NULL);
+		if (security != NM_IWD_NETWORK_SECURITY_8021X)
+			continue;
+
+		s_wifi = nm_connection_get_setting_wireless (conn);
+		if (!s_wifi)
+			continue;
+
+		ssid_name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wifi));
+
+		/* We already have an NMSettingsConnection matching this
+		 * KnownNetwork, whether it's saved or an in-memory connection
+		 * potentially created by ourselves.  Nothing to do here.
+		 */
+		if (nm_streq (ssid_name, name))
+			return NULL;
+	}
+
+	connection = nm_simple_connection_new ();
+
+	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_CONNECTION,
+	                                    NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	                                    NM_SETTING_CONNECTION_ID, name,
+	                                    NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid),
+	                                    NM_SETTING_CONNECTION_READ_ONLY, TRUE,
+	                                    NULL));
+	nm_connection_add_setting (connection, setting);
+
+	new_ssid = g_bytes_new (name, strlen (name));
+	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS,
+	                                    NM_SETTING_WIRELESS_SSID, new_ssid,
+	                                    NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
+	                                    NULL));
+	nm_connection_add_setting (connection, setting);
+
+	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY,
+	                                    NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+	                                    NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
+	                                    NULL));
+	nm_connection_add_setting (connection, setting);
+
+	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_802_1X, NULL));
+	nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), "external");
+	nm_connection_add_setting (connection, setting);
+
+	if (!nm_connection_normalize (connection, NULL, NULL, NULL))
+		return NULL;
+
+	settings_connection = nm_settings_add_connection (settings, connection,
+	                                                  FALSE, &error);
+	if (!settings_connection) {
+		_LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s",
+		       name, error->message);
+		g_error_free (error);
+		return NULL;
+	}
+
+	nm_settings_connection_set_flags (settings_connection,
+	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED |
+	                                  NM_SETTINGS_CONNECTION_INT_FLAGS_UNSAVED,
+	                                  TRUE);
+	return settings_connection;
+}
+
+static void
+mirror_8021x_connection_take_and_delete (NMSettingsConnection *sett_conn)
+{
+	NMSettingsConnectionIntFlags flags;
+
+	if (!sett_conn)
+		return;
+
+	flags = nm_settings_connection_get_flags (sett_conn);
+
+	/* If connection has not been saved since we created it
+	 * in interface_added it too can be removed now. */
+	if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_INT_FLAGS_NM_GENERATED))
+		nm_settings_connection_delete (sett_conn, NULL);
+
+	g_object_unref (sett_conn);
+}
+
 static void
 interface_added (GDBusObjectManager *object_manager, GDBusObject *object,
                  GDBusInterface *interface, gpointer user_data)
 {
 	NMIwdManager *self = user_data;
+	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+	GDBusProxy *proxy;
+	const char *iface_name;
+
+	if (!priv->running)
+		return;
+
+	g_return_if_fail (G_IS_DBUS_PROXY (interface));
 
-	set_device_dbus_object (self, interface, object);
+	proxy = G_DBUS_PROXY (interface);
+	iface_name = g_dbus_proxy_get_interface_name (proxy);
+
+	if (nm_streq (iface_name, NM_IWD_DEVICE_INTERFACE)) {
+		set_device_dbus_object (self, proxy, object);
+		return;
+	}
+
+	if (nm_streq (iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) {
+		KnownNetworkId *id;
+		KnownNetworkData *data;
+		NMIwdNetworkSecurity security;
+		const char *type_str, *name;
+		NMSettingsConnection *sett_conn = NULL;
+
+		type_str = get_property_string_or_null (proxy, "Type");
+		name = get_property_string_or_null (proxy, "Name");
+		if (!type_str || !name)
+			return;
+
+		if (nm_streq (type_str, "open"))
+			security = NM_IWD_NETWORK_SECURITY_NONE;
+		else if (nm_streq (type_str, "psk"))
+			security = NM_IWD_NETWORK_SECURITY_PSK;
+		else if (nm_streq (type_str, "8021x"))
+			security = NM_IWD_NETWORK_SECURITY_8021X;
+		else
+			return;
+
+		id = known_network_id_new (name, security);
+
+		data = g_hash_table_lookup (priv->known_networks, id);
+		if (data)
+			g_free (id);
+		else {
+			data = g_slice_new0 (KnownNetworkData);
+			data->known_network = g_object_ref (proxy);
+			g_hash_table_insert (priv->known_networks, id, data);
+		}
+
+		if (security == NM_IWD_NETWORK_SECURITY_8021X) {
+			sett_conn = mirror_8021x_connection (self, name);
+
+			if (   sett_conn
+			    && sett_conn != data->mirror_connection) {
+				NMSettingsConnection *sett_conn_old = data->mirror_connection;
+
+				data->mirror_connection = nm_g_object_ref (sett_conn);
+				mirror_8021x_connection_take_and_delete (sett_conn_old);
+			}
+		} else
+			mirror_8021x_connection_take_and_delete (g_steal_pointer (&data->mirror_connection));
+
+		return;
+	}
 }
 
 static void
@@ -313,15 +547,41 @@ interface_removed (GDBusObjectManager *object_manager, GDBusObject *object,
                    GDBusInterface *interface, gpointer user_data)
 {
 	NMIwdManager *self = user_data;
+	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+	GDBusProxy *proxy;
+	const char *iface_name;
 
-	/*
-	 * TODO: we may need to save the GDBusInterface or GDBusObject
-	 * pointer in the hash table because we may be no longer able to
-	 * access the Name property or map the name to ifindex with
-	 * if_nametoindex at this point.
-	 */
+	g_return_if_fail (G_IS_DBUS_PROXY (interface));
+
+	proxy = G_DBUS_PROXY (interface);
+	iface_name = g_dbus_proxy_get_interface_name (proxy);
+
+	if (nm_streq (iface_name, NM_IWD_DEVICE_INTERFACE)) {
+		set_device_dbus_object (self, proxy, NULL);
+		return;
+	}
+
+	if (nm_streq (iface_name, NM_IWD_KNOWN_NETWORK_INTERFACE)) {
+		KnownNetworkId id;
+		const char *type_str;
+
+		type_str = get_property_string_or_null (proxy, "Type");
+		id.name = get_property_string_or_null (proxy, "Name");
+		if (!type_str || !id.name)
+			return;
+
+		if (nm_streq (type_str, "open"))
+			id.security = NM_IWD_NETWORK_SECURITY_NONE;
+		else if (nm_streq (type_str, "psk"))
+			id.security = NM_IWD_NETWORK_SECURITY_PSK;
+		else if (nm_streq (type_str, "8021x"))
+			id.security = NM_IWD_NETWORK_SECURITY_8021X;
+		else
+			return;
 
-	set_device_dbus_object (self, interface, NULL);
+		g_hash_table_remove (priv->known_networks, &id);
+		return;
+	}
 }
 
 static gboolean
@@ -341,106 +601,42 @@ object_added (NMIwdManager *self, GDBusObject *object)
 	GList *interfaces, *iter;
 
 	interfaces = g_dbus_object_get_interfaces (object);
+
 	for (iter = interfaces; iter; iter = iter->next) {
 		GDBusInterface *interface = G_DBUS_INTERFACE (iter->data);
 
-		set_device_dbus_object (self, interface, object);
+		interface_added (NULL, object, interface, self);
 	}
 
 	g_list_free_full (interfaces, g_object_unref);
 }
 
 static void
-known_network_free (KnownNetworkData *network)
-{
-	g_free (network->name);
-	g_free (network);
-}
-
-static void
-list_known_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+release_object_manager (NMIwdManager *self)
 {
-	NMIwdManager *self = user_data;
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	gs_free_error GError *error = NULL;
-	gs_unref_variant GVariant *variant = NULL;
-	GVariantIter *networks, *props;
-
-	variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
-	                                      G_VARIANT_TYPE ("(aa{sv})"),
-	                                      &error);
-	if (!variant) {
-		_LOGE ("ListKnownNetworks() failed: %s", error->message);
-		return;
-	}
 
-	g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free);
-	priv->known_networks = NULL;
-
-	g_variant_get (variant, "(aa{sv})", &networks);
-
-	while (g_variant_iter_next (networks, "a{sv}", &props)) {
-		const gchar *key;
-		const gchar *name = NULL;
-		const gchar *type = NULL;
-		GVariant *val;
-		KnownNetworkData *network_data;
-
-		while (g_variant_iter_next (props, "{&sv}", &key, &val)) {
-			if (!strcmp (key, "Name"))
-				name = g_variant_get_string (val, NULL);
-
-			if (!strcmp (key, "Type"))
-				type = g_variant_get_string (val, NULL);
-
-			g_variant_unref (val);
-		}
+	if (!priv->object_manager)
+		return;
 
-		if (!name || !type)
-			goto next;
+	g_signal_handlers_disconnect_by_data (priv->object_manager, self);
 
-		network_data = g_new (KnownNetworkData, 1);
-		network_data->name = g_strdup (name);
-		if (!strcmp (type, "open"))
-			network_data->security = NM_IWD_NETWORK_SECURITY_NONE;
-		else if (!strcmp (type, "psk"))
-			network_data->security = NM_IWD_NETWORK_SECURITY_PSK;
-		else if (!strcmp (type, "8021x"))
-			network_data->security = NM_IWD_NETWORK_SECURITY_8021X;
+	if (priv->agent_id) {
+		GDBusConnection *agent_connection;
+		GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager);
 
-		priv->known_networks = g_slist_append (priv->known_networks,
-		                                       network_data);
+		agent_connection = g_dbus_object_manager_client_get_connection (omc);
 
-next:
-		g_variant_iter_free (props);
+		/* We're is called when we're shutting down (i.e. our DBus connection
+		 * is being closed, and IWD will detect this) or IWD was stopped so
+		 * in either case calling UnregisterAgent will not do anything.
+		 */
+		g_dbus_connection_unregister_object (agent_connection, priv->agent_id);
+		priv->agent_id = 0;
+		nm_clear_g_free (&priv->agent_path);
 	}
 
-	g_variant_iter_free (networks);
-
-	/* For completness we may want to call nm_device_emit_recheck_auto_activate
-	 * and nm_device_recheck_available_connections for all affected devices
-	 * now but the ListKnownNetworks call should have been really fast,
-	 * faster than any scan on any newly created devices could have happened.
-	 */
-}
-
-static void
-update_known_networks (NMIwdManager *self)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusInterface *known_networks_if;
-
-	known_networks_if = g_dbus_object_manager_get_interface (priv->object_manager,
-	                                                         "/",
-	                                                         NM_IWD_KNOWN_NETWORKS_INTERFACE);
-
-	g_dbus_proxy_call (G_DBUS_PROXY (known_networks_if),
-	                   "ListKnownNetworks",
-	                   g_variant_new ("()"),
-	                   G_DBUS_CALL_FLAGS_NONE, -1,
-	                   priv->cancellable, list_known_networks_cb, self);
-
-	g_object_unref (known_networks_if);
+	g_clear_object (&priv->object_manager);
 }
 
 static void prepare_object_manager (NMIwdManager *self);
@@ -455,8 +651,7 @@ name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
 	nm_assert (object_manager == priv->object_manager);
 
 	if (_om_has_name_owner (object_manager)) {
-		g_signal_handlers_disconnect_by_data (object_manager, self);
-		g_clear_object (&priv->object_manager);
+		release_object_manager (self);
 		prepare_object_manager (self);
 	} else {
 		const CList *tmp_lst;
@@ -492,28 +687,14 @@ device_added (NMManager *manager, NMDevice *device, gpointer user_data)
 	objects = g_dbus_object_manager_get_objects (priv->object_manager);
 	for (iter = objects; iter; iter = iter->next) {
 		GDBusObject *object = G_DBUS_OBJECT (iter->data);
-		GDBusInterface *interface;
-		GDBusProxy *proxy;
-		GVariant *value;
+		gs_unref_object GDBusInterface *interface = NULL;
 		const char *obj_ifname;
 
 		interface = g_dbus_object_get_interface (object,
 		                                         NM_IWD_DEVICE_INTERFACE);
-		if (!interface)
-			continue;
-
-		proxy = G_DBUS_PROXY (interface);
-		value = g_dbus_proxy_get_cached_property (proxy, "Name");
-		if (!value) {
-			g_object_unref (interface);
-			continue;
-		}
+		obj_ifname = get_property_string_or_null ((GDBusProxy *) interface, "Name");
 
-		obj_ifname = g_variant_get_string (value, NULL);
-		g_variant_unref (value);
-		g_object_unref (interface);
-
-		if (strcmp (nm_device_get_iface (device), obj_ifname))
+		if (!obj_ifname || strcmp (nm_device_get_iface (device), obj_ifname))
 			continue;
 
 		nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object);
@@ -535,7 +716,7 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
 	object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error);
 	if (object_manager == NULL) {
 		_LOGE ("failed to acquire IWD Object Manager: Wi-Fi will not be available (%s)",
-		       NM_G_ERROR_MSG (error));
+		       error->message);
 		g_clear_error (&error);
 		return;
 	}
@@ -549,11 +730,13 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
 
 	connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
 
-	priv->agent_id = iwd_agent_export (connection, self,
-	                                   &priv->agent_path, &error);
+	priv->agent_id = iwd_agent_export (connection,
+	                                   self,
+	                                   &priv->agent_path,
+	                                   &error);
 	if (!priv->agent_id) {
-		_LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks will not work: %s",
-		       NM_G_ERROR_MSG (error));
+		_LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks may not work: %s",
+		       error->message);
 		g_clear_error (&error);
 	}
 
@@ -567,6 +750,8 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
 		g_signal_connect (priv->object_manager, "interface-removed",
 		                  G_CALLBACK (interface_removed), self);
 
+		g_hash_table_remove_all (priv->known_networks);
+
 		objects = g_dbus_object_manager_get_objects (object_manager);
 		for (iter = objects; iter; iter = iter->next)
 			object_added (self, G_DBUS_OBJECT (iter->data));
@@ -575,8 +760,6 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
 
 		if (priv->agent_id)
 			register_agent (self);
-
-		update_known_networks (self);
 	}
 }
 
@@ -594,36 +777,13 @@ prepare_object_manager (NMIwdManager *self)
 }
 
 gboolean
-nm_iwd_manager_is_known_network (NMIwdManager *self, const gchar *name,
+nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name,
                                  NMIwdNetworkSecurity security)
 {
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	const GSList *iter;
-
-	for (iter = priv->known_networks; iter; iter = g_slist_next (iter)) {
-		const KnownNetworkData *network = iter->data;
-
-		if (!strcmp (network->name, name) && network->security == security)
-			return true;
-	}
+	KnownNetworkId kn_id = { name, security };
 
-	return false;
-}
-
-void
-nm_iwd_manager_network_connected (NMIwdManager *self, const gchar *name,
-                                  NMIwdNetworkSecurity security)
-{
-	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	KnownNetworkData *network_data;
-
-	if (nm_iwd_manager_is_known_network (self, name, security))
-		return;
-
-	network_data = g_new (KnownNetworkData, 1);
-	network_data->name = g_strdup (name);
-	network_data->security = security;
-	priv->known_networks = g_slist_append (priv->known_networks, network_data);
+	return g_hash_table_contains (priv->known_networks, &kn_id);
 }
 
 /*****************************************************************************/
@@ -642,6 +802,11 @@ nm_iwd_manager_init (NMIwdManager *self)
 
 	priv->cancellable = g_cancellable_new ();
 
+	priv->known_networks = g_hash_table_new_full ((GHashFunc) known_network_id_hash,
+	                                              (GEqualFunc) known_network_id_equal,
+	                                              g_free,
+	                                              (GDestroyNotify) known_network_data_free);
+
 	prepare_object_manager (self);
 }
 
@@ -651,30 +816,11 @@ dispose (GObject *object)
 	NMIwdManager *self = (NMIwdManager *) object;
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
 
-	if (priv->object_manager) {
-		if (priv->agent_id) {
-			GDBusConnection *connection;
-			GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager);
-
-			/* No need to unregister the agent as IWD will detect
-			 * our DBus connection being closed.
-			 */
-
-			connection = g_dbus_object_manager_client_get_connection (omc);
-
-			g_dbus_connection_unregister_object (connection, priv->agent_id);
-			priv->agent_id = 0;
-		}
-
-		g_clear_object (&priv->object_manager);
-	}
-
-	nm_clear_g_free (&priv->agent_path);
+	release_object_manager (self);
 
 	nm_clear_g_cancellable (&priv->cancellable);
 
-	g_slist_free_full (priv->known_networks, (GDestroyNotify) known_network_free);
-	priv->known_networks = NULL;
+	nm_clear_pointer (&priv->known_networks, g_hash_table_destroy);
 
 	if (priv->manager) {
 		g_signal_handlers_disconnect_by_data (priv->manager, self);
diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h
index 8e6b66ff..cf0e7262 100644
--- a/src/devices/wifi/nm-iwd-manager.h
+++ b/src/devices/wifi/nm-iwd-manager.h
@@ -22,6 +22,7 @@
 #define __NETWORKMANAGER_IWD_MANAGER_H__
 
 #include "devices/nm-device.h"
+#include "nm-wifi-utils.h"
 
 #define NM_IWD_BUS_TYPE                 G_BUS_TYPE_SYSTEM
 #define NM_IWD_SERVICE                  "net.connman.iwd"
@@ -33,15 +34,11 @@
 #define NM_IWD_AGENT_INTERFACE          "net.connman.iwd.Agent"
 #define NM_IWD_WSC_INTERFACE            \
 	"net.connman.iwd.WiFiSimpleConfiguration"
-#define NM_IWD_KNOWN_NETWORKS_INTERFACE "net.connman.iwd.KnownNetworks"
+#define NM_IWD_KNOWN_NETWORK_INTERFACE  "net.connman.iwd.KnownNetwork"
 #define NM_IWD_SIGNAL_AGENT_INTERFACE   "net.connman.iwd.SignalLevelAgent"
-
-typedef enum {
-	NM_IWD_NETWORK_SECURITY_NONE,
-	NM_IWD_NETWORK_SECURITY_WEP,
-	NM_IWD_NETWORK_SECURITY_PSK,
-	NM_IWD_NETWORK_SECURITY_8021X,
-} NMIwdNetworkSecurity;
+#define NM_IWD_AP_INTERFACE             "net.connman.iwd.AccessPoint"
+#define NM_IWD_ADHOC_INTERFACE          "net.connman.iwd.AdHoc"
+#define NM_IWD_STATION_INTERFACE        "net.connman.iwd.Station"
 
 #define NM_TYPE_IWD_MANAGER              (nm_iwd_manager_get_type ())
 #define NM_IWD_MANAGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IWD_MANAGER, NMIwdManager))
@@ -57,9 +54,7 @@ GType nm_iwd_manager_get_type (void);
 
 NMIwdManager *nm_iwd_manager_get (void);
 
-gboolean nm_iwd_manager_is_known_network (NMIwdManager *self, const gchar *name,
+gboolean nm_iwd_manager_is_known_network (NMIwdManager *self, const char *name,
                                           NMIwdNetworkSecurity security);
-void nm_iwd_manager_network_connected (NMIwdManager *self, const gchar *name,
-                                       NMIwdNetworkSecurity security);
 
 #endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index dd6d1deb..e5573383 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -58,7 +58,7 @@ struct _NMWifiAPPrivate {
 	char *supplicant_path;   /* D-Bus object path of this AP from wpa_supplicant */
 
 	/* Scanned or cached values */
-	GByteArray *       ssid;
+	GBytes *           ssid;
 	char *             address;
 	NM80211Mode        mode;
 	guint8             strength;
@@ -95,7 +95,7 @@ nm_wifi_ap_get_supplicant_path (NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->supplicant_path;
 }
 
-const GByteArray *
+GBytes *
 nm_wifi_ap_get_ssid (const NMWifiAP *ap)
 {
 	g_return_val_if_fail (NM_IS_WIFI_AP (ap), NULL);
@@ -103,43 +103,57 @@ nm_wifi_ap_get_ssid (const NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->ssid;
 }
 
-static GVariant *
-nm_wifi_ap_get_ssid_as_variant (const NMWifiAP *self)
+gboolean
+nm_wifi_ap_set_ssid_arr (NMWifiAP *ap,
+                         const guint8 *ssid,
+                         gsize ssid_len)
 {
-	const NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE (self);
+	NMWifiAPPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+
+	if (ssid_len > 32)
+		g_return_val_if_reached (FALSE);
+
+	priv = NM_WIFI_AP_GET_PRIVATE (ap);
+
+	if (nm_utils_gbytes_equal_mem (priv->ssid, ssid, ssid_len))
+		return FALSE;
+
+	nm_clear_pointer (&priv->ssid, g_bytes_unref);
+	if (ssid_len > 0)
+		priv->ssid = g_bytes_new (ssid, ssid_len);
 
-	if (priv->ssid) {
-		return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                  priv->ssid->data, priv->ssid->len, 1);
-	} else
-		return g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0);
+	_notify (ap, PROP_SSID);
+	return TRUE;
 }
 
 gboolean
-nm_wifi_ap_set_ssid (NMWifiAP *ap, const guint8 *ssid, gsize len)
+nm_wifi_ap_set_ssid (NMWifiAP *ap, GBytes *ssid)
 {
 	NMWifiAPPrivate *priv;
+	gsize l;
 
 	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
-	g_return_val_if_fail (ssid == NULL || len > 0, FALSE);
 
-	priv = NM_WIFI_AP_GET_PRIVATE (ap);
-
-	/* same SSID */
-	if ((ssid && priv->ssid) && (len == priv->ssid->len)) {
-		if (!memcmp (ssid, priv->ssid->data, len))
-			return FALSE;
+	if (ssid) {
+		l = g_bytes_get_size (ssid);
+		if (l == 0 || l > 32)
+			g_return_val_if_reached (FALSE);
 	}
 
-	if (priv->ssid) {
-		g_byte_array_free (priv->ssid, TRUE);
-		priv->ssid = NULL;
-	}
+	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
-	if (ssid) {
-		priv->ssid = g_byte_array_new ();
-		g_byte_array_append (priv->ssid, ssid, len);
-	}
+	if (ssid == priv->ssid)
+		return FALSE;
+	if (   ssid
+	    && priv->ssid
+	    && g_bytes_equal (ssid, priv->ssid))
+		return FALSE;
+
+	nm_clear_pointer (&priv->ssid, g_bytes_unref);
+	if (ssid)
+		priv->ssid = g_bytes_ref (ssid);
 
 	_notify (ap, PROP_SSID);
 	return TRUE;
@@ -814,10 +828,16 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
 		len = MIN (32, len);
 
 		/* Stupid ieee80211 layer uses <hidden> */
-		if (   bytes && len
-		    && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8))
-		    && !nm_utils_is_empty_ssid (bytes, len))
-			changed |= nm_wifi_ap_set_ssid (ap, bytes, len);
+		if (   bytes
+		    && len
+		    && !(   NM_IN_SET (len, 8, 9)
+		         && memcmp (bytes, "<hidden>", len) == 0)
+		    && !nm_utils_is_empty_ssid (bytes, len)) {
+			/* good */
+		} else
+			len = 0;
+
+		changed |= nm_wifi_ap_set_ssid_arr (ap, bytes, len);
 
 		g_variant_unref (v);
 	}
@@ -961,7 +981,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 	const char *supplicant_id = "-";
 	const char *export_path;
 	guint32 chan;
-	char b1[200];
+	gs_free char *ssid_to_free = NULL;
 
 	g_return_val_if_fail (NM_IS_WIFI_AP (self), NULL);
 
@@ -977,10 +997,9 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 		export_path = "/";
 
 	g_snprintf (str_buf, buf_len,
-	            "%17s %-32s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
+	            "%17s %-35s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
 	            priv->address ?: "(none)",
-	            nm_sprintf_buf (b1, "%s%s%s",
-	                            NM_PRINT_FMT_QUOTED (priv->ssid, "\"", nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len), "\"", "(none)")),
+	            (ssid_to_free = _nm_utils_ssid_to_string (priv->ssid)),
 	            (priv->mode == NM_802_11_MODE_ADHOC
 	                 ? '*'
 	                 : (priv->hotspot
@@ -1032,15 +1051,12 @@ nm_wifi_ap_check_compatible (NMWifiAP *self,
 		return FALSE;
 
 	ssid = nm_setting_wireless_get_ssid (s_wireless);
-	if (   (ssid && !priv->ssid)
-	    || (priv->ssid && !ssid))
-		return FALSE;
-
-	if (   ssid && priv->ssid &&
-	    !nm_utils_same_ssid (g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid),
-	                         priv->ssid->data, priv->ssid->len,
-	                         TRUE))
-		return FALSE;
+	if (ssid != priv->ssid) {
+		if (!ssid || !priv->ssid)
+			return FALSE;
+		if (!g_bytes_equal (ssid, priv->ssid))
+			return FALSE;
+	}
 
 	bssid = nm_setting_wireless_get_bssid (s_wireless);
 	if (bssid && (!priv->address || !nm_utils_hwaddr_matches (bssid, -1, priv->address, -1)))
@@ -1126,7 +1142,8 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_uint (value, priv->rsn_flags);
 		break;
 	case PROP_SSID:
-		g_value_take_variant (value, nm_wifi_ap_get_ssid_as_variant (self));
+		g_value_take_variant (value,
+		                      nm_utils_gbytes_to_variant_ay (priv->ssid));
 		break;
 	case PROP_FREQUENCY:
 		g_value_set_uint (value, priv->freq);
@@ -1146,7 +1163,7 @@ get_property (GObject *object, guint prop_id,
 	case PROP_LAST_SEEN:
 		g_value_set_int (value,
 		                 priv->last_seen > 0
-		                     ? (gint) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND)
+		                     ? (int) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND)
 		                     : -1);
 		break;
 	default:
@@ -1202,7 +1219,6 @@ nm_wifi_ap_new_fake_from_connection (NMConnection *connection)
 	NMWifiAPPrivate *priv;
 	NMSettingWireless *s_wireless;
 	NMSettingWirelessSecurity *s_wireless_sec;
-	GBytes *ssid;
 	const char *mode, *band, *key_mgmt;
 	guint32 channel;
 	NM80211ApSecurityFlags flags;
@@ -1213,14 +1229,12 @@ nm_wifi_ap_new_fake_from_connection (NMConnection *connection)
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	g_return_val_if_fail (s_wireless != NULL, NULL);
 
-	ssid = nm_setting_wireless_get_ssid (s_wireless);
-	g_return_val_if_fail (ssid != NULL, NULL);
-	g_return_val_if_fail (g_bytes_get_size (ssid) > 0, NULL);
-
 	ap = (NMWifiAP *) g_object_new (NM_TYPE_WIFI_AP, NULL);
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 	priv->fake = TRUE;
-	nm_wifi_ap_set_ssid (ap, g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
+
+	nm_wifi_ap_set_ssid (ap,
+	                     nm_setting_wireless_get_ssid (s_wireless));
 
 	// FIXME: bssid too?
 
@@ -1334,7 +1348,7 @@ finalize (GObject *object)
 
 	g_free (priv->supplicant_path);
 	if (priv->ssid)
-		g_byte_array_free (priv->ssid, TRUE);
+		g_bytes_unref (priv->ssid);
 	g_free (priv->address);
 
 	G_OBJECT_CLASS (nm_wifi_ap_parent_class)->finalize (object);
@@ -1520,8 +1534,8 @@ nm_wifi_ap_lookup_for_device (NMDevice *device, const char *exported_path)
 
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	ap = (NMWifiAP *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (device)),
-	                                                 exported_path);
+	ap = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (device)),
+	                                    exported_path);
 	if (   !ap
 	    || !NM_IS_WIFI_AP (ap)
 	    || ap->wifi_device != device)
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 4fdeee93..7462e9d1 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -72,10 +72,12 @@ gboolean          nm_wifi_ap_complete_connection      (NMWifiAP *self,
                                                        GError **error);
 
 const char *      nm_wifi_ap_get_supplicant_path      (NMWifiAP *ap);
-const GByteArray *nm_wifi_ap_get_ssid                 (const NMWifiAP *ap);
-gboolean          nm_wifi_ap_set_ssid                 (NMWifiAP *ap,
+GBytes           *nm_wifi_ap_get_ssid                 (const NMWifiAP *ap);
+gboolean          nm_wifi_ap_set_ssid_arr             (NMWifiAP *ap,
                                                        const guint8 *ssid,
-                                                       gsize len);
+                                                       gsize ssid_len);
+gboolean          nm_wifi_ap_set_ssid                 (NMWifiAP *ap,
+                                                       GBytes *ssid);
 const char *      nm_wifi_ap_get_address              (const NMWifiAP *ap);
 gboolean          nm_wifi_ap_set_address              (NMWifiAP *ap,
                                                        const char *addr);
diff --git a/src/devices/wifi/nm-wifi-common.c b/src/devices/wifi/nm-wifi-common.c
index 8e079d11..c95620e7 100644
--- a/src/devices/wifi/nm-wifi-common.c
+++ b/src/devices/wifi/nm-wifi-common.c
@@ -196,7 +196,7 @@ const NMDBusInterfaceInfoExtended nm_interface_info_device_wireless = {
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("HwAddress",            "s",  NM_DEVICE_HW_ADDRESS),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("PermHwAddress",        "s",  NM_DEVICE_PERM_HW_ADDRESS),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Mode",                 "u",  NM_DEVICE_WIFI_MODE),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("BitRate",              "u",  NM_DEVICE_WIFI_BITRATE),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Bitrate",              "u",  NM_DEVICE_WIFI_BITRATE),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("AccessPoints",         "ao", NM_DEVICE_WIFI_ACCESS_POINTS),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ActiveAccessPoint",    "o",  NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("WirelessCapabilities", "u",  NM_DEVICE_WIFI_CAPABILITIES),
diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c
index 044bd392..0f7836be 100644
--- a/src/devices/wifi/nm-wifi-utils.c
+++ b/src/devices/wifi/nm-wifi-utils.c
@@ -525,7 +525,7 @@ verify_adhoc (NMSettingWirelessSecurity *s_wsec,
 }
 
 gboolean
-nm_wifi_utils_complete_connection (const GByteArray *ap_ssid,
+nm_wifi_utils_complete_connection (GBytes *ap_ssid,
                                    const char *bssid,
                                    NM80211Mode ap_mode,
                                    guint32 ap_flags,
@@ -538,7 +538,7 @@ nm_wifi_utils_complete_connection (const GByteArray *ap_ssid,
 	NMSettingWireless *s_wifi;
 	NMSettingWirelessSecurity *s_wsec;
 	NMSetting8021x *s_8021x;
-	GBytes *ssid, *ap_ssid_bytes;
+	GBytes *ssid;
 	const char *mode, *key_mgmt, *auth_alg, *leap_username;
 	gboolean adhoc = FALSE;
 
@@ -548,20 +548,17 @@ nm_wifi_utils_complete_connection (const GByteArray *ap_ssid,
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 
 	/* Fill in missing SSID */
-	ap_ssid_bytes = ap_ssid ? g_bytes_new (ap_ssid->data, ap_ssid->len) : NULL;
 	ssid = nm_setting_wireless_get_ssid (s_wifi);
 	if (!ssid)
-		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid_bytes, NULL);
-	else if (!ap_ssid_bytes || !g_bytes_equal (ssid, ap_ssid_bytes)) {
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid, NULL);
+	else if (!ap_ssid || !g_bytes_equal (ssid, ap_ssid)) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
 		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
 		                     _("connection does not match access point"));
 		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID);
-		g_bytes_unref (ap_ssid_bytes);
 		return FALSE;
 	}
-	g_bytes_unref (ap_ssid_bytes);
 
 	if (lock_bssid && !nm_setting_wireless_get_bssid (s_wifi))
 		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL);
@@ -764,7 +761,7 @@ nm_wifi_utils_complete_connection (const GByteArray *ap_ssid,
 }
 
 guint32
-nm_wifi_utils_level_to_quality (gint val)
+nm_wifi_utils_level_to_quality (int val)
 {
 	if (val < 0) {
 		/* Assume dBm already; rough conversion: best = -40, worst = -100 */
@@ -783,8 +780,10 @@ nm_wifi_utils_level_to_quality (gint val)
 }
 
 gboolean
-nm_wifi_utils_is_manf_default_ssid (const GByteArray *ssid)
+nm_wifi_utils_is_manf_default_ssid (GBytes *ssid)
 {
+	const guint8 *ssid_p;
+	gsize ssid_l;
 	int i;
 	/*
 	 * List of manufacturer default SSIDs that are often unchanged by users.
@@ -806,11 +805,46 @@ nm_wifi_utils_is_manf_default_ssid (const GByteArray *ssid)
 		"TURBONETT",
 	};
 
+	ssid_p = g_bytes_get_data (ssid, &ssid_l);
+
 	for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) {
-		if (ssid->len == strlen (manf_defaults[i])) {
-			if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0)
+		if (ssid_l == strlen (manf_defaults[i])) {
+			if (memcmp (manf_defaults[i], ssid_p, ssid_l) == 0)
 				return TRUE;
 		}
 	}
 	return FALSE;
 }
+
+NMIwdNetworkSecurity
+nm_wifi_connection_get_iwd_security (NMConnection *connection,
+                                     gboolean *mapped)
+{
+	NMSettingWirelessSecurity *s_wireless_sec;
+	const char *key_mgmt = NULL;
+
+	if (!nm_connection_get_setting_wireless (connection))
+		goto error;
+
+	NM_SET_OUT (mapped, TRUE);
+
+	s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
+	if (!s_wireless_sec)
+		return NM_IWD_NETWORK_SECURITY_NONE;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
+	nm_assert (key_mgmt);
+
+	if (NM_IN_STRSET (key_mgmt, "none", "ieee8021x"))
+		return NM_IWD_NETWORK_SECURITY_WEP;
+
+	if (nm_streq (key_mgmt, "wpa-psk"))
+		return NM_IWD_NETWORK_SECURITY_PSK;
+
+	if (nm_streq (key_mgmt, "wpa-eap"))
+		return NM_IWD_NETWORK_SECURITY_8021X;
+
+error:
+	NM_SET_OUT (mapped, FALSE);
+	return NM_IWD_NETWORK_SECURITY_NONE;
+}
diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h
index def64dd6..03238c24 100644
--- a/src/devices/wifi/nm-wifi-utils.h
+++ b/src/devices/wifi/nm-wifi-utils.h
@@ -27,7 +27,14 @@
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-8021x.h"
 
-gboolean nm_wifi_utils_complete_connection (const GByteArray *ssid,
+typedef enum {
+	NM_IWD_NETWORK_SECURITY_NONE,
+	NM_IWD_NETWORK_SECURITY_WEP,
+	NM_IWD_NETWORK_SECURITY_PSK,
+	NM_IWD_NETWORK_SECURITY_8021X,
+} NMIwdNetworkSecurity;
+
+gboolean nm_wifi_utils_complete_connection (GBytes *ssid,
                                             const char *bssid,
                                             NM80211Mode mode,
                                             guint32 flags,
@@ -37,8 +44,11 @@ gboolean nm_wifi_utils_complete_connection (const GByteArray *ssid,
                                             gboolean lock_bssid,
                                             GError **error);
 
-guint32 nm_wifi_utils_level_to_quality (gint val);
+guint32 nm_wifi_utils_level_to_quality (int val);
+
+gboolean nm_wifi_utils_is_manf_default_ssid (GBytes *ssid);
 
-gboolean nm_wifi_utils_is_manf_default_ssid (const GByteArray *ssid);
+NMIwdNetworkSecurity nm_wifi_connection_get_iwd_security (NMConnection *connection,
+                                                          gboolean *mapped);
 
 #endif  /* __NM_WIFI_UTILS_H__ */
diff --git a/src/devices/wifi/tests/test-general.c b/src/devices/wifi/tests/test-general.c
index 89eebb22..f752bbfc 100644
--- a/src/devices/wifi/tests/test-general.c
+++ b/src/devices/wifi/tests/test-general.c
@@ -74,8 +74,7 @@ complete_connection (const char *ssid,
                      NMConnection *src,
                      GError **error)
 {
-	GByteArray *tmp;
-	gboolean success;
+	gs_unref_bytes GBytes *ssid_b = NULL;
 	NMSettingWireless *s_wifi;
 
 	/* Add a wifi setting if one doesn't exist */
@@ -85,20 +84,17 @@ complete_connection (const char *ssid,
 		nm_connection_add_setting (src, NM_SETTING (s_wifi));
 	}
 
-	tmp = g_byte_array_sized_new (strlen (ssid));
-	g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid));
-
-	success = nm_wifi_utils_complete_connection (tmp,
-	                                             bssid,
-	                                             mode,
-	                                             flags,
-	                                             wpa_flags,
-	                                             rsn_flags,
-	                                             src,
-	                                             lock_bssid,
-	                                             error);
-	g_byte_array_free (tmp, TRUE);
-	return success;
+	ssid_b = g_bytes_new (ssid, strlen (ssid));
+
+	return nm_wifi_utils_complete_connection (ssid_b,
+	                                          bssid,
+	                                          mode,
+	                                          flags,
+	                                          wpa_flags,
+	                                          rsn_flags,
+	                                          src,
+	                                          lock_bssid,
+	                                          error);
 }
 
 typedef struct {
@@ -127,7 +123,7 @@ set_items (NMSetting *setting, const KeyData *items)
 			g_assert (item->str == NULL);
 			g_object_set (G_OBJECT (setting), item->key, item->uint, NULL);
 		} else if (pspec->value_type == G_TYPE_INT) {
-			gint foo = (gint) item->uint;
+			int foo = (int) item->uint;
 
 			g_assert (item->str == NULL);
 			g_object_set (G_OBJECT (setting), item->key, foo, NULL);
@@ -1462,7 +1458,7 @@ main (int argc, char **argv)
 	                      test_ap_wpa_eap_connection_5);
 
 #define ADD_FUNC(func) do { \
-		gchar *name_idx = g_strdup_printf ("/wifi/wpa_psk/" G_STRINGIFY (func) "/%zd", i); \
+		char *name_idx = g_strdup_printf ("/wifi/wpa_psk/" G_STRINGIFY (func) "/%zd", i); \
 		g_test_add_data_func (name_idx, (gconstpointer) i, func); \
 		g_free (name_idx); \
 	} while (0)
@@ -1487,7 +1483,7 @@ main (int argc, char **argv)
 
 #undef ADD_FUNC
 #define ADD_FUNC(func) do { \
-		gchar *name_idx = g_strdup_printf ("/wifi/rsn_psk/" G_STRINGIFY (func) "/%zd", i); \
+		char *name_idx = g_strdup_printf ("/wifi/rsn_psk/" G_STRINGIFY (func) "/%zd", i); \
 		g_test_add_data_func (name_idx, (gconstpointer) i, func); \
 		g_free (name_idx); \
 	} while (0)
diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build
index abf58594..2c0ea5a1 100644
--- a/src/devices/wwan/meson.build
+++ b/src/devices/wwan/meson.build
@@ -35,10 +35,10 @@ libnm_wwan_dep = declare_dependency(
 
 core_plugins += libnm_wwan
 
-run_target(
+test(
   'check-wwan',
-  command: [check_exports, libnm_wwan.full_path(), linker_script],
-  depends: libnm_wwan
+  check_exports,
+  args: [libnm_wwan.full_path(), linker_script],
 )
 
 sources = files(
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 352b1c3e..4119d598 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -397,34 +397,67 @@ get_type_description (NMDevice *device)
 }
 
 static gboolean
-check_connection_compatible (NMDevice *device, NMConnection *connection)
+check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
 {
-	if (!NM_DEVICE_CLASS (nm_device_modem_parent_class)->check_connection_compatible (device, connection))
+	GError *local = NULL;
+
+	if (!NM_DEVICE_CLASS (nm_device_modem_parent_class)->check_connection_compatible (device, connection, error))
 		return FALSE;
 
-	return nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, connection);
+	if (!nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem,
+	                                           connection,
+	                                           error ? &local : NULL)) {
+		if (error) {
+			g_set_error (error,
+			             NM_UTILS_ERROR,
+			             g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE)
+			              ? NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE
+			              : NM_UTILS_ERROR_UNKNOWN,
+			             "modem is incompatible with connection: %s",
+			             local->message);
+			g_error_free (local);
+		}
+		return FALSE;
+	}
+	return TRUE;
 }
 
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
                             NMDeviceCheckConAvailableFlags flags,
-                            const char *specific_object)
+                            const char *specific_object,
+                            GError **error)
 {
 	NMDeviceModem *self = NM_DEVICE_MODEM (device);
 	NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
 	NMModemState state;
 
-	if (!priv->rf_enabled || !priv->modem)
+	if (!priv->rf_enabled) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "RFKILL for modem enabled");
 		return FALSE;
+	}
+
+	if (!priv->modem) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "modem not available");
+		return FALSE;
+	}
 
 	state = nm_modem_get_state (priv->modem);
-	if (state <= NM_MODEM_STATE_INITIALIZING)
+	if (state <= NM_MODEM_STATE_INITIALIZING) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "modem not initalized");
 		return FALSE;
+	}
 
 	if (state == NM_MODEM_STATE_LOCKED) {
-		if (!nm_connection_get_setting_gsm (connection))
+		if (!nm_connection_get_setting_gsm (connection)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "modem is locked without pin available");
 			return FALSE;
+		}
 	}
 
 	return TRUE;
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index e5678b96..253574d5 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -256,7 +256,7 @@ create_cdma_connect_properties (NMConnection *connection)
 {
 	NMSettingCdma *setting;
 	MMSimpleConnectProperties *properties;
-	const gchar *str;
+	const char *str;
 
 	setting = nm_connection_get_setting_cdma (connection);
 	properties = mm_simple_connect_properties_new ();
@@ -274,7 +274,7 @@ create_gsm_connect_properties (NMConnection *connection)
 	NMSettingGsm *setting;
 	NMSettingPpp *s_ppp;
 	MMSimpleConnectProperties *properties;
-	const gchar *str;
+	const char *str;
 
 	setting = nm_connection_get_setting_gsm (connection);
 	properties = mm_simple_connect_properties_new ();
@@ -617,44 +617,40 @@ act_stage1_prepare (NMModem *_self,
 /*****************************************************************************/
 
 static gboolean
-check_connection_compatible (NMModem *_self, NMConnection *connection)
+check_connection_compatible_with_modem (NMModem *_self, NMConnection *connection, GError **error)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 	MMModemCapability modem_caps;
-	NMSettingConnection *s_con;
 
 	modem_caps = mm_modem_get_current_capabilities (self->_priv.modem_iface);
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
 
 	if (MODEM_CAPS_3GPP (modem_caps)) {
-		NMSettingGsm *s_gsm;
-
-		if (!g_str_equal (nm_setting_connection_get_connection_type (s_con),
-		                  NM_SETTING_GSM_SETTING_NAME))
-			return FALSE;
-
-		s_gsm = nm_connection_get_setting_gsm (connection);
-		if (!s_gsm)
+		if (!_nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, error))
 			return FALSE;
 
 		return TRUE;
 	}
 
 	if (MODEM_CAPS_3GPP2 (modem_caps)) {
-		NMSettingCdma *s_cdma;
-
-		if (!g_str_equal (nm_setting_connection_get_connection_type (s_con),
-		                  NM_SETTING_CDMA_SETTING_NAME))
-			return FALSE;
-
-		s_cdma = nm_connection_get_setting_cdma (connection);
-		if (!s_cdma)
+		if (!_nm_connection_check_main_setting (connection, NM_SETTING_CDMA_SETTING_NAME, error))
 			return FALSE;
 
 		return TRUE;
 	}
 
+	if (   !_nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, NULL)
+	    && !_nm_connection_check_main_setting (connection, NM_SETTING_CDMA_SETTING_NAME, NULL)) {
+		nm_utils_error_set (error,
+		                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
+		                    "connection type %s is not supported by modem",
+		                    nm_connection_get_connection_type (connection));
+		return FALSE;
+	}
+
+	nm_utils_error_set (error,
+	                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+	                    "modem lacks capabilities for %s profile",
+	                    nm_connection_get_connection_type (connection));
 	return FALSE;
 }
 
@@ -868,12 +864,12 @@ static_stage3_ip4_done (NMModemBroadband *self)
 	GError *error = NULL;
 	gs_unref_object NMIP4Config *config = NULL;
 	const char *data_port;
-	const gchar *address_string;
-	const gchar *gw_string;
+	const char *address_string;
+	const char *gw_string;
 	guint32 address_network;
 	guint32 gw = 0;
 	NMPlatformIP4Address address;
-	const gchar **dns;
+	const char **dns;
 	guint i;
 	guint32 ip4_route_table, ip4_route_metric;
 	NMPlatformIP4Route *r;
@@ -979,10 +975,10 @@ stage3_ip6_done (NMModemBroadband *self)
 	GError *error = NULL;
 	NMIP6Config *config = NULL;
 	const char *data_port;
-	const gchar *address_string;
+	const char *address_string;
 	NMPlatformIP6Address address;
 	NMModemIPMethod ip_method;
-	const gchar **dns;
+	const char **dns;
 	guint i;
 
 	g_assert (self->_priv.ipv6_config);
@@ -1476,7 +1472,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass)
 	modem_class->deactivate_cleanup = deactivate_cleanup;
 	modem_class->set_mm_enabled = set_mm_enabled;
 	modem_class->get_user_pass = get_user_pass;
-	modem_class->check_connection_compatible = check_connection_compatible;
+	modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem;
 	modem_class->complete_connection = complete_connection;
 	modem_class->act_stage1_prepare = act_stage1_prepare;
 	modem_class->owns_port = owns_port;
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index 59cd2bca..dfc102f3 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -170,7 +170,7 @@ modm_handle_object_added (MMManager *modem_manager,
                           NMModemManager *self)
 {
 	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
-	const gchar *path;
+	const char *path;
 	MMModem *modem_iface;
 	NMModem *modem;
 	GError *error = NULL;
@@ -211,7 +211,7 @@ modm_handle_object_removed (MMManager *manager,
 {
 	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
 	NMModem *modem;
-	const gchar *path;
+	const char *path;
 
 	path = mm_object_get_path (modem_object);
 	modem = (NMModem *) g_hash_table_lookup (priv->modems, path);
@@ -246,7 +246,7 @@ modm_handle_name_owner_changed (MMManager *modem_manager,
                                 NMModemManager *self)
 {
 	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
-	gchar *name_owner;
+	char *name_owner;
 
 	/* Quit poking, if any */
 	nm_clear_g_source (&priv->modm.relaunch_id);
@@ -341,7 +341,7 @@ static void
 modm_manager_check_name_owner (NMModemManager *self)
 {
 	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
-	gs_free gchar *name_owner = NULL;
+	gs_free char *name_owner = NULL;
 
 	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->modm.manager));
 	if (name_owner) {
@@ -606,14 +606,14 @@ ofono_create_modem (NMModemManager *self, const char *path)
 
 static void
 ofono_signal_cb (GDBusProxy *proxy,
-                 gchar *sender_name,
-                 gchar *signal_name,
+                 char *sender_name,
+                 char *signal_name,
                  GVariant *parameters,
                  gpointer user_data)
 {
 	NMModemManager *self = NM_MODEM_MANAGER (user_data);
 	NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self);
-	gchar *object_path;
+	char *object_path;
 	NMModem *modem;
 
 	if (g_strcmp0 (signal_name, "ModemAdded") == 0) {
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index 8b81ac41..ea668590 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -279,47 +279,42 @@ deactivate_cleanup (NMModem *modem, NMDevice *device)
 }
 
 static gboolean
-check_connection_compatible (NMModem *modem,
-                             NMConnection *connection)
+check_connection_compatible_with_modem (NMModem *modem,
+                                        NMConnection *connection,
+                                        GError **error)
 {
 	NMModemOfono *self = NM_MODEM_OFONO (modem);
 	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
-	NMSettingConnection *s_con;
-	NMSettingGsm *s_gsm;
-	const char *uuid;
 	const char *id;
 
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
-
-	uuid = nm_connection_get_uuid (connection);
-	id = nm_connection_get_id (connection);
-
-	s_gsm = nm_connection_get_setting_gsm (connection);
-	if (!s_gsm)
+	if (!_nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, NULL)) {
+		nm_utils_error_set (error,
+		                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
+		                    "connection type %s is not supported by ofono modem",
+		                    nm_connection_get_connection_type (connection));
 		return FALSE;
+	}
 
 	if (!priv->imsi) {
-		_LOGW ("skipping %s/%s: no IMSI", uuid, id);
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "modem has no IMSI");
 		return FALSE;
 	}
 
-	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME)) {
-		_LOGD ("skipping %s/%s: not GSM", uuid, id);
-		return FALSE;
-	}
+	id = nm_connection_get_id (connection);
 
-	if (!g_strrstr (id, "/context")) {
-		_LOGD ("skipping %s/%s: unexpected ID", uuid, id);
+	if (!strstr (id, "/context")) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "the connection ID has no context");
 		return FALSE;
 	}
 
-	if (!g_strrstr (id, priv->imsi)) {
-		_LOGD ("skipping %s/%s: ID doesn't contain IMSI", uuid, id);
+	if (!strstr (id, priv->imsi)) {
+		nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+		                            "the connection ID does not contain the IMSI");
 		return FALSE;
 	}
 
-	_LOGD ("%s/%s compatible with IMSI %s", uuid, id, priv->imsi);
 	return TRUE;
 }
 
@@ -830,8 +825,8 @@ context_property_changed (GDBusProxy *proxy,
 	gboolean ret = FALSE;
 	gs_unref_variant GVariant *v_dict = NULL;
 	const char *interface;
-	const gchar *s;
-	const gchar **array, **iter;
+	const char *s;
+	const char **array, **iter;
 	guint32 address_network, gateway_network;
 	guint32 ip4_route_table, ip4_route_metric;
 	int ifindex;
@@ -1326,7 +1321,7 @@ nm_modem_ofono_class_init (NMModemOfonoClass *klass)
 	modem_class->disconnect = disconnect;
 	modem_class->disconnect_finish = disconnect_finish;
 	modem_class->deactivate_cleanup = deactivate_cleanup;
-	modem_class->check_connection_compatible = check_connection_compatible;
+	modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem;
 
 	modem_class->act_stage1_prepare = act_stage1_prepare;
 	modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start;
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 617d5c4a..59b081e9 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -291,7 +291,7 @@ nm_modem_get_supported_ip_types (NMModem *self)
 	return NM_MODEM_GET_PRIVATE (self)->ip_types;
 }
 
-const gchar *
+const char *
 nm_modem_ip_type_to_string (NMModemIPType ip_type)
 {
 	switch (ip_type) {
@@ -1027,35 +1027,29 @@ nm_modem_act_stage2_config (NMModem *self,
 /*****************************************************************************/
 
 gboolean
-nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
+nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection, GError **error)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
-	NMSettingConnection *s_con;
-
-	s_con = nm_connection_get_setting_connection (connection);
-	g_assert (s_con);
 
-	if (g_str_equal (nm_setting_connection_get_connection_type (s_con),
-	                 NM_SETTING_GSM_SETTING_NAME)) {
+	if (nm_streq0 (nm_connection_get_connection_type (connection),
+	               NM_SETTING_GSM_SETTING_NAME)) {
 		NMSettingGsm *s_gsm;
 		const char *str;
 
-		s_gsm = nm_connection_get_setting_gsm (connection);
+		s_gsm = _nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, error);
 		if (!s_gsm)
 			return FALSE;
 
 		str = nm_setting_gsm_get_device_id (s_gsm);
 		if (str) {
 			if (!priv->device_id) {
-				_LOGD ("%s/%s has device-id, device does not",
-				       nm_connection_get_uuid (connection),
-				       nm_connection_get_id (connection));
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "GSM profile has device-id, device does not");
 				return FALSE;
 			}
-			if (strcmp (str, priv->device_id)) {
-				_LOGD ("%s/%s device-id mismatch",
-				       nm_connection_get_uuid (connection),
-				       nm_connection_get_id (connection));
+			if (!nm_streq (str, priv->device_id)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "device has differing device-id than GSM profile");
 				return FALSE;
 			}
 		}
@@ -1065,30 +1059,26 @@ nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection)
 		 * are only compared if present on the device.
 		 */
 
-		str = nm_setting_gsm_get_sim_id (s_gsm);
-		if (str && priv->sim_id) {
-			if (strcmp (str, priv->sim_id)) {
-				_LOGD ("%s/%s sim-id mismatch",
-				       nm_connection_get_uuid (connection),
-				       nm_connection_get_id (connection));
+		if (   priv->sim_id
+		    && (str = nm_setting_gsm_get_sim_id (s_gsm))) {
+			if (!nm_streq (str, priv->sim_id)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "device has differing sim-id than GSM profile");
 				return FALSE;
 			}
 		}
 
-		str = nm_setting_gsm_get_sim_operator_id (s_gsm);
-		if (str && priv->sim_operator_id) {
-			if (strcmp (str, priv->sim_operator_id)) {
-				_LOGD ("%s/%s sim-operator-id mismatch",
-				       nm_connection_get_uuid (connection),
-				       nm_connection_get_id (connection));
+		if (   priv->sim_operator_id
+		    && (str = nm_setting_gsm_get_sim_operator_id (s_gsm))) {
+			if (!nm_streq (str, priv->sim_operator_id)) {
+				nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+				                            "device has differing sim-operator-id than GSM profile");
 				return FALSE;
 			}
 		}
 	}
 
-	if (NM_MODEM_GET_CLASS (self)->check_connection_compatible)
-		return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection);
-	return FALSE;
+	return NM_MODEM_GET_CLASS (self)->check_connection_compatible_with_modem (self, connection, error);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index cad9ece4..c73745ce 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -121,8 +121,9 @@ typedef struct {
 	                                            const char **user,
 	                                            const char **pass);
 
-	gboolean (*check_connection_compatible)    (NMModem *modem,
-	                                            NMConnection *connection);
+	gboolean (*check_connection_compatible_with_modem) (NMModem *modem,
+	                                                    NMConnection *connection,
+	                                                    GError **error);
 
 	gboolean (*complete_connection)            (NMModem *modem,
 	                                            NMConnection *connection,
@@ -185,7 +186,9 @@ void        nm_modem_get_capabilities (NMModem *self,
                                        NMDeviceModemCapabilities *modem_caps,
                                        NMDeviceModemCapabilities *current_caps);
 
-gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection);
+gboolean nm_modem_check_connection_compatible (NMModem *self,
+                                               NMConnection *connection,
+                                               GError **error);
 
 gboolean nm_modem_complete_connection (NMModem *self,
                                        NMConnection *connection,
@@ -273,7 +276,7 @@ void nm_modem_emit_ip6_config_result (NMModem *self,
                                       NMIP6Config *config,
                                       GError *error);
 
-const gchar *nm_modem_ip_type_to_string (NMModemIPType ip_type);
+const char *nm_modem_ip_type_to_string (NMModemIPType ip_type);
 
 guint32 nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source);