[RFC] IWD as wifi backend
- From: Andrew Zaborowski <andrew zaborowski intel com>
- To: networkmanager-list gnome org
- Subject: [RFC] IWD as wifi backend
- Date: Tue, 28 Nov 2017 23:09:40 +0100
Hi,
I'd like to gather comments on integrating NetworkManager with IWD for
Wifi support. IWD is a light-weight daemon that manages Wifi devices
and provides a DBus interface:
https://git.kernel.org/pub/scm/network/wireless/iwd.git/
It covers some wpa_supplicant functionality and some Network Manager
functionality (and adds some). In the patch at the bottom I made a quick
attempt at using IWD to replace NetworkManager's supplicant part and the
platform code in src/platform/wifi/ so that NetworkManager doesn't need
to know about nl80211 for example.
In the patch nm-device-iwd.c duplicates some code in nm-device-wifi.c so
optimally I guess the shared code should in a generic NMDeviceWifi class
and the two backends should be subclasses. BTW the patch doesn't
support PSK or EAP networks or AP mode, only basic unsecured networks
for now. The IWD interface doesn't provide actual BSSIDs and other
details that wpa_supplicant does, but it provides what nm-applet cares
about (for that matter it doesn't provide individial BSS / AP
information but compiles that into networks but that's again what the UI
cares about.)
The trickier part is leveraging IWD functionality that is already in
NetworkManager core, specifically Access Point ranking/selection, storage
of network settings (secrets and whether a network is known/unknown) and
autoconnect. Those parts are not provided by wpa_supplicant so there is
dissymmetry between the two potential backends. Our goal is to allow
IWD to be used with NetworkManager and its desktop GUIs together with
network devices other than wifi, but we'd also like to be testing the
IWD functionality already provided by NM. Our opinion is, for example,
that when Wifi is the preferred access technology on the machine (i.e.
ethernet is not plugged in), IWD can easily be given the task of
auto-selecting the wifi network as it can make more informed decisions.
This would require that IWD's known networks list be synchronised with
NM's known networks list or NM to pull the information from IWD instead
of storing it in /etc. To add more complication, our DBus interface
does not handle EAP configuration because EAP networks are configured
through admin-provisioned files, which is the normal scenario today, but
since NM and nm-applet still have complete GUIs for EAP authentication
methods, those differences between IWD with wpa_supplicant would bleed
into the NM's DBus interface and the clients, and would upset potential
users who do configure their own EAP secrets.
Note that IWD is still rather new and we're open to changes needed on
the IWD side. What are your opinions on the extent that IWD can be used
by NetworkManager? What are your opinions on integrating just a basic
version where IWD can be selected instead of wpa_supplicant? PSK and AP
modes can be supported easily and for EAP/8021x the backend can at least
support networks that it has checked (by SSID match only) that are also
provisioned with an IWD config file.
Best regards
---
Makefile.am | 13 +-
configure.ac | 20 +
src/devices/wifi/nm-device-iwd.c | 1924 +++++++++++++++++++++++++++++++++++++
src/devices/wifi/nm-device-iwd.h | 58 ++
src/devices/wifi/nm-iwd-manager.c | 343 +++++++
src/devices/wifi/nm-iwd-manager.h | 53 +
6 files changed, 2409 insertions(+), 2 deletions(-)
create mode 100644 src/devices/wifi/nm-device-iwd.c
create mode 100644 src/devices/wifi/nm-device-iwd.h
create mode 100644 src/devices/wifi/nm-iwd-manager.c
create mode 100644 src/devices/wifi/nm-iwd-manager.h
diff --git a/Makefile.am b/Makefile.am
index 7bce597e9..dbe5a4e14 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2632,8 +2632,6 @@ core_plugins += src/devices/wifi/libnm-device-plugin-wifi.la
src_devices_wifi_libnm_device_plugin_wifi_la_SOURCES = \
src/devices/wifi/nm-wifi-factory.c \
- src/devices/wifi/nm-device-wifi.c \
- src/devices/wifi/nm-device-wifi.h \
src/devices/wifi/nm-wifi-ap.c \
src/devices/wifi/nm-wifi-ap.h \
src/devices/wifi/nm-wifi-utils.c \
@@ -2641,6 +2639,17 @@ src_devices_wifi_libnm_device_plugin_wifi_la_SOURCES = \
src/devices/wifi/nm-device-olpc-mesh.c \
src/devices/wifi/nm-device-olpc-mesh.h
+if WITH_IWD
+src_devices_wifi_libnm_device_plugin_wifi_la_SOURCES += \
+ src/devices/wifi/nm-device-iwd.c \
+ src/devices/wifi/nm-device-iwd.h \
+ src/devices/wifi/nm-iwd-manager.c \
+ src/devices/wifi/nm-iwd-manager.h
+else
+ src/devices/wifi/nm-device-wifi.c \
+ src/devices/wifi/nm-device-wifi.h \
+endif
+
src_devices_wifi_libnm_device_plugin_wifi_la_CPPFLAGS = \
-I$(srcdir)/src \
-I$(builddir)/src \
diff --git a/configure.ac b/configure.ac
index 79791ab76..a6af48366 100644
--- a/configure.ac
+++ b/configure.ac
@@ -264,6 +264,26 @@ else
fi
dnl
+dnl Default to using wpa_supplicant but allow IWD as wifi backend
+dnl
+AC_ARG_WITH(iwd,
+ AS_HELP_STRING([--with-iwd=yes],
+ [Use IWD instead of wpa_supplicant as wifi backend]),
+ ac_with_iwd=$withval, ac_with_iwd="no")
+if test "$ac_with_iwd" != 'no'; then
+ ac_with_iwd='yes'
+fi
+if test x"$ac_with_iwd" = x"yes"; then
+ if test "$enable_wifi" != "yes"; then
+ AC_MSG_ERROR(Enabling IWD support and disabling Wi-Fi makes no sense)
+ fi
+ AC_DEFINE(WITH_IWD, 1, [Define to compile with the IWD backend])
+else
+ AC_DEFINE(WITH_IWD, 0, [Define to compile with the wpa_supplicant backend])
+fi
+AM_CONDITIONAL(WITH_IWD, test x"${ac_with_iwd}" = x"yes")
+
+dnl
dnl Check for newer VLAN flags
dnl
AC_MSG_CHECKING([Linux kernel VLAN_FLAG_LOOSE_BINDING enum value])
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
new file mode 100644
index 000000000..5f05e642c
--- /dev/null
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -0,0 +1,1924 @@
+/* -*- 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2017 Intel Corporation
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-iwd.h"
+
+#include <string.h>
+
+#include "nm-common-macros.h"
+#include "devices/nm-device.h"
+#include "devices/nm-device-private.h"
+#include "nm-utils.h"
+#include "nm-act-request.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "settings/nm-settings-connection.h"
+#include "settings/nm-settings.h"
+#include "nm-wifi-utils.h"
+#include "nm-core-internal.h"
+#include "nm-config.h"
+#include "nm-iwd-manager.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.Wireless.h"
+
+#include "devices/nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceWifi);
+
+static NM_CACHED_QUARK_FCN ("wireless-secrets-tries", wireless_secrets_tries_quark)
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi,
+ PROP_MODE,
+ PROP_BITRATE,
+ PROP_ACCESS_POINTS,
+ PROP_ACTIVE_ACCESS_POINT,
+ PROP_CAPABILITIES,
+ PROP_SCANNING,
+);
+
+enum {
+ ACCESS_POINT_ADDED,
+ ACCESS_POINT_REMOVED,
+ SCANNING_PROHIBITED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+ GDBusObject * dbus_obj;
+ GDBusProxy * dbus_proxy;
+ GHashTable * aps;
+ GHashTable * new_aps;
+ NMWifiAP * current_ap;
+ GCancellable * cancellable;
+ NMDeviceWifiCapabilities capabilities;
+ NMActRequestGetSecretsCallId wifi_secrets_id;
+ gboolean enabled:1;
+ gboolean can_scan:1;
+ gboolean scanning:1;
+} NMDeviceWifiPrivate;
+
+struct _NMDeviceWifi
+{
+ NMDevice parent;
+ NMDeviceWifiPrivate _priv;
+};
+
+struct _NMDeviceWifiClass
+{
+ NMDeviceClass parent;
+
+ /* Signals */
+ gboolean (*scanning_prohibited) (NMDeviceWifi *device, gboolean periodic);
+};
+
+/*****************************************************************************/
+
+G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_WIFI_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceWifi, NM_IS_DEVICE_WIFI)
+
+/*****************************************************************************/
+
+static void
+_ap_dump (NMDeviceWifi *self,
+ NMLogLevel log_level,
+ const NMWifiAP *ap,
+ const char *prefix,
+ gint32 now_s)
+{
+ char buf[1024];
+
+ buf[0] = '\0';
+ _NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-ap: %-7s %s",
+ prefix,
+ nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s));
+}
+
+/* Callers ensure we're not removing current_ap */
+static void
+ap_add_remove (NMDeviceWifi *self,
+ guint signum,
+ NMWifiAP *ap,
+ gboolean recheck_available_connections)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ nm_assert (NM_IN_SET (signum, ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED));
+
+ if (signum == ACCESS_POINT_ADDED) {
+ g_hash_table_insert (priv->aps,
+ (gpointer) nm_exported_object_export ((NMExportedObject *) ap),
+ g_object_ref (ap));
+ _ap_dump (self, LOGL_DEBUG, ap, "added", 0);
+ } else
+ _ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
+
+ g_signal_emit (self, signals[signum], 0, ap);
+
+ if (signum == ACCESS_POINT_REMOVED) {
+ g_hash_table_remove (priv->aps, nm_exported_object_get_path ((NMExportedObject *) ap));
+ nm_exported_object_unexport ((NMExportedObject *) ap);
+ g_object_unref (ap);
+ }
+
+ _notify (self, PROP_ACCESS_POINTS);
+
+ nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
+ if (recheck_available_connections)
+ nm_device_recheck_available_connections (NM_DEVICE (self));
+}
+
+static void
+set_current_ap (NMDeviceWifi *self, NMWifiAP *new_ap, gboolean recheck_available_connections)
+{
+ NMDeviceWifiPrivate *priv;
+ NMWifiAP *old_ap;
+
+ g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+ priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ old_ap = priv->current_ap;
+
+ if (old_ap == new_ap)
+ return;
+
+ if (new_ap)
+ priv->current_ap = g_object_ref (new_ap);
+ else
+ priv->current_ap = NULL;
+
+ if (old_ap) {
+ if (nm_wifi_ap_get_fake (old_ap))
+ ap_add_remove (self, ACCESS_POINT_REMOVED, old_ap, recheck_available_connections);
+ g_object_unref (old_ap);
+ }
+
+ _notify (self, PROP_ACTIVE_ACCESS_POINT);
+ _notify (self, PROP_MODE);
+}
+
+static gboolean
+update_ap_func (gpointer key, gpointer value, gpointer user_data)
+{
+ NMWifiAP *ap = value;
+ NMDeviceWifi *self = user_data;
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ NMWifiAP *new_ap = NULL;
+
+ if (priv->new_aps)
+ new_ap = g_hash_table_lookup (priv->new_aps,
+ nm_wifi_ap_get_supplicant_path (ap));
+
+ if (new_ap) {
+ g_hash_table_steal (priv->new_aps,
+ nm_wifi_ap_get_supplicant_path (ap));
+
+ if (nm_wifi_ap_set_strength (ap, nm_wifi_ap_get_strength (new_ap)))
+ _ap_dump (self, LOGL_TRACE, ap, "updated", 0);
+
+ g_object_unref (new_ap);
+ return FALSE;
+ }
+
+ if (ap == priv->current_ap)
+ /* Normally IWD will prevent the current AP from being
+ * removed from the list and set a low signal strength,
+ * but just making sure.
+ */
+ return FALSE;
+
+ _ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
+
+ g_signal_emit (self, signals[ACCESS_POINT_REMOVED], 0, ap);
+
+ nm_exported_object_unexport ((NMExportedObject *) ap);
+ g_object_unref (ap);
+
+ return TRUE;
+}
+
+static void
+remove_all_aps (NMDeviceWifi *self)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ if (!g_hash_table_size (priv->aps))
+ return;
+
+ set_current_ap (self, NULL, FALSE);
+
+ g_hash_table_foreach_remove (priv->aps, update_ap_func, self);
+
+ _notify (self, PROP_ACCESS_POINTS);
+ nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
+ nm_device_recheck_available_connections (NM_DEVICE (self));
+}
+
+static GVariant *
+vardict_from_network_type (const gchar *type)
+{
+ GVariantBuilder builder;
+ const gchar *key_mgmt = "";
+ const gchar *pairwise = "ccmp";
+
+ if (!strcmp (type, "psk"))
+ key_mgmt = "wpa-psk";
+ else if (!strcmp (type, "8021x"))
+ key_mgmt = "wpa-eap";
+ else
+ return NULL;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&builder, "{sv}", "KeyMgmt",
+ g_variant_new_strv (&key_mgmt, 1));
+ g_variant_builder_add (&builder, "{sv}", "Pairwise",
+ g_variant_new_strv (&pairwise, 1));
+ g_variant_builder_add (&builder, "{sv}", "Group",
+ g_variant_new_string ("ccmp"));
+ return g_variant_new ("a{sv}", &builder);
+}
+
+static void
+get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ NMDeviceWifi *self = user_data;
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ gs_free_error GError *error = NULL;
+ gs_unref_variant GVariant *variant = NULL;
+ GVariantIter *networks;
+ const gchar *path, *name, *type;
+ int16_t signal;
+ NMWifiAP *ap;
+ gboolean changed = FALSE;
+ GHashTableIter ap_iter;
+
+ variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
+ G_VARIANT_TYPE ("(a(osns))"),
+ &error);
+ if (!variant) {
+ _LOGE (LOGD_WIFI, "Device.GetOrderedNetworks failed: %s",
+ error->message);
+ return;
+ }
+
+ priv->new_aps = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_variant_get (variant, "(a(osns))", &networks);
+
+ while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) {
+ GVariantBuilder builder;
+ gs_unref_variant GVariant *props = NULL;
+ GVariant *rsn;
+ static uint32_t ap_id = 0;
+ uint8_t bssid[6];
+
+ /*
+ * What we get from IWD are networks, or ESSs, that may
+ * contain multiple APs, or BSSs, each. We don't get
+ * information about any specific BSSs within an ESS but
+ * we can safely present each ESS as an individual BSS to
+ * NM, which will be seen as ESSs comprising a single BSS
+ * each. NM won't be able to handle roaming but IWD already
+ * does that. We fake the BSSIDs as they don't play any
+ * role either.
+ */
+ bssid[0] = 0x00;
+ bssid[1] = 0x01;
+ bssid[2] = 0x02;
+ bssid[3] = ap_id >> 16;
+ bssid[4] = ap_id >> 8;
+ bssid[5] = ap_id++;
+
+ /* WEP not supported */
+ if (!strcmp (type, "wep"))
+ continue;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&builder, "{sv}", "BSSID",
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1));
+ g_variant_builder_add (&builder, "{sv}", "Mode",
+ g_variant_new_string ("infrastructure"));
+
+ rsn = vardict_from_network_type (type);
+ if (rsn)
+ g_variant_builder_add (&builder, "{sv}", "RSN", rsn);
+
+ props = g_variant_new ("a{sv}", &builder);
+
+ ap = nm_wifi_ap_new_from_properties (path, props);
+ nm_wifi_ap_set_ssid (ap, (const guint8 *) name, strlen (name));
+ nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (signal / 100));
+ nm_wifi_ap_set_freq (ap, 2417);
+ nm_wifi_ap_set_max_bitrate (ap, 65000);
+ g_hash_table_insert (priv->new_aps,
+ (gpointer) nm_wifi_ap_get_supplicant_path (ap),
+ ap);
+ }
+
+ g_variant_iter_free (networks);
+
+ if (g_hash_table_foreach_remove (priv->aps, update_ap_func, self))
+ changed = TRUE;
+
+ g_hash_table_iter_init (&ap_iter, priv->new_aps);
+ while (g_hash_table_iter_next (&ap_iter, NULL, (gpointer) &ap)) {
+ ap_add_remove (self, ACCESS_POINT_ADDED, ap, FALSE);
+ changed = TRUE;
+ }
+
+ g_hash_table_destroy (priv->new_aps);
+ priv->new_aps = NULL;
+
+ if (changed) {
+ _notify (self, PROP_ACCESS_POINTS);
+ nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
+ nm_device_recheck_available_connections (NM_DEVICE (self));
+ }
+}
+
+static void
+update_aps (NMDeviceWifi *self)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ if (!priv->cancellable)
+ priv->cancellable = g_cancellable_new ();
+
+ g_dbus_proxy_call (priv->dbus_proxy, "GetOrderedNetworks",
+ g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE,
+ 2000, priv->cancellable,
+ get_ordered_networks_cb, self);
+}
+
+static void
+send_disconnect (NMDeviceWifi *self)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ set_current_ap (self, NULL, TRUE);
+
+ if (disconnect && priv->dbus_obj)
+ send_disconnect (self);
+}
+
+static void
+deactivate (NMDevice *device)
+{
+ cleanup_association_attempt (NM_DEVICE_WIFI (device), TRUE);
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+ NMSettingConnection *s_con;
+ NMSettingWireless *s_wireless;
+ const char *mac;
+ const char * const *mac_blacklist;
+ int i;
+ const char *mode;
+ const char *perm_hw_addr;
+
+ if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection))
+ return FALSE;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME))
+ return FALSE;
+
+ s_wireless = nm_connection_get_setting_wireless (connection);
+ if (!s_wireless)
+ return FALSE;
+
+ perm_hw_addr = nm_device_get_permanent_hw_address (device);
+ mac = nm_setting_wireless_get_mac_address (s_wireless);
+ if (perm_hw_addr) {
+ if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
+ return FALSE;
+
+ /* Check for MAC address blacklist */
+ mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
+ for (i = 0; mac_blacklist[i]; i++) {
+ if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
+ g_warn_if_reached ();
+ return FALSE;
+ }
+
+ if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
+ return FALSE;
+ }
+ } else if (mac)
+ return FALSE;
+
+ mode = nm_setting_wireless_get_mode (s_wireless);
+ if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static NMWifiAP *
+get_ap_by_path (NMDeviceWifi *self, const char *path)
+{
+ g_return_val_if_fail (path != NULL, NULL);
+ return g_hash_table_lookup (NM_DEVICE_WIFI_GET_PRIVATE (self)->aps, path);
+
+}
+
+static NMWifiAP *
+find_first_compatible_ap (NMDeviceWifi *self,
+ NMConnection *connection,
+ gboolean allow_unstable_order)
+{
+ GHashTableIter iter;
+ NMWifiAP *ap;
+ NMWifiAP *cand_ap = NULL;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+
+ g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+ if (!nm_wifi_ap_check_compatible (ap, connection))
+ continue;
+ if (allow_unstable_order)
+ return ap;
+ if (!cand_ap || (nm_wifi_ap_get_id (cand_ap) < nm_wifi_ap_get_id (ap)))
+ cand_ap = ap;
+ }
+ return cand_ap;
+}
+
+static gboolean
+check_connection_available (NMDevice *device,
+ NMConnection *connection,
+ NMDeviceCheckConAvailableFlags flags,
+ const char *specific_object)
+{
+ NMSettingWireless *s_wifi;
+ const char *mode;
+
+ s_wifi = nm_connection_get_setting_wireless (connection);
+ g_return_val_if_fail (s_wifi, 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 = get_ap_by_path (NM_DEVICE_WIFI (device), specific_object);
+ return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE;
+ }
+
+ /* Only Infrastrusture mode at this time */
+ mode = nm_setting_wireless_get_mode (s_wifi);
+ if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+ return FALSE;
+
+ /* Hidden SSIDs not supported yet */
+ if (nm_setting_wireless_get_hidden (s_wifi))
+ return FALSE;
+
+ if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
+ return TRUE;
+
+ /* Check at least one AP is compatible with this connection */
+ return !!find_first_compatible_ap (NM_DEVICE_WIFI (device), connection, TRUE);
+}
+
+static gboolean
+is_manf_default_ssid (const GByteArray *ssid)
+{
+ int i;
+ /*
+ * List of manufacturer default SSIDs that are often unchanged by users.
+ *
+ * NOTE: this list should *not* contain networks that you would like to
+ * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot".
+ */
+ static const char *manf_defaults[] = {
+ "linksys",
+ "linksys-a",
+ "linksys-g",
+ "default",
+ "belkin54g",
+ "NETGEAR",
+ "o2DSL",
+ "WLAN",
+ "ALICE-WLAN",
+ "Speedport W 501V",
+ "TURBONETT",
+ };
+
+ for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) {
+ if (ssid->len == strlen (manf_defaults[i])) {
+ if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+ NMConnection *connection,
+ const char *specific_object,
+ const GSList *existing_connections,
+ GError **error)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMSettingWireless *s_wifi;
+ const char *setting_mac;
+ char *str_ssid = NULL;
+ NMWifiAP *ap;
+ const GByteArray *ssid = NULL;
+ GByteArray *tmp_ssid = NULL;
+ GBytes *setting_ssid = NULL;
+ const char *perm_hw_addr;
+ const char *mode;
+
+ s_wifi = nm_connection_get_setting_wireless (connection);
+
+ mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL;
+
+ if (s_wifi && !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 not given a specific object, we need at minimum an SSID */
+ if (!s_wifi) {
+ g_set_error_literal (error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "A 'wireless' setting is required if no AP path was given.");
+ return FALSE;
+ }
+
+ setting_ssid = nm_setting_wireless_get_ssid (s_wifi);
+ if (!setting_ssid || g_bytes_get_size (setting_ssid) == 0) {
+ g_set_error_literal (error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "A 'wireless' setting with a valid SSID is required if no AP
path was given.");
+ return FALSE;
+ }
+
+ /* Find a compatible AP in the scan list */
+ ap = find_first_compatible_ap (self, connection, FALSE);
+ 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;
+ }
+ } else {
+ ap = get_ap_by_path (self, specific_object);
+ if (!ap) {
+ g_set_error (error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND,
+ "The access point %s was not in the scan list.",
+ specific_object);
+ return FALSE;
+ }
+ }
+
+ /* Add a wifi setting if one doesn't exist yet */
+ if (!s_wifi) {
+ s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+ nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+ }
+
+ ssid = nm_wifi_ap_get_ssid (ap);
+
+ if (ssid == NULL) {
+ g_set_error_literal (error,
+ NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "A 'wireless' setting with a valid SSID is required.");
+ return FALSE;
+ }
+
+ if (!nm_wifi_ap_complete_connection (ap,
+ connection,
+ is_manf_default_ssid (ssid),
+ error)) {
+ if (tmp_ssid)
+ g_byte_array_unref (tmp_ssid);
+ return FALSE;
+ }
+
+ str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
+
+ nm_utils_complete_generic (nm_device_get_platform (device),
+ connection,
+ NM_SETTING_WIRELESS_SETTING_NAME,
+ existing_connections,
+ str_ssid,
+ str_ssid,
+ NULL,
+ TRUE);
+ g_free (str_ssid);
+ if (tmp_ssid)
+ g_byte_array_unref (tmp_ssid);
+
+ 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;
+ }
+ } else {
+ guint8 tmp[ETH_ALEN];
+
+ /* Lock the connection to this device by default if it uses a
+ * permanent MAC address (ie not a 'locally administered' one)
+ */
+ nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN);
+ if (!(tmp[0] & 0x02)) {
+ g_object_set (G_OBJECT (s_wifi),
+ NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr,
+ NULL);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ return priv->enabled && priv->dbus_obj;
+}
+
+static gboolean
+can_auto_connect (NMDevice *device,
+ NMConnection *connection,
+ char **specific_object)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMSettingWireless *s_wifi;
+ NMWifiAP *ap;
+ const char *mode;
+ guint64 timestamp = 0;
+
+ nm_assert (!specific_object || !*specific_object);
+
+ if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (device, connection, NULL))
+ return FALSE;
+
+ s_wifi = nm_connection_get_setting_wireless (connection);
+ g_return_val_if_fail (s_wifi, FALSE);
+
+ /* Only Infrastrusture mode */
+ mode = nm_setting_wireless_get_mode (s_wifi);
+ if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+ return FALSE;
+
+ /* Don't autoconnect to networks that have been tried at least once
+ * but haven't been successful, since these are often accidental choices
+ * from the menu and the user may not know the password.
+ */
+ if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), ×tamp)) {
+ if (timestamp == 0)
+ return FALSE;
+ }
+
+ ap = find_first_compatible_ap (self, connection, FALSE);
+ if (ap) {
+ /* All good; connection is usable */
+ NM_SET_OUT (specific_object, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT
(ap))));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+ap_id_compare (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+ guint64 a_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_a));
+ guint64 b_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_b));
+
+ return a_id < b_id ? -1 : (a_id == b_id ? 0 : 1);
+}
+
+static NMWifiAP **
+ap_list_get_sorted (NMDeviceWifi *self, gboolean include_without_ssid)
+{
+ NMDeviceWifiPrivate *priv;
+ NMWifiAP **list;
+ GHashTableIter iter;
+ NMWifiAP *ap;
+ gsize i, n;
+
+ priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ n = g_hash_table_size (priv->aps);
+ list = g_new (NMWifiAP *, n + 1);
+
+ i = 0;
+ if (n > 0) {
+ g_hash_table_iter_init (&iter, priv->aps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+ nm_assert (i < n);
+ if ( include_without_ssid
+ || nm_wifi_ap_get_ssid (ap))
+ list[i++] = ap;
+ }
+ nm_assert (i <= n);
+ nm_assert (!include_without_ssid || i == n);
+
+ g_qsort_with_data (list,
+ i,
+ sizeof (gpointer),
+ ap_id_compare,
+ NULL);
+ }
+ list[i] = NULL;
+ return list;
+}
+
+static const char **
+ap_list_get_sorted_paths (NMDeviceWifi *self, gboolean include_without_ssid)
+{
+ 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_wifi_get_access_points (NMDeviceWifi *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_wifi_get_all_access_points (NMDeviceWifi *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 (NMDeviceWifi *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)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMDeviceWifiPrivate *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_DEVICE_ERROR,
+ NM_DEVICE_ERROR_NOT_ALLOWED,
+ "Scanning not allowed at this time");
+ return;
+ }
+
+ priv = NM_DEVICE_WIFI_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_DEVICE_ERROR,
+ NM_DEVICE_ERROR_NOT_ALLOWED,
+ "Scanning 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_literal (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_wifi_request_scan (NMDeviceWifi *self,
+ GDBusMethodInvocation *context,
+ GVariant *options)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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_DEVICE_ERROR,
+ NM_DEVICE_ERROR_NOT_ALLOWED,
+ "Scanning 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 (NMDeviceWifi *self, gboolean periodic)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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;
+ NMDeviceWifi *self = user_data;
+ NMDeviceWifiPrivate *priv;
+
+ g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+ g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+ priv = NM_DEVICE_WIFI_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_SECRETS);
+ } else
+ nm_device_activate_schedule_stage1_device_prepare (device);
+}
+
+static void
+wifi_secrets_cancel (NMDeviceWifi *self)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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 (NMDeviceWifi *self,
+ const char *setting_name,
+ NMSecretAgentGetSecretsFlags flags)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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_secrets_cb,
+ self);
+ g_return_if_fail (priv->wifi_secrets_id);
+}
+
+static gboolean
+need_new_8021x_secrets (NMDeviceWifi *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_WIRELESS_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 (NMDeviceWifi *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 (NMDeviceWifi *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_SUPPLICANT_DISCONNECT);
+ wifi_secrets_get_secrets (self,
+ setting_name,
+ NM_SECRET_AGENT_GET_SECRETS_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)
+{
+ NMDeviceWifi *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_STATE_REASON_NO_SECRETS);
+ }
+ } else if ( !nm_utils_error_is_cancelled (error, TRUE)
+ && nm_device_is_activating (device))
+ goto failed;
+
+ /* Call Disconnect to make sure IWD's autoconnect is disabled */
+ cleanup_association_attempt (self, TRUE);
+
+ 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_FAILED);
+}
+
+static gboolean
+handle_auth_or_fail (NMDeviceWifi *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_WIFI (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)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMActStageReturn ret;
+ NMWifiAP *ap = NULL;
+ NMActRequest *req;
+ NMConnection *connection;
+ NMSettingWireless *s_wireless;
+ const char *ap_path;
+
+ ret = NM_DEVICE_CLASS (nm_device_wifi_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_exported_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)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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_get_supplicant_path (ap),
+ IWD_NETWORK_INTERFACE,
+ 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)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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_WIFI_GET_PRIVATE ((NMDeviceWifi *) device)->enabled;
+}
+
+static void
+set_enabled (NMDevice *device, gboolean enabled)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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_wifi_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)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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)
+{
+ NMDeviceWifi *device = NM_DEVICE_WIFI (object);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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 (NMDeviceWifi *self, const gchar *new_state)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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.
+ */
+ send_disconnect (self);
+
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+
+ return;
+ }
+
+ _LOGE (LOGD_WIFI, "State %s unknown", new_state);
+}
+
+static void
+scanning_changed (NMDeviceWifi *self, gboolean new_scanning)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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)
+{
+ NMDeviceWifi *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);
+}
+
+void
+nm_device_wifi_set_dbus_object (NMDeviceWifi *self, GDBusObject *object)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ GDBusInterface *interface;
+
+ if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object))
+ return;
+
+ if (priv->dbus_proxy) {
+ g_signal_handlers_disconnect_by_func (priv->dbus_proxy,
+ properties_changed, self);
+ g_clear_object (&priv->dbus_proxy);
+ }
+
+ if (priv->enabled)
+ nm_device_queue_recheck_available (NM_DEVICE (self),
+ NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+
+ if (!object) {
+ priv->can_scan = FALSE;
+
+ cleanup_association_attempt (self, FALSE);
+ return;
+ }
+
+ interface = g_dbus_object_get_interface (object, IWD_DEVICE_INTERFACE);
+ priv->dbus_proxy = G_DBUS_PROXY (interface);
+
+ g_signal_connect (priv->dbus_proxy, "g-properties-changed",
+ G_CALLBACK (properties_changed), self);
+
+ /* Call Disconnect to make sure IWD's autoconnect is disabled. We've
+ * most likely just brought the device UP so it would be in
+ * autoconnect by default.
+ */
+ send_disconnect (self);
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_wifi_init (NMDeviceWifi *self)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities)
+{
+ return g_object_new (NM_TYPE_DEVICE_WIFI,
+ 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_WIFI_CAPABILITIES, (guint) capabilities,
+ NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_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_wifi_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ nm_assert (g_hash_table_size (priv->aps) == 0);
+
+ g_hash_table_unref (priv->aps);
+
+ G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
+}
+
+static void
+nm_device_wifi_class_init (NMDeviceWifiClass *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_WIFI_MODE, "", "",
+ NM_802_11_MODE_UNKNOWN,
+ NM_802_11_MODE_AP,
+ NM_802_11_MODE_INFRA,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_BITRATE] =
+ g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, "", "",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_ACCESS_POINTS] =
+ g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, "", "",
+ G_TYPE_STRV,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_ACTIVE_ACCESS_POINT] =
+ g_param_spec_string (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_CAPABILITIES] =
+ g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, "", "",
+ 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ obj_properties[PROP_SCANNING] =
+ g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, "", "",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+ signals[ACCESS_POINT_ADDED] =
+ g_signal_new (NM_DEVICE_WIFI_ACCESS_POINT_ADDED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ NM_TYPE_WIFI_AP);
+
+ signals[ACCESS_POINT_REMOVED] =
+ g_signal_new (NM_DEVICE_WIFI_ACCESS_POINT_REMOVED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ NM_TYPE_WIFI_AP);
+
+ signals[SCANNING_PROHIBITED] =
+ g_signal_new (NM_DEVICE_WIFI_SCANNING_PROHIBITED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited),
+ NULL, NULL, NULL,
+ G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
+
+ nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+ NMDBUS_TYPE_DEVICE_WIFI_SKELETON,
+ "GetAccessPoints", impl_device_wifi_get_access_points,
+ "GetAllAccessPoints", impl_device_wifi_get_all_access_points,
+ "RequestScan", impl_device_wifi_request_scan,
+ NULL);
+}
diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h
new file mode 100644
index 000000000..bf4a70e63
--- /dev/null
+++ b/src/devices/wifi/nm-device-iwd.h
@@ -0,0 +1,58 @@
+/* -*- 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2016 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_IWD_H__
+#define __NETWORKMANAGER_DEVICE_IWD_H__
+
+#include "devices/nm-device.h"
+#include "nm-wifi-ap.h"
+
+#define NM_TYPE_DEVICE_WIFI (nm_device_wifi_get_type ())
+#define NM_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI,
NMDeviceWifi))
+#define NM_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIFI,
NMDeviceWifiClass))
+#define NM_IS_DEVICE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI))
+#define NM_IS_DEVICE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIFI))
+#define NM_DEVICE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIFI,
NMDeviceWifiClass))
+
+#define NM_DEVICE_WIFI_MODE "mode"
+#define NM_DEVICE_WIFI_BITRATE "bitrate"
+#define NM_DEVICE_WIFI_ACCESS_POINTS "access-points"
+#define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point"
+#define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities"
+#define NM_DEVICE_WIFI_SCANNING "scanning"
+
+/* signals */
+#define NM_DEVICE_WIFI_ACCESS_POINT_ADDED "access-point-added"
+#define NM_DEVICE_WIFI_ACCESS_POINT_REMOVED "access-point-removed"
+
+/* internal signals */
+#define NM_DEVICE_WIFI_SCANNING_PROHIBITED "scanning-prohibited"
+
+typedef struct _NMDeviceWifi NMDeviceWifi;
+typedef struct _NMDeviceWifiClass NMDeviceWifiClass;
+
+GType nm_device_wifi_get_type (void);
+
+NMDevice *nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities);
+
+void nm_device_wifi_set_dbus_object (NMDeviceWifi *device, GDBusObject *object);
+
+#endif /* __NETWORKMANAGER_DEVICE_IWD_H__ */
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
new file mode 100644
index 000000000..7e45f4df3
--- /dev/null
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -0,0 +1,343 @@
+/* -*- 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2005 - 2017 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-iwd-manager.h"
+
+#include <string.h>
+#include <net/if.h>
+
+#include "nm-logging.h"
+#include "nm-manager.h"
+#include "nm-device-iwd.h"
+
+/*****************************************************************************/
+
+typedef struct {
+ GCancellable *cancellable;
+ gboolean running;
+ GDBusObjectManager *object_manager;
+} NMIWDManagerPrivate;
+
+struct _NMIWDManager {
+ GObject parent;
+ NMIWDManagerPrivate _priv;
+};
+
+struct _NMIWDManagerClass {
+ GObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMIWDManager, nm_iwd_manager, G_TYPE_OBJECT)
+
+#define NM_IWD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMIWDManager, NM_IS_IWD_MANAGER)
+
+/*****************************************************************************/
+
+#define _NMLOG_PREFIX_NAME "iwd-manager"
+#define _NMLOG_DOMAIN LOGD_WIFI
+
+#define _NMLOG(level, ...) \
+ G_STMT_START { \
+ if (nm_logging_enabled (level, _NMLOG_DOMAIN)) { \
+ char __prefix[32]; \
+ \
+ if (self) \
+ g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"",
(self)); \
+ else \
+ g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
+ _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
+ "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
+ } G_STMT_END
+
+/*****************************************************************************/
+
+static void
+set_device_dbus_object (NMIWDManager *self, GDBusInterface *interface,
+ GDBusObject *object)
+{
+ NMIWDManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+ GDBusProxy *proxy;
+ GVariant *value;
+ const char *ifname;
+ gint ifindex;
+ NMDevice *device;
+ NMManager *manager;
+
+ if (!priv->running)
+ return;
+
+ g_return_if_fail (G_IS_DBUS_PROXY (interface));
+
+ proxy = G_DBUS_PROXY (interface);
+
+ if (strcmp (g_dbus_proxy_get_interface_name (proxy),
+ IWD_DEVICE_INTERFACE))
+ return;
+
+ value = g_dbus_proxy_get_cached_property (proxy, "Name");
+ if (!value) {
+ _LOGE ("Name not cached for Device at %s",
+ g_dbus_proxy_get_object_path (proxy));
+ return;
+ }
+
+ ifname = g_variant_get_string (value, NULL);
+ ifindex = if_nametoindex (ifname);
+ g_variant_unref (value);
+
+ if (!ifindex) {
+ _LOGE ("if_nametoindex failed for Name %s for Device at %s: %i",
+ ifname, g_dbus_proxy_get_object_path (proxy), errno);
+ return;
+ }
+
+ manager = nm_manager_get ();
+
+ device = nm_manager_get_device_by_ifindex (manager, ifindex);
+ if (!NM_IS_DEVICE_WIFI (device)) {
+ _LOGE ("IWD device named %s is not a Wifi device", ifname);
+ return;
+ }
+
+ nm_device_wifi_set_dbus_object (NM_DEVICE_WIFI (device), object);
+}
+
+static void
+interface_added (GDBusObjectManager *object_manager, GDBusObject *object,
+ GDBusInterface *interface, gpointer user_data)
+{
+ NMIWDManager *self = user_data;
+
+ set_device_dbus_object (self, interface, object);
+}
+
+static void
+interface_removed (GDBusObjectManager *object_manager, GDBusObject *object,
+ GDBusInterface *interface, gpointer user_data)
+{
+ NMIWDManager *self = user_data;
+
+ /*
+ * TODO: we may need to save the GDBusInterface or GDBusObject
+ * pointer in the hash table because we may be no longer able to
+ * access the Name property or map the name to ifindex with
+ * if_nametoindex at this point.
+ */
+
+ set_device_dbus_object (self, interface, NULL);
+}
+
+static gboolean
+_om_has_name_owner (GDBusObjectManager *object_manager)
+{
+ gs_free char *name_owner = NULL;
+
+ nm_assert (G_IS_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
+
+ name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT
(object_manager));
+ return !!name_owner;
+}
+
+static void
+object_added (NMIWDManager *self, GDBusObject *object)
+{
+ GList *interfaces, *iter;
+
+ interfaces = g_dbus_object_get_interfaces (object);
+ for (iter = interfaces; iter; iter = iter->next) {
+ GDBusInterface *interface = G_DBUS_INTERFACE (iter->data);
+
+ set_device_dbus_object (self, interface, object);
+ }
+
+ g_list_free_full (interfaces, g_object_unref);
+}
+
+static void
+name_owner_changed (GObject *object, GParamSpec *pspec, gpointer user_data)
+{
+ NMIWDManager *self = user_data;
+ NMIWDManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+ GDBusObjectManager *object_manager = G_DBUS_OBJECT_MANAGER (object);
+
+ nm_assert (object_manager == priv->object_manager);
+
+ if (_om_has_name_owner (object_manager)) {
+ GList *objects, *iter;
+
+ priv->running = true;
+
+ objects = g_dbus_object_manager_get_objects (object_manager);
+ for (iter = objects; iter; iter = iter->next)
+ object_added (self, G_DBUS_OBJECT (iter->data));
+
+ g_list_free_full (objects, g_object_unref);
+ } else {
+ NMManager *manager = nm_manager_get ();
+ const GSList *devices, *iter;
+
+ priv->running = false;
+
+ devices = nm_manager_get_devices (manager);
+ for (iter = devices; iter; iter = iter->next) {
+ NMDevice *device = NM_DEVICE (iter->data);
+
+ if (!NM_IS_DEVICE_WIFI (device))
+ continue;
+
+ nm_device_wifi_set_dbus_object (NM_DEVICE_WIFI (device),
+ NULL);
+ }
+ }
+}
+
+static void
+device_added (NMDevice *device, gpointer user_data)
+{
+ NMIWDManager *self = user_data;
+ NMIWDManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+ GList *objects, *iter;
+
+ if (!NM_IS_DEVICE_WIFI (device))
+ return;
+
+ if (!priv->running)
+ return;
+
+ objects = g_dbus_object_manager_get_objects (priv->object_manager);
+ for (iter = objects; iter; iter = iter->next) {
+ GDBusObject *object = G_DBUS_OBJECT (iter->data);
+ GDBusInterface *interface;
+ GDBusProxy *proxy;
+ GVariant *value;
+ const char *obj_ifname;
+
+ interface = g_dbus_object_get_interface (object,
+ IWD_DEVICE_INTERFACE);
+ if (!interface)
+ continue;
+
+ proxy = G_DBUS_PROXY (interface);
+ value = g_dbus_proxy_get_cached_property (proxy, "Name");
+ if (!value) {
+ g_object_unref (interface);
+ continue;
+ }
+
+ obj_ifname = g_variant_get_string (value, NULL);
+ g_variant_unref (value);
+ g_object_unref (interface);
+
+ if (strcmp (nm_device_get_iface (device), obj_ifname))
+ continue;
+
+ nm_device_wifi_set_dbus_object (NM_DEVICE_WIFI (device), object);
+ break;
+ }
+
+ g_list_free_full (objects, g_object_unref);
+}
+
+static void
+got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ NMIWDManager *self = user_data;
+ NMIWDManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+ GError *error = NULL;
+ GDBusObjectManager *object_manager;
+ NMManager *manager = nm_manager_get ();
+
+ g_clear_object (&priv->cancellable);
+
+ object_manager = g_dbus_object_manager_client_new_for_bus_finish (result, &error);
+ if (object_manager == NULL) {
+ _LOGE ("failed to acquire IWD Object Manager: Wi-Fi will not be available (%s)",
+ NM_G_ERROR_MSG (error));
+ g_clear_error (&error);
+ return;
+ }
+
+ priv->object_manager = object_manager;
+
+ g_signal_connect (priv->object_manager, "interface-added",
+ G_CALLBACK (interface_added), self);
+ g_signal_connect (priv->object_manager, "interface-removed",
+ G_CALLBACK (interface_removed), self);
+ g_signal_connect (priv->object_manager, "notify::name-owner",
+ G_CALLBACK (name_owner_changed), self);
+
+ name_owner_changed (G_OBJECT (object_manager), NULL, self);
+
+ g_signal_connect (manager, "device-added",
+ G_CALLBACK (device_added), self);
+}
+
+static void
+prepare_object_manager (NMIWDManager *self)
+{
+ NMIWDManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+
+ g_dbus_object_manager_client_new_for_bus (IWD_BUS_TYPE,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+ IWD_SERVICE, "/",
+ NULL, NULL, NULL,
+ priv->cancellable,
+ got_object_manager, self);
+}
+
+/*****************************************************************************/
+
+NM_DEFINE_SINGLETON_GETTER (NMIWDManager, nm_iwd_manager_get,
+ NM_TYPE_IWD_MANAGER);
+
+static void
+nm_iwd_manager_init (NMIWDManager *self)
+{
+ NMIWDManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+
+ priv->cancellable = g_cancellable_new ();
+ prepare_object_manager (self);
+}
+
+static void
+dispose (GObject *object)
+{
+ NMIWDManager *self = (NMIWDManager *) object;
+ NMIWDManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
+
+ nm_clear_g_cancellable (&priv->cancellable);
+
+ g_clear_object (&priv->object_manager);
+
+ G_OBJECT_CLASS (nm_iwd_manager_parent_class)->dispose (object);
+}
+
+static void
+nm_iwd_manager_class_init (NMIWDManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dispose;
+}
diff --git a/src/devices/wifi/nm-iwd-manager.h b/src/devices/wifi/nm-iwd-manager.h
new file mode 100644
index 000000000..27df9cd22
--- /dev/null
+++ b/src/devices/wifi/nm-iwd-manager.h
@@ -0,0 +1,53 @@
+/* -*- 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2017 Intel Corporation
+ */
+
+#ifndef __NETWORKMANAGER_IWD_MANAGER_H__
+#define __NETWORKMANAGER_IWD_MANAGER_H__
+
+#include "devices/nm-device.h"
+
+#define IWD_BUS_TYPE G_BUS_TYPE_SYSTEM
+#define IWD_SERVICE "net.connman.iwd"
+
+#define IWD_AGENT_MANAGER_INTERFACE "net.connman.iwd.AgentManager"
+#define IWD_WIPHY_INTERFACE "net.connman.iwd.Adapter"
+#define IWD_DEVICE_INTERFACE "net.connman.iwd.Device"
+#define IWD_NETWORK_INTERFACE "net.connman.iwd.Network"
+#define IWD_AGENT_INTERFACE "net.connman.iwd.Agent"
+#define IWD_WSC_INTERFACE \
+ "net.connman.iwd.WiFiSimpleConfiguration"
+#define IWD_KNOWN_NETWORKS_INTERFACE "net.connman.iwd.KnownNetworks"
+#define IWD_SIGNAL_AGENT_INTERFACE "net.connman.iwd.SignalLevelAgent"
+
+#define NM_TYPE_IWD_MANAGER (nm_iwd_manager_get_type ())
+#define NM_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_IWD_MANAGER,
NMIWDManager))
+#define NM_IWD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_IWD_MANAGER,
NMIWDManagerClass))
+#define NM_IS_IWD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_IWD_MANAGER))
+#define NM_IS_IWD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_IWD_MANAGER))
+#define NM_IWD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_IWD_MANAGER,
NMIWDManagerClass))
+
+typedef struct _NMIWDManager NMIWDManager;
+typedef struct _NMIWDManagerClass NMIWDManagerClass;
+
+GType nm_iwd_manager_get_type (void);
+
+NMIWDManager *nm_iwd_manager_get (void);
+
+#endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */
--
2.11.0
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]