about summary refs log tree commit diff
path: root/src/devices/wifi/nm-device-wifi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/wifi/nm-device-wifi.c')
-rw-r--r--src/devices/wifi/nm-device-wifi.c166
1 files changed, 118 insertions, 48 deletions
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 3a58c620..2ffba47a 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -1,4 +1,3 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* NetworkManager -- Network link manager
  *
  * This program is free software; you can redistribute it and/or modify
@@ -545,6 +544,60 @@ wake_on_wlan_restore (NMDeviceWifi *self)
 }
 
 static void
+disconnect_cb (NMSupplicantInterface *iface, GError *error, gpointer user_data)
+{
+	gs_unref_object NMDeviceWifi *self = NULL;
+	NMDeviceDeactivateCallback callback;
+	gpointer callback_user_data;
+
+	nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
+
+	/* error will be freed by sup_iface */
+	callback (NM_DEVICE (self), error, callback_user_data);
+}
+
+static void
+disconnect_cb_on_idle (gpointer user_data,
+                       GCancellable *cancellable)
+{
+	gs_unref_object NMDeviceWifi *self = NULL;
+	NMDeviceDeactivateCallback callback;
+	gpointer callback_user_data;
+	gs_free_error GError *cancelled_error = NULL;
+
+	nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
+
+	g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error);
+	callback (NM_DEVICE (self), cancelled_error, callback_user_data);
+}
+
+static void
+deactivate_async (NMDevice *device,
+                  GCancellable *cancellable,
+                  NMDeviceDeactivateCallback callback,
+                  gpointer callback_user_data) {
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	gpointer user_data;
+
+	nm_assert (G_IS_CANCELLABLE (cancellable));
+	nm_assert (callback);
+
+	user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data);
+	if (!priv->sup_iface) {
+		nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable);
+		return;
+	}
+
+	cleanup_association_attempt (self, FALSE);
+
+	nm_supplicant_interface_disconnect_async (priv->sup_iface,
+	                                          cancellable,
+	                                          disconnect_cb,
+	                                          user_data);
+}
+
+static void
 deactivate (NMDevice *device)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
@@ -695,6 +748,20 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError
 				return FALSE;
 			}
 		}
+	} else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_MESH) == 0) {
+		if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_MESH)) {
+			nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+			                            "the device does not support Mesh mode");
+			return FALSE;
+		}
+
+		if (priv->sup_iface) {
+			if (nm_supplicant_interface_get_mesh_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 Mesh mode");
+				return FALSE;
+			}
+		}
 	}
 
 	// FIXME: check channel/freq/band against bands the hardware supports
@@ -739,12 +806,13 @@ check_connection_available (NMDevice *device,
 		return TRUE;
 	}
 
-	/* Ad-Hoc and AP connections are always available because they may be
+	/* Ad-Hoc, AP and Mesh connections are always available because they may be
 	 * started at any time.
 	 */
 	mode = nm_setting_wireless_get_mode (s_wifi);
 	if (   g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0
-	    || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0)
+	    || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0
+	    || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_MESH) == 0)
 		return TRUE;
 
 	/* Hidden SSIDs obviously don't always appear in the scan list either.
@@ -778,13 +846,11 @@ complete_connection (NMDevice *device,
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMSettingWireless *s_wifi;
-	const char *setting_mac;
 	gs_free char *ssid_utf8 = NULL;
 	NMWifiAP *ap;
 	GBytes *ssid = NULL;
 	GBytes *setting_ssid = NULL;
 	gboolean hidden = FALSE;
-	const char *perm_hw_addr;
 	const char *mode;
 
 	s_wifi = nm_connection_get_setting_wireless (connection);
@@ -908,27 +974,12 @@ complete_connection (NMDevice *device,
 	                           ssid_utf8,
 	                           ssid_utf8,
 	                           NULL,
+	                           nm_setting_wireless_get_mac_address (s_wifi) ? NULL : nm_device_get_iface (device),
 	                           TRUE);
 
 	if (hidden)
 		g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
 
-	perm_hw_addr = nm_device_get_permanent_hw_address (device);
-	if (perm_hw_addr) {
-		setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
-		if (setting_mac) {
-			/* Make sure the setting MAC (if any) matches the device's permanent MAC */
-			if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) {
-				g_set_error_literal (error,
-				                     NM_CONNECTION_ERROR,
-				                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-				                     _("connection does not match device"));
-				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS);
-				return FALSE;
-			}
-		}
-	}
-
 	return TRUE;
 }
 
@@ -972,7 +1023,8 @@ can_auto_connect (NMDevice *device,
 	NMConnection *connection;
 	NMSettingWireless *s_wifi;
 	NMWifiAP *ap;
-	const char *method, *mode;
+	const char *method6, *mode;
+	gboolean auto4, auto6;
 	guint64 timestamp = 0;
 
 	nm_assert (!specific_object || !*specific_object);
@@ -985,13 +1037,20 @@ can_auto_connect (NMDevice *device,
 	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, AF_INET);
+	/* Always allow autoconnect for AP and non-autoconf Ad-Hoc or Mesh */
+	auto4 = nm_streq0 (nm_utils_get_ip_config_method (connection, AF_INET),
+	                   NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+	method6 = nm_utils_get_ip_config_method (connection, AF_INET6);
+	auto6 =    nm_streq0 (method6, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
+	        || nm_streq0 (method6, NM_SETTING_IP6_CONFIG_METHOD_DHCP);
+
 	mode = nm_setting_wireless_get_mode (s_wifi);
+
 	if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP))
 		return TRUE;
-	else if (   nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)
-	         && !nm_streq0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
+	else if (!auto4 && nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC))
+		return TRUE;
+	else if (!auto4 && !auto6 && nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_MESH))
 		return TRUE;
 
 	/* Don't autoconnect to networks that have been tried at least once
@@ -1531,13 +1590,14 @@ try_fill_ssid_for_hidden_ap (NMDeviceWifi *self,
 		NMSettingsConnection *sett_conn = connections[i];
 		NMSettingWireless *s_wifi;
 
+		if (!nm_settings_connection_has_seen_bssid (sett_conn, bssid))
+			continue;
 		s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (sett_conn));
-		if (s_wifi) {
-			if (nm_settings_connection_has_seen_bssid (sett_conn, bssid)) {
-				nm_wifi_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
-				break;
-			}
-		}
+		if (!s_wifi)
+			continue;
+
+		nm_wifi_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
+		break;
 	}
 }
 
@@ -1777,9 +1837,10 @@ supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface,
 	}
 	if (secrets) {
 		if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req),
-		                                         nm_act_request_get_applied_connection (req),
-		                                         NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-		                                         secrets, &error)) {
+		                                        nm_act_request_get_applied_connection (req),
+		                                        NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+		                                        secrets,
+		                                        &error)) {
 			wifi_secrets_cancel (self);
 			nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self));
 		} else {
@@ -2398,6 +2459,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 	g_assert (connection);
 
 	if (   priv->mode == NM_802_11_MODE_ADHOC
+	    || priv->mode == NM_802_11_MODE_MESH
 	    || priv->mode == NM_802_11_MODE_AP) {
 		/* In Ad-Hoc and AP modes there's nothing to check the encryption key
 		 * (if any), so supplicant timeouts here are almost certainly the wifi
@@ -2468,7 +2530,9 @@ build_supplicant_config (NMDeviceWifi *self,
 
 	config = nm_supplicant_config_new (
 		nm_supplicant_interface_get_pmf_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES,
-		nm_supplicant_interface_get_fils_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES);
+		nm_supplicant_interface_get_fils_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES,
+		nm_supplicant_interface_get_ft_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES,
+		nm_supplicant_interface_get_sha384_support (priv->sup_iface) == NM_SUPPLICANT_FEATURE_YES);
 
 	/* Warn if AP mode may not be supported */
 	if (   g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0
@@ -2635,7 +2699,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
 		/* Scanning not done in AP mode; clear the scan list */
 		remove_all_aps (self);
-	}
+	} else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_MESH) == 0)
+		priv->mode = NM_802_11_MODE_MESH;
 	_notify (self, PROP_MODE);
 
 	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
@@ -2655,8 +2720,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	if (!nm_device_hw_addr_set_cloned (device, connection, TRUE))
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
-	/* AP mode never uses a specific object or existing scanned AP */
-	if (priv->mode != NM_802_11_MODE_AP) {
+	/* AP and Mesh modes never use a specific object or existing scanned AP */
+	if (priv->mode != NM_802_11_MODE_AP && priv->mode != NM_802_11_MODE_MESH) {
 		ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
 		ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL;
 		if (ap)
@@ -2672,10 +2737,10 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	}
 
 	/* If the user is trying to connect to an AP that NM doesn't yet know about
-	 * (hidden network or something) or starting a Hotspot, create an fake AP
-	 * from the security settings in the connection.  This "fake" AP gets used
-	 * until the real one is found in the scan list (Ad-Hoc or Hidden), or until
-	 * the device is deactivated (Hotspot).
+	 * (hidden network or something), starting a Hotspot or joining a Mesh,
+	 * create a fake APfrom the security settings in the connection.  This "fake"
+	 * AP gets used until the real one is found in the scan list (Ad-Hoc or Hidden),
+	 * or until the device is deactivated (Hotspot).
 	 */
 	ap = nm_wifi_ap_new_fake_from_connection (connection);
 	g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
@@ -2763,6 +2828,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMSupplicantConfig *config = NULL;
+	NM80211Mode ap_mode;
 	NMActRequest *req;
 	NMWifiAP *ap;
 	NMConnection *connection;
@@ -2783,6 +2849,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 		goto out;
 	}
+	ap_mode = nm_wifi_ap_get_mode (ap);
 
 	connection = nm_act_request_get_applied_connection (req);
 	g_assert (connection);
@@ -2822,14 +2889,16 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 
 	priv->ssid_found = FALSE;
 
-	/* Supplicant requires an initial frequency for Ad-Hoc and Hotspot; if the user
-	 * didn't specify one and we didn't find an AP that matched the connection,
-	 * just pick a frequency the device supports.
+	/* Supplicant requires an initial frequency for Ad-Hoc, Hotspot and Mesh;
+	 * if the user didn't specify one and we didn't find an AP that matched
+	 * the connection, just pick a frequency the device supports.
 	 */
-	if ((nm_wifi_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) || nm_wifi_ap_is_hotspot (ap))
+	if (   ap_mode == NM_802_11_MODE_ADHOC
+	    || ap_mode == NM_802_11_MODE_MESH
+	    || nm_wifi_ap_is_hotspot (ap))
 		ensure_hotspot_frequency (self, s_wireless, ap);
 
-	if (nm_wifi_ap_get_mode (ap) == NM_802_11_MODE_INFRA)
+	if (ap_mode == NM_802_11_MODE_INFRA)
 		set_powersave (device);
 
 	/* Build up the supplicant configuration */
@@ -3386,6 +3455,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	device_class->get_configured_mtu = get_configured_mtu;
 	device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
 	device_class->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout;
+	device_class->deactivate_async = deactivate_async;
 	device_class->deactivate = deactivate;
 	device_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
 	device_class->unmanaged_on_quit = unmanaged_on_quit;