+{
+ gpointer *list;
+ gsize i, j;
+
+ list = (gpointer *) ap_list_get_sorted (self,
include_without_ssid);
+ for (i = 0, j = 0; list[i]; i++) {
+ NMWifiAP *ap = list[i];
+ const char *path;
+
+ /* update @list inplace to hold instead the export-
path. */
+ path = nm_exported_object_get_path
(NM_EXPORTED_OBJECT (ap));
+ nm_assert (path);
+ list[j++] = (gpointer) path;
+ }
+ return (const char **) list;
+}
+
+static void
+impl_device_iwd_get_access_points (NMDeviceIwd *self,
+ GDBusMethodInvocation *context)
+{
+ gs_free const char **list = NULL;
+ GVariant *v;
+
+ list = ap_list_get_sorted_paths (self, FALSE);
+ v = g_variant_new_objv (list, -1);
+ g_dbus_method_invocation_return_value (context,
g_variant_new_tuple (&v, 1));
+}
+
+static void
+impl_device_iwd_get_all_access_points (NMDeviceIwd *self,
+ GDBusMethodInvocation
*context)
+{
+ gs_free const char **list = NULL;
+ GVariant *v;
+
+ list = ap_list_get_sorted_paths (self, TRUE);
+ v = g_variant_new_objv (list, -1);
+ g_dbus_method_invocation_return_value (context,
g_variant_new_tuple (&v, 1));
+}
+
+static gboolean
+check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
+{
+ gboolean prohibited = FALSE;
+
+ g_signal_emit (self, signals[SCANNING_PROHIBITED], 0,
periodic, &prohibited);
+ return prohibited;
+}
+
+static void
+dbus_request_scan_cb (NMDevice *device,
+ GDBusMethodInvocation *context,
+ NMAuthSubject *subject,
+ GError *error,
+ gpointer user_data)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (device);
+ NMDeviceIwdPrivate *priv;
+ gs_unref_variant GVariant *scan_options = user_data;
+ gs_unref_ptrarray GPtrArray *ssids = NULL;
+
+ if (error) {
+ g_dbus_method_invocation_return_gerror (context,
error);
+ return;
+ }
+
+ if (check_scanning_prohibited (self, FALSE)) {
+ g_dbus_method_invocation_return_error_literal
(context,
+ NM_DE
VICE_ERROR,
+ NM_DE
VICE_ERROR_NOT_ALLOWED,
+ "Scan
ning not allowed at this time");
+ return;
+ }
+
+ priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ if ( !priv->enabled
+ || !priv->dbus_obj
+ || nm_device_get_state (device) <
NM_DEVICE_STATE_DISCONNECTED
+ || nm_device_is_activating (device)) {
+ g_dbus_method_invocation_return_error_literal
(context,
+ NM_DE
VICE_ERROR,
+ NM_DE
VICE_ERROR_NOT_ALLOWED,
+ "Scan
ning not allowed while unavailable");
+ return;
+ }
+
+ if (scan_options) {
+ gs_unref_variant GVariant *val =
g_variant_lookup_value (scan_options, "ssids", NULL);
+
+ if (val) {
+ g_dbus_method_invocation_return_error_litera
l (context,
+
NM_DEVICE_ERROR,
+
NM_DEVICE_ERROR_NOT_ALLOWED,
+
"'ssid' scan option not supported");
+ return;
+ }
+ }
+
+ g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new
("()"),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL,
NULL);
+ g_dbus_method_invocation_return_value (context, NULL);
+}
+
+static void
+impl_device_iwd_request_scan (NMDeviceIwd *self,
+ GDBusMethodInvocation *context,
+ GVariant *options)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+
+ if ( !priv->enabled
+ || !priv->dbus_obj
+ || nm_device_get_state (device) <
NM_DEVICE_STATE_DISCONNECTED
+ || nm_device_is_activating (device)) {
+ g_dbus_method_invocation_return_error_literal
(context,
+ NM_DE
VICE_ERROR,
+ NM_DE
VICE_ERROR_NOT_ALLOWED,
+ "Scan
ning not allowed while unavailable");
+ return;
+ }
+
+ /* Ask the manager to authenticate this request for us */
+ g_signal_emit_by_name (device,
+ NM_DEVICE_AUTH_REQUEST,
+ context,
+ NULL,
+ NM_AUTH_PERMISSION_NETWORK_CONTROL,
+ TRUE,
+ dbus_request_scan_cb,
+ options ? g_variant_ref (options) :
NULL);
+}
+
+static gboolean
+scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->dbus_obj != NULL, TRUE);
+
+ switch (nm_device_get_state (NM_DEVICE (self))) {
+ case NM_DEVICE_STATE_UNKNOWN:
+ case NM_DEVICE_STATE_UNMANAGED:
+ case NM_DEVICE_STATE_UNAVAILABLE:
+ case NM_DEVICE_STATE_PREPARE:
+ case NM_DEVICE_STATE_CONFIG:
+ case NM_DEVICE_STATE_NEED_AUTH:
+ case NM_DEVICE_STATE_IP_CONFIG:
+ case NM_DEVICE_STATE_IP_CHECK:
+ case NM_DEVICE_STATE_SECONDARIES:
+ case NM_DEVICE_STATE_DEACTIVATING:
+ /* Prohibit scans when unusable or activating */
+ 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;
+ }
+
+ /* Prohibit scans if IWD is busy */
+ return !priv->can_scan;
+}
+
+static void
+wifi_secrets_cb (NMActRequest *req,
+ NMActRequestGetSecretsCallId call_id,
+ NMSettingsConnection *connection,
+ GError *error,
+ gpointer user_data)
+{
+ NMDevice *device = user_data;
+ NMDeviceIwd *self = user_data;
+ NMDeviceIwdPrivate *priv;
+
+ g_return_if_fail (NM_IS_DEVICE_IWD (self));
+ g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+ priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ g_return_if_fail (priv->wifi_secrets_id == call_id);
+
+ priv->wifi_secrets_id = NULL;
+
+ if (g_error_matches (error, G_IO_ERROR,
G_IO_ERROR_CANCELLED))
+ return;
+
+ g_return_if_fail (req == nm_device_get_act_request
(device));
+ g_return_if_fail (nm_device_get_state (device) ==
NM_DEVICE_STATE_NEED_AUTH);
+ g_return_if_fail (nm_act_request_get_settings_connection
(req) == connection);
+
+ if (error) {
+ _LOGW (LOGD_WIFI, "%s", error->message);
+
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_NO_S
ECRETS);
+ } else
+ nm_device_activate_schedule_stage1_device_prepare
(device);
+}
+
+static void
+wifi_secrets_cancel (NMDeviceIwd *self)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ if (priv->wifi_secrets_id)
+ nm_act_request_cancel_secrets (NULL, priv-
wifi_secrets_id);
+ nm_assert (!priv->wifi_secrets_id);
+}
+
+static void
+wifi_secrets_get_secrets (NMDeviceIwd *self,
+ const char *setting_name,
+ NMSecretAgentGetSecretsFlags flags)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMActRequest *req;
+
+ wifi_secrets_cancel (self);
+
+ req = nm_device_get_act_request (NM_DEVICE (self));
+ g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+ priv->wifi_secrets_id = nm_act_request_get_secrets (req,
+ TRUE,
+ setting_
name,
+ flags,
+ NULL,
+ wifi_sec
rets_cb,
+ self);
+ g_return_if_fail (priv->wifi_secrets_id);
+}
+
+static gboolean
+need_new_8021x_secrets (NMDeviceIwd *self,
+ const char **setting_name)
+{
+ NMSetting8021x *s_8021x;
+ NMSettingWirelessSecurity *s_wsec;
+ NMSettingSecretFlags secret_flags =
NM_SETTING_SECRET_FLAG_NONE;
+ NMConnection *connection;
+
+ g_assert (setting_name != NULL);
+
+ connection = nm_device_get_applied_connection (NM_DEVICE
(self));
+ g_return_val_if_fail (connection != NULL, FALSE);
+
+ /* If it's an 802.1x or LEAP connection with "always
ask"/unsaved secrets
+ * then we need to ask again because it might be an OTP
token and the PIN
+ * may have changed.
+ */
+
+ s_8021x = nm_connection_get_setting_802_1x (connection);
+ if (s_8021x) {
+ if (!nm_setting_get_secret_flags (NM_SETTING
(s_8021x),
+ NM_SETTING_802_1X_
PASSWORD,
+ &secret_flags,
+ NULL))
+ g_assert_not_reached ();
+ if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+ *setting_name =
NM_SETTING_802_1X_SETTING_NAME;
+ return *setting_name ? TRUE : FALSE;
+ }
+
+ s_wsec = nm_connection_get_setting_wireless_security
(connection);
+ if (s_wsec) {
+ if (!nm_setting_get_secret_flags (NM_SETTING
(s_wsec),
+ NM_SETTING_WIRELES
S_SECURITY_LEAP_PASSWORD,
+ &secret_flags,
+ NULL))
+ g_assert_not_reached ();
+ if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+ *setting_name =
NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+ return *setting_name ? TRUE : FALSE;
+ }
+
+ /* Not a LEAP or 802.1x connection */
+ return FALSE;
+}
+
+static gboolean
+need_new_wpa_psk (NMDeviceIwd *self,
+ const char **setting_name)
+{
+ NMSettingWirelessSecurity *s_wsec;
+ NMConnection *connection;
+ const char *key_mgmt = NULL;
+
+ g_assert (setting_name != NULL);
+
+ connection = nm_device_get_applied_connection (NM_DEVICE
(self));
+ g_return_val_if_fail (connection != NULL, FALSE);
+
+ s_wsec = nm_connection_get_setting_wireless_security
(connection);
+ if (s_wsec)
+ key_mgmt = nm_setting_wireless_security_get_key_mgmt
(s_wsec);
+
+ if (g_strcmp0 (key_mgmt, "wpa-psk") == 0) {
+ /* We don't have any data from IWD about the
disconnect
+ * reason or association state when the disconnect
happened
+ * so just assume it was a bad password.
+ */
+ *setting_name =
NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+ return TRUE;
+ }
+
+ /* Not a WPA-PSK connection */
+ return FALSE;
+}
+
+static gboolean
+handle_8021x_or_psk_auth_fail (NMDeviceIwd *self)
+{
+ NMDevice *device = NM_DEVICE (self);
+ NMActRequest *req;
+ const char *setting_name = NULL;
+ gboolean handled = FALSE;
+
+ req = nm_device_get_act_request (NM_DEVICE (self));
+ g_return_val_if_fail (req != NULL, FALSE);
+
+ if ( need_new_8021x_secrets (self, &setting_name)
+ || need_new_wpa_psk (self, &setting_name)) {
+ nm_act_request_clear_secrets (req);
+
+ _LOGI (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) disconnected during
association, asking for new key");
+
+ cleanup_association_attempt (self, FALSE);
+ nm_device_state_changed (device,
NM_DEVICE_STATE_NEED_AUTH,
+ NM_DEVICE_STATE_REASON_SUPP
LICANT_DISCONNECT);
+ wifi_secrets_get_secrets (self,
+ setting_name,
+ NM_SECRET_AGENT_GET_SECRET
S_FLAG_ALLOW_INTERACTION
+ |
NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+static void
+network_connect_cb (GObject *source, GAsyncResult *res, gpointer
user_data)
+{
+ NMDeviceIwd *self = user_data;
+ NMDevice *device = NM_DEVICE (self);
+ gs_free_error GError *error = NULL;
+ gs_unref_variant GVariant *variant = NULL;
+ NMConnection *connection;
+ NMSettingWireless *s_wifi;
+ GBytes *ssid;
+
+ if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
+ G_VARIANT_TYPE ("()"),
+ &error)) {
+ gs_free gchar *dbus_error = NULL;
+
+ /* Connection failed; radio problems or if the
network wasn't
+ * open, the passwords or certificates may be wrong.
+ */
+
+ _LOGE (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) Network.Connect failed:
%s",
+ error->message);
+
+ connection = nm_device_get_applied_connection
(NM_DEVICE (self));
+ if (!connection ||
nm_connection_get_setting_wireless_security (connection))
+ goto failed;
+
+ 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")) {
+ if (handle_8021x_or_psk_auth_fail (self)) {
+ _LOGW (LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) asking for new secrets");
+ } else {
+ cleanup_association_attempt (self,
FALSE);
+ nm_device_state_changed (device,
NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_S
TATE_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);
+
+ return;
+ }
+
+ nm_assert (nm_device_get_state (device) ==
NM_DEVICE_STATE_CONFIG);
+
+ connection = nm_device_get_applied_connection (device);
+ if (!connection)
+ goto failed;
+
+ s_wifi = nm_connection_get_setting_wireless (connection);
+ if (!s_wifi)
+ goto failed;
+
+ ssid = nm_setting_wireless_get_ssid (s_wifi);
+ if (!ssid)
+ goto failed;
+
+ _LOGI (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) Stage 2 of 5 (Device Configure)
successful. Connected to '%s'.",
+ ssid ? nm_utils_escape_ssid (g_bytes_get_data (ssid,
NULL),
+ g_bytes_get_size (ssid))
: "(none)");
+ nm_device_activate_schedule_stage3_ip_config_start (device);
+ return;
+
+failed:
+ cleanup_association_attempt (self, FALSE);
+ nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_FAI
LED);
+}
+
+static gboolean
+handle_auth_or_fail (NMDeviceIwd *self,
+ NMActRequest *req,
+ gboolean new_secrets)
+{
+ const char *setting_name;
+ guint32 tries;
+ NMConnection *applied_connection;
+ NMSecretAgentGetSecretsFlags get_secret_flags =
NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+
+ g_return_val_if_fail (NM_IS_DEVICE_IWD (self), FALSE);
+
+ applied_connection = nm_act_request_get_applied_connection
(req);
+
+ tries = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT
(applied_connection), wireless_secrets_tries_quark ()));
+ if (tries > 3)
+ return FALSE;
+
+ nm_device_state_changed (NM_DEVICE (self),
NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+
+ nm_act_request_clear_secrets (req);
+ setting_name = nm_connection_need_secrets
(applied_connection, NULL);
+ if (!setting_name) {
+ _LOGW (LOGD_DEVICE, "Cleared secrets, but setting
didn't need any secrets.");
+ return FALSE;
+ }
+
+ if (new_secrets)
+ get_secret_flags |=
NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
+ wifi_secrets_get_secrets (self, setting_name,
get_secret_flags);
+ g_object_set_qdata (G_OBJECT (applied_connection),
wireless_secrets_tries_quark (), GUINT_TO_POINTER (++tries));
+ return TRUE;
+}
+
+/*******************************************************************
**********/
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason
*out_failure_reason)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (device);
+ NMActStageReturn ret;
+ NMWifiAP *ap = NULL;
+ NMActRequest *req;
+ NMConnection *connection;
+ NMSettingWireless *s_wireless;
+ const char *ap_path;
+
+ ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)-
act_stage1_prepare (device, out_failure_reason);
+ if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+ return ret;
+
+ req = nm_device_get_act_request (NM_DEVICE (self));
+ g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+
+ connection = nm_act_request_get_applied_connection (req);
+ g_return_val_if_fail (connection,
NM_ACT_STAGE_RETURN_FAILURE);
+
+ s_wireless = nm_connection_get_setting_wireless
(connection);
+ g_return_val_if_fail (s_wireless,
NM_ACT_STAGE_RETURN_FAILURE);
+
+ ap_path = nm_active_connection_get_specific_object
(NM_ACTIVE_CONNECTION (req));
+ ap = ap_path ? get_ap_by_path (self, ap_path) : NULL;
+ if (!ap) {
+ ap = find_first_compatible_ap (self, connection,
FALSE);
+
+ /* TODO: assuming hidden networks aren't supported
do we need
+ * to consider the case of APs that are not in the
scan list
+ * yet, for which nm-device-wifi.c creates the
temporary fake
+ * AP object?
+ */
+
+ nm_active_connection_set_specific_object
(NM_ACTIVE_CONNECTION (req),
+ nm_exporte
d_object_get_path (NM_EXPORTED_OBJECT (ap)));
+ }
+
+ set_current_ap (self, ap, FALSE);
+ return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *device, NMDeviceStateReason
*out_failure_reason)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (device);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+ NMActRequest *req;
+ NMWifiAP *ap;
+ NMConnection *connection;
+ const char *setting_name;
+ NMSettingWireless *s_wireless;
+ GError *error = NULL;
+ GDBusProxy *network_proxy;
+
+ 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);
+
+ s_wireless = nm_connection_get_setting_wireless
(connection);
+ g_assert (s_wireless);
+
+ /* If we need secrets, get them */
+ setting_name = nm_connection_need_secrets (connection,
NULL);
+ if (setting_name) {
+ _LOGI (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) access point '%s' has
security, but secrets are required.",
+ nm_connection_get_id (connection));
+
+ if (handle_auth_or_fail (self, req, FALSE))
+ ret = NM_ACT_STAGE_RETURN_POSTPONE;
+ else {
+ NM_SET_OUT (out_failure_reason,
NM_DEVICE_STATE_REASON_NO_SECRETS);
+ ret = NM_ACT_STAGE_RETURN_FAILURE;
+ }
+ goto out;
+ }
+
+ /* Have secrets or no secrets required */
+ if (nm_connection_get_setting_wireless_security
(connection)) {
+ _LOGI (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) connection '%s' has
security, and secrets exist. No new secrets needed.",
+ nm_connection_get_id (connection));
+ } else {
+ _LOGI (LOGD_DEVICE | LOGD_WIFI,
+ "Activation: (wifi) connection '%s' requires
no security. No secrets needed.",
+ nm_connection_get_id (connection));
+ }
+
+ /* Locate the IWD Network object */
+ network_proxy = g_dbus_proxy_new_for_bus_sync (IWD_BUS_TYPE,
+ G_DBUS_PROXY_
FLAGS_DO_NOT_LOAD_PROPERTIES |
+ G_DBUS_PROXY_
FLAGS_DO_NOT_CONNECT_SIGNALS,
+ NULL,
+ IWD_SERVICE,
+ nm_wifi_ap_ge
t_supplicant_path (ap),
+ IWD_NETWORK_I
NTERFACE,
+ NULL,
&error);
+ if (!network_proxy) {
+ return FALSE;
+
+ _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);
+ NM_SET_OUT (out_failure_reason,
NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+ goto out;
+ }
+
+ 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, -1,
+ priv->cancellable, network_connect_cb,
self);
+
+ g_object_unref (network_proxy);
+
+ /* We'll get stage3 started when the supplicant connects */
+ ret = NM_ACT_STAGE_RETURN_POSTPONE;
+
+out:
+ if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+ cleanup_association_attempt (self, FALSE);
+
+ return ret;
+}
+
+static guint32
+get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
+{
+ NMSettingWireless *setting;
+ gint64 mtu_default;
+ guint32 mtu;
+
+ nm_assert (NM_IS_DEVICE (device));
+ nm_assert (out_is_user_config);
+
+ setting = NM_SETTING_WIRELESS (nm_device_get_applied_setting
(device, NM_TYPE_SETTING_WIRELESS));
+ if (!setting)
+ g_return_val_if_reached (0);
+
+ mtu = nm_setting_wireless_get_mtu (setting);
+ if (mtu == 0) {
+ mtu_default =
nm_device_get_configured_mtu_from_connection_default (device,
"wifi.mtu");
+ if (mtu_default >= 0) {
+ *out_is_user_config = TRUE;
+ return (guint32) mtu_default;
+ }
+ }
+ *out_is_user_config = (mtu != 0);
+ return mtu;
+}
+
+static void
+activation_success_handler (NMDevice *device)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (device);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMActRequest *req;
+ NMConnection *applied_connection;
+
+ req = nm_device_get_act_request (device);
+ g_assert (req);
+
+ applied_connection = nm_act_request_get_applied_connection
(req);
+
+ /* Clear wireless secrets tries on success */
+ g_object_set_qdata (G_OBJECT (applied_connection),
wireless_secrets_tries_quark (), NULL);
+
+ /* There should always be a current AP */
+ g_warn_if_fail (priv->current_ap);
+}
+
+static void
+activation_failure_handler (NMDevice *device)
+{
+ NMConnection *applied_connection;
+
+ applied_connection = nm_device_get_applied_connection
(device);
+ g_assert (applied_connection);
+
+ /* Clear wireless secrets tries on failure */
+ g_object_set_qdata (G_OBJECT (applied_connection),
wireless_secrets_tries_quark (), NULL);
+}
+
+static void
+device_state_changed (NMDevice *device,
+ NMDeviceState new_state,
+ NMDeviceState old_state,
+ NMDeviceStateReason reason)
+{
+ 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);
+ else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE)
+ update_aps (self);
+
+ switch (new_state) {
+ case NM_DEVICE_STATE_UNMANAGED:
+ break;
+ case NM_DEVICE_STATE_UNAVAILABLE:
+ /*
+ * If the device is enabled and the IWD manager is
ready,
+ * transition to DISCONNECTED because the device is
now
+ * ready to use.
+ */
+ if (priv->enabled && priv->dbus_obj) {
+ nm_device_queue_recheck_available (device,
+ NM_DEVICE
_STATE_REASON_SUPPLICANT_AVAILABLE,
+ NM_DEVICE
_STATE_REASON_SUPPLICANT_FAILED);
+ }
+ break;
+ case NM_DEVICE_STATE_NEED_AUTH:
+ send_disconnect (self);
+ break;
+ case NM_DEVICE_STATE_IP_CHECK:
+ break;
+ case NM_DEVICE_STATE_ACTIVATED:
+ activation_success_handler (device);
+ break;
+ case NM_DEVICE_STATE_FAILED:
+ activation_failure_handler (device);
+ break;
+ case NM_DEVICE_STATE_DISCONNECTED:
+ break;
+ default:
+ break;
+ }
+}
+
+static gboolean
+get_enabled (NMDevice *device)
+{
+ return NM_DEVICE_IWD_GET_PRIVATE ((NMDeviceIwd *) device)-
enabled;
+}
+
+static void
+set_enabled (NMDevice *device, gboolean enabled)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (device);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDeviceState state;
+
+ enabled = !!enabled;
+
+ if (priv->enabled == enabled)
+ return;
+
+ priv->enabled = enabled;
+
+ _LOGD (LOGD_WIFI, "device now %s", enabled ? "enabled" :
"disabled");
+
+ state = nm_device_get_state (NM_DEVICE (self));
+ if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+ _LOGD (LOGD_WIFI, "(%s): device blocked by UNMANAGED
state",
+ enabled ? "enable" : "disable");
+ return;
+ }
+
+ if (enabled) {
+ if (state != NM_DEVICE_STATE_UNAVAILABLE)
+ _LOGW (LOGD_CORE, "not in expected
unavailable state!");
+
+ if (priv->dbus_obj)
+ nm_device_queue_recheck_available (NM_DEVICE
(self),
+ NM_DEVICE
_STATE_REASON_SUPPLICANT_AVAILABLE,
+ NM_DEVICE
_STATE_REASON_SUPPLICANT_FAILED);
+ } else {
+ nm_device_state_changed (NM_DEVICE (self),
+ NM_DEVICE_STATE_UNAVAILABLE
,
+ NM_DEVICE_STATE_REASON_NONE
);
+ }
+}
+
+static gboolean
+can_reapply_change (NMDevice *device,
+ const char *setting_name,
+ NMSetting *s_old,
+ NMSetting *s_new,
+ GHashTable *diffs,
+ GError **error)
+{
+ NMDeviceClass *device_class;
+
+ /* Only handle wireless setting here, delegate other
settings to parent class */
+ if (nm_streq (setting_name,
NM_SETTING_WIRELESS_SETTING_NAME)) {
+ return nm_device_hash_check_invalid_keys (diffs,
+ NM_SETTING
_WIRELESS_SETTING_NAME,
+ error,
+ NM_SETTING
_WIRELESS_MTU); /* reapplied with IP config */
+ }
+
+ device_class = NM_DEVICE_CLASS (nm_device_iwd_parent_class);
+ return device_class->can_reapply_change (device,
+ setting_name,
+ s_old,
+ s_new,
+ diffs,
+ error);
+}
+
+/*******************************************************************
**********/
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (object);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ gsize i;
+ char **list;
+
+ switch (prop_id) {
+ case PROP_MODE:
+ if (priv->current_ap)
+ g_value_set_uint (value,
NM_802_11_MODE_INFRA);
+ else
+ g_value_set_uint (value,
NM_802_11_MODE_UNKNOWN);
+ break;
+ case PROP_BITRATE:
+ g_value_set_uint (value, 65000);
+ break;
+ case PROP_CAPABILITIES:
+ g_value_set_uint (value, priv->capabilities);
+ break;
+ case PROP_ACCESS_POINTS:
+ list = (char **) ap_list_get_sorted_paths (self,
TRUE);
+ for (i = 0; list[i]; i++)
+ list[i] = g_strdup (list[i]);
+ g_value_take_boxed (value, list);
+ break;
+ case PROP_ACTIVE_ACCESS_POINT:
+ nm_utils_g_value_set_object_path (value, priv-
current_ap);
+ break;
+ case PROP_SCANNING:
+ g_value_set_boolean (value, priv->scanning);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id,
pspec);
+ break;
+ }
+}
+
+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
+state_changed (NMDeviceIwd *self, const gchar *new_state)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+
+ _LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is
%s", new_state);
+
+ /* Don't allow scanning while connecting, disconnecting or
roaming */
+ priv->can_scan = NM_IN_STRSET (new_state, "connected",
"disconnected");
+
+ if (NM_IN_STRSET (new_state, "connecting", "connected",
"roaming")) {
+ /* If we're activating, do nothing, the confirmation
of
+ * a connection success is handled in the
Device.Connect
+ * method return callback. Otherwise IWD must have
connected
+ * without Network Manager's will so for simplicity
force a
+ * disconnect.
+ */
+ if ( nm_device_is_activating (device)
+ || nm_device_get_state (device) ==
NM_DEVICE_STATE_ACTIVATED)
+ return;
+
+ _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 ( !nm_device_is_activating (device)
+ && nm_device_get_state (device) !=
NM_DEVICE_STATE_ACTIVATED)
+ 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);
+
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_SUPP
LICANT_DISCONNECT);
+
+ return;
+ }
+
+ _LOGE (LOGD_WIFI, "State %s unknown", new_state);
+}
+
+static void
+scanning_changed (NMDeviceIwd *self, gboolean new_scanning)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ if (new_scanning == priv->scanning)
+ return;
+
+ priv->scanning = new_scanning;
+
+ _notify (self, PROP_SCANNING);
+
+ if (!priv->scanning)
+ update_aps (self);
+}
+
+static void
+properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
+ GStrv invalidate_properties, gpointer user_data)
+{
+ NMDeviceIwd *self = user_data;
+ GVariantIter *iter;
+ const gchar *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));
+
+ g_variant_unref (value);
+ }
+
+ g_variant_iter_free (iter);
+}
+
+/*******************************************************************
**********/
+
+static void
+nm_device_iwd_init (NMDeviceIwd *self)
+{
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ priv->aps = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* Make sure the manager is running */
+ (void) nm_iwd_manager_get ();
+}
+
+NMDevice *
+nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities
capabilities)
+{
+ return g_object_new (NM_TYPE_DEVICE_IWD,
+ NM_DEVICE_IFACE, iface,
+ NM_DEVICE_TYPE_DESC, "802.11 WiFi",
+ 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);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (object);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ nm_clear_g_cancellable (&priv->cancellable);
+
+ wifi_secrets_cancel (self);
+
+ cleanup_association_attempt (self, TRUE);
+
+ g_clear_object (&priv->dbus_proxy);
+ g_clear_object (&priv->dbus_obj);
+
+ remove_all_aps (self);
+
+ G_OBJECT_CLASS (nm_device_iwd_parent_class)->dispose
(object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceIwd *self = NM_DEVICE_IWD (object);
+ NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+ nm_assert (g_hash_table_size (priv->aps) == 0);
+
+ g_hash_table_unref (priv->aps);
+
+ G_OBJECT_CLASS (nm_device_iwd_parent_class)->finalize
(object);
+}
+
+static void
+nm_device_iwd_class_init (NMDeviceIwdClass *klass)
+{
+ GObjectClass *object_class = G_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)
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ parent_class->can_auto_connect = can_auto_connect;
+ 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->deactivate = deactivate;
+ parent_class->can_reapply_change = can_reapply_change;
+
+ parent_class->state_changed = device_state_changed;
+
+ klass->scanning_prohibited = scanning_prohibited;
+
+ obj_properties[PROP_MODE] =
+ g_param_spec_uint (NM_DEVICE_IWD_MODE, "", "",
+ NM_802_11_MODE_UNKNOWN,
+ NM_802_11_MODE_AP,
+ NM_802_11_MODE_INFRA,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);