summary refs log tree commit diff
path: root/src/devices/wifi
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/wifi')
-rw-r--r--src/devices/wifi/meson.build6
-rw-r--r--src/devices/wifi/nm-device-iwd.c729
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c58
-rw-r--r--src/devices/wifi/nm-device-wifi.c428
-rw-r--r--src/devices/wifi/nm-iwd-manager.c552
-rw-r--r--src/devices/wifi/nm-iwd-manager.h20
-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-utils.c56
-rw-r--r--src/devices/wifi/nm-wifi-utils.h16
-rw-r--r--src/devices/wifi/tests/test-general.c34
11 files changed, 1207 insertions, 822 deletions
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..1d1be742 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";
@@ -228,6 +229,78 @@ vardict_from_network_type (const gchar *type)
 }
 
 static void
+insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint32_t ap_id)
+{
+	gs_unref_object GDBusProxy *network_proxy = NULL;
+	gs_unref_variant GVariant *name_value = NULL, *type_value = NULL;
+	const char *name, *type;
+	GVariantBuilder builder;
+	gs_unref_variant GVariant *props = NULL;
+	GVariant *rsn;
+	uint8_t bssid[6];
+	NMWifiAP *ap;
+
+	network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (),
+	                                                   path,
+	                                                   NM_IWD_NETWORK_INTERFACE);
+	if (!network_proxy)
+		return;
+
+	name_value = g_dbus_proxy_get_cached_property (network_proxy, "Name");
+	type_value = g_dbus_proxy_get_cached_property (network_proxy, "Type");
+	if (   !name_value
+	    || !g_variant_is_of_type (name_value, G_VARIANT_TYPE_STRING)
+	    || !type_value
+	    || !g_variant_is_of_type (type_value, G_VARIANT_TYPE_STRING))
+		return;
+
+	name = g_variant_get_string (name_value, NULL);
+	type = g_variant_get_string (type_value, NULL);
+
+	/* What we get from IWD are networks, or ESSs, that may contain
+	 * multiple APs, or BSSs, each.  We don't get information about any
+	 * specific BSSs within an ESS but we can safely present each ESS
+	 * as an individual BSS to NM, which will be seen as ESSs comprising
+	 * a single BSS each.  NM won't be able to handle roaming but IWD
+	 * already does that.  We fake the BSSIDs as they don't play any
+	 * role either.
+	 */
+	bssid[0] = 0x00;
+	bssid[1] = 0x01;
+	bssid[2] = 0x02;
+	bssid[3] = ap_id >> 16;
+	bssid[4] = ap_id >> 8;
+	bssid[5] = ap_id;
+
+	/* WEP not supported */
+	if (nm_streq (type, "wep"))
+		return;
+
+	g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+	g_variant_builder_add (&builder, "{sv}", "BSSID",
+	                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1));
+	g_variant_builder_add (&builder, "{sv}", "Mode",
+	                       g_variant_new_string ("infrastructure"));
+
+	rsn = vardict_from_network_type (type);
+	if (rsn)
+		g_variant_builder_add (&builder, "{sv}", "RSN", rsn);
+
+	props = g_variant_new ("a{sv}", &builder);
+
+	ap = nm_wifi_ap_new_from_properties (path, props);
+
+	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);
+	g_hash_table_insert (aps, (gpointer) nm_wifi_ap_get_supplicant_path (ap), ap);
+}
+
+static void
 get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 {
 	NMDeviceIwd *self = user_data;
@@ -235,75 +308,39 @@ 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;
 	GHashTableIter ap_iter;
 	gs_unref_hashtable GHashTable *new_aps = NULL;
+	/* Depending on whether we're using the Station interface or the Device
+	 * interface for compatibility with IWD <= 0.7, the return signature of
+	 * GetOrderedNetworks will be different.
+	 */
+	gboolean compat = priv->dbus_station_proxy == priv->dbus_device_proxy;
+	const char *return_sig = compat ? "(a(osns))" : "(a(on))";
+	static uint32_t ap_id = 0;
 
 	variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
-	                                      G_VARIANT_TYPE ("(a(osns))"),
+	                                      G_VARIANT_TYPE (return_sig),
 	                                      &error);
 	if (!variant) {
-		_LOGE (LOGD_WIFI, "Device.GetOrderedNetworks failed: %s",
+		_LOGE (LOGD_WIFI, "Station.GetOrderedNetworks failed: %s",
 		       error->message);
 		return;
 	}
 
 	new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
 
-	g_variant_get (variant, "(a(osns))", &networks);
-
-	while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) {
-		GVariantBuilder builder;
-		gs_unref_variant GVariant *props = NULL;
-		GVariant *rsn;
-		static uint32_t ap_id = 0;
-		uint8_t bssid[6];
+	g_variant_get (variant, return_sig, &networks);
 
-		/*
-		 * What we get from IWD are networks, or ESSs, that may
-		 * contain multiple APs, or BSSs, each.  We don't get
-		 * information about any specific BSSs within an ESS but
-		 * we can safely present each ESS as an individual BSS to
-		 * NM, which will be seen as ESSs comprising a single BSS
-		 * each.  NM won't be able to handle roaming but IWD already
-		 * does that.  We fake the BSSIDs as they don't play any
-		 * role either.
-		 */
-		bssid[0] = 0x00;
-		bssid[1] = 0x01;
-		bssid[2] = 0x02;
-		bssid[3] = ap_id >> 16;
-		bssid[4] = ap_id >> 8;
-		bssid[5] = ap_id++;
-
-		/* WEP not supported */
-		if (!strcmp (type, "wep"))
-			continue;
-
-		g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
-		g_variant_builder_add (&builder, "{sv}", "BSSID",
-		                       g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1));
-		g_variant_builder_add (&builder, "{sv}", "Mode",
-		                       g_variant_new_string ("infrastructure"));
-
-		rsn = vardict_from_network_type (type);
-		if (rsn)
-			g_variant_builder_add (&builder, "{sv}", "RSN", rsn);
-
-		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_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);
-		g_hash_table_insert (new_aps,
-		                     (gpointer) nm_wifi_ap_get_supplicant_path (ap),
-		                     ap);
+	if (compat) {
+		while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type))
+			insert_ap_from_network (new_aps, path, signal, ap_id++);
+	} else {
+		while (g_variant_iter_next (networks, "(&on)", &path, &signal))
+			insert_ap_from_network (new_aps, path, signal, ap_id++);
 	}
 
 	g_variant_iter_free (networks);
@@ -355,7 +392,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 +403,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);
 }
 
@@ -389,7 +426,7 @@ cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect)
 
 	set_current_ap (self, NULL, TRUE);
 
-	if (disconnect && priv->dbus_obj)
+	if (disconnect && priv->dbus_station_proxy)
 		send_disconnect (self);
 }
 
@@ -405,7 +442,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 +478,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 +499,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 +575,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 +588,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 +619,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 +655,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 +721,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 +732,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,17 +790,38 @@ 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);
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-	gs_unref_variant GVariant *value = NULL;
-
-	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);
+	return priv->enabled && priv->dbus_station_proxy;
 }
 
 static gboolean
@@ -777,11 +835,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 +848,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 +865,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 +873,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;
 	}
@@ -897,8 +958,7 @@ dbus_request_scan_cb (NMDevice *device,
 
 	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 
-	if (   !priv->enabled
-	    || !priv->dbus_obj
+	if (   !priv->can_scan
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
 	    || nm_device_is_activating (device)) {
 		g_dbus_method_invocation_return_error_literal (context,
@@ -921,7 +981,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);
@@ -939,8 +999,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self,
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
 
-	if (   !priv->enabled
-	    || !priv->dbus_obj
+	if (   !priv->can_scan
 	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
 	    || nm_device_is_activating (device)) {
 		g_dbus_method_invocation_return_error_literal (invocation,
@@ -1005,11 +1064,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 +1078,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 +1098,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 +1118,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 +1142,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 +1183,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;
 
@@ -1182,12 +1241,7 @@ secrets_error:
 	g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
 	                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
 	                                               "NM secrets request failed");
-
-	nm_device_state_changed (device,
-	                         NM_DEVICE_STATE_FAILED,
-	                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-
-	cleanup_association_attempt (self, TRUE);
+	/* Now wait for the Connect callback to update device state */
 }
 
 static void
@@ -1219,16 +1273,19 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 {
 	NMDeviceIwd *self = user_data;
 	NMDevice *device = NM_DEVICE (self);
+	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	gs_free_error GError *error = NULL;
 	NMConnection *connection;
 	NMSettingWireless *s_wifi;
 	GBytes *ssid;
-	gs_free gchar *str_ssid = NULL;
+	gs_free char *ssid_utf8 = NULL;
+	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED;
+	GVariant *value;
 
 	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.
@@ -1238,6 +1295,12 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		       "Activation: (wifi) Network.Connect failed: %s",
 		       error->message);
 
+		if (nm_utils_error_is_cancelled (error, TRUE))
+			return;
+
+		if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH))
+			return;
+
 		connection = nm_device_get_applied_connection (device);
 		if (!connection)
 			goto failed;
@@ -1245,20 +1308,17 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR))
 			dbus_error = g_dbus_error_get_remote_error (error);
 
-		/* If secrets were wrong, we'd be getting a net.connman.iwd.Failed */
 		if (nm_streq0 (dbus_error, "net.connman.iwd.Failed")) {
 			nm_connection_clear_secrets (connection);
 
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
-			                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-		} else if (   !nm_utils_error_is_cancelled (error, TRUE)
-		           && nm_device_is_activating (device))
-			goto failed;
-
-		/* Call Disconnect to make sure IWD's autoconnect is disabled */
-		cleanup_association_attempt (self, TRUE);
+			/* If secrets were wrong, we'd be getting a net.connman.iwd.Failed */
+			reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		} else if (nm_streq0 (dbus_error, "net.connman.iwd.Aborted")) {
+			/* If agent call was cancelled we'd be getting a net.connman.iwd.Aborted */
+			reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		}
 
-		return;
+		goto failed;
 	}
 
 	nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
@@ -1275,23 +1335,27 @@ 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:
-	cleanup_association_attempt (self, FALSE);
-	nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
-	                       NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	/* Call Disconnect to make sure IWD's autoconnect is disabled */
+	cleanup_association_attempt (self, TRUE);
+
+	nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, reason);
+
+	value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State");
+	if (!priv->can_connect && nm_streq0 (get_variant_state (value), "disconnected")) {
+		priv->can_connect = true;
+		nm_device_emit_recheck_auto_activate (device);
+	}
+	g_variant_unref (value);
 }
 
 static void
@@ -1299,7 +1363,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",
@@ -1361,7 +1425,6 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	NMActRequest *req;
 	NMWifiAP *ap;
 	NMConnection *connection;
-	GError *error = NULL;
 	GDBusProxy *network_proxy;
 
 	req = nm_device_get_act_request (device);
@@ -1391,21 +1454,13 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		goto out;
 	}
 
-	/* Locate the IWD Network object */
-	network_proxy = g_dbus_proxy_new_for_bus_sync (NM_IWD_BUS_TYPE,
-	                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                                               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                                               NULL,
-	                                               NM_IWD_SERVICE,
-	                                               nm_wifi_ap_get_supplicant_path (ap),
-	                                               NM_IWD_NETWORK_INTERFACE,
-	                                               NULL, &error);
+	network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (),
+	                                                   nm_wifi_ap_get_supplicant_path (ap),
+	                                                   NM_IWD_NETWORK_INTERFACE);
 	if (!network_proxy) {
 		_LOGE (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) could not get Network interface proxy for %s: %s",
-		       nm_wifi_ap_get_supplicant_path (ap),
-		       error->message);
-		g_clear_error (&error);
+		       "Activation: (wifi) could not get Network interface proxy for %s",
+		       nm_wifi_ap_get_supplicant_path (ap));
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 		goto out;
 	}
@@ -1452,7 +1507,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;
@@ -1506,7 +1561,7 @@ device_state_changed (NMDevice *device,
 		 * transition to DISCONNECTED because the device is now
 		 * ready to use.
 		 */
-		if (priv->enabled && priv->dbus_obj) {
+		if (priv->enabled && priv->dbus_station_proxy) {
 			nm_device_queue_recheck_available (device,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
@@ -1556,17 +1611,18 @@ set_enabled (NMDevice *device, gboolean enabled)
 		return;
 	}
 
-	if (priv->dbus_proxy)
+	if (priv->dbus_obj)
 		set_powered (self, enabled);
 
 	if (enabled) {
 		if (state != NM_DEVICE_STATE_UNAVAILABLE)
 			_LOGW (LOGD_CORE, "not in expected unavailable state!");
 
-		if (priv->dbus_obj)
+		if (priv->dbus_station_proxy) {
 			nm_device_queue_recheck_available (device,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 			                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		}
 	} else {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_UNAVAILABLE,
@@ -1667,7 +1723,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);
@@ -1684,13 +1740,6 @@ state_changed (NMDeviceIwd *self, const gchar *new_state)
 	/* Don't allow scanning while connecting, disconnecting or roaming */
 	priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected");
 
-	/* Don't allow new connection until iwd exits disconnecting */
-	can_connect = NM_IN_STRSET (new_state, "disconnected");
-	if (can_connect != priv->can_connect) {
-		priv->can_connect = can_connect;
-		nm_device_emit_recheck_auto_activate (device);
-	}
-
 	if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) {
 		/* If we were connecting, do nothing, the confirmation of
 		 * a connection success is handled in the Device.Connect
@@ -1704,39 +1753,39 @@ state_changed (NMDeviceIwd *self, const gchar *new_state)
 		_LOGW (LOGD_DEVICE | LOGD_WIFI,
 		       "Unsolicited connection success, asking IWD to disconnect");
 		send_disconnect (self);
-
-		return;
 	} else if (NM_IN_STRSET (new_state, "disconnecting", "disconnected")) {
-		if (!iwd_connection)
-			return;
-
 		/* Call Disconnect on the IWD device object to make sure it
 		 * disables its own autoconnect.
-		 *
-		 * Note we could instead call net.connman.iwd.KnownNetworks.ForgetNetwork
-		 * and leave the device in autoconnect.  This way if NetworkManager
-		 * changes any settings for this connection, they'd be taken into
-		 * account on the next connection attempt.  But both methods are
-		 * a hack, we'll perhaps need an IWD API to "connect once" without
-		 * storing anything.
 		 */
 		send_disconnect (self);
 
 		/*
-		 * If IWD is still handling the Connect call, let our callback
-		 * for the dbus method handle the failure.
+		 * If IWD is still handling the Connect call, let our Connect
+		 * callback for the dbus method handle the failure.  The main
+		 * reason we can't handle the failure here is because the method
+		 * callback will have more information on the specific failure
+		 * reason.
 		 */
 		if (dev_state == NM_DEVICE_STATE_CONFIG)
 			return;
 
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_FAILED,
-	                                 NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
-
+		if (iwd_connection)
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+	} else if (!nm_streq (new_state, "unknown")) {
+		_LOGE (LOGD_WIFI, "State %s unknown", new_state);
 		return;
 	}
 
-	_LOGE (LOGD_WIFI, "State %s unknown", new_state);
+	/* Don't allow new connection until iwd exits disconnecting and no
+	 * Connect callback is pending.
+	 */
+	can_connect = NM_IN_STRSET (new_state, "disconnected");
+	if (can_connect != priv->can_connect) {
+		priv->can_connect = can_connect;
+		nm_device_emit_recheck_auto_activate (device);
+	}
 }
 
 static void
@@ -1761,32 +1810,112 @@ 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)
 {
+	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
 	nm_device_queue_recheck_available (NM_DEVICE (self),
 	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 	                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+
+	if (new_powered) {
+		GDBusInterface *interface;
+		GVariant *value;
+
+		if (priv->dbus_station_proxy)
+			return;
+
+		interface = g_dbus_object_get_interface (priv->dbus_obj, 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.
+			 */
+			value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State");
+			if (!value) {
+				_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
+				       NM_IWD_STATION_INTERFACE,
+				       g_dbus_object_get_object_path (priv->dbus_obj));
+				return;
+			}
+
+			g_variant_unref (value);
+			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);
+
+		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_station_proxy, "g-properties-changed",
+		                  G_CALLBACK (station_properties_changed), self);
+
+		/* Call Disconnect to make sure IWD's autoconnect is disabled.
+		 * Autoconnect is the default state after device is brought UP.
+		 */
+		if (priv->enabled)
+			send_disconnect (self);
+	} else {
+		if (!priv->dbus_station_proxy)
+			return;
+
+		g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy,
+		                                      station_properties_changed, self);
+		g_clear_object (&priv->dbus_station_proxy);
+
+		priv->can_scan = FALSE;
+		priv->scanning = FALSE;
+		priv->scan_requested = FALSE;
+		priv->can_connect = FALSE;
+		cleanup_association_attempt (self, FALSE);
+	}
 }
 
 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);
 	}
@@ -1800,51 +1929,50 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	GDBusInterface *interface;
 	GVariant *value;
+	gboolean powered;
 
 	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->enabled)
+	if (priv->enabled && priv->dbus_station_proxy) {
 		nm_device_queue_recheck_available (NM_DEVICE (self),
 		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
 		                                   NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	}
 
-	if (!object) {
-		priv->can_scan = FALSE;
+	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);
 
-		cleanup_association_attempt (self, FALSE);
-		return;
+		powered_changed (self, FALSE);
 	}
 
-	interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE);
-	priv->dbus_proxy = G_DBUS_PROXY (interface);
+	if (!object)
+		return;
 
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "Scanning");
-	priv->scanning = g_variant_get_boolean (value);
-	g_variant_unref (value);
-	priv->scan_requested = FALSE;
+	interface = g_dbus_object_get_interface (object, NM_IWD_DEVICE_INTERFACE);
+	if (!interface) {
+		_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
+		       NM_IWD_DEVICE_INTERFACE,
+		       g_dbus_object_get_object_path (object));
+		g_clear_object (&priv->dbus_obj);
+		return;
+	}
 
-	value = g_dbus_proxy_get_cached_property (priv->dbus_proxy, "State");
-	state_changed (self, g_variant_get_string (value, NULL));
-	g_variant_unref (value);
+	priv->dbus_device_proxy = G_DBUS_PROXY (interface);
 
-	g_signal_connect (priv->dbus_proxy, "g-properties-changed",
-	                  G_CALLBACK (properties_changed), self);
+	g_signal_connect (priv->dbus_device_proxy, "g-properties-changed",
+	                  G_CALLBACK (device_properties_changed), self);
 
-	set_powered (self, priv->enabled);
+	value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered");
+	powered = get_variant_boolean (value, "Powered");
+	g_variant_unref (value);
 
-	/* Call Disconnect to make sure IWD's autoconnect is disabled.
-	 * Autoconnect is the default state after device is brought UP.
-	 */
-	if (priv->enabled)
-		send_disconnect (self);
+	if (powered != priv->enabled)
+		set_powered (self, priv->enabled);
+	else if (powered)
+		powered_changed (self, TRUE);
 }
 
 gboolean
@@ -1852,8 +1980,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 +2065,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 +2081,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 +2089,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..9ae81139 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)) {
@@ -302,7 +279,7 @@ companion_state_changed_cb (NMDeviceWifi *companion,
 }
 
 static gboolean
-companion_scan_prohibited_cb (NMDeviceWifi *companion, gpointer user_data)
+companion_scan_prohibited_cb (NMDeviceWifi *companion, gboolean periodic, gpointer user_data)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
 	NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
@@ -369,7 +346,7 @@ device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data)
 		nm_device_queue_recheck_available (NM_DEVICE (self),
 		                                   NM_DEVICE_STATE_REASON_NONE,
 		                                   NM_DEVICE_STATE_REASON_NONE);
-		nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, TRUE);
+		nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_COMPANION, FALSE);
 	}
 }
 
@@ -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 0dd6fa74..2ce84618 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -96,6 +96,7 @@ typedef struct {
 	bool              requested_scan:1;
 	bool              ssid_found:1;
 	bool              is_scanning:1;
+	bool              hidden_probe_scan_warn:1;
 
 	gint64            last_scan; /* milliseconds */
 	gint32            scheduled_scan_time; /* seconds */
@@ -121,6 +122,8 @@ typedef struct {
 	gint32 hw_addr_scan_expire;
 
 	guint             wps_timeout_id;
+
+	NMSettingWirelessWakeOnWLan wowlan_restore;
 } NMDeviceWifiPrivate;
 
 struct _NMDeviceWifi
@@ -436,7 +439,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 +511,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 +543,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 +605,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 +616,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 +638,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 +691,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 +709,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 +738,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 +762,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 +836,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 +866,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 +880,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 +959,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 +972,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 +1072,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 +1087,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 +1100,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 +1268,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 +1292,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 +1307,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;
@@ -1321,23 +1351,30 @@ request_wireless_scan (NMDeviceWifi *self,
 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested");
 
 		if (!ssids) {
-			ssids = hidden_ssids = build_hidden_probe_list (self);
+			hidden_ssids = build_hidden_probe_list (self);
+			if (hidden_ssids) {
+				if (priv->hidden_probe_scan_warn) {
+					priv->hidden_probe_scan_warn = FALSE;
+					_LOGW (LOGD_WIFI, "wifi-scan: active scanning for networks due to profiles with wifi.hidden=yes. This makes you trackable");
+				}
+				ssids = hidden_ssids;
+			} else
+				priv->hidden_probe_scan_warn = TRUE;
 		}
 
 		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 +1382,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 +1523,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 +1545,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 +1574,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 +1898,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 +2043,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 +2055,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 +2167,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 +2192,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 +2374,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 +2415,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 +2465,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 +2476,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
@@ -2598,31 +2644,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
@@ -2659,8 +2703,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) {
@@ -2677,6 +2719,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,
@@ -2727,8 +2772,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
@@ -3134,7 +3181,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,7 +3254,9 @@ nm_device_wifi_init (NMDeviceWifi *self)
 
 	c_list_init (&priv->aps_lst_head);
 
+	priv->hidden_probe_scan_warn = TRUE;
 	priv->mode = NM_802_11_MODE_INFRA;
+	priv->wowlan_restore = NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE;
 }
 
 static void
@@ -3274,9 +3324,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;
@@ -3286,29 +3334,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..a3da9791 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,192 @@ 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);
+
+	/* "password" and "private-key-password" may be requested by the IWD agent
+	 * from NM and IWD will implement a specific secret cache policy so by
+	 * default respect that policy and don't save copies of those secrets in
+	 * NM settings.  The saved values can not be used anyway because of our
+	 * use of NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW.
+	 */
+	setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_802_1X,
+	                                    NM_SETTING_802_1X_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	                                    NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	                                    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;
 
-	set_device_dbus_object (self, interface, object);
+	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, 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 +556,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);
 
-	set_device_dbus_object (self, interface, NULL);
+	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;
+
+		g_hash_table_remove (priv->known_networks, &id);
+		return;
+	}
 }
 
 static gboolean
@@ -341,106 +610,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 +660,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 +696,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 = g_variant_get_string (value, NULL);
-		g_variant_unref (value);
-		g_object_unref (interface);
+		obj_ifname = get_property_string_or_null ((GDBusProxy *) interface, "Name");
 
-		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 +725,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 +739,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 +759,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 +769,6 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
 
 		if (priv->agent_id)
 			register_agent (self);
-
-		update_known_networks (self);
 	}
 }
 
@@ -594,36 +786,28 @@ 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;
+	return g_hash_table_contains (priv->known_networks, &kn_id);
 }
 
-void
-nm_iwd_manager_network_connected (NMIwdManager *self, const gchar *name,
-                                  NMIwdNetworkSecurity security)
+GDBusProxy *
+nm_iwd_manager_get_dbus_interface (NMIwdManager *self, const char *path,
+                                   const char *name)
 {
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	KnownNetworkData *network_data;
+	GDBusInterface *interface;
 
-	if (nm_iwd_manager_is_known_network (self, name, security))
-		return;
+	if (!priv->object_manager)
+		return NULL;
+
+	interface = g_dbus_object_manager_get_interface (priv->object_manager, path, name);
 
-	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 interface ? G_DBUS_PROXY (interface) : NULL;
 }
 
 /*****************************************************************************/
@@ -642,6 +826,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 +840,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..57b7007a 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,10 @@ 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);
+
+GDBusProxy *nm_iwd_manager_get_dbus_interface (NMIwdManager *self, const char *path,
+                                               const char *name);
 
 #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-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)