summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2018-10-11 16:07:25 +0200
committerThomas Haller <thaller@redhat.com>2018-10-11 16:07:45 +0200
commitc925b72e1f8869c5e7b2763f13ae0db5763229aa (patch)
tree03a00d82fd18b87ef554bcfe76a753b71105332f
parent6e41d7906737ee79b10cf7c94036e1e080e8cab3 (diff)
parentb3dad27a4a095d28269580bfe420e5c549b0be47 (diff)
downloadNetworkManager-c925b72e1f8869c5e7b2763f13ae0db5763229aa.tar.gz
wifi/iwd: merge branch 'balrog-kun/iwd-ap-adhoc'
https://github.com/NetworkManager/NetworkManager/pull/221
-rw-r--r--src/devices/wifi/nm-device-iwd.c945
-rw-r--r--src/devices/wifi/nm-device-iwd.h2
-rw-r--r--src/devices/wifi/nm-wifi-factory.c46
3 files changed, 714 insertions, 279 deletions
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index 1d1be742b6..9038b68d14 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -41,6 +41,7 @@
#include "nm-config.h"
#include "nm-iwd-manager.h"
#include "nm-dbus-manager.h"
+#include "nm-dbus-compat.h"
#include "devices/nm-device-logging.h"
_LOG_DECLARE_SELF(NMDeviceIwd);
@@ -69,6 +70,8 @@ typedef struct {
GDBusObject * dbus_obj;
GDBusProxy * dbus_device_proxy;
GDBusProxy * dbus_station_proxy;
+ GDBusProxy * dbus_ap_proxy;
+ GDBusProxy * dbus_adhoc_proxy;
CList aps_lst_head;
NMWifiAP * current_ap;
GCancellable * cancellable;
@@ -80,6 +83,7 @@ typedef struct {
bool can_connect:1;
bool scanning:1;
bool scan_requested:1;
+ bool act_mode_switch:1;
gint64 last_scan;
} NMDeviceIwdPrivate;
@@ -304,7 +308,7 @@ static void
get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
NMDeviceIwd *self = user_data;
- NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDeviceIwdPrivate *priv;
gs_free_error GError *error = NULL;
gs_unref_variant GVariant *variant = NULL;
GVariantIter *networks;
@@ -314,23 +318,32 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
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))";
+ gboolean compat;
+ const char *return_sig;
static uint32_t ap_id = 0;
- variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
- G_VARIANT_TYPE (return_sig),
- &error);
+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
if (!variant) {
_LOGE (LOGD_WIFI, "Station.GetOrderedNetworks failed: %s",
error->message);
return;
}
+ priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ /* 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.
+ */
+ compat = priv->dbus_station_proxy == priv->dbus_device_proxy;
+ return_sig = compat ? "(a(osns))" : "(a(on))";
+
+ if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (return_sig))) {
+ _LOGE (LOGD_WIFI, "Station.GetOrderedNetworks returned type %s instead of %s",
+ g_variant_get_type_string (variant), return_sig);
+ return;
+ }
+
new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
g_variant_get (variant, return_sig, &networks);
@@ -431,36 +444,55 @@ cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect)
}
static void
+reset_mode (NMDeviceIwd *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ g_dbus_proxy_call (priv->dbus_device_proxy,
+ DBUS_INTERFACE_PROPERTIES ".Set",
+ g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE,
+ "Mode",
+ g_variant_new_string ("station")),
+ G_DBUS_CALL_FLAGS_NONE, 2000,
+ cancellable, callback, user_data);
+}
+
+static void
deactivate (NMDevice *device)
{
- cleanup_association_attempt (NM_DEVICE_IWD (device), TRUE);
+ NMDeviceIwd *self = NM_DEVICE_IWD (device);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ if (!priv->dbus_obj)
+ return;
+
+ cleanup_association_attempt (self, TRUE);
+ priv->act_mode_switch = FALSE;
+
+ if (!priv->dbus_station_proxy)
+ reset_mode (self, NULL, NULL, NULL);
}
static gboolean
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_station_proxy, res, error);
- return variant != NULL;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-typedef struct {
- NMDeviceIwd *self;
- GAsyncReadyCallback callback;
- gpointer user_data;
-} DeactivateContext;
-
static void
disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
- DeactivateContext *ctx = user_data;
+ GTask *task = user_data;
+ gs_unref_variant GVariant *variant = NULL;
+ GError *error = NULL;
- ctx->callback (G_OBJECT (ctx->self), res, ctx->user_data);
+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
+ if (variant)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
- g_object_unref (ctx->self);
- g_slice_free (DeactivateContext, ctx);
+ g_object_unref (task);
}
static void
@@ -471,15 +503,24 @@ deactivate_async (NMDevice *device,
{
NMDeviceIwd *self = NM_DEVICE_IWD (device);
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
- DeactivateContext *ctx;
+ GTask *task;
- ctx = g_slice_new0 (DeactivateContext);
- ctx->self = g_object_ref (self);
- ctx->callback = callback;
- ctx->user_data = user_data;
+ task = g_task_new (self, cancellable, callback, user_data);
- g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"),
- G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx);
+ if (!priv->dbus_obj) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ cleanup_association_attempt (self, FALSE);
+ priv->act_mode_switch = FALSE;
+
+ if (priv->dbus_station_proxy) {
+ g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, task);
+ } else
+ reset_mode (self, cancellable, disconnect_cb, task);
}
static gboolean
@@ -510,13 +551,37 @@ is_connection_known_network (NMConnection *connection)
}
static gboolean
+is_ap_known_network (NMWifiAP *ap)
+{
+ GDBusProxy *network_proxy;
+ gs_unref_variant GVariant *known_network = NULL;
+
+ 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)
+ return FALSE;
+
+ known_network = g_dbus_proxy_get_cached_property (network_proxy, "KnownNetwork");
+ g_object_unref (network_proxy);
+
+ return known_network
+ && g_variant_is_of_type (known_network, G_VARIANT_TYPE_OBJECT_PATH);
+}
+
+static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
+ NMDeviceIwd *self = NM_DEVICE_IWD (device);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
NMSettingWireless *s_wireless;
const char *mac;
const char * const *mac_blacklist;
int i;
const char *perm_hw_addr;
+ const char *mode;
+ NMIwdNetworkSecurity security;
+ gboolean mapped;
if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection, error))
return FALSE;
@@ -549,23 +614,65 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError
return FALSE;
}
- if (!NM_IN_STRSET (nm_setting_wireless_get_mode (s_wireless),
- NULL,
- NM_SETTING_WIRELESS_MODE_INFRA)) {
+ /* Hidden SSIDs not supported in any mode (client or AP) */
+ if (nm_setting_wireless_get_hidden (s_wireless)) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "IWD only support infrastructure type profiles");
+ "hidden networks not supported by the IWD backend");
return FALSE;
}
- /* 8021x networks can only be used if they've been provisioned on the IWD side and
- * thus are Known Networks.
- */
- if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) {
- if (!is_connection_known_network (connection)) {
+ security = nm_wifi_connection_get_iwd_security (connection, &mapped);
+ if (!mapped) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "connection authentication type not supported by IWD backend");
+ return FALSE;
+ }
+
+ mode = nm_setting_wireless_get_mode (s_wireless);
+ if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) {
+ /* 8021x networks can only be used if they've been provisioned on the IWD side and
+ * thus are Known Networks.
+ */
+ if (security == 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 connections must have IWD provisioning files");
+ return FALSE;
+ }
+ } else if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "IWD backend only supports Open, PSK and 802.1x network "
+ "authentication in Infrastructure mode");
+ return FALSE;
+ }
+ } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP)) {
+ if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "device does not support Access Point mode");
+ return FALSE;
+ }
+
+ if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_PSK)) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
- "802.1x profile is not a known network");
+ "IWD backend only supports PSK authentication in AP mode");
return FALSE;
}
+ } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) {
+ if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "device does not support Ad-Hoc mode");
+ return FALSE;
+ }
+
+ if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "IWD backend only supports Open and PSK authentication in Ad-Hoc mode");
+ return FALSE;
+ }
+ } else {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "%s type profiles not supported by IWD backend");
+ return FALSE;
}
return TRUE;
@@ -582,42 +689,15 @@ check_connection_available (NMDevice *device,
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
NMSettingWireless *s_wifi;
const char *mode;
+ NMWifiAP *ap = NULL;
s_wifi = nm_connection_get_setting_wireless (connection);
g_return_val_if_fail (s_wifi, FALSE);
- /* Only Infrastrusture mode at this time */
- mode = nm_setting_wireless_get_mode (s_wifi);
- 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)) {
- 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 (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
* also be available in general (without @specific_object). */
if (specific_object) {
- NMWifiAP *ap;
-
ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
if (!ap) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
@@ -629,18 +709,36 @@ check_connection_available (NMDevice *device,
"requested access point is not compatible with profile");
return FALSE;
}
- return TRUE;
}
+ /* AP and Ad-Hoc connections can be activated independent of the scan list */
+ mode = nm_setting_wireless_get_mode (s_wifi);
+ if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC))
+ return TRUE;
+
if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
return TRUE;
- if (!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection)) {
+ if (!ap)
+ ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+
+ if (!ap) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"no compatible access point found");
return FALSE;
}
+ /* 8021x networks can only be used if they've been provisioned on the IWD side and
+ * thus are Known Networks.
+ */
+ if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) {
+ if (!is_ap_known_network (ap)) {
+ nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+ "802.1x network is not an IWD Known Network (missing provisioning file?)");
+ return FALSE;
+ }
+ }
+
return TRUE;
}
@@ -666,15 +764,11 @@ complete_connection (NMDevice *device,
mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL;
- if (mode && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) {
- g_set_error_literal (error,
- NM_DEVICE_ERROR,
- NM_DEVICE_ERROR_INVALID_CONNECTION,
- "Only Infrastructure mode is supported.");
- return FALSE;
- }
-
- if (!specific_object) {
+ if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) {
+ if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error))
+ return FALSE;
+ ap = NULL;
+ } else if (!specific_object) {
/* If not given a specific object, we need at minimum an SSID */
if (!s_wifi) {
g_set_error_literal (error,
@@ -696,11 +790,16 @@ complete_connection (NMDevice *device,
/* Find a compatible AP in the scan list */
ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
if (!ap) {
- g_set_error_literal (error,
- NM_DEVICE_ERROR,
- NM_DEVICE_ERROR_INVALID_CONNECTION,
- "No compatible AP in the scan list and hidden SSIDs not supported.");
- return FALSE;
+ if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) {
+ g_set_error_literal (error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "No compatible AP in the scan list and hidden SSIDs not supported.");
+ return FALSE;
+ }
+
+ if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error))
+ return FALSE;
}
} else {
ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object);
@@ -720,7 +819,10 @@ complete_connection (NMDevice *device,
nm_connection_add_setting (connection, NM_SETTING (s_wifi));
}
- ssid = nm_wifi_ap_get_ssid (ap);
+ ssid = nm_setting_wireless_get_ssid (s_wifi);
+ if (!ssid && ap)
+ ssid = nm_wifi_ap_get_ssid (ap);
+
if (!ssid) {
g_set_error_literal (error,
NM_DEVICE_ERROR,
@@ -729,11 +831,13 @@ complete_connection (NMDevice *device,
return FALSE;
}
- if (!nm_wifi_ap_complete_connection (ap,
- connection,
- nm_wifi_utils_is_manf_default_ssid (ssid),
- error))
- return FALSE;
+ if (ap) {
+ if (!nm_wifi_ap_complete_connection (ap,
+ connection,
+ nm_wifi_utils_is_manf_default_ssid (ssid),
+ error))
+ return FALSE;
+ }
ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
nm_utils_complete_generic (nm_device_get_platform (device),
@@ -745,19 +849,6 @@ complete_connection (NMDevice *device,
NULL,
TRUE);
- /* 8021x networks can only be used if they've been provisioned on the IWD side and
- * thus are Known Networks.
- */
- 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,
- NM_DEVICE_ERROR_INVALID_CONNECTION,
- "This 8021x network has not been provisioned on this machine");
- return FALSE;
- }
- }
-
perm_hw_addr = nm_device_get_permanent_hw_address (device);
if (perm_hw_addr) {
setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
@@ -820,8 +911,20 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
{
NMDeviceIwd *self = NM_DEVICE_IWD (device);
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
-
- return priv->enabled && priv->dbus_station_proxy;
+ NMDeviceState state = nm_device_get_state (device);
+
+ /* Available if either the device is UP and in station mode
+ * or in AP/Ad-Hoc modes while activating or activated. Device
+ * may be temporarily DOWN while activating or deactivating and
+ * we don't want it to be marked unavailable because of this.
+ *
+ * For reference:
+ * We call nm_device_queue_recheck_available whenever
+ * priv->enabled changes or priv->dbus_station_proxy changes.
+ */
+ return priv->enabled
+ && ( priv->dbus_station_proxy
+ || (state >= NM_DEVICE_STATE_CONFIG && state <= NM_DEVICE_STATE_DEACTIVATING));
}
static gboolean
@@ -829,8 +932,7 @@ get_autoconnect_allowed (NMDevice *device)
{
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device));
- return is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE)
- && priv->can_connect;
+ return priv->can_connect;
}
static gboolean
@@ -856,7 +958,9 @@ can_auto_connect (NMDevice *device,
s_wifi = nm_connection_get_setting_wireless (connection);
g_return_val_if_fail (s_wifi, FALSE);
- /* Only Infrastrusture mode */
+ /* Don't auto-activate AP or Ad-Hoc connections.
+ * Note the wpa_supplicant backend has the opposite policy.
+ */
mode = nm_setting_wireless_get_mode (s_wifi);
if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
return FALSE;
@@ -870,14 +974,6 @@ can_auto_connect (NMDevice *device,
return FALSE;
}
- /* 8021x networks can only be used if they've been provisioned on the IWD side and
- * thus are Known Networks.
- */
- if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) {
- if (!is_connection_known_network (connection))
- return FALSE;
- }
-
ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
if (ap) {
/* All good; connection is usable */
@@ -908,11 +1004,11 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
NMDeviceIwd *self = user_data;
NMDeviceIwdPrivate *priv;
+ gs_unref_variant GVariant *variant = NULL;
gs_free_error GError *error = NULL;
- if ( !_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
- G_VARIANT_TYPE ("()"), &error)
- && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
+ if (!variant && nm_utils_error_is_cancelled (error, FALSE))
return;
priv = NM_DEVICE_IWD_GET_PRIVATE (self);
@@ -958,9 +1054,7 @@ dbus_request_scan_cb (NMDevice *device,
priv = NM_DEVICE_IWD_GET_PRIVATE (self);
- if ( !priv->can_scan
- || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
- || nm_device_is_activating (device)) {
+ if (!priv->can_scan) {
g_dbus_method_invocation_return_error_literal (context,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_NOT_ALLOWED,
@@ -999,9 +1093,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self,
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
NMDevice *device = NM_DEVICE (self);
- if ( !priv->can_scan
- || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
- || nm_device_is_activating (device)) {
+ if (!priv->can_scan) {
g_dbus_method_invocation_return_error_literal (invocation,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_NOT_ALLOWED,
@@ -1041,8 +1133,6 @@ scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
return TRUE;
case NM_DEVICE_STATE_DISCONNECTED:
case NM_DEVICE_STATE_FAILED:
- /* Can always scan when disconnected */
- return FALSE;
case NM_DEVICE_STATE_ACTIVATED:
break;
}
@@ -1199,7 +1289,7 @@ wifi_secrets_cb (NMActRequest *req,
priv->wifi_secrets_id = NULL;
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ if (nm_utils_error_is_cancelled (error, FALSE)) {
g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_CONNECTION,
"NM secrets request cancelled");
@@ -1274,6 +1364,7 @@ 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_unref_variant GVariant *variant = NULL;
gs_free_error GError *error = NULL;
NMConnection *connection;
NMSettingWireless *s_wifi;
@@ -1282,9 +1373,8 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
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)) {
+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
+ if (!variant) {
gs_free char *dbus_error = NULL;
/* Connection failed; radio problems or if the network wasn't
@@ -1295,7 +1385,7 @@ 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))
+ if (nm_utils_error_is_cancelled (error, FALSE))
return;
if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH))
@@ -1359,12 +1449,189 @@ failed:
}
static void
+act_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ NMDeviceIwd *self = user_data;
+ NMDevice *device = NM_DEVICE (self);
+ gs_unref_variant GVariant *variant = NULL;
+ gs_free_error GError *error = NULL;
+
+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
+ if (!variant && nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ /* Change state to FAILED unless already done by state_changed
+ * which may have been triggered by the station interface
+ * appearing on DBus.
+ */
+ if (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG)
+ nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+}
+
+static void
+act_start_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ NMDeviceIwd *self = user_data;
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+ gs_unref_variant GVariant *variant = NULL;
+ gs_free_error GError *error = NULL;
+ NMSettingWireless *s_wireless;
+ GBytes *ssid;
+ gs_free char *ssid_utf8 = NULL;
+
+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
+ if (!variant) {
+ _LOGE (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) Network.Connect failed: %s",
+ error->message);
+
+ if (nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG))
+ return;
+
+ goto error;
+ }
+
+ nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
+
+ s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS);
+ if (!s_wireless)
+ goto error;
+
+ ssid = nm_setting_wireless_get_ssid (s_wireless);
+ if (!ssid)
+ goto error;
+
+ ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
+
+ _LOGI (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Started '%s'.",
+ ssid_utf8);
+
+ nm_device_activate_schedule_stage3_ip_config_start (device);
+ return;
+
+error:
+ reset_mode (self, priv->cancellable, act_failed_cb, self);
+}
+
+/* Check if we're activating an AP/AdHoc connection and if the target
+ * DBus interface has appeared already. If so proceed to call Start or
+ * StartOpen on that interface.
+ */
+static void act_check_interface (NMDeviceIwd *self)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+ gs_free_error GError *error = NULL;
+ NMConnection *connection;
+ NMSettingWireless *s_wireless;
+ NMSettingWirelessSecurity *s_wireless_sec;
+ GDBusProxy *proxy = NULL;
+ GBytes *ssid;
+ gs_free char *ssid_utf8 = NULL;
+ const char *mode;
+
+ if (!priv->act_mode_switch)
+ return;
+
+ connection = nm_device_get_settings_connection_get_connection (device);
+ s_wireless = nm_connection_get_setting_wireless (connection);
+
+ mode = nm_setting_wireless_get_mode (s_wireless);
+ if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP))
+ proxy = priv->dbus_ap_proxy;
+ else if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC))
+ proxy = priv->dbus_adhoc_proxy;
+
+ if (!proxy)
+ return;
+
+ priv->act_mode_switch = FALSE;
+
+ if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG))
+ return;
+
+ ssid = nm_setting_wireless_get_ssid (s_wireless);
+ if (!ssid)
+ goto failed;
+
+ ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
+
+ s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
+
+ if (!s_wireless_sec) {
+ g_dbus_proxy_call (proxy, "StartOpen",
+ g_variant_new ("(s)", ssid_utf8),
+ G_DBUS_CALL_FLAGS_NONE, G_MAXINT,
+ priv->cancellable, act_start_cb, self);
+ } else {
+ const char *psk = nm_setting_wireless_security_get_psk (s_wireless_sec);
+
+ if (!psk) {
+ _LOGE (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) No PSK for '%s'.",
+ ssid_utf8);
+ goto failed;
+ }
+
+ g_dbus_proxy_call (proxy, "Start",
+ g_variant_new ("(ss)", ssid_utf8, psk),
+ G_DBUS_CALL_FLAGS_NONE, G_MAXINT,
+ priv->cancellable, act_start_cb, self);
+ }
+
+ _LOGD (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) Called Start('%s').",
+ ssid_utf8);
+ return;
+
+failed:
+ reset_mode (self, priv->cancellable, act_failed_cb, self);
+}
+
+static void
+act_set_mode_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ NMDeviceIwd *self = user_data;
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+ gs_unref_variant GVariant *variant = NULL;
+ gs_free_error GError *error = NULL;
+
+ variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error);
+ if (!variant) {
+ _LOGE (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) Setting Device.Mode failed: %s",
+ error->message);
+
+ if (nm_utils_error_is_cancelled (error, FALSE))
+ return;
+
+ if ( !NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)
+ || !priv->act_mode_switch)
+ return;
+
+ priv->act_mode_switch = FALSE;
+ nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+ return;
+ }
+
+ _LOGD (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) IWD Device.Mode set successfully");
+
+ act_check_interface (self);
+}
+
+static void
set_powered (NMDeviceIwd *self, gboolean powered)
{
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
g_dbus_proxy_call (priv->dbus_device_proxy,
- "org.freedesktop.DBus.Properties.Set",
+ DBUS_INTERFACE_PROPERTIES ".Set",
g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE,
"Powered",
g_variant_new ("b", powered)),
@@ -1384,6 +1651,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
NMActRequest *req;
NMConnection *connection;
NMSettingWireless *s_wireless;
+ const char *mode;
const char *ap_path;
ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason);
@@ -1399,20 +1667,51 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
s_wireless = nm_connection_get_setting_wireless (connection);
g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE);
+ /* AP mode never uses a specific object or existing scanned AP */
+ mode = nm_setting_wireless_get_mode (s_wireless);
+ if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP))
+ goto add_new;
+
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) {
- ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
- if (!ap) {
- NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
- return NM_ACT_STAGE_RETURN_FAILURE;
- }
+ if (ap) {
+ set_current_ap (self, ap, TRUE);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+ }
+ ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
+ if (ap) {
nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
+ set_current_ap (self, ap, TRUE);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
}
+ if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) {
+ /* Hidden networks not supported at this time */
+ return NM_ACT_STAGE_RETURN_FAILURE;
+ }
+
+add_new:
+ /* 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 (Ad-Hoc or Hotspot).
+ */
+ ap = nm_wifi_ap_new_fake_from_connection (connection);
+ g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ if (nm_wifi_ap_is_hotspot (ap))
+ nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device));
+
+ g_object_freeze_notify (G_OBJECT (self));
+ ap_add_remove (self, TRUE, ap, FALSE);
+ g_object_thaw_notify (G_OBJECT (self));
set_current_ap (self, ap, FALSE);
+ nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
+ nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
+ g_object_unref (ap);
return NM_ACT_STAGE_RETURN_SUCCESS;
}
@@ -1423,60 +1722,88 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
NMActRequest *req;
- NMWifiAP *ap;
NMConnection *connection;
- GDBusProxy *network_proxy;
+ NMSettingWireless *s_wireless;
+ const char *mode;
req = nm_device_get_act_request (device);
g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
- ap = priv->current_ap;
- if (!ap) {
- NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
- goto out;
- }
-
connection = nm_act_request_get_applied_connection (req);
g_assert (connection);
- /* 802.1x networks that are not IWD Known Networks will definitely
- * fail, for other combinations we will let the Connect call fail
- * or ask us for any missing secrets through the Agent.
- */
- if ( !is_connection_known_network (connection)
- && nm_connection_get_setting_802_1x (connection)) {
- _LOGI (LOGD_DEVICE | LOGD_WIFI,
- "Activation: (wifi) access point '%s' has 802.1x security, but is not configured.",
- nm_connection_get_id (connection));
+ s_wireless = nm_connection_get_setting_wireless (connection);
+ g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE);
- NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
- ret = NM_ACT_STAGE_RETURN_FAILURE;
- goto out;
- }
+ mode = nm_setting_wireless_get_mode (s_wireless);
+ if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) {
+ GDBusProxy *network_proxy;
+ NMWifiAP *ap = priv->current_ap;
- 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",
- nm_wifi_ap_get_supplicant_path (ap));
- NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
- goto out;
- }
+ if (!ap) {
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+ goto out;
+ }
- if (!priv->cancellable)
- priv->cancellable = g_cancellable_new ();
+ /* 802.1x networks that are not IWD Known Networks will definitely
+ * fail, for other combinations we will let the Connect call fail
+ * or ask us for any missing secrets through the Agent.
+ */
+ if ( nm_connection_get_setting_802_1x (connection)
+ && !is_ap_known_network (ap)) {
+ _LOGI (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) access point '%s' has 802.1x security but is not configured in IWD.",
+ nm_connection_get_id (connection));
+
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
+ goto out;
+ }
- /* Call Network.Connect. No timeout because IWD already handles
- * timeouts.
- */
- g_dbus_proxy_call (network_proxy, "Connect",
- g_variant_new ("()"),
- G_DBUS_CALL_FLAGS_NONE, G_MAXINT,
- priv->cancellable, network_connect_cb, self);
+ 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",
+ nm_wifi_ap_get_supplicant_path (ap));
+ NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+ goto out;
+ }
- g_object_unref (network_proxy);
+ if (!priv->cancellable)
+ priv->cancellable = g_cancellable_new ();
+
+ /* Call Network.Connect. No timeout because IWD already handles
+ * timeouts.
+ */
+ g_dbus_proxy_call (network_proxy, "Connect",
+ g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE, G_MAXINT,
+ priv->cancellable, network_connect_cb, self);
+
+ g_object_unref (network_proxy);
+ } else if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) {
+ const char *iwd_mode;
+
+ /* We need to first set interface mode (Device.Mode) to ap or ad-hoc.
+ * We can't directly queue a call to the Start/StartOpen method on
+ * the DBus interface that's going to be created after the property
+ * set call returns.
+ */
+ iwd_mode = nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP) ? "ap" : "ad-hoc";
+
+ if (!priv->cancellable)
+ priv->cancellable = g_cancellable_new ();
+
+ g_dbus_proxy_call (priv->dbus_device_proxy,
+ DBUS_INTERFACE_PROPERTIES ".Set",
+ g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE,
+ "Mode",
+ g_variant_new ("s", iwd_mode)),
+ G_DBUS_CALL_FLAGS_NONE, 2000,
+ priv->cancellable, act_set_mode_cb, self);
+ priv->act_mode_switch = TRUE;
+ }
/* We'll get stage3 started when the supplicant connects */
ret = NM_ACT_STAGE_RETURN_POSTPONE;
@@ -1521,7 +1848,7 @@ schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state)
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
guint interval;
- if (current_state <= NM_DEVICE_STATE_UNAVAILABLE)
+ if (!priv->can_scan)
return;
if (current_state == NM_DEVICE_STATE_DISCONNECTED)
@@ -1536,6 +1863,23 @@ schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state)
}
static void
+set_can_scan (NMDeviceIwd *self, gboolean can_scan)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
+
+ if (priv->can_scan == can_scan)
+ return;
+
+ priv->can_scan = can_scan;
+
+ if (priv->can_scan && !priv->periodic_scan_id && !priv->scan_requested && !priv->scanning)
+ schedule_periodic_scan (self, state);
+ else if (!priv->can_scan && priv->periodic_scan_id)
+ nm_clear_g_source (&priv->periodic_scan_id);
+}
+
+static void
device_state_changed (NMDevice *device,
NMDeviceState new_state,
NMDeviceState old_state,
@@ -1544,14 +1888,6 @@ device_state_changed (NMDevice *device,
NMDeviceIwd *self = NM_DEVICE_IWD (device);
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
- if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) {
- remove_all_aps (self);
- nm_clear_g_source (&priv->periodic_scan_id);
- } else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) {
- update_aps (self);
- schedule_periodic_scan (self, new_state);
- }
-
switch (new_state) {
case NM_DEVICE_STATE_UNMANAGED:
break;
@@ -1669,10 +2005,13 @@ get_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_MODE:
- if (priv->current_ap)
- g_value_set_uint (value, NM_802_11_MODE_INFRA);
- else
+ if (!priv->current_ap)
g_value_set_uint (value, NM_802_11_MODE_UNKNOWN);
+ else if (nm_wifi_ap_is_hotspot (priv->current_ap))
+ g_value_set_uint (value, NM_802_11_MODE_AP);
+ else
+ g_value_set_uint (value, nm_wifi_ap_get_mode (priv->current_ap));
+
break;
case PROP_BITRATE:
g_value_set_uint (value, 65000);
@@ -1702,24 +2041,6 @@ get_property (GObject *object, guint prop_id,
}
}
-static void
-set_property (GObject *object, guint prop_id,
- const GValue *value, GParamSpec *pspec)
-{
- NMDeviceIwd *device = NM_DEVICE_IWD (object);
- NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (device);
-
- switch (prop_id) {
- case PROP_CAPABILITIES:
- /* construct-only */
- priv->capabilities = g_value_get_uint (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
/*****************************************************************************/
static void
@@ -1729,7 +2050,7 @@ state_changed (NMDeviceIwd *self, const char *new_state)
NMDevice *device = NM_DEVICE (self);
NMDeviceState dev_state = nm_device_get_state (device);
gboolean iwd_connection = FALSE;
- gboolean can_connect;
+ gboolean can_connect = priv->can_connect;
_LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is %s", new_state);
@@ -1738,7 +2059,9 @@ state_changed (NMDeviceIwd *self, const char *new_state)
iwd_connection = TRUE;
/* Don't allow scanning while connecting, disconnecting or roaming */
- priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected");
+ set_can_scan (self, NM_IN_STRSET (new_state, "connected", "disconnected"));
+
+ priv->can_connect = FALSE;
if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) {
/* If we were connecting, do nothing, the confirmation of
@@ -1781,10 +2104,10 @@ state_changed (NMDeviceIwd *self, const char *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);
+ if (NM_IN_STRSET (new_state, "disconnected")) {
+ priv->can_connect = TRUE;
+ if (!can_connect)
+ nm_device_emit_recheck_auto_activate (device);
}
}
@@ -1833,21 +2156,82 @@ station_properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
}
static void
+ap_adhoc_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 (nm_streq (key, "Started")) {
+ gboolean new_started = get_variant_boolean (value, "Started");
+
+ _LOGI (LOGD_DEVICE | LOGD_WIFI, "IWD AP/AdHoc state is now %s", new_started ? "Started" : "Stopped");
+ }
+
+ 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);
+ GDBusInterface *interface;
+ GVariant *value;
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;
+ interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_AP_INTERFACE) : NULL;
- if (priv->dbus_station_proxy)
- return;
+ if (priv->dbus_ap_proxy) {
+ g_signal_handlers_disconnect_by_func (priv->dbus_ap_proxy,
+ ap_adhoc_properties_changed, self);
+ g_clear_object (&priv->dbus_ap_proxy);
+ }
+
+ if (interface) {
+ priv->dbus_ap_proxy = G_DBUS_PROXY (interface);
+ g_signal_connect (priv->dbus_ap_proxy, "g-properties-changed",
+ G_CALLBACK (ap_adhoc_properties_changed), self);
+
+ if (priv->act_mode_switch)
+ act_check_interface (self);
+ else
+ reset_mode (self, NULL, NULL, NULL);
+ }
+
+ interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_ADHOC_INTERFACE) : NULL;
+
+ if (priv->dbus_adhoc_proxy) {
+ g_signal_handlers_disconnect_by_func (priv->dbus_adhoc_proxy,
+ ap_adhoc_properties_changed, self);
+ g_clear_object (&priv->dbus_adhoc_proxy);
+ }
+
+ if (interface) {
+ priv->dbus_adhoc_proxy = G_DBUS_PROXY (interface);
+ g_signal_connect (priv->dbus_adhoc_proxy, "g-properties-changed",
+ G_CALLBACK (ap_adhoc_properties_changed), self);
+
+ if (priv->act_mode_switch)
+ act_check_interface (self);
+ else
+ reset_mode (self, NULL, NULL, NULL);
+ }
+ /* We expect one of the three interfaces to always be present when
+ * device is Powered so if AP and AdHoc are not present we should
+ * be in station mode.
+ */
+ if (new_powered && !priv->dbus_ap_proxy && !priv->dbus_adhoc_proxy) {
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
@@ -1858,18 +2242,30 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered)
* priv->dbus_station_proxy both point at the Device interface.
*/
value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State");
- if (!value) {
+ if (value) {
+ g_variant_unref (value);
+ interface = g_object_ref (priv->dbus_device_proxy);
+ } else {
_LOGE (LOGD_WIFI, "Interface %s not found on obj %s",
NM_IWD_STATION_INTERFACE,
g_dbus_object_get_object_path (priv->dbus_obj));
- return;
+ interface = NULL;
}
- g_variant_unref (value);
- interface = g_object_ref (priv->dbus_device_proxy);
}
+ } else
+ interface = NULL;
+ if (priv->dbus_station_proxy) {
+ g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy,
+ station_properties_changed, self);
+ g_clear_object (&priv->dbus_station_proxy);
+ }
+
+ if (interface) {
priv->dbus_station_proxy = G_DBUS_PROXY (interface);
+ g_signal_connect (priv->dbus_station_proxy, "g-properties-changed",
+ G_CALLBACK (station_properties_changed), self);
value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "Scanning");
priv->scanning = get_variant_boolean (value, "Scanning");
@@ -1879,27 +2275,14 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered)
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);
+ update_aps (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;
+ set_can_scan (self, FALSE);
priv->scanning = FALSE;
priv->scan_requested = FALSE;
priv->can_connect = FALSE;
cleanup_association_attempt (self, FALSE);
+ remove_all_aps (self);
}
}
@@ -1928,24 +2311,24 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
{
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
GDBusInterface *interface;
- GVariant *value;
+ gs_unref_variant GVariant *value = NULL;
+ gs_unref_object GDBusProxy *adapter_proxy = NULL;
+ GVariantIter *iter;
+ const char *mode;
gboolean powered;
+ NMDeviceWifiCapabilities capabilities;
if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object))
return;
- 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 (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);
powered_changed (self, FALSE);
+
+ priv->act_mode_switch = FALSE;
}
if (!object)
@@ -1965,14 +2348,63 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
g_signal_connect (priv->dbus_device_proxy, "g-properties-changed",
G_CALLBACK (device_properties_changed), self);
+ /* Parse list of interface modes supported by adapter (wiphy) */
+
+ value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Adapter");
+ if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH)) {
+ nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+ "Adapter property not cached or not an object path");
+ goto error;
+ }
+
+ adapter_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (),
+ g_variant_get_string (value, NULL),
+ NM_IWD_WIPHY_INTERFACE);
+ if (!adapter_proxy) {
+ nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+ "Can't get DBus proxy for IWD Adapter for IWD Device");
+ goto error;
+ }
+
+ g_variant_unref (value);
+ value = g_dbus_proxy_get_cached_property (adapter_proxy, "SupportedModes");
+ if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) {
+ nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+ "SupportedModes property not cached or not a string array");
+ goto error;
+ }
+
+ capabilities = NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN;
+
+ g_variant_get (value, "as", &iter);
+ while (g_variant_iter_next (iter, "&s", &mode)) {
+ if (nm_streq (mode, "ap"))
+ capabilities |= NM_WIFI_DEVICE_CAP_AP;
+ else if (nm_streq (mode, "ad-hoc"))
+ capabilities |= NM_WIFI_DEVICE_CAP_ADHOC;
+ }
+ g_variant_iter_free (iter);
+
+ if (priv->capabilities != capabilities) {
+ priv->capabilities = capabilities;
+ _notify (self, PROP_CAPABILITIES);
+ }
+
+ g_variant_unref (value);
value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered");
powered = get_variant_boolean (value, "Powered");
- g_variant_unref (value);
if (powered != priv->enabled)
set_powered (self, priv->enabled);
else if (powered)
powered_changed (self, TRUE);
+
+ return;
+
+error:
+ g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy,
+ device_properties_changed, self);
+ g_clear_object (&priv->dbus_device_proxy);
}
gboolean
@@ -2041,7 +2473,7 @@ nm_device_iwd_init (NMDeviceIwd *self)
}
NMDevice *
-nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities)
+nm_device_iwd_new (const char *iface)
{
return g_object_new (NM_TYPE_DEVICE_IWD,
NM_DEVICE_IFACE, iface,
@@ -2049,7 +2481,6 @@ nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities)
NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI,
NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI,
NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN,
- NM_DEVICE_IWD_CAPABILITIES, (guint) capabilities,
NULL);
}
@@ -2067,6 +2498,8 @@ dispose (GObject *object)
g_clear_object (&priv->dbus_device_proxy);
g_clear_object (&priv->dbus_station_proxy);
+ g_clear_object (&priv->dbus_ap_proxy);
+ g_clear_object (&priv->dbus_adhoc_proxy);
g_clear_object (&priv->dbus_obj);
remove_all_aps (self);
@@ -2084,7 +2517,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass)
NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
object_class->get_property = get_property;
- object_class->set_property = set_property;
object_class->dispose = dispose;
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless);
@@ -2144,8 +2576,7 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass)
obj_properties[PROP_CAPABILITIES] =
g_param_spec_uint (NM_DEVICE_IWD_CAPABILITIES, "", "",
0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_SCANNING] =
diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h
index 825123b1d8..4a2bd31e1f 100644
--- a/src/devices/wifi/nm-device-iwd.h
+++ b/src/devices/wifi/nm-device-iwd.h
@@ -47,7 +47,7 @@ typedef struct _NMDeviceIwdClass NMDeviceIwdClass;
GType nm_device_iwd_get_type (void);
-NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities);
+NMDevice *nm_device_iwd_new (const char *iface);
void nm_device_iwd_set_dbus_object (NMDeviceIwd *device, GDBusObject *object);
diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c
index 6b8e5fb85a..4e9d1ecb20 100644
--- a/src/devices/wifi/nm-wifi-factory.c
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -75,8 +75,6 @@ create_device (NMDeviceFactory *factory,
NMConnection *connection,
gboolean *out_ignore)
{
- NMDeviceWifiCapabilities capabilities;
- NM80211Mode mode;
gs_free char *backend = NULL;
g_return_val_if_fail (iface != NULL, NULL);
@@ -84,23 +82,6 @@ create_device (NMDeviceFactory *factory,
g_return_val_if_fail (g_strcmp0 (iface, plink->name) == 0, NULL);
g_return_val_if_fail (NM_IN_SET (plink->type, NM_LINK_TYPE_WIFI, NM_LINK_TYPE_OLPC_MESH), NULL);
- if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET,
- plink->ifindex,
- &capabilities)) {
- nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) failed to initialize Wi-Fi driver for ifindex %d", iface, plink->ifindex);
- return NULL;
- }
-
- /* Ignore monitor-mode and other unhandled interface types.
- * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage
- * them if/when they change to a handled type.
- */
- mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex);
- if (mode == NM_802_11_MODE_UNKNOWN) {
- *out_ignore = TRUE;
- return NULL;
- }
-
if (plink->type != NM_LINK_TYPE_WIFI)
return nm_device_olpc_mesh_new (iface);
@@ -116,11 +97,34 @@ create_device (NMDeviceFactory *factory,
iface,
NM_PRINT_FMT_QUOTE_STRING (backend),
WITH_IWD ? " (iwd support enabled)" : "");
- if (!backend || !strcasecmp (backend, "wpa_supplicant"))
+ if (!backend || !strcasecmp (backend, "wpa_supplicant")) {
+ NMDeviceWifiCapabilities capabilities;
+ NM80211Mode mode;
+
+ if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET,
+ plink->ifindex,
+ &capabilities)) {
+ nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
+ "(%s) failed to initialize Wi-Fi driver for ifindex %d",
+ iface, plink->ifindex);
+ return NULL;
+ }
+
+ /* Ignore monitor-mode and other unhandled interface types.
+ * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage
+ * them if/when they change to a handled type.
+ */
+ mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex);
+ if (mode == NM_802_11_MODE_UNKNOWN) {
+ *out_ignore = TRUE;
+ return NULL;
+ }
+
return nm_device_wifi_new (iface, capabilities);
+ }
#if WITH_IWD
else if (!strcasecmp (backend, "iwd"))
- return nm_device_iwd_new (iface, capabilities);
+ return nm_device_iwd_new (iface);
#endif
nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) config: unknown or unsupported wifi-backend %s", iface, backend);