From 06e3c6d02fe997e715345d3ce6a290be3862346e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 15 Apr 2014 17:12:13 -0500 Subject: wifi: make Wi-Fi support a plugin Make Wi-Fi support a plugin using the new device factory interface. Provides a 7% size reduction in the core NM binary. Before After NM: 1154104 1071992 (-7%) Wi-Fi: 0 110464 (all results from stripped files) --- .gitignore | 1 + configure.ac | 2 + po/POTFILES.in | 2 +- src/Makefile.am | 11 +- src/devices/nm-device-olpc-mesh.c | 652 ----- src/devices/nm-device-olpc-mesh.h | 82 - src/devices/nm-device-wifi.c | 3648 -------------------------- src/devices/nm-device-wifi.h | 95 - src/devices/wifi/Makefile.am | 78 + src/devices/wifi/exports.ver | 7 + src/devices/wifi/nm-device-olpc-mesh.c | 653 +++++ src/devices/wifi/nm-device-olpc-mesh.h | 82 + src/devices/wifi/nm-device-wifi.c | 3668 +++++++++++++++++++++++++++ src/devices/wifi/nm-device-wifi.h | 94 + src/devices/wifi/nm-wifi-ap-utils.c | 721 ++++++ src/devices/wifi/nm-wifi-ap-utils.h | 45 + src/devices/wifi/nm-wifi-ap.c | 1293 ++++++++++ src/devices/wifi/nm-wifi-ap.h | 121 + src/devices/wifi/nm-wifi-factory.c | 114 + src/devices/wifi/tests/Makefile.am | 28 + src/devices/wifi/tests/test-wifi-ap-utils.c | 1542 +++++++++++ src/nm-manager.c | 53 - src/nm-policy.c | 1 - src/nm-wifi-ap-utils.c | 721 ------ src/nm-wifi-ap-utils.h | 45 - src/nm-wifi-ap.c | 1293 ---------- src/nm-wifi-ap.h | 121 - src/tests/Makefile.am | 11 +- src/tests/test-wifi-ap-utils.c | 1542 ----------- 29 files changed, 8452 insertions(+), 8274 deletions(-) delete mode 100644 src/devices/nm-device-olpc-mesh.c delete mode 100644 src/devices/nm-device-olpc-mesh.h delete mode 100644 src/devices/nm-device-wifi.c delete mode 100644 src/devices/nm-device-wifi.h create mode 100644 src/devices/wifi/Makefile.am create mode 100644 src/devices/wifi/exports.ver create mode 100644 src/devices/wifi/nm-device-olpc-mesh.c create mode 100644 src/devices/wifi/nm-device-olpc-mesh.h create mode 100644 src/devices/wifi/nm-device-wifi.c create mode 100644 src/devices/wifi/nm-device-wifi.h create mode 100644 src/devices/wifi/nm-wifi-ap-utils.c create mode 100644 src/devices/wifi/nm-wifi-ap-utils.h create mode 100644 src/devices/wifi/nm-wifi-ap.c create mode 100644 src/devices/wifi/nm-wifi-ap.h create mode 100644 src/devices/wifi/nm-wifi-factory.c create mode 100644 src/devices/wifi/tests/Makefile.am create mode 100644 src/devices/wifi/tests/test-wifi-ap-utils.c delete mode 100644 src/nm-wifi-ap-utils.c delete mode 100644 src/nm-wifi-ap-utils.h delete mode 100644 src/nm-wifi-ap.c delete mode 100644 src/nm-wifi-ap.h delete mode 100644 src/tests/test-wifi-ap-utils.c diff --git a/.gitignore b/.gitignore index 3678dc36e1..b3245af97b 100644 --- a/.gitignore +++ b/.gitignore @@ -186,6 +186,7 @@ valgrind-*.log /src/tests/test-ip6-config /src/tests/test-wifi-ap-utils /src/tests/test-resolvconf-capture +/src/devices/wifi/tests/test-wifi-ap-utils /src/dnsmasq-manager/tests/test-dnsmasq-utils /src/dhcp-manager/tests/test-dhcp-dhclient /src/dhcp-manager/tests/test-dnsmasq-utils diff --git a/configure.ac b/configure.ac index 74db8c1129..cfccfc2e00 100644 --- a/configure.ac +++ b/configure.ac @@ -800,6 +800,8 @@ src/rdisc/tests/Makefile src/devices/adsl/Makefile src/devices/wimax/Makefile src/devices/bluetooth/Makefile +src/devices/wifi/Makefile +src/devices/wifi/tests/Makefile src/devices/wwan/Makefile libnm-util/libnm-util.pc libnm-util/Makefile diff --git a/po/POTFILES.in b/po/POTFILES.in index 36bc69ffa0..6368a9f59f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -53,9 +53,9 @@ src/devices/nm-device-bond.c src/devices/nm-device-bridge.c src/devices/nm-device-ethernet.c src/devices/nm-device-infiniband.c -src/devices/nm-device-olpc-mesh.c src/devices/nm-device-team.c src/devices/nm-device-vlan.c +src/devices/wifi/nm-device-olpc-mesh.c src/devices/wwan/nm-modem-broadband.c src/devices/wwan/nm-modem-old.c src/nm-manager.c diff --git a/src/Makefile.am b/src/Makefile.am index d467e803ca..53fc35c1fb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,6 +7,7 @@ SUBDIRS = \ devices/adsl \ devices/wwan \ devices/bluetooth \ + devices/wifi \ dhcp-manager \ ppp-manager \ settings/plugins @@ -78,8 +79,6 @@ nm_sources = \ devices/nm-device-infiniband.h \ devices/nm-device-macvlan.c \ devices/nm-device-macvlan.h \ - devices/nm-device-olpc-mesh.c \ - devices/nm-device-olpc-mesh.h \ devices/nm-device-private.h \ devices/nm-device-team.c \ devices/nm-device-team.h \ @@ -91,8 +90,6 @@ nm_sources = \ devices/nm-device-vlan.h \ devices/nm-device-vxlan.c \ devices/nm-device-vxlan.h \ - devices/nm-device-wifi.c \ - devices/nm-device-wifi.h \ \ dhcp-manager/nm-dhcp-client.c \ dhcp-manager/nm-dhcp-client.h \ @@ -241,10 +238,6 @@ nm_sources = \ nm-session-utils.h \ nm-sleep-monitor.h \ nm-types.h \ - nm-wifi-ap-utils.c \ - nm-wifi-ap-utils.h \ - nm-wifi-ap.c \ - nm-wifi-ap.h \ NetworkManagerUtils.c \ NetworkManagerUtils.h @@ -297,13 +290,11 @@ glue_sources = \ nm-device-gre-glue.h \ nm-device-infiniband-glue.h \ nm-device-macvlan-glue.h \ - nm-device-olpc-mesh-glue.h \ nm-device-team-glue.h \ nm-device-tun-glue.h \ nm-device-veth-glue.h \ nm-device-vlan-glue.h \ nm-device-vxlan-glue.h \ - nm-device-wifi-glue.h \ nm-dhcp4-config-glue.h \ nm-dhcp6-config-glue.h \ nm-ip4-config-glue.h \ diff --git a/src/devices/nm-device-olpc-mesh.c b/src/devices/nm-device-olpc-mesh.c deleted file mode 100644 index 19c86975c2..0000000000 --- a/src/devices/nm-device-olpc-mesh.c +++ /dev/null @@ -1,652 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * Dan Williams - * Sjoerd Simons - * Daniel Drake - * - * 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. - * - * (C) Copyright 2005 - 2011 Red Hat, Inc. - * (C) Copyright 2008 Collabora Ltd. - * (C) Copyright 2009 One Laptop per Child - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nm-device.h" -#include "nm-device-wifi.h" -#include "nm-device-olpc-mesh.h" -#include "nm-device-private.h" -#include "nm-utils.h" -#include "nm-logging.h" -#include "NetworkManagerUtils.h" -#include "nm-activation-request.h" -#include "nm-setting-connection.h" -#include "nm-setting-olpc-mesh.h" -#include "nm-manager.h" -#include "nm-enum-types.h" -#include "nm-dbus-manager.h" - -/* This is a bug; but we can't really change API now... */ -#include "NetworkManagerVPN.h" - - -#include "nm-device-olpc-mesh-glue.h" - -G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) - -#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate)) - - -enum { - PROP_0, - PROP_COMPANION, - PROP_ACTIVE_CHANNEL, - - LAST_PROP -}; - -#define NM_OLPC_MESH_ERROR (nm_olpc_mesh_error_quark ()) - - -struct _NMDeviceOlpcMeshPrivate { - gboolean dispose_has_run; - - NMDevice * companion; - gboolean stage1_waiting; - guint device_added_id; - guint device_removed_id; - guint cmp_state_changed_id; - guint cmp_scanning_id; - guint cmp_scanning_allowed_id; - guint cmp_autoconnect_allowed_id; -}; - -static void state_changed (NMDevice *device, NMDeviceState new_state, - NMDeviceState old_state, NMDeviceStateReason reason); - -static GQuark -nm_olpc_mesh_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("nm-mesh-error"); - return quark; -} - -static void -nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self) -{ - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - - priv->dispose_has_run = FALSE; - priv->companion = NULL; - priv->stage1_waiting = FALSE; -} - -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - GObject *object; - GObjectClass *klass; - NMDeviceOlpcMesh *self; - NMDeviceWifiCapabilities caps; - - klass = G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class); - object = klass->constructor (type, n_construct_params, construct_params); - if (!object) - return NULL; - - self = NM_DEVICE_OLPC_MESH (object); - - nm_log_dbg (LOGD_HW | LOGD_OLPC, "(%s): kernel ifindex %d", - nm_device_get_iface (NM_DEVICE (self)), - nm_device_get_ifindex (NM_DEVICE (self))); - - if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), &caps)) { - nm_log_warn (LOGD_HW | LOGD_OLPC, "(%s): failed to initialize WiFi driver", - nm_device_get_iface (NM_DEVICE (self))); - g_object_unref (object); - return NULL; - } - - /* shorter timeout for mesh connectivity */ - nm_device_set_dhcp_timeout (NM_DEVICE (self), 20); - return object; -} - -static gboolean -check_connection_compatible (NMDevice *device, - NMConnection *connection, - GError **error) -{ - NMSettingConnection *s_con; - NMSettingOlpcMesh *s_mesh; - - if (!NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->check_connection_compatible (device, connection, error)) - return FALSE; - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - - if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_OLPC_MESH_SETTING_NAME)) { - g_set_error (error, - NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, - "The connection was not a Mesh connection."); - return FALSE; - } - - s_mesh = nm_connection_get_setting_olpc_mesh (connection); - if (!s_mesh) { - g_set_error (error, - NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_INVALID, - "The connection was not a valid Mesh connection."); - return FALSE; - } - - return TRUE; -} - -static gboolean -can_auto_connect (NMDevice *device, - NMConnection *connection, - char **specific_object) -{ - return FALSE; -} - -#define DEFAULT_SSID "olpc-mesh" - -static gboolean -complete_connection (NMDevice *device, - NMConnection *connection, - const char *specific_object, - const GSList *existing_connections, - GError **error) -{ - NMSettingOlpcMesh *s_mesh; - GByteArray *tmp; - - s_mesh = nm_connection_get_setting_olpc_mesh (connection); - if (!s_mesh) { - s_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new (); - nm_connection_add_setting (connection, NM_SETTING (s_mesh)); - } - - if (!nm_setting_olpc_mesh_get_ssid (s_mesh)) { - tmp = g_byte_array_sized_new (strlen (DEFAULT_SSID)); - g_byte_array_append (tmp, (const guint8 *) DEFAULT_SSID, strlen (DEFAULT_SSID)); - g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_SSID, tmp, NULL); - g_byte_array_free (tmp, TRUE); - } - - if (!nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh)) { - const guint8 anycast[ETH_ALEN] = { 0xC0, 0x27, 0xC0, 0x27, 0xC0, 0x27 }; - - tmp = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (tmp, anycast, sizeof (anycast)); - g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, tmp, NULL); - g_byte_array_free (tmp, TRUE); - - } - - nm_utils_complete_generic (connection, - NM_SETTING_OLPC_MESH_SETTING_NAME, - existing_connections, - _("Mesh %d"), - NULL, - FALSE); /* No IPv6 by default */ - - return TRUE; -} - -/****************************************************************************/ - -static NMActStageReturn -act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) -{ - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev); - NMActStageReturn ret; - gboolean scanning; - - ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (dev, reason); - if (ret != NM_ACT_STAGE_RETURN_SUCCESS) - return ret; - - /* disconnect companion device, if it is connected */ - if (nm_device_get_act_request (NM_DEVICE (priv->companion))) { - nm_log_info (LOGD_OLPC, "(%s): disconnecting companion device %s", - nm_device_get_iface (dev), - nm_device_get_iface (priv->companion)); - /* FIXME: VPN stuff here is a bug; but we can't really change API now... */ - nm_device_state_changed (NM_DEVICE (priv->companion), - NM_DEVICE_STATE_DISCONNECTED, - NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); - nm_log_info (LOGD_OLPC, "(%s): companion %s disconnected", - nm_device_get_iface (dev), - nm_device_get_iface (priv->companion)); - } - - - /* wait with continuing configuration untill the companion device is done scanning */ - g_object_get (priv->companion, "scanning", &scanning, NULL); - if (scanning) { - priv->stage1_waiting = TRUE; - return NM_ACT_STAGE_RETURN_POSTPONE; - } - - return NM_ACT_STAGE_RETURN_SUCCESS; -} - -static void -_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel) -{ - int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); - - if (nm_platform_mesh_get_channel (ifindex) != channel) { - if (nm_platform_mesh_set_channel (ifindex, channel)) - g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL); - } -} - -static NMActStageReturn -act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); - NMConnection *connection; - NMSettingOlpcMesh *s_mesh; - guint32 channel; - const GByteArray *anycast_addr_array; - guint8 *anycast_addr = NULL; - - connection = nm_device_get_connection (dev); - g_assert (connection); - - s_mesh = nm_connection_get_setting_olpc_mesh (connection); - g_assert (s_mesh); - - channel = nm_setting_olpc_mesh_get_channel (s_mesh); - if (channel != 0) - _mesh_set_channel (self, channel); - nm_platform_mesh_set_ssid (nm_device_get_ifindex (dev), - nm_setting_olpc_mesh_get_ssid (s_mesh)); - - anycast_addr_array = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh); - if (anycast_addr_array) - anycast_addr = anycast_addr_array->data; - - nm_device_set_dhcp_anycast_address (dev, anycast_addr); - return NM_ACT_STAGE_RETURN_SUCCESS; -} - -static void -companion_cleanup (NMDeviceOlpcMesh *self) -{ - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - - if (priv->companion == NULL) - return; - - if (priv->cmp_state_changed_id) { - g_signal_handler_disconnect (priv->companion, priv->cmp_state_changed_id); - priv->cmp_state_changed_id = 0; - } - - if (priv->cmp_scanning_id) { - g_signal_handler_disconnect (priv->companion, priv->cmp_scanning_id); - priv->cmp_scanning_id = 0; - } - - if (priv->cmp_scanning_allowed_id) { - g_signal_handler_disconnect (priv->companion, priv->cmp_scanning_allowed_id); - priv->cmp_scanning_allowed_id = 0; - } - - if (priv->cmp_autoconnect_allowed_id) { - g_signal_handler_disconnect (priv->companion, priv->cmp_autoconnect_allowed_id); - priv->cmp_autoconnect_allowed_id = 0; - } - - priv->companion = NULL; - g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION); -} - -static void -dispose (GObject *object) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object); - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - - if (priv->dispose_has_run) { - G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); - return; - } - priv->dispose_has_run = TRUE; - - companion_cleanup (self); - - if (priv->device_added_id) - g_signal_handler_disconnect (nm_manager_get (), priv->device_added_id); - if (priv->device_removed_id) - g_signal_handler_disconnect (nm_manager_get (), priv->device_removed_id); - - G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); -} - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object); - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device); - - switch (prop_id) { - case PROP_COMPANION: - if (priv->companion) - g_value_set_boxed (value, nm_device_get_path (priv->companion)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_ACTIVE_CHANNEL: - g_value_set_uint (value, nm_platform_mesh_get_channel (nm_device_get_ifindex (NM_DEVICE (device)))); - 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) -{ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate)); - - object_class->constructor = constructor; - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->dispose = dispose; - - parent_class->check_connection_compatible = check_connection_compatible; - parent_class->can_auto_connect = can_auto_connect; - parent_class->complete_connection = complete_connection; - - parent_class->act_stage1_prepare = act_stage1_prepare; - parent_class->act_stage2_config = act_stage2_config; - - parent_class->state_changed = state_changed; - - /* Properties */ - g_object_class_install_property - (object_class, PROP_COMPANION, - g_param_spec_boxed (NM_DEVICE_OLPC_MESH_COMPANION, - "Companion device", - "Companion device object path", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_ACTIVE_CHANNEL, - g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, - "Active channel", - "Active channel", - 0, G_MAXUINT32, 0, - G_PARAM_READABLE)); - - nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), - G_TYPE_FROM_CLASS (klass), - &dbus_glib_nm_device_olpc_mesh_object_info); - - dbus_g_error_domain_register (NM_OLPC_MESH_ERROR, NULL, - NM_TYPE_OLPC_MESH_ERROR); -} - -static void -companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_data) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - gboolean scanning; - - if (!priv->stage1_waiting) - return; - - g_object_get (companion, "scanning", &scanning, NULL); - - if (!scanning) { - priv->stage1_waiting = FALSE; - nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self)); - } -} - -/* disconnect from mesh if someone starts using the companion */ -static void -companion_state_changed_cb (NMDeviceWifi *companion, - NMDeviceState state, - NMDeviceState old_state, - NMDeviceStateReason reason, - gpointer user_data) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); - NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self)); - - if ( self_state < NM_DEVICE_STATE_PREPARE - || self_state > NM_DEVICE_STATE_ACTIVATED - || state < NM_DEVICE_STATE_PREPARE - || state > NM_DEVICE_STATE_ACTIVATED) - return; - - nm_log_dbg (LOGD_OLPC, "(%s): disconnecting mesh due to companion connectivity", - nm_device_get_iface (NM_DEVICE (self))); - /* FIXME: VPN stuff here is a bug; but we can't really change API now... */ - nm_device_state_changed (NM_DEVICE (self), - NM_DEVICE_STATE_DISCONNECTED, - NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); -} - -static gboolean -companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); - - /* Don't allow the companion to scan while configuring the mesh interface */ - return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG); -} - -static gboolean -companion_autoconnect_allowed_cb (NMDeviceWifi *companion, gpointer user_data) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); - - /* Don't allow the companion to autoconnect while a mesh connection is - * active */ - return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_ACTIVATED); -} - -static gboolean -is_companion (NMDeviceOlpcMesh *self, NMDevice *other) -{ - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - const guint8 *my_addr, *their_addr; - guint their_addr_len; - - if (!NM_IS_DEVICE_WIFI (other)) - return FALSE; - - my_addr = nm_device_get_hw_address (NM_DEVICE (self), NULL); - their_addr = nm_device_get_hw_address (other, &their_addr_len); - if ( (their_addr_len != ETH_ALEN) - || (memcmp (my_addr, their_addr, ETH_ALEN) != 0)) - return FALSE; - - priv->companion = other; - - /* When we've found the companion, stop listening for other devices */ - if (priv->device_added_id) { - g_signal_handler_disconnect (nm_manager_get (), priv->device_added_id); - priv->device_added_id = 0; - } - - nm_device_state_changed (NM_DEVICE (self), - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - - nm_log_info (LOGD_OLPC, "(%s): found companion WiFi device %s", - nm_device_get_iface (NM_DEVICE (self)), - nm_device_get_iface (other)); - - priv->cmp_state_changed_id = g_signal_connect (G_OBJECT (other), "state-changed", - G_CALLBACK (companion_state_changed_cb), self); - - priv->cmp_scanning_id = g_signal_connect (G_OBJECT (other), "notify::scanning", - G_CALLBACK (companion_notify_cb), self); - - priv->cmp_scanning_allowed_id = g_signal_connect (G_OBJECT (other), "scanning-allowed", - G_CALLBACK (companion_scan_allowed_cb), self); - - priv->cmp_autoconnect_allowed_id = g_signal_connect (G_OBJECT (other), "autoconnect-allowed", - G_CALLBACK (companion_autoconnect_allowed_cb), self); - - g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION); - - return TRUE; -} - -static void -device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); - - is_companion (self, other); -} - -static void -device_removed_cb (NMManager *manager, NMDevice *other, gpointer user_data) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); - - if (other == NM_DEVICE_OLPC_MESH_GET_PRIVATE (self)->companion) - companion_cleanup (self); -} - -static gboolean -check_companion_cb (gpointer user_data) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); - NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); - NMManager *manager; - GSList *list; - - if (priv->companion != NULL) { - nm_device_state_changed (NM_DEVICE (user_data), - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_NONE); - goto done; - } - - if (priv->device_added_id != 0) - goto done; - - manager = nm_manager_get (); - - priv->device_added_id = g_signal_connect (manager, "device-added", - G_CALLBACK (device_added_cb), self); - if (!priv->device_removed_id) { - priv->device_removed_id = g_signal_connect (manager, "device-removed", - G_CALLBACK (device_removed_cb), self); - } - - /* Try to find the companion if it's already known to the NMManager */ - for (list = nm_manager_get_devices (manager); list ; list = g_slist_next (list)) { - if (is_companion (self, NM_DEVICE (list->data))) - break; - } - - done: - nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE); - return FALSE; -} - -static void -state_changed (NMDevice *device, NMDeviceState new_state, - NMDeviceState old_state, NMDeviceStateReason reason) -{ - NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); - - switch (new_state) { - case NM_DEVICE_STATE_UNMANAGED: - break; - case NM_DEVICE_STATE_UNAVAILABLE: - /* If transitioning to UNAVAILABLE and the companion device is known then - * transition to DISCONNECTED otherwise wait for our companion. - */ - g_idle_add (check_companion_cb, self); - nm_device_add_pending_action (device, "waiting for companion", TRUE); - break; - case NM_DEVICE_STATE_ACTIVATED: - break; - case NM_DEVICE_STATE_FAILED: - break; - case NM_DEVICE_STATE_DISCONNECTED: - break; - default: - break; - } -} - - -NMDevice * -nm_device_olpc_mesh_new (NMPlatformLink *platform_device) -{ - g_return_val_if_fail (platform_device != NULL, NULL); - - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_OLPC_MESH, - NM_DEVICE_PLATFORM_DEVICE, platform_device, - NM_DEVICE_TYPE_DESC, "802.11 OLPC Mesh", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_OLPC_MESH, - NULL); -} diff --git a/src/devices/nm-device-olpc-mesh.h b/src/devices/nm-device-olpc-mesh.h deleted file mode 100644 index c25dd8e53c..0000000000 --- a/src/devices/nm-device-olpc-mesh.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ - -/* NetworkManager -- Network link manager - * - * Dan Williams - * Sjoerd Simons - * Daniel Drake - * - * 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. - * - * (C) Copyright 2005 Red Hat, Inc. - * (C) Copyright 2008 Collabora Ltd. - * (C) Copyright 2009 One Laptop per Child - */ - -#ifndef NM_DEVICE_OLPC_MESH_H -#define NM_DEVICE_OLPC_MESH_H - -#include -#include - -#include "nm-device.h" - -G_BEGIN_DECLS - -#define NM_TYPE_DEVICE_OLPC_MESH (nm_device_olpc_mesh_get_type ()) -#define NM_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh)) -#define NM_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) -#define NM_IS_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH)) -#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OLPC_MESH)) -#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) - -typedef enum -{ - NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH = 0, /*< nick=ConnectionNotMesh >*/ - NM_OLPC_MESH_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ - NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ -} NMOlpcMeshError; - -#define NM_DEVICE_OLPC_MESH_COMPANION "companion" -#define NM_DEVICE_OLPC_MESH_BITRATE "bitrate" -#define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel" - -#ifndef NM_DEVICE_OLPC_MESH_DEFINED -#define NM_DEVICE_OLPC_MESH_DEFINED -typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh; -#endif - -typedef struct _NMDeviceOlpcMeshClass NMDeviceOlpcMeshClass; -typedef struct _NMDeviceOlpcMeshPrivate NMDeviceOlpcMeshPrivate; - -struct _NMDeviceOlpcMesh -{ - NMDevice parent; -}; - -struct _NMDeviceOlpcMeshClass -{ - NMDeviceClass parent; - -}; - - -GType nm_device_olpc_mesh_get_type (void); - -NMDevice *nm_device_olpc_mesh_new (NMPlatformLink *platform_device); - -G_END_DECLS - -#endif /* NM_DEVICE_OLPC_MESH_H */ diff --git a/src/devices/nm-device-wifi.c b/src/devices/nm-device-wifi.c deleted file mode 100644 index 9fa56d44ea..0000000000 --- a/src/devices/nm-device-wifi.c +++ /dev/null @@ -1,3648 +0,0 @@ -/* -*- 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 - 2012 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nm-glib-compat.h" -#include "nm-dbus-manager.h" -#include "nm-device.h" -#include "nm-device-wifi.h" -#include "nm-device-private.h" -#include "nm-utils.h" -#include "nm-logging.h" -#include "NetworkManagerUtils.h" -#include "nm-activation-request.h" -#include "nm-supplicant-manager.h" -#include "nm-supplicant-interface.h" -#include "nm-supplicant-config.h" -#include "nm-setting-connection.h" -#include "nm-setting-wireless.h" -#include "nm-setting-wireless-security.h" -#include "nm-setting-8021x.h" -#include "nm-setting-ip4-config.h" -#include "nm-setting-ip6-config.h" -#include "nm-platform.h" -#include "nm-manager-auth.h" -#include "nm-settings-connection.h" -#include "nm-enum-types.h" -#include "nm-dbus-glib-types.h" - - -static gboolean impl_device_get_access_points (NMDeviceWifi *device, - GPtrArray **aps, - GError **err); - -static gboolean impl_device_get_all_access_points (NMDeviceWifi *device, - GPtrArray **aps, - GError **err); - -static void impl_device_request_scan (NMDeviceWifi *device, - GHashTable *options, - DBusGMethodInvocation *context); - -#include "nm-device-wifi-glue.h" - - -/* All of these are in seconds */ -#define SCAN_INTERVAL_MIN 3 -#define SCAN_INTERVAL_STEP 20 -#define SCAN_INTERVAL_MAX 120 - -#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries" - -G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) - -#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate)) - - -enum { - PROP_0, - PROP_PERM_HW_ADDRESS, - PROP_MODE, - PROP_BITRATE, - PROP_ACCESS_POINTS, - PROP_ACTIVE_ACCESS_POINT, - PROP_CAPABILITIES, - PROP_SCANNING, - - LAST_PROP -}; - -enum { - ACCESS_POINT_ADDED, - ACCESS_POINT_REMOVED, - HIDDEN_AP_FOUND, - SCANNING_ALLOWED, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct Supplicant { - NMSupplicantManager *mgr; - NMSupplicantInterface *iface; - - guint iface_error_id; - - /* Timeouts and idles */ - guint iface_con_error_cb_id; - guint con_timeout_id; -} Supplicant; - -struct _NMDeviceWifiPrivate { - gboolean disposed; - - guint8 perm_hw_addr[ETH_ALEN]; /* Permanent MAC address */ - guint8 initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */ - - gint8 invalid_strength_counter; - - GSList * ap_list; - NMAccessPoint * current_ap; - guint32 rate; - gboolean enabled; /* rfkilled or not */ - - gint32 scheduled_scan_time; - guint8 scan_interval; /* seconds */ - guint pending_scan_id; - guint scanlist_cull_id; - gboolean requested_scan; - - Supplicant supplicant; - gboolean ssid_found; - NM80211Mode mode; - - guint32 failed_link_count; - guint periodic_source_id; - guint link_timeout_id; - - NMDeviceWifiCapabilities capabilities; -}; - -static gboolean check_scanning_allowed (NMDeviceWifi *self); - -static void schedule_scan (NMDeviceWifi *self, gboolean backoff); - -static void cancel_pending_scan (NMDeviceWifi *self); - -static void cleanup_association_attempt (NMDeviceWifi * self, - gboolean disconnect); - -static void remove_supplicant_timeouts (NMDeviceWifi *self); - -static void supplicant_iface_state_cb (NMSupplicantInterface *iface, - guint32 new_state, - guint32 old_state, - int disconnect_reason, - gpointer user_data); - -static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface, - const char *object_path, - GHashTable *properties, - NMDeviceWifi * self); - -static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, - const char *object_path, - GHashTable *properties, - NMDeviceWifi *self); - -static void supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, - const char *object_path, - NMDeviceWifi *self); - -static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface, - gboolean success, - NMDeviceWifi * self); - -static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, - GParamSpec * pspec, - NMDeviceWifi * self); - -static void schedule_scanlist_cull (NMDeviceWifi *self); - -static gboolean request_wireless_scan (gpointer user_data); - -static void remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap); - -/*****************************************************************/ - -#define NM_WIFI_ERROR (nm_wifi_error_quark ()) - -static GQuark -nm_wifi_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("nm-wifi-error"); - return quark; -} - -/*****************************************************************/ - -static GObject* -constructor (GType type, - guint n_construct_params, - GObjectConstructParam *construct_params) -{ - GObject *object; - GObjectClass *klass; - NMDeviceWifi *self; - NMDeviceWifiPrivate *priv; - - klass = G_OBJECT_CLASS (nm_device_wifi_parent_class); - object = klass->constructor (type, n_construct_params, construct_params); - if (!object) - return NULL; - - self = NM_DEVICE_WIFI (object); - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d", - nm_device_get_iface (NM_DEVICE (self)), - nm_device_get_ifindex (NM_DEVICE (self))); - - if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), - &priv->capabilities)) { - nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): failed to initialize WiFi driver", - nm_device_get_iface (NM_DEVICE (self))); - g_object_unref (object); - return NULL; - } - - if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP) { - nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver supports Access Point (AP) mode", - nm_device_get_iface (NM_DEVICE (self))); - } - - /* Connect to the supplicant manager */ - priv->supplicant.mgr = nm_supplicant_manager_get (); - g_assert (priv->supplicant.mgr); - - return object; -} - -static gboolean -supplicant_interface_acquire (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - g_return_val_if_fail (self != NULL, FALSE); - /* interface already acquired? */ - g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE); - - priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr, - nm_device_get_iface (NM_DEVICE (self)), - TRUE); - if (priv->supplicant.iface == NULL) { - nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.", - nm_device_get_iface (NM_DEVICE (self))); - return FALSE; - } - - if (nm_supplicant_interface_get_state (priv->supplicant.iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) - nm_device_add_pending_action (NM_DEVICE (self), "waiting for supplicant", TRUE); - - g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_STATE, - G_CALLBACK (supplicant_iface_state_cb), - self); - g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_NEW_BSS, - G_CALLBACK (supplicant_iface_new_bss_cb), - self); - g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_BSS_UPDATED, - G_CALLBACK (supplicant_iface_bss_updated_cb), - self); - g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_BSS_REMOVED, - G_CALLBACK (supplicant_iface_bss_removed_cb), - self); - g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_SCAN_DONE, - G_CALLBACK (supplicant_iface_scan_done_cb), - self); - g_signal_connect (priv->supplicant.iface, - "notify::scanning", - G_CALLBACK (supplicant_iface_notify_scanning_cb), - self); - - return TRUE; -} - -static void -remove_supplicant_interface_error_handler (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - if (!priv->supplicant.iface) - return; - - if (priv->supplicant.iface_error_id > 0) { - g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id); - priv->supplicant.iface_error_id = 0; - } - - if (priv->supplicant.iface_con_error_cb_id > 0) { - g_source_remove (priv->supplicant.iface_con_error_cb_id); - priv->supplicant.iface_con_error_cb_id = 0; - } -} - -static void -supplicant_interface_release (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv; - - g_return_if_fail (self != NULL); - - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - cancel_pending_scan (self); - - /* Reset the scan interval to be pretty frequent when disconnected */ - priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP; - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds", - nm_device_get_iface (NM_DEVICE (self)), - priv->scan_interval); - - remove_supplicant_interface_error_handler (self); - - if (priv->scanlist_cull_id) { - g_source_remove (priv->scanlist_cull_id); - priv->scanlist_cull_id = 0; - } - - if (priv->supplicant.iface) { - /* Clear supplicant interface signal handlers */ - g_signal_handlers_disconnect_matched (priv->supplicant.iface, G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, self); - - /* Tell the supplicant to disconnect from the current AP */ - nm_supplicant_interface_disconnect (priv->supplicant.iface); - - nm_supplicant_manager_iface_release (priv->supplicant.mgr, priv->supplicant.iface); - priv->supplicant.iface = NULL; - } -} - -static NMAccessPoint * -get_ap_by_path (NMDeviceWifi *self, const char *path) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GSList *iter; - - if (!path) - return NULL; - - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - if (g_strcmp0 (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0) - return NM_AP (iter->data); - } - return NULL; -} - -static NMAccessPoint * -get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GSList *iter; - - if (!path) - return NULL; - - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - if (g_strcmp0 (path, nm_ap_get_supplicant_path (NM_AP (iter->data))) == 0) - return NM_AP (iter->data); - } - return NULL; -} - -static NMAccessPoint * -find_active_ap (NMDeviceWifi *self, - NMAccessPoint *ignore_ap, - gboolean match_hidden) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - const char *iface = nm_device_get_iface (NM_DEVICE (self)); - int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); - struct ether_addr bssid; - GByteArray *ssid; - GSList *iter; - int i = 0; - NMAccessPoint *match_nofreq = NULL, *active_ap = NULL; - gboolean found_a_band = FALSE; - gboolean found_bg_band = FALSE; - NM80211Mode devmode; - guint32 devfreq; - - nm_platform_wifi_get_bssid (ifindex, &bssid); - nm_log_dbg (LOGD_WIFI, "(%s): active BSSID: %02x:%02x:%02x:%02x:%02x:%02x", - iface, - bssid.ether_addr_octet[0], bssid.ether_addr_octet[1], - bssid.ether_addr_octet[2], bssid.ether_addr_octet[3], - bssid.ether_addr_octet[4], bssid.ether_addr_octet[5]); - - if (!nm_ethernet_address_is_valid (&bssid)) - return NULL; - - ssid = nm_platform_wifi_get_ssid (ifindex); - nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s", - iface, - ssid ? "'" : "", - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", - ssid ? "'" : ""); - - devmode = nm_platform_wifi_get_mode (ifindex); - devfreq = nm_platform_wifi_get_frequency (ifindex); - - /* When matching hidden APs, do a second pass that ignores the SSID check, - * because NM might not yet know the SSID of the hidden AP in the scan list - * and therefore it won't get matched the first time around. - */ - while (i++ < (match_hidden ? 2 : 1)) { - nm_log_dbg (LOGD_WIFI, " Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : ""); - - /* Find this SSID + BSSID in the device's AP list */ - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - NMAccessPoint *ap = NM_AP (iter->data); - const struct ether_addr *ap_bssid = nm_ap_get_address (ap); - const GByteArray *ap_ssid = nm_ap_get_ssid (ap); - NM80211Mode apmode; - guint32 apfreq; - - nm_log_dbg (LOGD_WIFI, " AP: %s%s%s %02x:%02x:%02x:%02x:%02x:%02x", - ap_ssid ? "'" : "", - ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)", - ap_ssid ? "'" : "", - ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1], - ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3], - ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]); - - if (ap == ignore_ap) { - nm_log_dbg (LOGD_WIFI, " ignored"); - continue; - } - - if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) { - nm_log_dbg (LOGD_WIFI, " BSSID mismatch"); - continue; - } - - if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) { - nm_log_dbg (LOGD_WIFI, " SSID mismatch"); - continue; - } - - apmode = nm_ap_get_mode (ap); - if (devmode != apmode) { - nm_log_dbg (LOGD_WIFI, " mode mismatch (device %d, ap %d)", - devmode, apmode); - continue; - } - - apfreq = nm_ap_get_freq (ap); - if (devfreq != apfreq) { - nm_log_dbg (LOGD_WIFI, " frequency mismatch (device %u, ap %u)", - devfreq, apfreq); - - if (match_nofreq == NULL) - match_nofreq = ap; - - if (apfreq > 4000) - found_a_band = TRUE; - else if (apfreq > 2000) - found_bg_band = TRUE; - continue; - } - - // FIXME: handle security settings here too - nm_log_dbg (LOGD_WIFI, " matched"); - active_ap = ap; - goto done; - } - } - - /* Some proprietary drivers (wl.o) report tuned frequency (like when - * scanning) instead of the associated AP's frequency. This is a great - * example of how WEXT is underspecified. We use frequency to find the - * active AP in the scan list because some configurations use the same - * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to - * make sure we get the right AP in the right band. This configuration - * is uncommon though, and the frequency check penalizes closed drivers we - * can't fix. Because we're not total dicks, ignore the frequency condition - * if the associated BSSID/SSID exists only in one band since that's most - * likely the AP we want. Sometimes wl.o returns a frequency of 0, so if - * we can't match the AP based on frequency at all, just give up. - */ - if (match_nofreq && ((found_a_band != found_bg_band) || (devfreq == 0))) { - const struct ether_addr *ap_bssid = nm_ap_get_address (match_nofreq); - const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq); - - nm_log_dbg (LOGD_WIFI, " matched %s%s%s %02x:%02x:%02x:%02x:%02x:%02x", - ap_ssid ? "'" : "", - ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)", - ap_ssid ? "'" : "", - ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1], - ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3], - ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]); - - active_ap = match_nofreq; - goto done; - } - - nm_log_dbg (LOGD_WIFI, " No matching AP found."); - -done: - if (ssid) - g_byte_array_free (ssid, TRUE); - return active_ap; -} - -static void -update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap) -{ - NMActRequest *req; - NMConnection *connection; - - g_return_if_fail (NM_IS_DEVICE_WIFI (self)); - - if (ap == NULL) - return; - - /* Don't cache the BSSID for Ad-Hoc APs */ - if (nm_ap_get_mode (ap) != NM_802_11_MODE_INFRA) - return; - - if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) { - req = nm_device_get_act_request (NM_DEVICE (self)); - if (req) { - connection = nm_act_request_get_connection (req); - nm_settings_connection_add_seen_bssid (NM_SETTINGS_CONNECTION (connection), - nm_ap_get_address (ap)); - } - } -} - -static void -set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_available_connections, gboolean force_remove_old_ap) -{ - NMDeviceWifiPrivate *priv; - NMAccessPoint *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); - - /* Move the current AP to the front of the scan list. Since we - * do a lot of searches looking for the current AP, it saves - * time to have it in front. - */ - priv->ap_list = g_slist_remove (priv->ap_list, new_ap); - priv->ap_list = g_slist_prepend (priv->ap_list, new_ap); - - /* Update seen BSSIDs cache */ - update_seen_bssids_cache (self, priv->current_ap); - } else - priv->current_ap = NULL; - - if (old_ap) { - NM80211Mode mode = nm_ap_get_mode (old_ap); - - if (force_remove_old_ap || mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_ap_get_fake (old_ap)) { - remove_access_point (self, old_ap); - if (recheck_available_connections) - nm_device_recheck_available_connections (NM_DEVICE (self)); - } - g_object_unref (old_ap); - } - - g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT); -} - -static void -periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); - NMAccessPoint *new_ap; - guint32 new_rate; - int percent; - NMDeviceState state; - guint32 supplicant_state; - - /* BSSID and signal strength have meaningful values only if the device - * is activated and not scanning. - */ - state = nm_device_get_state (NM_DEVICE (self)); - if (state != NM_DEVICE_STATE_ACTIVATED) - return; - - /* Only update current AP if we're actually talking to something, otherwise - * assume the old one (if any) is still valid until we're told otherwise or - * the connection fails. - */ - supplicant_state = nm_supplicant_interface_get_state (priv->supplicant.iface); - if ( supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING - || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED - || nm_supplicant_interface_get_scanning (priv->supplicant.iface)) - return; - - /* In AP mode we currently have nothing to do. */ - if (priv->mode == NM_802_11_MODE_AP) - return; - - /* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where - * multiple IBSS stations using the same SSID will eventually switch to - * using the same BSSID to avoid network segmentation. When this happens, - * the card's reported BSSID will change, but the new BSS may not - * be in the scan list, since scanning isn't done in ad-hoc mode for - * various reasons. So pull the BSSID from the card and update the - * current AP with it, if the current AP is adhoc. - */ - if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) { - struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; - - nm_platform_wifi_get_bssid (ifindex, &bssid); - /* 0x02 means "locally administered" and should be OR-ed into - * the first byte of IBSS BSSIDs. - */ - if ( (bssid.ether_addr_octet[0] & 0x02) - && nm_ethernet_address_is_valid (&bssid)) - nm_ap_set_address (priv->current_ap, &bssid); - } - - new_ap = find_active_ap (self, ignore_ap, FALSE); - if (new_ap) { - /* Try to smooth out the strength. Atmel cards, for example, will give no strength - * one second and normal strength the next. - */ - percent = nm_platform_wifi_get_quality (ifindex); - if (percent >= 0 || ++priv->invalid_strength_counter > 3) { - nm_ap_set_strength (new_ap, (gint8) percent); - priv->invalid_strength_counter = 0; - } - } - - if (new_ap != priv->current_ap) { - const struct ether_addr *new_bssid = NULL; - const GByteArray *new_ssid = NULL; - const struct ether_addr *old_bssid = NULL; - const GByteArray *old_ssid = NULL; - char *old_addr = NULL, *new_addr = NULL; - - if (new_ap) { - new_bssid = nm_ap_get_address (new_ap); - new_addr = nm_utils_hwaddr_ntoa (new_bssid, ARPHRD_ETHER); - new_ssid = nm_ap_get_ssid (new_ap); - } - - if (priv->current_ap) { - old_bssid = nm_ap_get_address (priv->current_ap); - old_addr = nm_utils_hwaddr_ntoa (old_bssid, ARPHRD_ETHER); - old_ssid = nm_ap_get_ssid (priv->current_ap); - } - - nm_log_info (LOGD_WIFI, "(%s): roamed from BSSID %s (%s) to %s (%s)", - nm_device_get_iface (NM_DEVICE (self)), - old_addr ? old_addr : "(none)", - old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)", - new_addr ? new_addr : "(none)", - new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)"); - g_free (old_addr); - g_free (new_addr); - - set_current_ap (self, new_ap, TRUE, FALSE); - } - - new_rate = nm_platform_wifi_get_rate (ifindex); - if (new_rate != priv->rate) { - priv->rate = new_rate; - g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE); - } -} - -static gboolean -periodic_update_cb (gpointer user_data) -{ - periodic_update (NM_DEVICE_WIFI (user_data), NULL); - return TRUE; -} - -static gboolean -bring_up (NMDevice *device, gboolean *no_firmware) -{ - if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled) - return FALSE; - - return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->bring_up (device, no_firmware); -} - -static void -emit_ap_added_removed (NMDeviceWifi *self, - guint signum, - NMAccessPoint *ap, - gboolean recheck_available_connections) -{ - g_signal_emit (self, signals[signum], 0, ap); - g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_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 -remove_access_point (NMDeviceWifi *device, - NMAccessPoint *ap) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - g_return_if_fail (ap); - g_return_if_fail (ap != priv->current_ap); - g_return_if_fail (g_slist_find (priv->ap_list, ap)); - - priv->ap_list = g_slist_remove (priv->ap_list, ap); - emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE); - g_object_unref (ap); -} - -static void -remove_all_aps (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - if (priv->ap_list) { - set_current_ap (self, NULL, FALSE, FALSE); - - while (priv->ap_list) - remove_access_point (self, NM_AP (priv->ap_list->data)); - - nm_device_recheck_available_connections (NM_DEVICE (self)); - } -} - -static void -deactivate (NMDevice *dev) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - int ifindex = nm_device_get_ifindex (dev); - NMActRequest *req; - NMConnection *connection; - NM80211Mode old_mode = priv->mode; - - req = nm_device_get_act_request (dev); - if (req) { - connection = nm_act_request_get_connection (req); - /* Clear wireless secrets tries when deactivating */ - g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); - } - - if (priv->periodic_source_id) { - g_source_remove (priv->periodic_source_id); - priv->periodic_source_id = 0; - } - - cleanup_association_attempt (self, TRUE); - - priv->rate = 0; - - /* If the AP is 'fake', i.e. it wasn't actually found from - * a scan but the user tried to connect to it manually (maybe it - * was non-broadcasting or something) get rid of it, because 'fake' - * APs should only live for as long as we're connected to them. - **/ - set_current_ap (self, NULL, TRUE, FALSE); - - /* Clear any critical protocol notification in the Wi-Fi stack */ - nm_platform_wifi_indicate_addressing_running (ifindex, FALSE); - - /* Reset MAC address back to initial address */ - nm_device_set_hw_addr (dev, priv->initial_hw_addr, "reset", LOGD_WIFI); - - /* Ensure we're in infrastructure mode after deactivation; some devices - * (usually older ones) don't scan well in adhoc mode. - */ - if (nm_platform_wifi_get_mode (ifindex) != NM_802_11_MODE_INFRA) { - nm_device_take_down (NM_DEVICE (self), TRUE); - nm_platform_wifi_set_mode (ifindex, NM_802_11_MODE_INFRA); - nm_device_bring_up (NM_DEVICE (self), TRUE, NULL); - } - - if (priv->mode != NM_802_11_MODE_INFRA) { - priv->mode = NM_802_11_MODE_INFRA; - g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE); - } - - /* Ensure we trigger a scan after deactivating a Hotspot */ - if (old_mode == NM_802_11_MODE_AP) { - cancel_pending_scan (self); - request_wireless_scan (self); - } -} - -static gboolean -is_adhoc_wpa (NMConnection *connection) -{ - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - const char *mode, *key_mgmt; - - /* The kernel doesn't support Ad-Hoc WPA connections well at this time, - * and turns them into open networks. It's been this way since at least - * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. - */ - - s_wifi = nm_connection_get_setting_wireless (connection); - g_return_val_if_fail (s_wifi != NULL, FALSE); - - mode = nm_setting_wireless_get_mode (s_wifi); - if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) != 0) - return FALSE; - - s_wsec = nm_connection_get_setting_wireless_security (connection); - if (!s_wsec) - return FALSE; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - if (g_strcmp0 (key_mgmt, "wpa-none") != 0) - return FALSE; - - return TRUE; -} - -static gboolean -check_connection_compatible (NMDevice *device, - NMConnection *connection, - GError **error) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMSettingConnection *s_con; - NMSettingWireless *s_wireless; - const GByteArray *mac; - const GSList *mac_blacklist, *mac_blacklist_iter; - const char *mode; - - if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection, error)) - 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)) { - g_set_error (error, - NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, - "The connection was not a WiFi connection."); - return FALSE; - } - - s_wireless = nm_connection_get_setting_wireless (connection); - if (!s_wireless) { - g_set_error (error, - NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INVALID, - "The connection was not a valid WiFi connection."); - return FALSE; - } - - - mac = nm_setting_wireless_get_mac_address (s_wireless); - if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) { - g_set_error (error, - NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, - "The connection's MAC address did not match this device."); - return FALSE; - } - - /* Check for MAC address blacklist */ - mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); - for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter; - mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) { - struct ether_addr addr; - - if (!ether_aton_r (mac_blacklist_iter->data, &addr)) { - g_warn_if_reached (); - continue; - } - if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) { - g_set_error (error, - NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, - "The connection's MAC address (%s) is blacklisted in %s.", - (char *) mac_blacklist_iter->data, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); - return FALSE; - } - } - - if (is_adhoc_wpa (connection)) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, - "WPA Ad-Hoc disabled due to kernel bugs"); - return FALSE; - } - - /* Early exit if supplicant or device doesn't support requested mode */ - mode = nm_setting_wireless_get_mode (s_wireless); - if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) { - if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED, - "Ad-Hoc mode is not supported by this device."); - return FALSE; - } - } else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) { - if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, - "Access Point (AP) mode is not supported by this device."); - return FALSE; - } - - if (priv->supplicant.iface) { - switch (nm_supplicant_interface_get_ap_support (priv->supplicant.iface)) { - case AP_SUPPORT_NO: - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, - "Access Point (AP) mode is not supported by the supplicant."); - return FALSE; - case AP_SUPPORT_YES: - case AP_SUPPORT_UNKNOWN: - default: - break; - } - } - } - - // FIXME: check channel/freq/band against bands the hardware supports - // FIXME: check encryption against device capabilities - // FIXME: check bitrate against device capabilities - - return TRUE; -} - - -static gboolean -_internal_check_connection_available (NMDevice *device, - NMConnection *connection, - const char *specific_object, - gboolean ignore_ap_list) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); - NMSettingWireless *s_wifi; - const char *mode; - GSList *ap_iter = NULL; - - s_wifi = nm_connection_get_setting_wireless (connection); - g_return_val_if_fail (s_wifi, FALSE); - - if (specific_object) { - NMAccessPoint *ap; - - ap = get_ap_by_path (NM_DEVICE_WIFI (device), specific_object); - return ap ? nm_ap_check_compatible (ap, connection) : FALSE; - } - - /* Ad-Hoc and AP connections are always available because they may be - * started at any time. - */ - mode = nm_setting_wireless_get_mode (s_wifi); - if ( g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0 - || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) - return TRUE; - - /* Hidden SSIDs obviously don't always appear in the scan list either */ - if (nm_setting_wireless_get_hidden (s_wifi) || ignore_ap_list) - return TRUE; - - /* check if its visible */ - for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) { - if (nm_ap_check_compatible (NM_AP (ap_iter->data), connection)) - return TRUE; - } - - return FALSE; -} - -static gboolean -check_connection_available (NMDevice *device, - NMConnection *connection, - const char *specific_object) -{ - return _internal_check_connection_available (device, connection, specific_object, FALSE); -} - -/* FIXME: remove this function when we require the 'hidden' property to be - * set before a hidden connection can be activated. - */ -static gboolean -check_connection_available_wifi_hidden (NMDevice *device, - NMConnection *connection) -{ - return _internal_check_connection_available (device, connection, NULL, TRUE); -} - -/* - * 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", -}; - -#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) - -static gboolean -is_manf_default_ssid (const GByteArray *ssid) -{ - int i; - - for (i = 0; i < ARRAY_SIZE (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); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - NMSetting8021x *s_8021x; - const GByteArray *setting_mac; - char *format, *str_ssid = NULL; - NMAccessPoint *ap = NULL; - const GByteArray *ssid = NULL; - GSList *iter; - gboolean hidden = FALSE; - - s_wifi = nm_connection_get_setting_wireless (connection); - s_wsec = nm_connection_get_setting_wireless_security (connection); - s_8021x = nm_connection_get_setting_802_1x (connection); - - if (!specific_object) { - /* If not given a specific object, we need at minimum an SSID */ - if (!s_wifi) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INVALID, - "A 'wireless' setting is required if no AP path was given."); - return FALSE; - } - - ssid = nm_setting_wireless_get_ssid (s_wifi); - if (!ssid || !ssid->len) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INVALID, - "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 */ - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - if (nm_ap_check_compatible (NM_AP (iter->data), connection)) { - ap = NM_AP (iter->data); - break; - } - } - - /* If we still don't have an AP, then the WiFI settings needs to be - * fully specified by the client. Might not be able to find an AP - * if the network isn't broadcasting the SSID for example. - */ - if (!ap) { - GSList *settings = NULL; - gboolean valid; - - settings = g_slist_prepend (settings, s_wifi); - if (s_wsec) - settings = g_slist_prepend (settings, s_wsec); - if (s_8021x) - settings = g_slist_prepend (settings, s_8021x); - valid = nm_setting_verify (NM_SETTING (s_wifi), settings, error); - g_slist_free (settings); - if (!valid) - return FALSE; - - hidden = TRUE; - } - } else { - ap = get_ap_by_path (self, specific_object); - if (!ap) { - g_set_error (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_ACCESS_POINT_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)); - } - - if (ap) { - ssid = nm_ap_get_ssid (ap); - - if (ssid == NULL) { - /* The AP must be hidden. Connecting to a WiFi AP requires the SSID - * as part of the initial handshake, so check the connection details - * for the SSID. The AP object will still be used for encryption - * settings and such. - */ - ssid = nm_setting_wireless_get_ssid (s_wifi); - } - - if (ssid == NULL) { - /* If there's no SSID on the AP itself, and no SSID in the - * connection data, then we cannot connect at all. Return an error. - */ - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INVALID, - "A 'wireless' setting with a valid SSID is required for hidden access points."); - return FALSE; - } - - /* If the SSID is a well-known SSID, lock the connection to the AP's - * specific BSSID so NM doesn't autoconnect to some random wifi net. - */ - if (!nm_ap_complete_connection (ap, - connection, - is_manf_default_ssid (ssid), - error)) - return FALSE; - } - - /* The kernel doesn't support Ad-Hoc WPA connections well at this time, - * and turns them into open networks. It's been this way since at least - * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. - */ - if (is_adhoc_wpa (connection)) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_ERROR, - NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, - "WPA Ad-Hoc disabled due to kernel bugs"); - return FALSE; - } - - g_assert (ssid); - str_ssid = nm_utils_ssid_to_utf8 (ssid); - format = g_strdup_printf ("%s %%d", str_ssid); - - nm_utils_complete_generic (connection, - NM_SETTING_WIRELESS_SETTING_NAME, - existing_connections, - format, - str_ssid, - TRUE); - g_free (str_ssid); - g_free (format); - - if (hidden) - g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); - - 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 (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) { - g_set_error (error, - NM_SETTING_WIRELESS_ERROR, - NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, - NM_SETTING_WIRELESS_MAC_ADDRESS); - return FALSE; - } - } else { - GByteArray *mac; - const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - - /* Lock the connection to this device by default if it uses a - * permanent MAC address (ie not a 'locally administered' one) - */ - if ( !(priv->perm_hw_addr[0] & 0x02) - && memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) { - mac = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN); - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL); - g_byte_array_free (mac, TRUE); - } - } - - return TRUE; -} - -static gboolean -is_available (NMDevice *dev) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMSupplicantInterface *sup_iface; - guint32 state; - - if (!priv->enabled) { - nm_log_dbg (LOGD_WIFI, "(%s): not available because not enabled", - nm_device_get_iface (dev)); - return FALSE; - } - - sup_iface = priv->supplicant.iface; - if (!sup_iface) { - nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant not running", - nm_device_get_iface (dev)); - return FALSE; - } - - state = nm_supplicant_interface_get_state (sup_iface); - if ( state < NM_SUPPLICANT_INTERFACE_STATE_READY - || state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) { - nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant interface not ready", - nm_device_get_iface (dev)); - return FALSE; - } - - return TRUE; -} - -static gboolean -can_auto_connect (NMDevice *dev, - NMConnection *connection, - char **specific_object) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GSList *ap_iter; - const char *method = NULL; - guint64 timestamp = 0; - - if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (dev, connection, specific_object)) - 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; - } - - /* Use the connection if it's a shared connection */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) - return TRUE; - - for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) { - NMAccessPoint *ap = NM_AP (ap_iter->data); - - if (nm_ap_check_compatible (ap, connection)) { - /* All good; connection is usable */ - *specific_object = (char *) nm_ap_get_dbus_path (ap); - return TRUE; - } - } - - return FALSE; -} - -static void -ap_list_dump (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GSList * elt; - int i = 0; - - g_return_if_fail (NM_IS_DEVICE_WIFI (self)); - - nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list:"); - for (elt = priv->ap_list; elt; elt = g_slist_next (elt), i++) { - NMAccessPoint * ap = NM_AP (elt->data); - nm_ap_dump (ap, "List AP: "); - } - nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list: done"); -} - -static gboolean -impl_device_get_access_points (NMDeviceWifi *self, - GPtrArray **aps, - GError **err) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GSList *elt; - - *aps = g_ptr_array_new (); - for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) { - NMAccessPoint *ap = NM_AP (elt->data); - - if (nm_ap_get_ssid (ap)) - g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (ap))); - } - return TRUE; -} - -static gboolean -impl_device_get_all_access_points (NMDeviceWifi *self, - GPtrArray **aps, - GError **err) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GSList *elt; - - *aps = g_ptr_array_new (); - for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) - g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (NM_AP (elt->data)))); - return TRUE; -} - -static void -request_scan_cb (NMDevice *device, - DBusGMethodInvocation *context, - GError *error, - gpointer user_data) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (device); - GError *local = NULL; - - if (error) { - dbus_g_method_return_error (context, error); - return; - } - - if (!check_scanning_allowed (self)) { - local = g_error_new_literal (NM_WIFI_ERROR, - NM_WIFI_ERROR_SCAN_NOT_ALLOWED, - "Scanning not allowed at this time"); - dbus_g_method_return_error (context, local); - g_error_free (local); - return; - } - - cancel_pending_scan (self); - request_wireless_scan (self); - dbus_g_method_return (context); -} - -static void -impl_device_request_scan (NMDeviceWifi *self, - GHashTable *options, - DBusGMethodInvocation *context) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMDevice *device = NM_DEVICE (self); - gint32 last_scan; - GError *error; - - if ( !priv->enabled - || !priv->supplicant.iface - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { - error = g_error_new_literal (NM_WIFI_ERROR, - NM_WIFI_ERROR_SCAN_NOT_ALLOWED, - "Scanning not allowed while unavailable or activating"); - goto error; - } - - if (nm_supplicant_interface_get_scanning (priv->supplicant.iface)) { - error = g_error_new_literal (NM_WIFI_ERROR, - NM_WIFI_ERROR_SCAN_NOT_ALLOWED, - "Scanning not allowed while already scanning"); - goto error; - } - - last_scan = nm_supplicant_interface_get_last_scan_time (priv->supplicant.iface); - if (last_scan && (nm_utils_get_monotonic_timestamp_s () - last_scan) < 10) { - error = g_error_new_literal (NM_WIFI_ERROR, - NM_WIFI_ERROR_SCAN_NOT_ALLOWED, - "Scanning not allowed immediately following previous scan"); - goto error; - } - - /* 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, - request_scan_cb, - NULL); - return; - -error: - dbus_g_method_return_error (context, error); - g_error_free (error); -} - -static gboolean -scanning_allowed (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - guint32 sup_state; - NMActRequest *req; - - g_return_val_if_fail (priv->supplicant.iface != NULL, FALSE); - - /* Scanning not done in AP mode */ - if (priv->mode == NM_802_11_MODE_AP) - return FALSE; - - 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: - /* Don't scan when unusable or activating */ - return FALSE; - case NM_DEVICE_STATE_DISCONNECTED: - case NM_DEVICE_STATE_FAILED: - /* Can always scan when disconnected */ - return TRUE; - case NM_DEVICE_STATE_ACTIVATED: - /* Need to do further checks when activated */ - break; - } - - /* Don't scan if the supplicant is busy */ - sup_state = nm_supplicant_interface_get_state (priv->supplicant.iface); - if ( sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING - || sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED - || sup_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE - || sup_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE - || nm_supplicant_interface_get_scanning (priv->supplicant.iface)) - return FALSE; - - req = nm_device_get_act_request (NM_DEVICE (self)); - if (req) { - NMConnection *connection; - NMSettingWireless *s_wifi; - const char *ip4_method = NULL; - const GByteArray *bssid; - - /* Don't scan when a shared connection is active; it makes drivers mad */ - connection = nm_act_request_get_connection (req); - ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - - if (!strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) - return FALSE; - - /* Don't scan when the connection is locked to a specifc AP, since - * intra-ESS roaming (which requires periodic scanning) isn't being - * used due to the specific AP lock. (bgo #513820) - */ - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - bssid = nm_setting_wireless_get_bssid (s_wifi); - if (bssid && bssid->len == ETH_ALEN) - return FALSE; - } - - return TRUE; -} - -static gboolean -scanning_allowed_accumulator (GSignalInvocationHint *ihint, - GValue *return_accu, - const GValue *handler_return, - gpointer data) -{ - if (!g_value_get_boolean (handler_return)) - g_value_set_boolean (return_accu, FALSE); - return TRUE; -} - -static gboolean -check_scanning_allowed (NMDeviceWifi *self) -{ - GValue instance = G_VALUE_INIT; - GValue retval = G_VALUE_INIT; - - g_value_init (&instance, G_TYPE_OBJECT); - g_value_take_object (&instance, self); - - g_value_init (&retval, G_TYPE_BOOLEAN); - g_value_set_boolean (&retval, TRUE); - - /* Use g_signal_emitv() rather than g_signal_emit() to avoid the return - * value being changed if no handlers are connected */ - g_signal_emitv (&instance, signals[SCANNING_ALLOWED], 0, &retval); - - return g_value_get_boolean (&retval); -} - -static gboolean -hidden_filter_func (NMConnectionProvider *provider, - NMConnection *connection, - gpointer user_data) -{ - NMSettingWireless *s_wifi; - - s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); - return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE; -} - -static GPtrArray * -build_hidden_probe_list (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - guint max_scan_ssids = nm_supplicant_interface_get_max_scan_ssids (priv->supplicant.iface); - GSList *connections, *iter; - GPtrArray *ssids = NULL; - static GByteArray *nullssid = NULL; - - /* Need at least two: wildcard SSID and one or more hidden SSIDs */ - if (max_scan_ssids < 2) - return NULL; - - /* Static wildcard SSID used for every scan */ - if (G_UNLIKELY (nullssid == NULL)) - nullssid = g_byte_array_new (); - - connections = nm_connection_provider_get_best_connections (nm_connection_provider_get (), - max_scan_ssids - 1, - NM_SETTING_WIRELESS_SETTING_NAME, - NULL, - hidden_filter_func, - NULL); - if (connections && connections->data) { - ssids = g_ptr_array_sized_new (max_scan_ssids - 1); - g_ptr_array_add (ssids, nullssid); /* Add wildcard SSID */ - } - - for (iter = connections; iter; iter = g_slist_next (iter)) { - NMConnection *connection = iter->data; - NMSettingWireless *s_wifi; - const GByteArray *ssid; - - s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - ssid = nm_setting_wireless_get_ssid (s_wifi); - g_assert (ssid); - g_ptr_array_add (ssids, (gpointer) ssid); - } - g_slist_free (connections); - - return ssids; -} - -static gboolean -request_wireless_scan (gpointer user_data) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gboolean backoff = FALSE; - GPtrArray *ssids = NULL; - - if (priv->requested_scan) { - /* There's already a scan in progress */ - return FALSE; - } - - if (check_scanning_allowed (self)) { - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scanning requested", - nm_device_get_iface (NM_DEVICE (self))); - - ssids = build_hidden_probe_list (self); - - if (nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) { - if (ssids) { - guint i; - char *foo; - - for (i = 0; i < ssids->len; i++) { - foo = nm_utils_ssid_to_utf8 (g_ptr_array_index (ssids, i)); - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): (%d) probe scanning SSID '%s'", - nm_device_get_iface (NM_DEVICE (self)), - i, foo ? foo : ""); - g_free (foo); - } - } else { - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): no SSIDs to probe scan", - nm_device_get_iface (NM_DEVICE (self))); - } - } - - if (nm_supplicant_interface_request_scan (priv->supplicant.iface, ssids)) { - /* success */ - backoff = TRUE; - priv->requested_scan = TRUE; - nm_device_add_pending_action (NM_DEVICE (self), "scan", TRUE); - } - - if (ssids) { - /* Elements owned by the connections, so we don't free them here */ - g_ptr_array_free (ssids, TRUE); - } - } else { - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan requested but not allowed at this time", - nm_device_get_iface (NM_DEVICE (self))); - } - - priv->pending_scan_id = 0; - schedule_scan (self, backoff); - return FALSE; -} - - -/* - * schedule_scan - * - * Schedule a wireless scan. - * - */ -static void -schedule_scan (NMDeviceWifi *self, gboolean backoff) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gint32 now = nm_utils_get_monotonic_timestamp_s (); - - /* Cancel the pending scan if it would happen later than (now + the scan_interval) */ - if (priv->pending_scan_id) { - if (now + priv->scan_interval < priv->scheduled_scan_time) - cancel_pending_scan (self); - } - - if (!priv->pending_scan_id) { - guint factor = 2, next_scan = priv->scan_interval; - - if ( nm_device_is_activating (NM_DEVICE (self)) - || (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED)) - factor = 1; - - priv->pending_scan_id = g_timeout_add_seconds (next_scan, - request_wireless_scan, - self); - - priv->scheduled_scan_time = now + priv->scan_interval; - if (backoff && (priv->scan_interval < (SCAN_INTERVAL_MAX / factor))) { - priv->scan_interval += (SCAN_INTERVAL_STEP / factor); - /* Ensure the scan interval will never be less than 20s... */ - priv->scan_interval = MAX(priv->scan_interval, SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP); - /* ... or more than 120s */ - priv->scan_interval = MIN(priv->scan_interval, SCAN_INTERVAL_MAX); - } else if (!backoff && (priv->scan_interval == 0)) { - /* Invalid combination; would cause continual rescheduling of - * the scan and hog CPU. Reset to something minimally sane. - */ - priv->scan_interval = 5; - } - - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scheduled scan in %d seconds (interval now %d seconds)", - nm_device_get_iface (NM_DEVICE (self)), - next_scan, - priv->scan_interval); - - } -} - - -static void -cancel_pending_scan (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - if (priv->pending_scan_id) { - g_source_remove (priv->pending_scan_id); - priv->pending_scan_id = 0; - } -} - -static void -supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, - gboolean success, - NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan %s", - nm_device_get_iface (NM_DEVICE (self)), - success ? "successful" : "failed"); - - schedule_scan (self, success); - - /* Ensure that old APs get removed, which otherwise only - * happens when there are new BSSes. - */ - schedule_scanlist_cull (self); - - if (priv->requested_scan) { - priv->requested_scan = FALSE; - nm_device_remove_pending_action (NM_DEVICE (self), "scan", TRUE); - } -} - -/**************************************************************************** - * WPA Supplicant control stuff - * - */ - -#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" -#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5] - -/* - * merge_scanned_ap - * - * If there is already an entry that matches the BSSID and ESSID of the - * AP to merge, replace that entry with the scanned AP. Otherwise, add - * the scanned AP to the list. - * - * TODO: possibly need to differentiate entries based on security too; i.e. if - * there are two scan results with the same BSSID and SSID but different - * security options? - * - */ -static void -merge_scanned_ap (NMDeviceWifi *self, - NMAccessPoint *merge_ap) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMAccessPoint *found_ap = NULL; - const GByteArray *ssid; - const struct ether_addr *bssid; - gboolean strict_match = TRUE; - - /* Let the manager try to fill in the SSID from seen-bssids lists */ - bssid = nm_ap_get_address (merge_ap); - ssid = nm_ap_get_ssid (merge_ap); - if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) { - /* Let the manager try to fill the AP's SSID from the database */ - g_signal_emit (self, signals[HIDDEN_AP_FOUND], 0, merge_ap); - - ssid = nm_ap_get_ssid (merge_ap); - if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) { - /* Yay, matched it, no longer treat as hidden */ - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): matched hidden AP " MAC_FMT " => '%s'", - nm_device_get_iface (NM_DEVICE (self)), - MAC_ARG (bssid->ether_addr_octet), - nm_utils_escape_ssid (ssid->data, ssid->len)); - nm_ap_set_broadcast (merge_ap, FALSE); - } else { - /* Didn't have an entry for this AP in the database */ - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): failed to match hidden AP " MAC_FMT, - nm_device_get_iface (NM_DEVICE (self)), - MAC_ARG (bssid->ether_addr_octet)); - } - } - - /* If the incoming scan result matches the hidden AP that NM is currently - * connected to but hasn't been seen in the scan list yet, don't use - * strict matching. Because the capabilities of the fake AP have to be - * constructed from the NMConnection of the activation request, they won't - * always be the same as the capabilities of the real AP from the scan. - */ - if (priv->current_ap && nm_ap_get_fake (priv->current_ap)) - strict_match = FALSE; - - found_ap = get_ap_by_supplicant_path (self, nm_ap_get_supplicant_path (merge_ap)); - if (!found_ap) - found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match); - if (found_ap) { - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): merging AP '%s' " MAC_FMT " (%p) with existing (%p)", - nm_device_get_iface (NM_DEVICE (self)), - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", - MAC_ARG (bssid->ether_addr_octet), - merge_ap, - found_ap); - - nm_ap_set_supplicant_path (found_ap, nm_ap_get_supplicant_path (merge_ap)); - nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap)); - nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap)); - nm_ap_set_rsn_flags (found_ap, nm_ap_get_rsn_flags (merge_ap)); - nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap)); - nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap)); - nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap)); - nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap)); - nm_ap_set_max_bitrate (found_ap, nm_ap_get_max_bitrate (merge_ap)); - - /* If the AP is noticed in a scan, it's automatically no longer - * fake, since it clearly exists somewhere. - */ - nm_ap_set_fake (found_ap, FALSE); - } else { - /* New entry in the list */ - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): adding new AP '%s' " MAC_FMT " (%p)", - nm_device_get_iface (NM_DEVICE (self)), - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", - MAC_ARG (bssid->ether_addr_octet), - merge_ap); - - g_object_ref (merge_ap); - priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap); - nm_ap_export_to_dbus (merge_ap); - emit_ap_added_removed (self, ACCESS_POINT_ADDED, merge_ap, TRUE); - } -} - -#define WPAS_REMOVED_TAG "supplicant-removed" - -static gboolean -cull_scan_list (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gint32 now = nm_utils_get_monotonic_timestamp_s (); - GSList *outdated_list = NULL; - GSList *elt; - guint32 removed = 0, total = 0; - - priv->scanlist_cull_id = 0; - - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): checking scan list for outdated APs", - nm_device_get_iface (NM_DEVICE (self))); - - /* Walk the access point list and remove any access points older than - * three times the inactive scan interval. - */ - for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) { - NMAccessPoint *ap = elt->data; - const guint prune_interval_s = SCAN_INTERVAL_MAX * 3; - gint32 last_seen; - - /* Don't cull the associated AP or manually created APs */ - if (ap == priv->current_ap) - continue; - g_assert (!nm_ap_get_fake (ap)); /* only the current_ap can be fake */ - - /* Don't cull APs still known to the supplicant. Since the supplicant - * doesn't yet emit property updates for "last seen" we have to rely - * on changing signal strength for updating "last seen". But if the - * AP's strength doesn't change we won't get any updates for the AP, - * and we'll end up here even if the AP was still found by the - * supplicant in the last scan. - */ - if ( nm_ap_get_supplicant_path (ap) - && g_object_get_data (G_OBJECT (ap), WPAS_REMOVED_TAG) == NULL) - continue; - - last_seen = nm_ap_get_last_seen (ap); - if (!last_seen || last_seen + prune_interval_s < now) - outdated_list = g_slist_prepend (outdated_list, ap); - } - - /* Remove outdated APs */ - for (elt = outdated_list; elt; elt = g_slist_next (elt)) { - NMAccessPoint *outdated_ap = NM_AP (elt->data); - const struct ether_addr *bssid; - const GByteArray *ssid; - - bssid = nm_ap_get_address (outdated_ap); - ssid = nm_ap_get_ssid (outdated_ap); - nm_log_dbg (LOGD_WIFI_SCAN, - " removing %02x:%02x:%02x:%02x:%02x:%02x (%s%s%s)", - bssid->ether_addr_octet[0], bssid->ether_addr_octet[1], - bssid->ether_addr_octet[2], bssid->ether_addr_octet[3], - bssid->ether_addr_octet[4], bssid->ether_addr_octet[5], - ssid ? "'" : "", - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", - ssid ? "'" : ""); - - remove_access_point (self, outdated_ap); - removed++; - } - g_slist_free (outdated_list); - - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): removed %d APs (of %d)", - nm_device_get_iface (NM_DEVICE (self)), - removed, total); - - ap_list_dump (self); - - if(removed > 0) - nm_device_recheck_available_connections (NM_DEVICE (self)); - - return FALSE; -} - -static void -schedule_scanlist_cull (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - /* Cull the scan list after the last request for it has come in */ - if (priv->scanlist_cull_id) - g_source_remove (priv->scanlist_cull_id); - priv->scanlist_cull_id = g_timeout_add_seconds (4, (GSourceFunc) cull_scan_list, self); -} - -static void -supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, - const char *object_path, - GHashTable *properties, - NMDeviceWifi *self) -{ - NMDeviceState state; - NMAccessPoint *ap; - - g_return_if_fail (self != NULL); - g_return_if_fail (properties != NULL); - g_return_if_fail (iface != NULL); - - /* Ignore new APs when unavailable, unmanaged, or in AP mode */ - state = nm_device_get_state (NM_DEVICE (self)); - if (state <= NM_DEVICE_STATE_UNAVAILABLE) - return; - if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) - return; - - ap = nm_ap_new_from_properties (object_path, properties); - if (ap) { - nm_ap_dump (ap, "New AP: "); - - /* Add the AP to the device's AP list */ - merge_scanned_ap (self, ap); - g_object_unref (ap); - } else { - nm_log_warn (LOGD_WIFI_SCAN, "(%s): invalid AP properties received", - nm_device_get_iface (NM_DEVICE (self))); - } - - /* Remove outdated access points */ - schedule_scanlist_cull (self); -} - -static void -supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, - const char *object_path, - GHashTable *properties, - NMDeviceWifi *self) -{ - NMDeviceState state; - NMAccessPoint *ap; - - g_return_if_fail (self != NULL); - g_return_if_fail (object_path != NULL); - g_return_if_fail (properties != NULL); - - /* Ignore new APs when unavailable or unamnaged */ - state = nm_device_get_state (NM_DEVICE (self)); - if (state <= NM_DEVICE_STATE_UNAVAILABLE) - return; - - /* Update the AP's last-seen property */ - ap = get_ap_by_supplicant_path (self, object_path); - if (ap) - nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ()); - - /* Remove outdated access points */ - schedule_scanlist_cull (self); -} - -static void -supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, - const char *object_path, - NMDeviceWifi *self) -{ - NMAccessPoint *ap; - - g_return_if_fail (self != NULL); - g_return_if_fail (object_path != NULL); - - ap = get_ap_by_supplicant_path (self, object_path); - if (ap) - g_object_set_data (G_OBJECT (ap), WPAS_REMOVED_TAG, GUINT_TO_POINTER (TRUE)); -} - - -static void -cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - remove_supplicant_interface_error_handler (self); - remove_supplicant_timeouts (self); - if (disconnect && priv->supplicant.iface) - nm_supplicant_interface_disconnect (priv->supplicant.iface); -} - -static void -wifi_secrets_cb (NMActRequest *req, - guint32 call_id, - NMConnection *connection, - GError *error, - gpointer user_data) -{ - NMDevice *dev = NM_DEVICE (user_data); - - g_return_if_fail (req == nm_device_get_act_request (dev)); - g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); - g_return_if_fail (nm_act_request_get_connection (req) == connection); - - if (error) { - nm_log_warn (LOGD_WIFI, "%s", error->message); - nm_device_state_changed (dev, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); - } else - nm_device_activate_schedule_stage1_device_prepare (dev); -} - -static void -remove_link_timeout (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv; - - g_return_if_fail (self != NULL); - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - if (priv->link_timeout_id) { - g_source_remove (priv->link_timeout_id); - priv->link_timeout_id = 0; - } -} - - -/* - * link_timeout_cb - * - * Called when the link to the access point has been down for a specified - * period of time. - */ -static gboolean -link_timeout_cb (gpointer user_data) -{ - NMDevice *dev = NM_DEVICE (user_data); - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev)); - - priv->link_timeout_id = 0; - - /* Disconnect event while activated; the supplicant hasn't been able - * to reassociate within the timeout period, so the connection must - * fail. - */ - if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED) - return FALSE; - - /* If the access point failed, and wasn't found by the supplicant when it - * attempted to reconnect, then it's probably out of range or turned off. - * Remove it from the list and if it's actually still present, it'll be - * found in the next scan. - */ - if (priv->ssid_found == FALSE && priv->current_ap) - set_current_ap (self, NULL, TRUE, TRUE); - - nm_device_state_changed (dev, - NM_DEVICE_STATE_FAILED, - priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT : - NM_DEVICE_STATE_REASON_SSID_NOT_FOUND); - return FALSE; -} - -static gboolean -need_new_8021x_secrets (NMDeviceWifi *self, - guint32 old_state, - 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_connection (NM_DEVICE (self)); - g_return_val_if_fail (connection != NULL, FALSE); - - /* 802.1x stuff only happens in the supplicant's ASSOCIATED state when it's - * attempting to authenticate with the AP. - */ - if (old_state != NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED) - return 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) { - nm_setting_get_secret_flags (NM_SETTING (s_8021x), - NM_SETTING_802_1X_PASSWORD, - &secret_flags, - NULL); - 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) { - nm_setting_get_secret_flags (NM_SETTING (s_wsec), - NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, - &secret_flags, - NULL); - 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, - guint32 old_state, - const char **setting_name) -{ - NMSettingWirelessSecurity *s_wsec; - NMConnection *connection; - const char *key_mgmt = NULL; - - g_assert (setting_name != NULL); - - connection = nm_device_get_connection (NM_DEVICE (self)); - g_return_val_if_fail (connection != NULL, FALSE); - - /* A bad PSK will cause the supplicant to disconnect during the 4-way handshake */ - if (old_state != NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE) - return 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) { - *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, - guint32 new_state, - guint32 old_state, - int disconnect_reason) -{ - NMDevice *device = NM_DEVICE (self); - NMActRequest *req; - NMConnection *connection; - const char *setting_name = NULL; - gboolean handled = FALSE; - - g_return_val_if_fail (new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE); - - req = nm_device_get_act_request (NM_DEVICE (self)); - g_return_val_if_fail (req != NULL, FALSE); - - connection = nm_act_request_get_connection (req); - g_assert (connection); - - if ( need_new_8021x_secrets (self, old_state, &setting_name) - || need_new_wpa_psk (self, old_state, &setting_name)) { - - nm_connection_clear_secrets (connection); - - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): disconnected during association," - " asking for new key.", nm_device_get_iface (device)); - - cleanup_association_attempt (self, TRUE); - nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - nm_act_request_get_secrets (req, - setting_name, - NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION - | NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW, - NULL, - wifi_secrets_cb, - self); - handled = TRUE; - } - - return handled; -} - -static void -supplicant_iface_state_cb (NMSupplicantInterface *iface, - guint32 new_state, - guint32 old_state, - int disconnect_reason, - gpointer user_data) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMDevice *device = NM_DEVICE (self); - NMDeviceState devstate; - gboolean scanning; - - if (new_state == old_state) - return; - - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "(%s): supplicant interface state: %s -> %s", - nm_device_get_iface (device), - nm_supplicant_interface_state_to_string (old_state), - nm_supplicant_interface_state_to_string (new_state)); - - devstate = nm_device_get_state (device); - scanning = nm_supplicant_interface_get_scanning (iface); - - /* In these states we know the supplicant is actually talking to something */ - if ( new_state >= NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING - && new_state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) - priv->ssid_found = TRUE; - - switch (new_state) { - case NM_SUPPLICANT_INTERFACE_STATE_READY: - priv->scan_interval = SCAN_INTERVAL_MIN; - - /* If the interface can now be activated because the supplicant is now - * available, transition to DISCONNECTED. - */ - if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE); - } - - nm_log_dbg (LOGD_WIFI_SCAN, - "(%s): supplicant ready, requesting initial scan", - nm_device_get_iface (device)); - - /* Request a scan to get latest results */ - cancel_pending_scan (self); - request_wireless_scan (self); - - if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) - nm_device_remove_pending_action (device, "waiting for supplicant", TRUE); - break; - case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: - remove_supplicant_interface_error_handler (self); - remove_supplicant_timeouts (self); - - /* If this is the initial association during device activation, - * schedule the next activation stage. - */ - if (devstate == NM_DEVICE_STATE_CONFIG) { - NMConnection *connection; - NMSettingWireless *s_wifi; - const GByteArray *ssid; - - connection = nm_device_get_connection (NM_DEVICE (self)); - g_return_if_fail (connection); - - s_wifi = nm_connection_get_setting_wireless (connection); - g_return_if_fail (s_wifi); - - ssid = nm_setting_wireless_get_ssid (s_wifi); - g_return_if_fail (ssid); - - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless) Stage 2 of 5 (Device Configure) " - "successful. %s '%s'.", - nm_device_get_iface (device), - priv->mode == NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot" : - "Connected to wireless network", - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)"); - nm_device_activate_schedule_stage3_ip_config_start (device); - } else if (devstate == NM_DEVICE_STATE_ACTIVATED) - periodic_update (self, NULL); - break; - case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED: - if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) { - /* Disconnect of an 802.1x/LEAP connection during authentication, - * or disconnect of a WPA-PSK connection during the 4-way handshake, - * often means secrets are wrong. Not always the case, but until we - * have more information from wpa_supplicant about why the - * disconnect happened this is the best we can do. - */ - if (handle_8021x_or_psk_auth_fail (self, new_state, old_state, disconnect_reason)) - break; - } - - /* Otherwise it might be a stupid driver or some transient error, so - * let the supplicant try to reconnect a few more times. Give it more - * time if a scan is in progress since the link might be dropped during - * the scan but will be re-established when the scan is done. - */ - if (devstate == NM_DEVICE_STATE_ACTIVATED) { - if (priv->link_timeout_id == 0) { - priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, link_timeout_cb, self); - priv->ssid_found = FALSE; - } - } - break; - case NM_SUPPLICANT_INTERFACE_STATE_DOWN: - cleanup_association_attempt (self, FALSE); - - /* If the device is already in UNAVAILABLE state then the state change - * is a NOP and the interface won't be re-acquired in the device state - * change handler. So ensure we have a new one here so that we're - * ready if the supplicant comes back. - */ - supplicant_interface_release (self); - supplicant_interface_acquire (self); - - nm_device_state_changed (device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - break; - default: - break; - } - - /* Signal scanning state changes */ - if ( new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING - || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) - g_object_notify (G_OBJECT (self), "scanning"); -} - -struct iface_con_error_cb_data { - NMDeviceWifi *self; - char *name; - char *message; -}; - -static gboolean -supplicant_iface_connection_error_cb_handler (gpointer user_data) -{ - NMDeviceWifi *self; - NMDeviceWifiPrivate *priv; - struct iface_con_error_cb_data * cb_data = (struct iface_con_error_cb_data *) user_data; - - g_return_val_if_fail (cb_data != NULL, FALSE); - - self = cb_data->self; - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - if (!nm_device_is_activating (NM_DEVICE (self))) - goto out; - - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): association request to the supplicant " - "failed: %s - %s", - nm_device_get_iface (NM_DEVICE (self)), - cb_data->name, - cb_data->message); - - cleanup_association_attempt (self, TRUE); - nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - -out: - priv->supplicant.iface_con_error_cb_id = 0; - g_free (cb_data->name); - g_free (cb_data->message); - g_slice_free (struct iface_con_error_cb_data, cb_data); - return FALSE; -} - - -static void -supplicant_iface_connection_error_cb (NMSupplicantInterface * iface, - const char * name, - const char * message, - NMDeviceWifi * self) -{ - NMDeviceWifiPrivate *priv; - struct iface_con_error_cb_data *cb_data; - guint id; - - g_return_if_fail (self != NULL); - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - cb_data = g_slice_new0 (struct iface_con_error_cb_data); - cb_data->self = self; - cb_data->name = g_strdup (name); - cb_data->message = g_strdup (message); - - if (priv->supplicant.iface_con_error_cb_id) - g_source_remove (priv->supplicant.iface_con_error_cb_id); - - id = g_idle_add (supplicant_iface_connection_error_cb_handler, cb_data); - priv->supplicant.iface_con_error_cb_id = id; -} - -static void -supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface, - GParamSpec *pspec, - NMDeviceWifi *self) -{ - NMDeviceState state; - gboolean scanning; - - scanning = nm_supplicant_interface_get_scanning (iface); - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): now %s", - nm_device_get_iface (NM_DEVICE (self)), - scanning ? "scanning" : "idle"); - - g_object_notify (G_OBJECT (self), "scanning"); - - /* Run a quick update of current AP when coming out of a scan */ - state = nm_device_get_state (NM_DEVICE (self)); - if (!scanning && state == NM_DEVICE_STATE_ACTIVATED) - periodic_update (self, NULL); -} - -static void -remove_supplicant_connection_timeout (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv; - - g_return_if_fail (self != NULL); - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - /* Remove any pending timeouts on the request */ - if (priv->supplicant.con_timeout_id) { - g_source_remove (priv->supplicant.con_timeout_id); - priv->supplicant.con_timeout_id = 0; - } -} - -static NMActStageReturn -handle_auth_or_fail (NMDeviceWifi *self, - NMActRequest *req, - gboolean new_secrets) -{ - const char *setting_name; - guint32 tries; - NMConnection *connection; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - - g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE); - - if (!req) { - req = nm_device_get_act_request (NM_DEVICE (self)); - g_assert (req); - } - - connection = nm_act_request_get_connection (req); - g_assert (connection); - - tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES)); - if (tries > 3) - return NM_ACT_STAGE_RETURN_FAILURE; - - nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); - - nm_connection_clear_secrets (connection); - setting_name = nm_connection_need_secrets (connection, NULL); - if (setting_name) { - NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION; - - if (new_secrets) - flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW; - nm_act_request_get_secrets (req, setting_name, flags, NULL, wifi_secrets_cb, self); - - g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else - nm_log_warn (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); - - return ret; -} - -/* - * supplicant_connection_timeout_cb - * - * Called when the supplicant has been unable to connect to an access point - * within a specified period of time. - */ -static gboolean -supplicant_connection_timeout_cb (gpointer user_data) -{ - NMDevice *dev = NM_DEVICE (user_data); - NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMActRequest *req; - NMConnection *connection; - - cleanup_association_attempt (self, TRUE); - - if (!nm_device_is_activating (dev)) - return FALSE; - - /* Timed out waiting for a successful connection to the AP; if the AP's - * security requires network-side authentication (like WPA or 802.1x) - * and the connection attempt timed out then it's likely the authentication - * information (passwords, pin codes, etc) are wrong. - */ - - req = nm_device_get_act_request (dev); - g_assert (req); - - connection = nm_act_request_get_connection (req); - g_assert (connection); - - if ( priv->mode == NM_802_11_MODE_ADHOC - || priv->mode == NM_802_11_MODE_AP) { - /* In Ad-Hoc and AP modes there's nothing to check the encryption key - * (if any), so supplicant timeouts here are almost certainly the wifi - * driver being really stupid. - */ - nm_log_warn (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): %s network creation took " - "too long, failing activation.", - nm_device_get_iface (dev), - priv->mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc" : "Hotspot"); - nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); - return FALSE; - } - - g_assert (priv->mode == NM_802_11_MODE_INFRA); - - if (priv->ssid_found && nm_connection_get_setting_wireless_security (connection)) { - guint64 timestamp = 0; - gboolean new_secrets = TRUE; - - /* Connection failed; either driver problems, the encryption key is - * wrong, or the passwords or certificates were wrong. - */ - nm_log_warn (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): association took too long.", - nm_device_get_iface (dev)); - - /* Ask for new secrets only if we've never activated this connection - * before. If we've connected before, don't bother the user with - * dialogs, just retry or fail, and if we never connect the user can - * fix the password somewhere else. - */ - if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), ×tamp)) - new_secrets = !timestamp; - - if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE) { - nm_log_warn (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): asking for new secrets", - nm_device_get_iface (dev)); - } else { - nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); - } - } else { - nm_log_warn (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): association took too long, " - "failing activation.", - nm_device_get_iface (dev)); - nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, - priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT : - NM_DEVICE_STATE_REASON_SSID_NOT_FOUND); - } - - return FALSE; -} - - -static gboolean -start_supplicant_connection_timeout (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv; - guint id; - - g_return_val_if_fail (self != NULL, FALSE); - - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - /* Set up a timeout on the connection attempt to fail it after 25 seconds */ - id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self); - if (id == 0) { - nm_log_err (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): couldn't start supplicant " - "timeout timer.", - nm_device_get_iface (NM_DEVICE (self))); - return FALSE; - } - priv->supplicant.con_timeout_id = id; - return TRUE; -} - - -static void -remove_supplicant_timeouts (NMDeviceWifi *self) -{ - g_return_if_fail (self != NULL); - - remove_supplicant_connection_timeout (self); - remove_link_timeout (self); -} - -static NMSupplicantConfig * -build_supplicant_config (NMDeviceWifi *self, - NMConnection *connection, - guint32 fixed_freq) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMSupplicantConfig *config = NULL; - NMSettingWireless *s_wireless; - NMSettingWirelessSecurity *s_wireless_sec; - - g_return_val_if_fail (self != NULL, NULL); - - s_wireless = nm_connection_get_setting_wireless (connection); - g_return_val_if_fail (s_wireless != NULL, NULL); - - config = nm_supplicant_config_new (); - if (!config) - return NULL; - - /* Warn if AP mode may not be supported */ - if ( g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0 - && nm_supplicant_interface_get_ap_support (priv->supplicant.iface) == AP_SUPPORT_UNKNOWN) { - nm_log_warn (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out."); - } - - if (!nm_supplicant_config_add_setting_wireless (config, - s_wireless, - fixed_freq)) { - nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config."); - goto error; - } - - s_wireless_sec = nm_connection_get_setting_wireless_security (connection); - if (s_wireless_sec) { - NMSetting8021x *s_8021x; - const char *con_uuid = nm_connection_get_uuid (connection); - - g_assert (con_uuid); - s_8021x = nm_connection_get_setting_802_1x (connection); - if (!nm_supplicant_config_add_setting_wireless_security (config, - s_wireless_sec, - s_8021x, - con_uuid)) { - nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to " - "supplicant config."); - goto error; - } - } else { - if (!nm_supplicant_config_add_no_security (config)) { - nm_log_err (LOGD_WIFI, "Couldn't add unsecured option to supplicant config."); - goto error; - } - } - - return config; - -error: - g_object_unref (config); - return NULL; -} - -/****************************************************************************/ - -static void -update_permanent_hw_address (NMDevice *dev) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - struct ifreq req; - struct ethtool_perm_addr *epaddr = NULL; - int fd, ret; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_HW, "could not open control socket."); - return; - } - - /* Get permanent MAC address */ - memset (&req, 0, sizeof (struct ifreq)); - strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ); - - epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN); - epaddr->cmd = ETHTOOL_GPERMADDR; - epaddr->size = ETH_ALEN; - req.ifr_data = (void *) epaddr; - - errno = 0; - ret = ioctl (fd, SIOCETHTOOL, &req); - if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) { - nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)", - nm_device_get_iface (dev), errno); - /* Fall back to current address */ - memcpy (epaddr->data, nm_device_get_hw_address (dev, NULL), ETH_ALEN); - } - - if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) { - memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN); - g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS); - } - - g_free (epaddr); - close (fd); -} - -static void -update_initial_hw_address (NMDevice *dev) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - char *mac_str; - - /* This sets initial MAC address from current MAC address. It should only - * be called from NMDevice constructor() to really get the initial address. - */ - memcpy (priv->initial_hw_addr, nm_device_get_hw_address (dev, NULL), ETH_ALEN); - - mac_str = nm_utils_hwaddr_ntoa (priv->initial_hw_addr, ARPHRD_ETHER); - nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s", - nm_device_get_iface (dev), mac_str); - g_free (mac_str); -} - -static NMActStageReturn -act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMActStageReturn ret; - NMAccessPoint *ap = NULL; - NMActRequest *req; - NMConnection *connection; - NMSettingWireless *s_wireless; - const GByteArray *cloned_mac; - GSList *iter; - const char *mode; - const char *ap_path; - - ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (dev, 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 != NULL, NM_ACT_STAGE_RETURN_FAILURE); - - connection = nm_act_request_get_connection (req); - g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); - - s_wireless = nm_connection_get_setting_wireless (connection); - g_assert (s_wireless); - - mode = nm_setting_wireless_get_mode (s_wireless); - if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) - priv->mode = NM_802_11_MODE_INFRA; - else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) - priv->mode = NM_802_11_MODE_ADHOC; - else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) { - priv->mode = NM_802_11_MODE_AP; - - /* Scanning not done in AP mode; clear the scan list */ - remove_all_aps (self); - } - g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE); - - /* The kernel doesn't support Ad-Hoc WPA connections well at this time, - * and turns them into open networks. It's been this way since at least - * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. - */ - if (is_adhoc_wpa (connection)) { - nm_log_warn (LOGD_WIFI, "Ad-Hoc WPA disabled due to kernel bugs"); - *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; - return NM_ACT_STAGE_RETURN_FAILURE; - } - - /* Set spoof MAC to the interface */ - cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); - if (cloned_mac && (cloned_mac->len == ETH_ALEN)) - nm_device_set_hw_addr (dev, (const guint8 *) cloned_mac->data, "set", LOGD_WIFI); - - /* AP mode never uses a specific object or existing scanned AP */ - if (priv->mode != NM_802_11_MODE_AP) { - - 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) - goto done; - - /* Find a compatible AP in the scan list */ - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - NMAccessPoint *candidate = NM_AP (iter->data); - - if (nm_ap_check_compatible (candidate, connection)) { - ap = candidate; - break; - } - } - } - - if (ap) { - nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap)); - goto done; - } - - /* If the user is trying to connect to an AP that NM doesn't yet know about - * (hidden network or something) or starting a Hotspot, create an fake AP - * from the security settings in the connection. This "fake" AP gets used - * until the real one is found in the scan list (Ad-Hoc or Hidden), or until - * the device is deactivated (Hotspot). - */ - ap = nm_ap_new_fake_from_connection (connection); - g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); - - if (nm_ap_get_mode (ap) == NM_802_11_MODE_INFRA) - nm_ap_set_broadcast (ap, FALSE); - else if (nm_ap_is_hotspot (ap)) - nm_ap_set_address (ap, (const struct ether_addr *) nm_device_get_hw_address (dev, NULL)); - - priv->ap_list = g_slist_prepend (priv->ap_list, ap); - nm_ap_export_to_dbus (ap); - g_object_freeze_notify (G_OBJECT (self)); - set_current_ap (self, ap, FALSE, FALSE); - emit_ap_added_removed (self, ACCESS_POINT_ADDED, ap, TRUE); - g_object_thaw_notify (G_OBJECT (self)); - nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap)); - return NM_ACT_STAGE_RETURN_SUCCESS; - -done: - set_current_ap (self, ap, TRUE, FALSE); - return NM_ACT_STAGE_RETURN_SUCCESS; -} - -static void -ensure_hotspot_frequency (NMDeviceWifi *self, - NMSettingWireless *s_wifi, - NMAccessPoint *ap) -{ - const char *band = nm_setting_wireless_get_band (s_wifi); - const guint32 a_freqs[] = { 5180, 5200, 5220, 5745, 5765, 5785, 5805, 0 }; - const guint32 bg_freqs[] = { 2412, 2437, 2462, 2472, 0 }; - guint32 freq = 0; - - g_assert (ap); - - if (nm_ap_get_freq (ap)) - return; - - if (g_strcmp0 (band, "a") == 0) - freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), a_freqs); - else - freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs); - - if (!freq) - freq = (g_strcmp0 (band, "a") == 0) ? 5180 : 2462; - - nm_ap_set_freq (ap, freq); -} - -static NMActStageReturn -act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - const char *iface = nm_device_get_iface (dev); - NMSupplicantConfig *config = NULL; - gulong id = 0; - NMActRequest *req; - NMAccessPoint *ap; - NMConnection *connection; - const char *setting_name; - NMSettingWireless *s_wireless; - - g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); - - remove_supplicant_timeouts (self); - - req = nm_device_get_act_request (dev); - g_assert (req); - - ap = priv->current_ap; - if (!ap) { - *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED; - goto out; - } - - connection = nm_act_request_get_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) { - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): access point '%s' has security," - " but secrets are required.", - iface, nm_connection_get_id (connection)); - - ret = handle_auth_or_fail (self, req, FALSE); - if (ret == NM_ACT_STAGE_RETURN_FAILURE) - *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; - goto out; - } - - /* have secrets, or no secrets required */ - if (nm_connection_get_setting_wireless_security (connection)) { - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): connection '%s' has security" - ", and secrets exist. No new secrets needed.", - iface, nm_connection_get_id (connection)); - } else { - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): connection '%s' requires no " - "security. No secrets needed.", - iface, nm_connection_get_id (connection)); - } - - priv->ssid_found = FALSE; - - /* Supplicant requires an initial frequency for Ad-Hoc and Hotspot; if the user - * didn't specify one and we didn't find an AP that matched the connection, - * just pick a frequency the device supports. - */ - if ((nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) || nm_ap_is_hotspot (ap)) - ensure_hotspot_frequency (self, s_wireless, ap); - - /* Build up the supplicant configuration */ - config = build_supplicant_config (self, connection, nm_ap_get_freq (ap)); - if (config == NULL) { - nm_log_err (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): couldn't build wireless configuration.", - iface); - *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; - goto out; - } - - /* Hook up error signal handler to capture association errors */ - id = g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR, - G_CALLBACK (supplicant_iface_connection_error_cb), - self); - priv->supplicant.iface_error_id = id; - - if (!nm_supplicant_interface_set_config (priv->supplicant.iface, config)) { - nm_log_err (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): couldn't send wireless " - "configuration to the supplicant.", iface); - *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; - goto out; - } - - if (!start_supplicant_connection_timeout (self)) { - *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; - goto out; - } - - if (!priv->periodic_source_id) - priv->periodic_source_id = g_timeout_add_seconds (6, periodic_update_cb, self); - - /* 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, TRUE); - - if (config) { - /* Supplicant interface object refs the config; we no longer care about - * it after this function. - */ - g_object_unref (config); - } - return ret; -} - -static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *reason) -{ - NMConnection *connection; - NMSettingIP4Config *s_ip4; - const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - - connection = nm_device_get_connection (device); - g_assert (connection); - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) - method = nm_setting_ip4_config_get_method (s_ip4); - - /* Indicate that a critical protocol is about to start */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE); - - return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, reason); -} - -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *reason) -{ - NMConnection *connection; - NMSettingIP6Config *s_ip6; - const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; - - connection = nm_device_get_connection (device); - g_assert (connection); - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6) - method = nm_setting_ip6_config_get_method (s_ip6); - - /* Indicate that a critical protocol is about to start */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 || - strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE); - - return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, reason); -} - -static void -ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) -{ - NMConnection *connection; - NMSettingWireless *s_wifi; - guint32 mtu; - - connection = nm_device_get_connection (device); - g_assert (connection); - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - - /* MTU override */ - mtu = nm_setting_wireless_get_mtu (s_wifi); - if (mtu) - nm_ip4_config_set_mtu (config, mtu); -} - -static gboolean -is_static_wep (NMConnection *connection) -{ - NMSettingWirelessSecurity *s_wsec; - const char *str; - - g_return_val_if_fail (connection != NULL, FALSE); - - s_wsec = nm_connection_get_setting_wireless_security (connection); - if (!s_wsec) - return FALSE; - - str = nm_setting_wireless_security_get_key_mgmt (s_wsec); - if (g_strcmp0 (str, "none") != 0) - return FALSE; - - str = nm_setting_wireless_security_get_auth_alg (s_wsec); - if (g_strcmp0 (str, "leap") == 0) - return FALSE; - - return TRUE; -} - -static NMActStageReturn -handle_ip_config_timeout (NMDeviceWifi *self, - NMConnection *connection, - gboolean may_fail, - gboolean *chain_up, - NMDeviceStateReason *reason) -{ - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - - g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); - - if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) { - *chain_up = TRUE; - return ret; - } - - /* If IP configuration times out and it's a static WEP connection, that - * usually means the WEP key is wrong. WEP's Open System auth mode has - * no provision for figuring out if the WEP key is wrong, so you just have - * to wait for DHCP to fail to figure it out. For all other WiFi security - * types (open, WPA, 802.1x, etc) if the secrets/certs were wrong the - * connection would have failed before IP configuration. - */ - if (!may_fail && is_static_wep (connection)) { - /* Activation failed, we must have bad encryption key */ - nm_log_warn (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): could not get IP configuration for " - "connection '%s'.", - nm_device_get_iface (NM_DEVICE (self)), - nm_connection_get_id (connection)); - - ret = handle_auth_or_fail (self, NULL, TRUE); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "Activation (%s/wireless): asking for new secrets", - nm_device_get_iface (NM_DEVICE (self))); - } else { - *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; - } - } else { - /* Not static WEP or failure allowed; let superclass handle it */ - *chain_up = TRUE; - } - - return ret; -} - - -static NMActStageReturn -act_stage4_ip4_config_timeout (NMDevice *dev, NMDeviceStateReason *reason) -{ - NMConnection *connection; - NMSettingIP4Config *s_ip4; - gboolean may_fail = FALSE, chain_up = FALSE; - NMActStageReturn ret; - - connection = nm_device_get_connection (dev); - g_assert (connection); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - may_fail = nm_setting_ip4_config_get_may_fail (s_ip4); - - ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason); - if (chain_up) - ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (dev, reason); - - return ret; -} - -static NMActStageReturn -act_stage4_ip6_config_timeout (NMDevice *dev, NMDeviceStateReason *reason) -{ - NMConnection *connection; - NMSettingIP6Config *s_ip6; - gboolean may_fail = FALSE, chain_up = FALSE; - NMActStageReturn ret; - - connection = nm_device_get_connection (dev); - g_assert (connection); - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - may_fail = nm_setting_ip6_config_get_may_fail (s_ip6); - - ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason); - if (chain_up) - ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (dev, reason); - - return ret; -} - -static void -activation_success_handler (NMDevice *dev) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - int ifindex = nm_device_get_ifindex (dev); - NMAccessPoint *ap; - struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; - NMAccessPoint *tmp_ap = NULL; - NMActRequest *req; - NMConnection *connection; - - req = nm_device_get_act_request (dev); - g_assert (req); - - connection = nm_act_request_get_connection (req); - g_assert (connection); - - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (ifindex, FALSE); - - /* Clear wireless secrets tries on success */ - g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); - - ap = priv->current_ap; - - /* If the AP isn't fake, it was found in the scan list and all its - * details are known. - */ - if (!ap || !nm_ap_get_fake (ap)){ - ap = NULL; - goto done; - } - - /* If the activate AP was fake, it probably won't have a BSSID at all. - * But if activation was successful, the card will know the BSSID. Grab - * the BSSID off the card and fill in the BSSID of the activation AP. - */ - nm_platform_wifi_get_bssid (ifindex, &bssid); - if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap))) - nm_ap_set_address (ap, &bssid); - if (!nm_ap_get_freq (ap)) - nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (ifindex)); - if (!nm_ap_get_max_bitrate (ap)) - nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (ifindex)); - - tmp_ap = find_active_ap (self, ap, TRUE); - if (tmp_ap) { - const GByteArray *ssid = nm_ap_get_ssid (tmp_ap); - - /* Found a better match in the scan list than the fake AP. Use it - * instead. - */ - - /* If the better match was a hidden AP, update it's SSID */ - if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) - nm_ap_set_ssid (tmp_ap, nm_ap_get_ssid (ap)); - - nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), - nm_ap_get_dbus_path (tmp_ap)); - } - -done: - periodic_update (self, ap); - - /* ap might be already unrefed, because it was a fake_ap. But we don't touch it... */ - if (tmp_ap && ap == priv->current_ap) { - /* Strange, we would expect periodic_update() to find a better AP - * then the fake one and reset it. Reset the fake current_ap to NULL - * now, which will remove the fake ap. - **/ - set_current_ap (self, NULL, TRUE, FALSE); - } - - /* No need to update seen BSSIDs cache, that is done by set_current_ap() already */ - - /* Reset scan interval to something reasonable */ - priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2); -} - -static void -activation_failure_handler (NMDevice *dev) -{ - NMConnection *connection; - - connection = nm_device_get_connection (dev); - g_assert (connection); - - /* Clear wireless secrets tries on failure */ - g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); - - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (dev), FALSE); -} - -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); - gboolean clear_aps = FALSE; - - if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { - /* Clean up the supplicant interface because in these states the - * device cannot be used. - */ - if (priv->supplicant.iface) - supplicant_interface_release (self); - - if (priv->periodic_source_id) { - g_source_remove (priv->periodic_source_id); - priv->periodic_source_id = 0; - } - - cleanup_association_attempt (self, TRUE); - remove_all_aps (self); - } - - switch (new_state) { - case NM_DEVICE_STATE_UNMANAGED: - clear_aps = TRUE; - break; - case NM_DEVICE_STATE_UNAVAILABLE: - /* If the device is enabled and the supplicant manager is ready, - * acquire a supplicant interface and transition to DISCONNECTED because - * the device is now ready to use. - */ - if (priv->enabled && (nm_device_get_firmware_missing (device) == FALSE)) { - if (!priv->supplicant.iface) - supplicant_interface_acquire (self); - } - clear_aps = TRUE; - break; - case NM_DEVICE_STATE_NEED_AUTH: - if (priv->supplicant.iface) - nm_supplicant_interface_disconnect (priv->supplicant.iface); - break; - case NM_DEVICE_STATE_IP_CHECK: - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE); - 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: - /* Kick off a scan to get latest results */ - priv->scan_interval = SCAN_INTERVAL_MIN; - cancel_pending_scan (self); - request_wireless_scan (self); - break; - default: - break; - } - - if (clear_aps) - remove_all_aps (self); -} - -static void -set_enabled (NMDevice *device, gboolean enabled) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMDeviceState state; - - if (priv->enabled == enabled) - return; - - priv->enabled = enabled; - - nm_log_dbg (LOGD_WIFI, "(%s): device now %s", - nm_device_get_iface (NM_DEVICE (device)), - enabled ? "enabled" : "disabled"); - - state = nm_device_get_state (NM_DEVICE (self)); - if (state < NM_DEVICE_STATE_UNAVAILABLE) { - nm_log_dbg (LOGD_WIFI, "(%s): %s blocked by UNMANAGED state", - enabled ? "enable" : "disable", - nm_device_get_iface (NM_DEVICE (device))); - return; - } - - if (enabled) { - gboolean no_firmware = FALSE; - - if (state != NM_DEVICE_STATE_UNAVAILABLE) - nm_log_warn (LOGD_CORE, "not in expected unavailable state!"); - - if (!nm_device_bring_up (NM_DEVICE (self), TRUE, &no_firmware)) { - nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up", - nm_device_get_iface (NM_DEVICE (device))); - - if (no_firmware) - nm_device_set_firmware_missing (NM_DEVICE (device), TRUE); - else { - /* The device sucks, or the kernel was lying to us about the killswitch state */ - priv->enabled = FALSE; - } - return; - } - - /* Re-initialize the supplicant interface and wait for it to be ready */ - if (priv->supplicant.iface) - supplicant_interface_release (self); - supplicant_interface_acquire (self); - - nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state", - nm_device_get_iface (NM_DEVICE (device))); - } else { - nm_device_state_changed (NM_DEVICE (self), - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_NONE); - nm_device_take_down (NM_DEVICE (self), TRUE); - } -} - -/********************************************************************/ - -NMDevice * -nm_device_wifi_new (NMPlatformLink *platform_device) -{ - g_return_val_if_fail (platform_device != NULL, NULL); - - return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIFI, - NM_DEVICE_PLATFORM_DEVICE, platform_device, - NM_DEVICE_TYPE_DESC, "802.11 WiFi", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, - NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, - NULL); -} - -static void -nm_device_wifi_init (NMDeviceWifi *self) -{ - NM_DEVICE_WIFI_GET_PRIVATE (self)->mode = NM_802_11_MODE_INFRA; -} - -static void -dispose (GObject *object) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (object); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - if (priv->disposed) { - G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); - return; - } - - priv->disposed = TRUE; - - if (priv->periodic_source_id) { - g_source_remove (priv->periodic_source_id); - priv->periodic_source_id = 0; - } - - cleanup_association_attempt (self, TRUE); - supplicant_interface_release (self); - - if (priv->supplicant.mgr) { - g_object_unref (priv->supplicant.mgr); - priv->supplicant.mgr = NULL; - } - - remove_all_aps (self); - - G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); -} - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMDeviceWifi *device = NM_DEVICE_WIFI (object); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); - GPtrArray *array; - GSList *iter; - - switch (prop_id) { - case PROP_PERM_HW_ADDRESS: - g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->perm_hw_addr, ARPHRD_ETHER)); - break; - case PROP_MODE: - g_value_set_uint (value, priv->mode); - break; - case PROP_BITRATE: - g_value_set_uint (value, priv->rate); - break; - case PROP_CAPABILITIES: - g_value_set_uint (value, priv->capabilities); - break; - case PROP_ACCESS_POINTS: - array = g_ptr_array_sized_new (4); - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) - g_ptr_array_add (array, g_strdup (nm_ap_get_dbus_path (NM_AP (iter->data)))); - g_value_take_boxed (value, array); - break; - case PROP_ACTIVE_ACCESS_POINT: - if (priv->current_ap) - g_value_set_boxed (value, nm_ap_get_dbus_path (priv->current_ap)); - else - g_value_set_boxed (value, "/"); - break; - case PROP_SCANNING: - g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->supplicant.iface)); - 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) -{ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static void -nm_device_wifi_class_init (NMDeviceWifiClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate)); - - object_class->constructor = constructor; - object_class->get_property = get_property; - object_class->set_property = set_property; - object_class->dispose = dispose; - - parent_class->bring_up = bring_up; - parent_class->update_permanent_hw_address = update_permanent_hw_address; - parent_class->update_initial_hw_address = update_initial_hw_address; - 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->check_connection_available_wifi_hidden = check_connection_available_wifi_hidden; - parent_class->complete_connection = complete_connection; - parent_class->set_enabled = set_enabled; - - parent_class->act_stage1_prepare = act_stage1_prepare; - parent_class->act_stage2_config = act_stage2_config; - parent_class->ip4_config_pre_commit = ip4_config_pre_commit; - parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; - parent_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; - parent_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; - parent_class->deactivate = deactivate; - - parent_class->state_changed = device_state_changed; - - klass->scanning_allowed = scanning_allowed; - - /* Properties */ - g_object_class_install_property (object_class, PROP_PERM_HW_ADDRESS, - g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, - "Permanent MAC Address", - "Permanent hardware MAC address", - NULL, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, PROP_MODE, - g_param_spec_uint (NM_DEVICE_WIFI_MODE, - "Mode", - "Mode", - NM_802_11_MODE_UNKNOWN, - NM_802_11_MODE_AP, - NM_802_11_MODE_INFRA, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, PROP_BITRATE, - g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, - "Bitrate", - "Bitrate", - 0, G_MAXUINT32, 0, - G_PARAM_READABLE)); - - g_object_class_install_property - (object_class, PROP_ACCESS_POINTS, - g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, - "Access points", - "Access points", - DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, PROP_ACTIVE_ACCESS_POINT, - g_param_spec_boxed (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, - "Active access point", - "Currently active access point", - DBUS_TYPE_G_OBJECT_PATH, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, PROP_CAPABILITIES, - g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, - "Wireless Capabilities", - "Wireless Capabilities", - 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, PROP_SCANNING, - g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, - "Scanning", - "Scanning", - FALSE, - G_PARAM_READABLE)); - - /* Signals */ - signals[ACCESS_POINT_ADDED] = - g_signal_new ("access-point-added", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_OBJECT); - - signals[ACCESS_POINT_REMOVED] = - g_signal_new ("access-point-removed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_OBJECT); - - signals[HIDDEN_AP_FOUND] = - g_signal_new ("hidden-ap-found", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMDeviceWifiClass, hidden_ap_found), - NULL, NULL, NULL, - G_TYPE_NONE, 1, - G_TYPE_OBJECT); - - signals[SCANNING_ALLOWED] = - g_signal_new ("scanning-allowed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed), - scanning_allowed_accumulator, NULL, NULL, - G_TYPE_BOOLEAN, 0); - - nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), - G_TYPE_FROM_CLASS (klass), - &dbus_glib_nm_device_wifi_object_info); - - dbus_g_error_domain_register (NM_WIFI_ERROR, NULL, NM_TYPE_WIFI_ERROR); -} - - diff --git a/src/devices/nm-device-wifi.h b/src/devices/nm-device-wifi.h deleted file mode 100644 index 5e4b1a61dd..0000000000 --- a/src/devices/nm-device-wifi.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- 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 - 2010 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#ifndef NM_DEVICE_WIFI_H -#define NM_DEVICE_WIFI_H - -#include -#include -#include - -#include "nm-device.h" -#include "nm-wifi-ap.h" - -struct NMAccessPointList; - -G_BEGIN_DECLS - -#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)) - -typedef enum { - NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, /*< nick=ConnectionNotWireless >*/ - NM_WIFI_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ - NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ - NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, /*< nick=AccessPointNotFound >*/ - NM_WIFI_ERROR_SCAN_NOT_ALLOWED, /*< nick=ScanNotAllowed >*/ - NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, /*< nick=ApModeUnsupported >*/ - NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED, /*< nick=AdhocModeUnsupported >*/ -} NMWifiError; - -#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address" -#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" - -#ifndef NM_DEVICE_WIFI_DEFINED -#define NM_DEVICE_WIFI_DEFINED -typedef struct _NMDeviceWifi NMDeviceWifi; -#endif - -typedef struct _NMDeviceWifiClass NMDeviceWifiClass; -typedef struct _NMDeviceWifiPrivate NMDeviceWifiPrivate; - -struct _NMDeviceWifi -{ - NMDevice parent; - - /*< private >*/ - NMDeviceWifiPrivate *priv; -}; - -struct _NMDeviceWifiClass -{ - NMDeviceClass parent; - - /* Signals */ - void (*access_point_added) (NMDeviceWifi *device, NMAccessPoint *ap); - void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap); - void (*hidden_ap_found) (NMDeviceWifi *device, NMAccessPoint *ap); - gboolean (*scanning_allowed) (NMDeviceWifi *device); -}; - - -GType nm_device_wifi_get_type (void); - -NMDevice *nm_device_wifi_new (NMPlatformLink *platform_device); - -G_END_DECLS - -#endif /* NM_DEVICE_WIFI_H */ diff --git a/src/devices/wifi/Makefile.am b/src/devices/wifi/Makefile.am new file mode 100644 index 0000000000..5f3ce286b7 --- /dev/null +++ b/src/devices/wifi/Makefile.am @@ -0,0 +1,78 @@ +include $(GLIB_MAKEFILE) + +@GNOME_CODE_COVERAGE_RULES@ + +SUBDIRS=. tests + +AM_CPPFLAGS = \ + -I${top_srcdir}/src \ + -I${top_builddir}/src \ + -I${top_srcdir}/src/logging \ + -I${top_srcdir}/src/devices \ + -I${top_srcdir}/src/settings \ + -I${top_srcdir}/src/platform \ + -I${top_srcdir}/src/supplicant-manager \ + -I${top_builddir}/include \ + -I${top_srcdir}/include \ + -I${top_builddir}/libnm-util \ + -I${top_srcdir}/libnm-util \ + -DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + $(DBUS_CFLAGS) \ + $(POLKIT_CFLAGS) \ + $(LIBNL_CFLAGS) \ + $(GUDEV_CFLAGS) + +GLIB_GENERATED = nm-wifi-enum-types.h nm-wifi-enum-types.c +GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM +GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM +nm_wifi_enum_types_sources = \ + $(srcdir)/nm-device-wifi.h \ + $(srcdir)/nm-wifi-ap.h \ + $(srcdir)/nm-device-olpc-mesh.h + +glue_sources = \ + nm-device-wifi-glue.h \ + nm-device-olpc-mesh-glue.h + +%-glue.h: $(top_srcdir)/introspection/%.xml + $(AM_V_GEN) dbus-binding-tool --prefix=$(subst -,_,$(subst -glue.h,,$@)) --mode=glib-server --output=$@ $< + +BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources) + +pkglib_LTLIBRARIES = libnm-device-plugin-wifi.la + +libnm_device_plugin_wifi_la_SOURCES = \ + nm-wifi-factory.c \ + nm-device-wifi.c \ + nm-device-wifi.h \ + nm-wifi-ap.c \ + nm-wifi-ap.h \ + nm-wifi-ap-utils.c \ + nm-wifi-ap-utils.h \ + nm-device-olpc-mesh.c \ + nm-device-olpc-mesh.h \ + \ + $(BUILT_SOURCES) + +SYMBOL_VIS_FILE=$(srcdir)/exports.ver + +libnm_device_plugin_wifi_la_LDFLAGS = \ + -module -avoid-version \ + -Wl,--version-script=$(SYMBOL_VIS_FILE) + +libnm_device_plugin_wifi_la_LIBADD = \ + $(DBUS_LIBS) \ + $(GUDEV_LIBS) + +CLEANFILES = $(BUILT_SOURCES) + +EXTRA_DIST = $(SYMBOL_VIS_FILE) + +if ENABLE_TESTS + +check-local: + $(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wifi.so $(SYMBOL_VIS_FILE) + +endif + diff --git a/src/devices/wifi/exports.ver b/src/devices/wifi/exports.ver new file mode 100644 index 0000000000..d2c451244b --- /dev/null +++ b/src/devices/wifi/exports.ver @@ -0,0 +1,7 @@ +{ +global: + nm_device_factory_create; + nm_device_factory_get_device_type; +local: + *; +}; diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c new file mode 100644 index 0000000000..cfb12fc086 --- /dev/null +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -0,0 +1,653 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * Dan Williams + * Sjoerd Simons + * Daniel Drake + * + * 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. + * + * (C) Copyright 2005 - 2011 Red Hat, Inc. + * (C) Copyright 2008 Collabora Ltd. + * (C) Copyright 2009 One Laptop per Child + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nm-device.h" +#include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" +#include "nm-device-private.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-activation-request.h" +#include "nm-setting-connection.h" +#include "nm-setting-olpc-mesh.h" +#include "nm-manager.h" +#include "nm-enum-types.h" +#include "nm-dbus-manager.h" +#include "nm-wifi-enum-types.h" + +/* This is a bug; but we can't really change API now... */ +#include "NetworkManagerVPN.h" + + +#include "nm-device-olpc-mesh-glue.h" + +G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE) + +#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate)) + + +enum { + PROP_0, + PROP_COMPANION, + PROP_ACTIVE_CHANNEL, + + LAST_PROP +}; + +#define NM_OLPC_MESH_ERROR (nm_olpc_mesh_error_quark ()) + + +struct _NMDeviceOlpcMeshPrivate { + gboolean dispose_has_run; + + NMDevice * companion; + gboolean stage1_waiting; + guint device_added_id; + guint device_removed_id; + guint cmp_state_changed_id; + guint cmp_scanning_id; + guint cmp_scanning_allowed_id; + guint cmp_autoconnect_allowed_id; +}; + +static void state_changed (NMDevice *device, NMDeviceState new_state, + NMDeviceState old_state, NMDeviceStateReason reason); + +static GQuark +nm_olpc_mesh_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-mesh-error"); + return quark; +} + +static void +nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + priv->dispose_has_run = FALSE; + priv->companion = NULL; + priv->stage1_waiting = FALSE; +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + GObjectClass *klass; + NMDeviceOlpcMesh *self; + NMDeviceWifiCapabilities caps; + + klass = G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class); + object = klass->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + self = NM_DEVICE_OLPC_MESH (object); + + nm_log_dbg (LOGD_HW | LOGD_OLPC, "(%s): kernel ifindex %d", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); + + if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), &caps)) { + nm_log_warn (LOGD_HW | LOGD_OLPC, "(%s): failed to initialize WiFi driver", + nm_device_get_iface (NM_DEVICE (self))); + g_object_unref (object); + return NULL; + } + + /* shorter timeout for mesh connectivity */ + nm_device_set_dhcp_timeout (NM_DEVICE (self), 20); + return object; +} + +static gboolean +check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMSettingConnection *s_con; + NMSettingOlpcMesh *s_mesh; + + if (!NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->check_connection_compatible (device, connection, error)) + return FALSE; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_OLPC_MESH_SETTING_NAME)) { + g_set_error (error, + NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, + "The connection was not a Mesh connection."); + return FALSE; + } + + s_mesh = nm_connection_get_setting_olpc_mesh (connection); + if (!s_mesh) { + g_set_error (error, + NM_OLPC_MESH_ERROR, NM_OLPC_MESH_ERROR_CONNECTION_INVALID, + "The connection was not a valid Mesh connection."); + return FALSE; + } + + return TRUE; +} + +static gboolean +can_auto_connect (NMDevice *device, + NMConnection *connection, + char **specific_object) +{ + return FALSE; +} + +#define DEFAULT_SSID "olpc-mesh" + +static gboolean +complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) +{ + NMSettingOlpcMesh *s_mesh; + GByteArray *tmp; + + s_mesh = nm_connection_get_setting_olpc_mesh (connection); + if (!s_mesh) { + s_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new (); + nm_connection_add_setting (connection, NM_SETTING (s_mesh)); + } + + if (!nm_setting_olpc_mesh_get_ssid (s_mesh)) { + tmp = g_byte_array_sized_new (strlen (DEFAULT_SSID)); + g_byte_array_append (tmp, (const guint8 *) DEFAULT_SSID, strlen (DEFAULT_SSID)); + g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_SSID, tmp, NULL); + g_byte_array_free (tmp, TRUE); + } + + if (!nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh)) { + const guint8 anycast[ETH_ALEN] = { 0xC0, 0x27, 0xC0, 0x27, 0xC0, 0x27 }; + + tmp = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (tmp, anycast, sizeof (anycast)); + g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, tmp, NULL); + g_byte_array_free (tmp, TRUE); + + } + + nm_utils_complete_generic (connection, + NM_SETTING_OLPC_MESH_SETTING_NAME, + existing_connections, + _("Mesh %d"), + NULL, + FALSE); /* No IPv6 by default */ + + return TRUE; +} + +/****************************************************************************/ + +static NMActStageReturn +act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev); + NMActStageReturn ret; + gboolean scanning; + + ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (dev, reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + + /* disconnect companion device, if it is connected */ + if (nm_device_get_act_request (NM_DEVICE (priv->companion))) { + nm_log_info (LOGD_OLPC, "(%s): disconnecting companion device %s", + nm_device_get_iface (dev), + nm_device_get_iface (priv->companion)); + /* FIXME: VPN stuff here is a bug; but we can't really change API now... */ + nm_device_state_changed (NM_DEVICE (priv->companion), + NM_DEVICE_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); + nm_log_info (LOGD_OLPC, "(%s): companion %s disconnected", + nm_device_get_iface (dev), + nm_device_get_iface (priv->companion)); + } + + + /* wait with continuing configuration untill the companion device is done scanning */ + g_object_get (priv->companion, "scanning", &scanning, NULL); + if (scanning) { + priv->stage1_waiting = TRUE; + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel) +{ + int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); + + if (nm_platform_mesh_get_channel (ifindex) != channel) { + if (nm_platform_mesh_set_channel (ifindex, channel)) + g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL); + } +} + +static NMActStageReturn +act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev); + NMConnection *connection; + NMSettingOlpcMesh *s_mesh; + guint32 channel; + const GByteArray *anycast_addr_array; + guint8 *anycast_addr = NULL; + + connection = nm_device_get_connection (dev); + g_assert (connection); + + s_mesh = nm_connection_get_setting_olpc_mesh (connection); + g_assert (s_mesh); + + channel = nm_setting_olpc_mesh_get_channel (s_mesh); + if (channel != 0) + _mesh_set_channel (self, channel); + nm_platform_mesh_set_ssid (nm_device_get_ifindex (dev), + nm_setting_olpc_mesh_get_ssid (s_mesh)); + + anycast_addr_array = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh); + if (anycast_addr_array) + anycast_addr = anycast_addr_array->data; + + nm_device_set_dhcp_anycast_address (dev, anycast_addr); + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +companion_cleanup (NMDeviceOlpcMesh *self) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + if (priv->companion == NULL) + return; + + if (priv->cmp_state_changed_id) { + g_signal_handler_disconnect (priv->companion, priv->cmp_state_changed_id); + priv->cmp_state_changed_id = 0; + } + + if (priv->cmp_scanning_id) { + g_signal_handler_disconnect (priv->companion, priv->cmp_scanning_id); + priv->cmp_scanning_id = 0; + } + + if (priv->cmp_scanning_allowed_id) { + g_signal_handler_disconnect (priv->companion, priv->cmp_scanning_allowed_id); + priv->cmp_scanning_allowed_id = 0; + } + + if (priv->cmp_autoconnect_allowed_id) { + g_signal_handler_disconnect (priv->companion, priv->cmp_autoconnect_allowed_id); + priv->cmp_autoconnect_allowed_id = 0; + } + + priv->companion = NULL; + g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION); +} + +static void +dispose (GObject *object) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + + if (priv->dispose_has_run) { + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); + return; + } + priv->dispose_has_run = TRUE; + + companion_cleanup (self); + + if (priv->device_added_id) + g_signal_handler_disconnect (nm_manager_get (), priv->device_added_id); + if (priv->device_removed_id) + g_signal_handler_disconnect (nm_manager_get (), priv->device_removed_id); + + G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device); + + switch (prop_id) { + case PROP_COMPANION: + if (priv->companion) + g_value_set_boxed (value, nm_device_get_path (priv->companion)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_ACTIVE_CHANNEL: + g_value_set_uint (value, nm_platform_mesh_get_channel (nm_device_get_ifindex (NM_DEVICE (device)))); + 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) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate)); + + object_class->constructor = constructor; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + + parent_class->check_connection_compatible = check_connection_compatible; + parent_class->can_auto_connect = can_auto_connect; + parent_class->complete_connection = complete_connection; + + parent_class->act_stage1_prepare = act_stage1_prepare; + parent_class->act_stage2_config = act_stage2_config; + + parent_class->state_changed = state_changed; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_COMPANION, + g_param_spec_boxed (NM_DEVICE_OLPC_MESH_COMPANION, + "Companion device", + "Companion device object path", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_ACTIVE_CHANNEL, + g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL, + "Active channel", + "Active channel", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE)); + + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_olpc_mesh_object_info); + + dbus_g_error_domain_register (NM_OLPC_MESH_ERROR, NULL, + NM_TYPE_OLPC_MESH_ERROR); +} + +static void +companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + gboolean scanning; + + if (!priv->stage1_waiting) + return; + + g_object_get (companion, "scanning", &scanning, NULL); + + if (!scanning) { + priv->stage1_waiting = FALSE; + nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self)); + } +} + +/* disconnect from mesh if someone starts using the companion */ +static void +companion_state_changed_cb (NMDeviceWifi *companion, + NMDeviceState state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self)); + + if ( self_state < NM_DEVICE_STATE_PREPARE + || self_state > NM_DEVICE_STATE_ACTIVATED + || state < NM_DEVICE_STATE_PREPARE + || state > NM_DEVICE_STATE_ACTIVATED) + return; + + nm_log_dbg (LOGD_OLPC, "(%s): disconnecting mesh due to companion connectivity", + nm_device_get_iface (NM_DEVICE (self))); + /* FIXME: VPN stuff here is a bug; but we can't really change API now... */ + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED); +} + +static gboolean +companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); + + /* Don't allow the companion to scan while configuring the mesh interface */ + return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG); +} + +static gboolean +companion_autoconnect_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); + + /* Don't allow the companion to autoconnect while a mesh connection is + * active */ + return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_ACTIVATED); +} + +static gboolean +is_companion (NMDeviceOlpcMesh *self, NMDevice *other) +{ + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + const guint8 *my_addr, *their_addr; + guint their_addr_len; + + if (!NM_IS_DEVICE_WIFI (other)) + return FALSE; + + my_addr = nm_device_get_hw_address (NM_DEVICE (self), NULL); + their_addr = nm_device_get_hw_address (other, &their_addr_len); + if ( (their_addr_len != ETH_ALEN) + || (memcmp (my_addr, their_addr, ETH_ALEN) != 0)) + return FALSE; + + priv->companion = other; + + /* When we've found the companion, stop listening for other devices */ + if (priv->device_added_id) { + g_signal_handler_disconnect (nm_manager_get (), priv->device_added_id); + priv->device_added_id = 0; + } + + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + + nm_log_info (LOGD_OLPC, "(%s): found companion WiFi device %s", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_iface (other)); + + priv->cmp_state_changed_id = g_signal_connect (G_OBJECT (other), "state-changed", + G_CALLBACK (companion_state_changed_cb), self); + + priv->cmp_scanning_id = g_signal_connect (G_OBJECT (other), "notify::scanning", + G_CALLBACK (companion_notify_cb), self); + + priv->cmp_scanning_allowed_id = g_signal_connect (G_OBJECT (other), "scanning-allowed", + G_CALLBACK (companion_scan_allowed_cb), self); + + priv->cmp_autoconnect_allowed_id = g_signal_connect (G_OBJECT (other), "autoconnect-allowed", + G_CALLBACK (companion_autoconnect_allowed_cb), self); + + g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION); + + return TRUE; +} + +static void +device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + + is_companion (self, other); +} + +static void +device_removed_cb (NMManager *manager, NMDevice *other, gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + + if (other == NM_DEVICE_OLPC_MESH_GET_PRIVATE (self)->companion) + companion_cleanup (self); +} + +static gboolean +check_companion_cb (gpointer user_data) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); + NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self); + NMManager *manager; + GSList *list; + + if (priv->companion != NULL) { + nm_device_state_changed (NM_DEVICE (user_data), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + goto done; + } + + if (priv->device_added_id != 0) + goto done; + + manager = nm_manager_get (); + + priv->device_added_id = g_signal_connect (manager, "device-added", + G_CALLBACK (device_added_cb), self); + if (!priv->device_removed_id) { + priv->device_removed_id = g_signal_connect (manager, "device-removed", + G_CALLBACK (device_removed_cb), self); + } + + /* Try to find the companion if it's already known to the NMManager */ + for (list = nm_manager_get_devices (manager); list ; list = g_slist_next (list)) { + if (is_companion (self, NM_DEVICE (list->data))) + break; + } + + done: + nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE); + return FALSE; +} + +static void +state_changed (NMDevice *device, NMDeviceState new_state, + NMDeviceState old_state, NMDeviceStateReason reason) +{ + NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); + + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + break; + case NM_DEVICE_STATE_UNAVAILABLE: + /* If transitioning to UNAVAILABLE and the companion device is known then + * transition to DISCONNECTED otherwise wait for our companion. + */ + g_idle_add (check_companion_cb, self); + nm_device_add_pending_action (device, "waiting for companion", TRUE); + break; + case NM_DEVICE_STATE_ACTIVATED: + break; + case NM_DEVICE_STATE_FAILED: + break; + case NM_DEVICE_STATE_DISCONNECTED: + break; + default: + break; + } +} + + +NMDevice * +nm_device_olpc_mesh_new (NMPlatformLink *platform_device) +{ + g_return_val_if_fail (platform_device != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_OLPC_MESH, + NM_DEVICE_PLATFORM_DEVICE, platform_device, + NM_DEVICE_TYPE_DESC, "802.11 OLPC Mesh", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_OLPC_MESH, + NULL); +} diff --git a/src/devices/wifi/nm-device-olpc-mesh.h b/src/devices/wifi/nm-device-olpc-mesh.h new file mode 100644 index 0000000000..c25dd8e53c --- /dev/null +++ b/src/devices/wifi/nm-device-olpc-mesh.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* NetworkManager -- Network link manager + * + * Dan Williams + * Sjoerd Simons + * Daniel Drake + * + * 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. + * + * (C) Copyright 2005 Red Hat, Inc. + * (C) Copyright 2008 Collabora Ltd. + * (C) Copyright 2009 One Laptop per Child + */ + +#ifndef NM_DEVICE_OLPC_MESH_H +#define NM_DEVICE_OLPC_MESH_H + +#include +#include + +#include "nm-device.h" + +G_BEGIN_DECLS + +#define NM_TYPE_DEVICE_OLPC_MESH (nm_device_olpc_mesh_get_type ()) +#define NM_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh)) +#define NM_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) +#define NM_IS_DEVICE_OLPC_MESH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_OLPC_MESH)) +#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass)) + +typedef enum +{ + NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH = 0, /*< nick=ConnectionNotMesh >*/ + NM_OLPC_MESH_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ + NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ +} NMOlpcMeshError; + +#define NM_DEVICE_OLPC_MESH_COMPANION "companion" +#define NM_DEVICE_OLPC_MESH_BITRATE "bitrate" +#define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel" + +#ifndef NM_DEVICE_OLPC_MESH_DEFINED +#define NM_DEVICE_OLPC_MESH_DEFINED +typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh; +#endif + +typedef struct _NMDeviceOlpcMeshClass NMDeviceOlpcMeshClass; +typedef struct _NMDeviceOlpcMeshPrivate NMDeviceOlpcMeshPrivate; + +struct _NMDeviceOlpcMesh +{ + NMDevice parent; +}; + +struct _NMDeviceOlpcMeshClass +{ + NMDeviceClass parent; + +}; + + +GType nm_device_olpc_mesh_get_type (void); + +NMDevice *nm_device_olpc_mesh_new (NMPlatformLink *platform_device); + +G_END_DECLS + +#endif /* NM_DEVICE_OLPC_MESH_H */ diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c new file mode 100644 index 0000000000..231b2681ca --- /dev/null +++ b/src/devices/wifi/nm-device-wifi.c @@ -0,0 +1,3668 @@ +/* -*- 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 - 2012 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nm-glib-compat.h" +#include "nm-dbus-manager.h" +#include "nm-device.h" +#include "nm-device-wifi.h" +#include "nm-device-private.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "NetworkManagerUtils.h" +#include "nm-activation-request.h" +#include "nm-supplicant-manager.h" +#include "nm-supplicant-interface.h" +#include "nm-supplicant-config.h" +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-8021x.h" +#include "nm-setting-ip4-config.h" +#include "nm-setting-ip6-config.h" +#include "nm-platform.h" +#include "nm-manager-auth.h" +#include "nm-settings-connection.h" +#include "nm-enum-types.h" +#include "nm-dbus-glib-types.h" +#include "nm-wifi-enum-types.h" +#include "nm-connection-provider.h" + + +static gboolean impl_device_get_access_points (NMDeviceWifi *device, + GPtrArray **aps, + GError **err); + +static gboolean impl_device_get_all_access_points (NMDeviceWifi *device, + GPtrArray **aps, + GError **err); + +static void impl_device_request_scan (NMDeviceWifi *device, + GHashTable *options, + DBusGMethodInvocation *context); + +#include "nm-device-wifi-glue.h" + + +/* All of these are in seconds */ +#define SCAN_INTERVAL_MIN 3 +#define SCAN_INTERVAL_STEP 20 +#define SCAN_INTERVAL_MAX 120 + +#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries" + +G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate)) + + +enum { + PROP_0, + PROP_PERM_HW_ADDRESS, + PROP_MODE, + PROP_BITRATE, + PROP_ACCESS_POINTS, + PROP_ACTIVE_ACCESS_POINT, + PROP_CAPABILITIES, + PROP_SCANNING, + + LAST_PROP +}; + +enum { + ACCESS_POINT_ADDED, + ACCESS_POINT_REMOVED, + SCANNING_ALLOWED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct Supplicant { + NMSupplicantManager *mgr; + NMSupplicantInterface *iface; + + guint iface_error_id; + + /* Timeouts and idles */ + guint iface_con_error_cb_id; + guint con_timeout_id; +} Supplicant; + +struct _NMDeviceWifiPrivate { + gboolean disposed; + + guint8 perm_hw_addr[ETH_ALEN]; /* Permanent MAC address */ + guint8 initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */ + + gint8 invalid_strength_counter; + + GSList * ap_list; + NMAccessPoint * current_ap; + guint32 rate; + gboolean enabled; /* rfkilled or not */ + + gint32 scheduled_scan_time; + guint8 scan_interval; /* seconds */ + guint pending_scan_id; + guint scanlist_cull_id; + gboolean requested_scan; + + Supplicant supplicant; + gboolean ssid_found; + NM80211Mode mode; + + guint32 failed_link_count; + guint periodic_source_id; + guint link_timeout_id; + + NMDeviceWifiCapabilities capabilities; +}; + +static gboolean check_scanning_allowed (NMDeviceWifi *self); + +static void schedule_scan (NMDeviceWifi *self, gboolean backoff); + +static void cancel_pending_scan (NMDeviceWifi *self); + +static void cleanup_association_attempt (NMDeviceWifi * self, + gboolean disconnect); + +static void remove_supplicant_timeouts (NMDeviceWifi *self); + +static void supplicant_iface_state_cb (NMSupplicantInterface *iface, + guint32 new_state, + guint32 old_state, + int disconnect_reason, + gpointer user_data); + +static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface, + const char *object_path, + GHashTable *properties, + NMDeviceWifi * self); + +static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, + const char *object_path, + GHashTable *properties, + NMDeviceWifi *self); + +static void supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, + const char *object_path, + NMDeviceWifi *self); + +static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface, + gboolean success, + NMDeviceWifi * self); + +static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, + GParamSpec * pspec, + NMDeviceWifi * self); + +static void schedule_scanlist_cull (NMDeviceWifi *self); + +static gboolean request_wireless_scan (gpointer user_data); + +static void remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap); + +/*****************************************************************/ + +#define NM_WIFI_ERROR (nm_wifi_error_quark ()) + +static GQuark +nm_wifi_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("nm-wifi-error"); + return quark; +} + +/*****************************************************************/ + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + GObjectClass *klass; + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + + klass = G_OBJECT_CLASS (nm_device_wifi_parent_class); + object = klass->constructor (type, n_construct_params, construct_params); + if (!object) + return NULL; + + self = NM_DEVICE_WIFI (object); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d", + nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self))); + + if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), + &priv->capabilities)) { + nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): failed to initialize WiFi driver", + nm_device_get_iface (NM_DEVICE (self))); + g_object_unref (object); + return NULL; + } + + if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP) { + nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver supports Access Point (AP) mode", + nm_device_get_iface (NM_DEVICE (self))); + } + + /* Connect to the supplicant manager */ + priv->supplicant.mgr = nm_supplicant_manager_get (); + g_assert (priv->supplicant.mgr); + + return object; +} + +static gboolean +supplicant_interface_acquire (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + g_return_val_if_fail (self != NULL, FALSE); + /* interface already acquired? */ + g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE); + + priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr, + nm_device_get_iface (NM_DEVICE (self)), + TRUE); + if (priv->supplicant.iface == NULL) { + nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.", + nm_device_get_iface (NM_DEVICE (self))); + return FALSE; + } + + if (nm_supplicant_interface_get_state (priv->supplicant.iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + nm_device_add_pending_action (NM_DEVICE (self), "waiting for supplicant", TRUE); + + g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_STATE, + G_CALLBACK (supplicant_iface_state_cb), + self); + g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_NEW_BSS, + G_CALLBACK (supplicant_iface_new_bss_cb), + self); + g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_BSS_UPDATED, + G_CALLBACK (supplicant_iface_bss_updated_cb), + self); + g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_BSS_REMOVED, + G_CALLBACK (supplicant_iface_bss_removed_cb), + self); + g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_SCAN_DONE, + G_CALLBACK (supplicant_iface_scan_done_cb), + self); + g_signal_connect (priv->supplicant.iface, + "notify::scanning", + G_CALLBACK (supplicant_iface_notify_scanning_cb), + self); + + return TRUE; +} + +static void +remove_supplicant_interface_error_handler (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (!priv->supplicant.iface) + return; + + if (priv->supplicant.iface_error_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_error_id); + priv->supplicant.iface_error_id = 0; + } + + if (priv->supplicant.iface_con_error_cb_id > 0) { + g_source_remove (priv->supplicant.iface_con_error_cb_id); + priv->supplicant.iface_con_error_cb_id = 0; + } +} + +static void +supplicant_interface_release (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (self != NULL); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + cancel_pending_scan (self); + + /* Reset the scan interval to be pretty frequent when disconnected */ + priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP; + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds", + nm_device_get_iface (NM_DEVICE (self)), + priv->scan_interval); + + remove_supplicant_interface_error_handler (self); + + if (priv->scanlist_cull_id) { + g_source_remove (priv->scanlist_cull_id); + priv->scanlist_cull_id = 0; + } + + if (priv->supplicant.iface) { + /* Clear supplicant interface signal handlers */ + g_signal_handlers_disconnect_matched (priv->supplicant.iface, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, self); + + /* Tell the supplicant to disconnect from the current AP */ + nm_supplicant_interface_disconnect (priv->supplicant.iface); + + nm_supplicant_manager_iface_release (priv->supplicant.mgr, priv->supplicant.iface); + priv->supplicant.iface = NULL; + } +} + +static NMAccessPoint * +get_ap_by_path (NMDeviceWifi *self, const char *path) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *iter; + + if (!path) + return NULL; + + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + if (g_strcmp0 (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0) + return NM_AP (iter->data); + } + return NULL; +} + +static NMAccessPoint * +get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *iter; + + if (!path) + return NULL; + + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + if (g_strcmp0 (path, nm_ap_get_supplicant_path (NM_AP (iter->data))) == 0) + return NM_AP (iter->data); + } + return NULL; +} + +static NMAccessPoint * +find_active_ap (NMDeviceWifi *self, + NMAccessPoint *ignore_ap, + gboolean match_hidden) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + const char *iface = nm_device_get_iface (NM_DEVICE (self)); + int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); + struct ether_addr bssid; + GByteArray *ssid; + GSList *iter; + int i = 0; + NMAccessPoint *match_nofreq = NULL, *active_ap = NULL; + gboolean found_a_band = FALSE; + gboolean found_bg_band = FALSE; + NM80211Mode devmode; + guint32 devfreq; + + nm_platform_wifi_get_bssid (ifindex, &bssid); + nm_log_dbg (LOGD_WIFI, "(%s): active BSSID: %02x:%02x:%02x:%02x:%02x:%02x", + iface, + bssid.ether_addr_octet[0], bssid.ether_addr_octet[1], + bssid.ether_addr_octet[2], bssid.ether_addr_octet[3], + bssid.ether_addr_octet[4], bssid.ether_addr_octet[5]); + + if (!nm_ethernet_address_is_valid (&bssid)) + return NULL; + + ssid = nm_platform_wifi_get_ssid (ifindex); + nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s", + iface, + ssid ? "'" : "", + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", + ssid ? "'" : ""); + + devmode = nm_platform_wifi_get_mode (ifindex); + devfreq = nm_platform_wifi_get_frequency (ifindex); + + /* When matching hidden APs, do a second pass that ignores the SSID check, + * because NM might not yet know the SSID of the hidden AP in the scan list + * and therefore it won't get matched the first time around. + */ + while (i++ < (match_hidden ? 2 : 1)) { + nm_log_dbg (LOGD_WIFI, " Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : ""); + + /* Find this SSID + BSSID in the device's AP list */ + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint *ap = NM_AP (iter->data); + const struct ether_addr *ap_bssid = nm_ap_get_address (ap); + const GByteArray *ap_ssid = nm_ap_get_ssid (ap); + NM80211Mode apmode; + guint32 apfreq; + + nm_log_dbg (LOGD_WIFI, " AP: %s%s%s %02x:%02x:%02x:%02x:%02x:%02x", + ap_ssid ? "'" : "", + ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)", + ap_ssid ? "'" : "", + ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1], + ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3], + ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]); + + if (ap == ignore_ap) { + nm_log_dbg (LOGD_WIFI, " ignored"); + continue; + } + + if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) { + nm_log_dbg (LOGD_WIFI, " BSSID mismatch"); + continue; + } + + if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) { + nm_log_dbg (LOGD_WIFI, " SSID mismatch"); + continue; + } + + apmode = nm_ap_get_mode (ap); + if (devmode != apmode) { + nm_log_dbg (LOGD_WIFI, " mode mismatch (device %d, ap %d)", + devmode, apmode); + continue; + } + + apfreq = nm_ap_get_freq (ap); + if (devfreq != apfreq) { + nm_log_dbg (LOGD_WIFI, " frequency mismatch (device %u, ap %u)", + devfreq, apfreq); + + if (match_nofreq == NULL) + match_nofreq = ap; + + if (apfreq > 4000) + found_a_band = TRUE; + else if (apfreq > 2000) + found_bg_band = TRUE; + continue; + } + + // FIXME: handle security settings here too + nm_log_dbg (LOGD_WIFI, " matched"); + active_ap = ap; + goto done; + } + } + + /* Some proprietary drivers (wl.o) report tuned frequency (like when + * scanning) instead of the associated AP's frequency. This is a great + * example of how WEXT is underspecified. We use frequency to find the + * active AP in the scan list because some configurations use the same + * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to + * make sure we get the right AP in the right band. This configuration + * is uncommon though, and the frequency check penalizes closed drivers we + * can't fix. Because we're not total dicks, ignore the frequency condition + * if the associated BSSID/SSID exists only in one band since that's most + * likely the AP we want. Sometimes wl.o returns a frequency of 0, so if + * we can't match the AP based on frequency at all, just give up. + */ + if (match_nofreq && ((found_a_band != found_bg_band) || (devfreq == 0))) { + const struct ether_addr *ap_bssid = nm_ap_get_address (match_nofreq); + const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq); + + nm_log_dbg (LOGD_WIFI, " matched %s%s%s %02x:%02x:%02x:%02x:%02x:%02x", + ap_ssid ? "'" : "", + ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)", + ap_ssid ? "'" : "", + ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1], + ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3], + ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]); + + active_ap = match_nofreq; + goto done; + } + + nm_log_dbg (LOGD_WIFI, " No matching AP found."); + +done: + if (ssid) + g_byte_array_free (ssid, TRUE); + return active_ap; +} + +static void +update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap) +{ + NMActRequest *req; + NMConnection *connection; + + g_return_if_fail (NM_IS_DEVICE_WIFI (self)); + + if (ap == NULL) + return; + + /* Don't cache the BSSID for Ad-Hoc APs */ + if (nm_ap_get_mode (ap) != NM_802_11_MODE_INFRA) + return; + + if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) { + req = nm_device_get_act_request (NM_DEVICE (self)); + if (req) { + connection = nm_act_request_get_connection (req); + nm_settings_connection_add_seen_bssid (NM_SETTINGS_CONNECTION (connection), + nm_ap_get_address (ap)); + } + } +} + +static void +set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_available_connections, gboolean force_remove_old_ap) +{ + NMDeviceWifiPrivate *priv; + NMAccessPoint *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); + + /* Move the current AP to the front of the scan list. Since we + * do a lot of searches looking for the current AP, it saves + * time to have it in front. + */ + priv->ap_list = g_slist_remove (priv->ap_list, new_ap); + priv->ap_list = g_slist_prepend (priv->ap_list, new_ap); + + /* Update seen BSSIDs cache */ + update_seen_bssids_cache (self, priv->current_ap); + } else + priv->current_ap = NULL; + + if (old_ap) { + NM80211Mode mode = nm_ap_get_mode (old_ap); + + if (force_remove_old_ap || mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_ap_get_fake (old_ap)) { + remove_access_point (self, old_ap); + if (recheck_available_connections) + nm_device_recheck_available_connections (NM_DEVICE (self)); + } + g_object_unref (old_ap); + } + + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT); +} + +static void +periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + int ifindex = nm_device_get_ifindex (NM_DEVICE (self)); + NMAccessPoint *new_ap; + guint32 new_rate; + int percent; + NMDeviceState state; + guint32 supplicant_state; + + /* BSSID and signal strength have meaningful values only if the device + * is activated and not scanning. + */ + state = nm_device_get_state (NM_DEVICE (self)); + if (state != NM_DEVICE_STATE_ACTIVATED) + return; + + /* Only update current AP if we're actually talking to something, otherwise + * assume the old one (if any) is still valid until we're told otherwise or + * the connection fails. + */ + supplicant_state = nm_supplicant_interface_get_state (priv->supplicant.iface); + if ( supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING + || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED + || nm_supplicant_interface_get_scanning (priv->supplicant.iface)) + return; + + /* In AP mode we currently have nothing to do. */ + if (priv->mode == NM_802_11_MODE_AP) + return; + + /* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where + * multiple IBSS stations using the same SSID will eventually switch to + * using the same BSSID to avoid network segmentation. When this happens, + * the card's reported BSSID will change, but the new BSS may not + * be in the scan list, since scanning isn't done in ad-hoc mode for + * various reasons. So pull the BSSID from the card and update the + * current AP with it, if the current AP is adhoc. + */ + if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) { + struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; + + nm_platform_wifi_get_bssid (ifindex, &bssid); + /* 0x02 means "locally administered" and should be OR-ed into + * the first byte of IBSS BSSIDs. + */ + if ( (bssid.ether_addr_octet[0] & 0x02) + && nm_ethernet_address_is_valid (&bssid)) + nm_ap_set_address (priv->current_ap, &bssid); + } + + new_ap = find_active_ap (self, ignore_ap, FALSE); + if (new_ap) { + /* Try to smooth out the strength. Atmel cards, for example, will give no strength + * one second and normal strength the next. + */ + percent = nm_platform_wifi_get_quality (ifindex); + if (percent >= 0 || ++priv->invalid_strength_counter > 3) { + nm_ap_set_strength (new_ap, (gint8) percent); + priv->invalid_strength_counter = 0; + } + } + + if (new_ap != priv->current_ap) { + const struct ether_addr *new_bssid = NULL; + const GByteArray *new_ssid = NULL; + const struct ether_addr *old_bssid = NULL; + const GByteArray *old_ssid = NULL; + char *old_addr = NULL, *new_addr = NULL; + + if (new_ap) { + new_bssid = nm_ap_get_address (new_ap); + new_addr = nm_utils_hwaddr_ntoa (new_bssid, ARPHRD_ETHER); + new_ssid = nm_ap_get_ssid (new_ap); + } + + if (priv->current_ap) { + old_bssid = nm_ap_get_address (priv->current_ap); + old_addr = nm_utils_hwaddr_ntoa (old_bssid, ARPHRD_ETHER); + old_ssid = nm_ap_get_ssid (priv->current_ap); + } + + nm_log_info (LOGD_WIFI, "(%s): roamed from BSSID %s (%s) to %s (%s)", + nm_device_get_iface (NM_DEVICE (self)), + old_addr ? old_addr : "(none)", + old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)", + new_addr ? new_addr : "(none)", + new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)"); + g_free (old_addr); + g_free (new_addr); + + set_current_ap (self, new_ap, TRUE, FALSE); + } + + new_rate = nm_platform_wifi_get_rate (ifindex); + if (new_rate != priv->rate) { + priv->rate = new_rate; + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE); + } +} + +static gboolean +periodic_update_cb (gpointer user_data) +{ + periodic_update (NM_DEVICE_WIFI (user_data), NULL); + return TRUE; +} + +static gboolean +bring_up (NMDevice *device, gboolean *no_firmware) +{ + if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled) + return FALSE; + + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->bring_up (device, no_firmware); +} + +static void +emit_ap_added_removed (NMDeviceWifi *self, + guint signum, + NMAccessPoint *ap, + gboolean recheck_available_connections) +{ + g_signal_emit (self, signals[signum], 0, ap); + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_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 +remove_access_point (NMDeviceWifi *device, + NMAccessPoint *ap) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + g_return_if_fail (ap); + g_return_if_fail (ap != priv->current_ap); + g_return_if_fail (g_slist_find (priv->ap_list, ap)); + + priv->ap_list = g_slist_remove (priv->ap_list, ap); + emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE); + g_object_unref (ap); +} + +static void +remove_all_aps (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->ap_list) { + set_current_ap (self, NULL, FALSE, FALSE); + + while (priv->ap_list) + remove_access_point (self, NM_AP (priv->ap_list->data)); + + nm_device_recheck_available_connections (NM_DEVICE (self)); + } +} + +static void +deactivate (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + int ifindex = nm_device_get_ifindex (dev); + NMActRequest *req; + NMConnection *connection; + NM80211Mode old_mode = priv->mode; + + req = nm_device_get_act_request (dev); + if (req) { + connection = nm_act_request_get_connection (req); + /* Clear wireless secrets tries when deactivating */ + g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); + } + + if (priv->periodic_source_id) { + g_source_remove (priv->periodic_source_id); + priv->periodic_source_id = 0; + } + + cleanup_association_attempt (self, TRUE); + + priv->rate = 0; + + /* If the AP is 'fake', i.e. it wasn't actually found from + * a scan but the user tried to connect to it manually (maybe it + * was non-broadcasting or something) get rid of it, because 'fake' + * APs should only live for as long as we're connected to them. + **/ + set_current_ap (self, NULL, TRUE, FALSE); + + /* Clear any critical protocol notification in the Wi-Fi stack */ + nm_platform_wifi_indicate_addressing_running (ifindex, FALSE); + + /* Reset MAC address back to initial address */ + nm_device_set_hw_addr (dev, priv->initial_hw_addr, "reset", LOGD_WIFI); + + /* Ensure we're in infrastructure mode after deactivation; some devices + * (usually older ones) don't scan well in adhoc mode. + */ + if (nm_platform_wifi_get_mode (ifindex) != NM_802_11_MODE_INFRA) { + nm_device_take_down (NM_DEVICE (self), TRUE); + nm_platform_wifi_set_mode (ifindex, NM_802_11_MODE_INFRA); + nm_device_bring_up (NM_DEVICE (self), TRUE, NULL); + } + + if (priv->mode != NM_802_11_MODE_INFRA) { + priv->mode = NM_802_11_MODE_INFRA; + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE); + } + + /* Ensure we trigger a scan after deactivating a Hotspot */ + if (old_mode == NM_802_11_MODE_AP) { + cancel_pending_scan (self); + request_wireless_scan (self); + } +} + +static gboolean +is_adhoc_wpa (NMConnection *connection) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + const char *mode, *key_mgmt; + + /* The kernel doesn't support Ad-Hoc WPA connections well at this time, + * and turns them into open networks. It's been this way since at least + * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. + */ + + s_wifi = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wifi != NULL, FALSE); + + mode = nm_setting_wireless_get_mode (s_wifi); + if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) != 0) + return FALSE; + + s_wsec = nm_connection_get_setting_wireless_security (connection); + if (!s_wsec) + return FALSE; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + if (g_strcmp0 (key_mgmt, "wpa-none") != 0) + return FALSE; + + return TRUE; +} + +static gboolean +check_connection_compatible (NMDevice *device, + NMConnection *connection, + GError **error) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSettingConnection *s_con; + NMSettingWireless *s_wireless; + const GByteArray *mac; + const GSList *mac_blacklist, *mac_blacklist_iter; + const char *mode; + + if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection, error)) + 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)) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, + "The connection was not a WiFi connection."); + return FALSE; + } + + s_wireless = nm_connection_get_setting_wireless (connection); + if (!s_wireless) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INVALID, + "The connection was not a valid WiFi connection."); + return FALSE; + } + + + mac = nm_setting_wireless_get_mac_address (s_wireless); + if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, + "The connection's MAC address did not match this device."); + return FALSE; + } + + /* Check for MAC address blacklist */ + mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless); + for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter; + mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) { + struct ether_addr addr; + + if (!ether_aton_r (mac_blacklist_iter->data, &addr)) { + g_warn_if_reached (); + continue; + } + if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0) { + g_set_error (error, + NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, + "The connection's MAC address (%s) is blacklisted in %s.", + (char *) mac_blacklist_iter->data, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST); + return FALSE; + } + } + + if (is_adhoc_wpa (connection)) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, + "WPA Ad-Hoc disabled due to kernel bugs"); + return FALSE; + } + + /* Early exit if supplicant or device doesn't support requested mode */ + mode = nm_setting_wireless_get_mode (s_wireless); + if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED, + "Ad-Hoc mode is not supported by this device."); + return FALSE; + } + } else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, + "Access Point (AP) mode is not supported by this device."); + return FALSE; + } + + if (priv->supplicant.iface) { + switch (nm_supplicant_interface_get_ap_support (priv->supplicant.iface)) { + case AP_SUPPORT_NO: + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, + "Access Point (AP) mode is not supported by the supplicant."); + return FALSE; + case AP_SUPPORT_YES: + case AP_SUPPORT_UNKNOWN: + default: + break; + } + } + } + + // FIXME: check channel/freq/band against bands the hardware supports + // FIXME: check encryption against device capabilities + // FIXME: check bitrate against device capabilities + + return TRUE; +} + + +static gboolean +_internal_check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object, + gboolean ignore_ap_list) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + NMSettingWireless *s_wifi; + const char *mode; + GSList *ap_iter = NULL; + + s_wifi = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wifi, FALSE); + + if (specific_object) { + NMAccessPoint *ap; + + ap = get_ap_by_path (NM_DEVICE_WIFI (device), specific_object); + return ap ? nm_ap_check_compatible (ap, connection) : FALSE; + } + + /* Ad-Hoc and AP connections are always available because they may be + * started at any time. + */ + mode = nm_setting_wireless_get_mode (s_wifi); + if ( g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0 + || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) + return TRUE; + + /* Hidden SSIDs obviously don't always appear in the scan list either */ + if (nm_setting_wireless_get_hidden (s_wifi) || ignore_ap_list) + return TRUE; + + /* check if its visible */ + for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) { + if (nm_ap_check_compatible (NM_AP (ap_iter->data), connection)) + return TRUE; + } + + return FALSE; +} + +static gboolean +check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object) +{ + return _internal_check_connection_available (device, connection, specific_object, FALSE); +} + +/* FIXME: remove this function when we require the 'hidden' property to be + * set before a hidden connection can be activated. + */ +static gboolean +check_connection_available_wifi_hidden (NMDevice *device, + NMConnection *connection) +{ + return _internal_check_connection_available (device, connection, NULL, TRUE); +} + +/* + * 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", +}; + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) + +static gboolean +is_manf_default_ssid (const GByteArray *ssid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE (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); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x *s_8021x; + const GByteArray *setting_mac; + char *format, *str_ssid = NULL; + NMAccessPoint *ap = NULL; + const GByteArray *ssid = NULL; + GSList *iter; + gboolean hidden = FALSE; + + s_wifi = nm_connection_get_setting_wireless (connection); + s_wsec = nm_connection_get_setting_wireless_security (connection); + s_8021x = nm_connection_get_setting_802_1x (connection); + + if (!specific_object) { + /* If not given a specific object, we need at minimum an SSID */ + if (!s_wifi) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_CONNECTION_INVALID, + "A 'wireless' setting is required if no AP path was given."); + return FALSE; + } + + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid || !ssid->len) { + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_CONNECTION_INVALID, + "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 */ + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + if (nm_ap_check_compatible (NM_AP (iter->data), connection)) { + ap = NM_AP (iter->data); + break; + } + } + + /* If we still don't have an AP, then the WiFI settings needs to be + * fully specified by the client. Might not be able to find an AP + * if the network isn't broadcasting the SSID for example. + */ + if (!ap) { + GSList *settings = NULL; + gboolean valid; + + settings = g_slist_prepend (settings, s_wifi); + if (s_wsec) + settings = g_slist_prepend (settings, s_wsec); + if (s_8021x) + settings = g_slist_prepend (settings, s_8021x); + valid = nm_setting_verify (NM_SETTING (s_wifi), settings, error); + g_slist_free (settings); + if (!valid) + return FALSE; + + hidden = TRUE; + } + } else { + ap = get_ap_by_path (self, specific_object); + if (!ap) { + g_set_error (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_ACCESS_POINT_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)); + } + + if (ap) { + ssid = nm_ap_get_ssid (ap); + + if (ssid == NULL) { + /* The AP must be hidden. Connecting to a WiFi AP requires the SSID + * as part of the initial handshake, so check the connection details + * for the SSID. The AP object will still be used for encryption + * settings and such. + */ + ssid = nm_setting_wireless_get_ssid (s_wifi); + } + + if (ssid == NULL) { + /* If there's no SSID on the AP itself, and no SSID in the + * connection data, then we cannot connect at all. Return an error. + */ + g_set_error_literal (error, + NM_WIFI_ERROR, + NM_WIFI_ERROR_CONNECTION_INVALID, + "A 'wireless' setting with a valid SSID is required for hidden access points."); + return FALSE; + } + + /* If the SSID is a well-known SSID, lock the connection to the AP's + * specific BSSID so NM doesn't autoconnect to some random wifi net. + */ + if (!nm_ap_complete_connection (ap, + connection, + is_manf_default_ssid (ssid), + error)) + return FALSE; + } + + /* The kernel doesn't support Ad-Hoc WPA connections well at this time, + * and turns them into open networks. It's been this way since at least + * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. + */ + if (is_adhoc_wpa (connection)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc disabled due to kernel bugs"); + return FALSE; + } + + g_assert (ssid); + str_ssid = nm_utils_ssid_to_utf8 (ssid); + format = g_strdup_printf ("%s %%d", str_ssid); + + nm_utils_complete_generic (connection, + NM_SETTING_WIRELESS_SETTING_NAME, + existing_connections, + format, + str_ssid, + TRUE); + g_free (str_ssid); + g_free (format); + + if (hidden) + g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL); + + 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 (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRELESS_MAC_ADDRESS); + return FALSE; + } + } else { + GByteArray *mac; + const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + + /* Lock the connection to this device by default if it uses a + * permanent MAC address (ie not a 'locally administered' one) + */ + if ( !(priv->perm_hw_addr[0] & 0x02) + && memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) { + mac = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL); + g_byte_array_free (mac, TRUE); + } + } + + return TRUE; +} + +static gboolean +is_available (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSupplicantInterface *sup_iface; + guint32 state; + + if (!priv->enabled) { + nm_log_dbg (LOGD_WIFI, "(%s): not available because not enabled", + nm_device_get_iface (dev)); + return FALSE; + } + + sup_iface = priv->supplicant.iface; + if (!sup_iface) { + nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant not running", + nm_device_get_iface (dev)); + return FALSE; + } + + state = nm_supplicant_interface_get_state (sup_iface); + if ( state < NM_SUPPLICANT_INTERFACE_STATE_READY + || state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) { + nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant interface not ready", + nm_device_get_iface (dev)); + return FALSE; + } + + return TRUE; +} + +static gboolean +can_auto_connect (NMDevice *dev, + NMConnection *connection, + char **specific_object) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *ap_iter; + const char *method = NULL; + guint64 timestamp = 0; + + if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (dev, connection, specific_object)) + 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; + } + + /* Use the connection if it's a shared connection */ + method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + return TRUE; + + for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) { + NMAccessPoint *ap = NM_AP (ap_iter->data); + + if (nm_ap_check_compatible (ap, connection)) { + /* All good; connection is usable */ + *specific_object = (char *) nm_ap_get_dbus_path (ap); + return TRUE; + } + } + + return FALSE; +} + +static void +ap_list_dump (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList * elt; + int i = 0; + + g_return_if_fail (NM_IS_DEVICE_WIFI (self)); + + nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list:"); + for (elt = priv->ap_list; elt; elt = g_slist_next (elt), i++) { + NMAccessPoint * ap = NM_AP (elt->data); + nm_ap_dump (ap, "List AP: "); + } + nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list: done"); +} + +static gboolean +impl_device_get_access_points (NMDeviceWifi *self, + GPtrArray **aps, + GError **err) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *elt; + + *aps = g_ptr_array_new (); + for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) { + NMAccessPoint *ap = NM_AP (elt->data); + + if (nm_ap_get_ssid (ap)) + g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (ap))); + } + return TRUE; +} + +static gboolean +impl_device_get_all_access_points (NMDeviceWifi *self, + GPtrArray **aps, + GError **err) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GSList *elt; + + *aps = g_ptr_array_new (); + for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) + g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (NM_AP (elt->data)))); + return TRUE; +} + +static void +request_scan_cb (NMDevice *device, + DBusGMethodInvocation *context, + GError *error, + gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + GError *local = NULL; + + if (error) { + dbus_g_method_return_error (context, error); + return; + } + + if (!check_scanning_allowed (self)) { + local = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed at this time"); + dbus_g_method_return_error (context, local); + g_error_free (local); + return; + } + + cancel_pending_scan (self); + request_wireless_scan (self); + dbus_g_method_return (context); +} + +static void +impl_device_request_scan (NMDeviceWifi *self, + GHashTable *options, + DBusGMethodInvocation *context) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gint32 last_scan; + GError *error; + + if ( !priv->enabled + || !priv->supplicant.iface + || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED + || nm_device_is_activating (device)) { + error = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed while unavailable or activating"); + goto error; + } + + if (nm_supplicant_interface_get_scanning (priv->supplicant.iface)) { + error = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed while already scanning"); + goto error; + } + + last_scan = nm_supplicant_interface_get_last_scan_time (priv->supplicant.iface); + if (last_scan && (nm_utils_get_monotonic_timestamp_s () - last_scan) < 10) { + error = g_error_new_literal (NM_WIFI_ERROR, + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, + "Scanning not allowed immediately following previous scan"); + goto error; + } + + /* 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, + request_scan_cb, + NULL); + return; + +error: + dbus_g_method_return_error (context, error); + g_error_free (error); +} + +static gboolean +scanning_allowed (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + guint32 sup_state; + NMActRequest *req; + + g_return_val_if_fail (priv->supplicant.iface != NULL, FALSE); + + /* Scanning not done in AP mode */ + if (priv->mode == NM_802_11_MODE_AP) + return FALSE; + + 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: + /* Don't scan when unusable or activating */ + return FALSE; + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_FAILED: + /* Can always scan when disconnected */ + return TRUE; + case NM_DEVICE_STATE_ACTIVATED: + /* Need to do further checks when activated */ + break; + } + + /* Don't scan if the supplicant is busy */ + sup_state = nm_supplicant_interface_get_state (priv->supplicant.iface); + if ( sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING + || sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED + || sup_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE + || sup_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE + || nm_supplicant_interface_get_scanning (priv->supplicant.iface)) + return FALSE; + + req = nm_device_get_act_request (NM_DEVICE (self)); + if (req) { + NMConnection *connection; + NMSettingWireless *s_wifi; + const char *ip4_method = NULL; + const GByteArray *bssid; + + /* Don't scan when a shared connection is active; it makes drivers mad */ + connection = nm_act_request_get_connection (req); + ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + + if (!strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) + return FALSE; + + /* Don't scan when the connection is locked to a specifc AP, since + * intra-ESS roaming (which requires periodic scanning) isn't being + * used due to the specific AP lock. (bgo #513820) + */ + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + bssid = nm_setting_wireless_get_bssid (s_wifi); + if (bssid && bssid->len == ETH_ALEN) + return FALSE; + } + + return TRUE; +} + +static gboolean +scanning_allowed_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer data) +{ + if (!g_value_get_boolean (handler_return)) + g_value_set_boolean (return_accu, FALSE); + return TRUE; +} + +static gboolean +check_scanning_allowed (NMDeviceWifi *self) +{ + GValue instance = G_VALUE_INIT; + GValue retval = G_VALUE_INIT; + + g_value_init (&instance, G_TYPE_OBJECT); + g_value_take_object (&instance, self); + + g_value_init (&retval, G_TYPE_BOOLEAN); + g_value_set_boolean (&retval, TRUE); + + /* Use g_signal_emitv() rather than g_signal_emit() to avoid the return + * value being changed if no handlers are connected */ + g_signal_emitv (&instance, signals[SCANNING_ALLOWED], 0, &retval); + + return g_value_get_boolean (&retval); +} + +static gboolean +hidden_filter_func (NMConnectionProvider *provider, + NMConnection *connection, + gpointer user_data) +{ + NMSettingWireless *s_wifi; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); + return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE; +} + +static GPtrArray * +build_hidden_probe_list (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + guint max_scan_ssids = nm_supplicant_interface_get_max_scan_ssids (priv->supplicant.iface); + GSList *connections, *iter; + GPtrArray *ssids = NULL; + static GByteArray *nullssid = NULL; + + /* Need at least two: wildcard SSID and one or more hidden SSIDs */ + if (max_scan_ssids < 2) + return NULL; + + /* Static wildcard SSID used for every scan */ + if (G_UNLIKELY (nullssid == NULL)) + nullssid = g_byte_array_new (); + + connections = nm_connection_provider_get_best_connections (nm_connection_provider_get (), + max_scan_ssids - 1, + NM_SETTING_WIRELESS_SETTING_NAME, + NULL, + hidden_filter_func, + NULL); + if (connections && connections->data) { + ssids = g_ptr_array_sized_new (max_scan_ssids - 1); + g_ptr_array_add (ssids, nullssid); /* Add wildcard SSID */ + } + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = iter->data; + NMSettingWireless *s_wifi; + const GByteArray *ssid; + + s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + ssid = nm_setting_wireless_get_ssid (s_wifi); + g_assert (ssid); + g_ptr_array_add (ssids, (gpointer) ssid); + } + g_slist_free (connections); + + return ssids; +} + +static gboolean +request_wireless_scan (gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gboolean backoff = FALSE; + GPtrArray *ssids = NULL; + + if (priv->requested_scan) { + /* There's already a scan in progress */ + return FALSE; + } + + if (check_scanning_allowed (self)) { + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scanning requested", + nm_device_get_iface (NM_DEVICE (self))); + + ssids = build_hidden_probe_list (self); + + if (nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) { + if (ssids) { + guint i; + char *foo; + + for (i = 0; i < ssids->len; i++) { + foo = nm_utils_ssid_to_utf8 (g_ptr_array_index (ssids, i)); + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): (%d) probe scanning SSID '%s'", + nm_device_get_iface (NM_DEVICE (self)), + i, foo ? foo : ""); + g_free (foo); + } + } else { + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): no SSIDs to probe scan", + nm_device_get_iface (NM_DEVICE (self))); + } + } + + if (nm_supplicant_interface_request_scan (priv->supplicant.iface, ssids)) { + /* success */ + backoff = TRUE; + priv->requested_scan = TRUE; + nm_device_add_pending_action (NM_DEVICE (self), "scan", TRUE); + } + + if (ssids) { + /* Elements owned by the connections, so we don't free them here */ + g_ptr_array_free (ssids, TRUE); + } + } else { + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan requested but not allowed at this time", + nm_device_get_iface (NM_DEVICE (self))); + } + + priv->pending_scan_id = 0; + schedule_scan (self, backoff); + return FALSE; +} + + +/* + * schedule_scan + * + * Schedule a wireless scan. + * + */ +static void +schedule_scan (NMDeviceWifi *self, gboolean backoff) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gint32 now = nm_utils_get_monotonic_timestamp_s (); + + /* Cancel the pending scan if it would happen later than (now + the scan_interval) */ + if (priv->pending_scan_id) { + if (now + priv->scan_interval < priv->scheduled_scan_time) + cancel_pending_scan (self); + } + + if (!priv->pending_scan_id) { + guint factor = 2, next_scan = priv->scan_interval; + + if ( nm_device_is_activating (NM_DEVICE (self)) + || (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED)) + factor = 1; + + priv->pending_scan_id = g_timeout_add_seconds (next_scan, + request_wireless_scan, + self); + + priv->scheduled_scan_time = now + priv->scan_interval; + if (backoff && (priv->scan_interval < (SCAN_INTERVAL_MAX / factor))) { + priv->scan_interval += (SCAN_INTERVAL_STEP / factor); + /* Ensure the scan interval will never be less than 20s... */ + priv->scan_interval = MAX(priv->scan_interval, SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP); + /* ... or more than 120s */ + priv->scan_interval = MIN(priv->scan_interval, SCAN_INTERVAL_MAX); + } else if (!backoff && (priv->scan_interval == 0)) { + /* Invalid combination; would cause continual rescheduling of + * the scan and hog CPU. Reset to something minimally sane. + */ + priv->scan_interval = 5; + } + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scheduled scan in %d seconds (interval now %d seconds)", + nm_device_get_iface (NM_DEVICE (self)), + next_scan, + priv->scan_interval); + + } +} + + +static void +cancel_pending_scan (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->pending_scan_id) { + g_source_remove (priv->pending_scan_id); + priv->pending_scan_id = 0; + } +} + +static void +supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, + gboolean success, + NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan %s", + nm_device_get_iface (NM_DEVICE (self)), + success ? "successful" : "failed"); + + schedule_scan (self, success); + + /* Ensure that old APs get removed, which otherwise only + * happens when there are new BSSes. + */ + schedule_scanlist_cull (self); + + if (priv->requested_scan) { + priv->requested_scan = FALSE; + nm_device_remove_pending_action (NM_DEVICE (self), "scan", TRUE); + } +} + +/**************************************************************************** + * WPA Supplicant control stuff + * + */ + +static void +try_fill_ssid_for_hidden_ap (NMAccessPoint *ap) +{ + const struct ether_addr *bssid; + const GSList *connections, *iter; + + g_return_if_fail (nm_ap_get_ssid (ap) == NULL); + + bssid = nm_ap_get_address (ap); + g_assert (bssid); + + /* Look for this AP's BSSID in the seen-bssids list of a connection, + * and if a match is found, copy over the SSID */ + connections = nm_connection_provider_get_connections (nm_connection_provider_get ()); + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingWireless *s_wifi; + + s_wifi = nm_connection_get_setting_wireless (connection); + if (s_wifi) { + if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid)) { + nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi)); + break; + } + } + } +} + +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5] + +/* + * merge_scanned_ap + * + * If there is already an entry that matches the BSSID and ESSID of the + * AP to merge, replace that entry with the scanned AP. Otherwise, add + * the scanned AP to the list. + * + * TODO: possibly need to differentiate entries based on security too; i.e. if + * there are two scan results with the same BSSID and SSID but different + * security options? + * + */ +static void +merge_scanned_ap (NMDeviceWifi *self, + NMAccessPoint *merge_ap) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMAccessPoint *found_ap = NULL; + const GByteArray *ssid; + const struct ether_addr *bssid; + gboolean strict_match = TRUE; + + /* Let the manager try to fill in the SSID from seen-bssids lists */ + bssid = nm_ap_get_address (merge_ap); + ssid = nm_ap_get_ssid (merge_ap); + if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) { + /* Try to fill the SSID from the AP database */ + try_fill_ssid_for_hidden_ap (merge_ap); + + ssid = nm_ap_get_ssid (merge_ap); + if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) { + /* Yay, matched it, no longer treat as hidden */ + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): matched hidden AP " MAC_FMT " => '%s'", + nm_device_get_iface (NM_DEVICE (self)), + MAC_ARG (bssid->ether_addr_octet), + nm_utils_escape_ssid (ssid->data, ssid->len)); + nm_ap_set_broadcast (merge_ap, FALSE); + } else { + /* Didn't have an entry for this AP in the database */ + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): failed to match hidden AP " MAC_FMT, + nm_device_get_iface (NM_DEVICE (self)), + MAC_ARG (bssid->ether_addr_octet)); + } + } + + /* If the incoming scan result matches the hidden AP that NM is currently + * connected to but hasn't been seen in the scan list yet, don't use + * strict matching. Because the capabilities of the fake AP have to be + * constructed from the NMConnection of the activation request, they won't + * always be the same as the capabilities of the real AP from the scan. + */ + if (priv->current_ap && nm_ap_get_fake (priv->current_ap)) + strict_match = FALSE; + + found_ap = get_ap_by_supplicant_path (self, nm_ap_get_supplicant_path (merge_ap)); + if (!found_ap) + found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match); + if (found_ap) { + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): merging AP '%s' " MAC_FMT " (%p) with existing (%p)", + nm_device_get_iface (NM_DEVICE (self)), + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", + MAC_ARG (bssid->ether_addr_octet), + merge_ap, + found_ap); + + nm_ap_set_supplicant_path (found_ap, nm_ap_get_supplicant_path (merge_ap)); + nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap)); + nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap)); + nm_ap_set_rsn_flags (found_ap, nm_ap_get_rsn_flags (merge_ap)); + nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap)); + nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap)); + nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap)); + nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap)); + nm_ap_set_max_bitrate (found_ap, nm_ap_get_max_bitrate (merge_ap)); + + /* If the AP is noticed in a scan, it's automatically no longer + * fake, since it clearly exists somewhere. + */ + nm_ap_set_fake (found_ap, FALSE); + } else { + /* New entry in the list */ + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): adding new AP '%s' " MAC_FMT " (%p)", + nm_device_get_iface (NM_DEVICE (self)), + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", + MAC_ARG (bssid->ether_addr_octet), + merge_ap); + + g_object_ref (merge_ap); + priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap); + nm_ap_export_to_dbus (merge_ap); + emit_ap_added_removed (self, ACCESS_POINT_ADDED, merge_ap, TRUE); + } +} + +#define WPAS_REMOVED_TAG "supplicant-removed" + +static gboolean +cull_scan_list (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gint32 now = nm_utils_get_monotonic_timestamp_s (); + GSList *outdated_list = NULL; + GSList *elt; + guint32 removed = 0, total = 0; + + priv->scanlist_cull_id = 0; + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): checking scan list for outdated APs", + nm_device_get_iface (NM_DEVICE (self))); + + /* Walk the access point list and remove any access points older than + * three times the inactive scan interval. + */ + for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) { + NMAccessPoint *ap = elt->data; + const guint prune_interval_s = SCAN_INTERVAL_MAX * 3; + gint32 last_seen; + + /* Don't cull the associated AP or manually created APs */ + if (ap == priv->current_ap) + continue; + g_assert (!nm_ap_get_fake (ap)); /* only the current_ap can be fake */ + + /* Don't cull APs still known to the supplicant. Since the supplicant + * doesn't yet emit property updates for "last seen" we have to rely + * on changing signal strength for updating "last seen". But if the + * AP's strength doesn't change we won't get any updates for the AP, + * and we'll end up here even if the AP was still found by the + * supplicant in the last scan. + */ + if ( nm_ap_get_supplicant_path (ap) + && g_object_get_data (G_OBJECT (ap), WPAS_REMOVED_TAG) == NULL) + continue; + + last_seen = nm_ap_get_last_seen (ap); + if (!last_seen || last_seen + prune_interval_s < now) + outdated_list = g_slist_prepend (outdated_list, ap); + } + + /* Remove outdated APs */ + for (elt = outdated_list; elt; elt = g_slist_next (elt)) { + NMAccessPoint *outdated_ap = NM_AP (elt->data); + const struct ether_addr *bssid; + const GByteArray *ssid; + + bssid = nm_ap_get_address (outdated_ap); + ssid = nm_ap_get_ssid (outdated_ap); + nm_log_dbg (LOGD_WIFI_SCAN, + " removing %02x:%02x:%02x:%02x:%02x:%02x (%s%s%s)", + bssid->ether_addr_octet[0], bssid->ether_addr_octet[1], + bssid->ether_addr_octet[2], bssid->ether_addr_octet[3], + bssid->ether_addr_octet[4], bssid->ether_addr_octet[5], + ssid ? "'" : "", + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", + ssid ? "'" : ""); + + remove_access_point (self, outdated_ap); + removed++; + } + g_slist_free (outdated_list); + + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): removed %d APs (of %d)", + nm_device_get_iface (NM_DEVICE (self)), + removed, total); + + ap_list_dump (self); + + if(removed > 0) + nm_device_recheck_available_connections (NM_DEVICE (self)); + + return FALSE; +} + +static void +schedule_scanlist_cull (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* Cull the scan list after the last request for it has come in */ + if (priv->scanlist_cull_id) + g_source_remove (priv->scanlist_cull_id); + priv->scanlist_cull_id = g_timeout_add_seconds (4, (GSourceFunc) cull_scan_list, self); +} + +static void +supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, + const char *object_path, + GHashTable *properties, + NMDeviceWifi *self) +{ + NMDeviceState state; + NMAccessPoint *ap; + + g_return_if_fail (self != NULL); + g_return_if_fail (properties != NULL); + g_return_if_fail (iface != NULL); + + /* Ignore new APs when unavailable, unmanaged, or in AP mode */ + state = nm_device_get_state (NM_DEVICE (self)); + if (state <= NM_DEVICE_STATE_UNAVAILABLE) + return; + if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) + return; + + ap = nm_ap_new_from_properties (object_path, properties); + if (ap) { + nm_ap_dump (ap, "New AP: "); + + /* Add the AP to the device's AP list */ + merge_scanned_ap (self, ap); + g_object_unref (ap); + } else { + nm_log_warn (LOGD_WIFI_SCAN, "(%s): invalid AP properties received", + nm_device_get_iface (NM_DEVICE (self))); + } + + /* Remove outdated access points */ + schedule_scanlist_cull (self); +} + +static void +supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, + const char *object_path, + GHashTable *properties, + NMDeviceWifi *self) +{ + NMDeviceState state; + NMAccessPoint *ap; + + g_return_if_fail (self != NULL); + g_return_if_fail (object_path != NULL); + g_return_if_fail (properties != NULL); + + /* Ignore new APs when unavailable or unamnaged */ + state = nm_device_get_state (NM_DEVICE (self)); + if (state <= NM_DEVICE_STATE_UNAVAILABLE) + return; + + /* Update the AP's last-seen property */ + ap = get_ap_by_supplicant_path (self, object_path); + if (ap) + nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ()); + + /* Remove outdated access points */ + schedule_scanlist_cull (self); +} + +static void +supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, + const char *object_path, + NMDeviceWifi *self) +{ + NMAccessPoint *ap; + + g_return_if_fail (self != NULL); + g_return_if_fail (object_path != NULL); + + ap = get_ap_by_supplicant_path (self, object_path); + if (ap) + g_object_set_data (G_OBJECT (ap), WPAS_REMOVED_TAG, GUINT_TO_POINTER (TRUE)); +} + + +static void +cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + remove_supplicant_interface_error_handler (self); + remove_supplicant_timeouts (self); + if (disconnect && priv->supplicant.iface) + nm_supplicant_interface_disconnect (priv->supplicant.iface); +} + +static void +wifi_secrets_cb (NMActRequest *req, + guint32 call_id, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + NMDevice *dev = NM_DEVICE (user_data); + + g_return_if_fail (req == nm_device_get_act_request (dev)); + g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); + g_return_if_fail (nm_act_request_get_connection (req) == connection); + + if (error) { + nm_log_warn (LOGD_WIFI, "%s", error->message); + nm_device_state_changed (dev, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } else + nm_device_activate_schedule_stage1_device_prepare (dev); +} + +static void +remove_link_timeout (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->link_timeout_id) { + g_source_remove (priv->link_timeout_id); + priv->link_timeout_id = 0; + } +} + + +/* + * link_timeout_cb + * + * Called when the link to the access point has been down for a specified + * period of time. + */ +static gboolean +link_timeout_cb (gpointer user_data) +{ + NMDevice *dev = NM_DEVICE (user_data); + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev)); + + priv->link_timeout_id = 0; + + /* Disconnect event while activated; the supplicant hasn't been able + * to reassociate within the timeout period, so the connection must + * fail. + */ + if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED) + return FALSE; + + /* If the access point failed, and wasn't found by the supplicant when it + * attempted to reconnect, then it's probably out of range or turned off. + * Remove it from the list and if it's actually still present, it'll be + * found in the next scan. + */ + if (priv->ssid_found == FALSE && priv->current_ap) + set_current_ap (self, NULL, TRUE, TRUE); + + nm_device_state_changed (dev, + NM_DEVICE_STATE_FAILED, + priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT : + NM_DEVICE_STATE_REASON_SSID_NOT_FOUND); + return FALSE; +} + +static gboolean +need_new_8021x_secrets (NMDeviceWifi *self, + guint32 old_state, + 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_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection != NULL, FALSE); + + /* 802.1x stuff only happens in the supplicant's ASSOCIATED state when it's + * attempting to authenticate with the AP. + */ + if (old_state != NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED) + return 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) { + nm_setting_get_secret_flags (NM_SETTING (s_8021x), + NM_SETTING_802_1X_PASSWORD, + &secret_flags, + NULL); + 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) { + nm_setting_get_secret_flags (NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + &secret_flags, + NULL); + 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, + guint32 old_state, + const char **setting_name) +{ + NMSettingWirelessSecurity *s_wsec; + NMConnection *connection; + const char *key_mgmt = NULL; + + g_assert (setting_name != NULL); + + connection = nm_device_get_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection != NULL, FALSE); + + /* A bad PSK will cause the supplicant to disconnect during the 4-way handshake */ + if (old_state != NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE) + return 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) { + *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, + guint32 new_state, + guint32 old_state, + int disconnect_reason) +{ + NMDevice *device = NM_DEVICE (self); + NMActRequest *req; + NMConnection *connection; + const char *setting_name = NULL; + gboolean handled = FALSE; + + g_return_val_if_fail (new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE); + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_val_if_fail (req != NULL, FALSE); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if ( need_new_8021x_secrets (self, old_state, &setting_name) + || need_new_wpa_psk (self, old_state, &setting_name)) { + + nm_connection_clear_secrets (connection); + + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): disconnected during association," + " asking for new key.", nm_device_get_iface (device)); + + cleanup_association_attempt (self, TRUE); + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + nm_act_request_get_secrets (req, + setting_name, + NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW, + NULL, + wifi_secrets_cb, + self); + handled = TRUE; + } + + return handled; +} + +static void +supplicant_iface_state_cb (NMSupplicantInterface *iface, + guint32 new_state, + guint32 old_state, + int disconnect_reason, + gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + NMDeviceState devstate; + gboolean scanning; + + if (new_state == old_state) + return; + + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "(%s): supplicant interface state: %s -> %s", + nm_device_get_iface (device), + nm_supplicant_interface_state_to_string (old_state), + nm_supplicant_interface_state_to_string (new_state)); + + devstate = nm_device_get_state (device); + scanning = nm_supplicant_interface_get_scanning (iface); + + /* In these states we know the supplicant is actually talking to something */ + if ( new_state >= NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING + && new_state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) + priv->ssid_found = TRUE; + + switch (new_state) { + case NM_SUPPLICANT_INTERFACE_STATE_READY: + priv->scan_interval = SCAN_INTERVAL_MIN; + + /* If the interface can now be activated because the supplicant is now + * available, transition to DISCONNECTED. + */ + if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE); + } + + nm_log_dbg (LOGD_WIFI_SCAN, + "(%s): supplicant ready, requesting initial scan", + nm_device_get_iface (device)); + + /* Request a scan to get latest results */ + cancel_pending_scan (self); + request_wireless_scan (self); + + if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) + nm_device_remove_pending_action (device, "waiting for supplicant", TRUE); + break; + case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: + remove_supplicant_interface_error_handler (self); + remove_supplicant_timeouts (self); + + /* If this is the initial association during device activation, + * schedule the next activation stage. + */ + if (devstate == NM_DEVICE_STATE_CONFIG) { + NMConnection *connection; + NMSettingWireless *s_wifi; + const GByteArray *ssid; + + connection = nm_device_get_connection (NM_DEVICE (self)); + g_return_if_fail (connection); + + s_wifi = nm_connection_get_setting_wireless (connection); + g_return_if_fail (s_wifi); + + ssid = nm_setting_wireless_get_ssid (s_wifi); + g_return_if_fail (ssid); + + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless) Stage 2 of 5 (Device Configure) " + "successful. %s '%s'.", + nm_device_get_iface (device), + priv->mode == NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot" : + "Connected to wireless network", + ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)"); + nm_device_activate_schedule_stage3_ip_config_start (device); + } else if (devstate == NM_DEVICE_STATE_ACTIVATED) + periodic_update (self, NULL); + break; + case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED: + if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) { + /* Disconnect of an 802.1x/LEAP connection during authentication, + * or disconnect of a WPA-PSK connection during the 4-way handshake, + * often means secrets are wrong. Not always the case, but until we + * have more information from wpa_supplicant about why the + * disconnect happened this is the best we can do. + */ + if (handle_8021x_or_psk_auth_fail (self, new_state, old_state, disconnect_reason)) + break; + } + + /* Otherwise it might be a stupid driver or some transient error, so + * let the supplicant try to reconnect a few more times. Give it more + * time if a scan is in progress since the link might be dropped during + * the scan but will be re-established when the scan is done. + */ + if (devstate == NM_DEVICE_STATE_ACTIVATED) { + if (priv->link_timeout_id == 0) { + priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, link_timeout_cb, self); + priv->ssid_found = FALSE; + } + } + break; + case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + cleanup_association_attempt (self, FALSE); + + /* If the device is already in UNAVAILABLE state then the state change + * is a NOP and the interface won't be re-acquired in the device state + * change handler. So ensure we have a new one here so that we're + * ready if the supplicant comes back. + */ + supplicant_interface_release (self); + supplicant_interface_acquire (self); + + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + break; + default: + break; + } + + /* Signal scanning state changes */ + if ( new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING + || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) + g_object_notify (G_OBJECT (self), "scanning"); +} + +struct iface_con_error_cb_data { + NMDeviceWifi *self; + char *name; + char *message; +}; + +static gboolean +supplicant_iface_connection_error_cb_handler (gpointer user_data) +{ + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + struct iface_con_error_cb_data * cb_data = (struct iface_con_error_cb_data *) user_data; + + g_return_val_if_fail (cb_data != NULL, FALSE); + + self = cb_data->self; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (!nm_device_is_activating (NM_DEVICE (self))) + goto out; + + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): association request to the supplicant " + "failed: %s - %s", + nm_device_get_iface (NM_DEVICE (self)), + cb_data->name, + cb_data->message); + + cleanup_association_attempt (self, TRUE); + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + +out: + priv->supplicant.iface_con_error_cb_id = 0; + g_free (cb_data->name); + g_free (cb_data->message); + g_slice_free (struct iface_con_error_cb_data, cb_data); + return FALSE; +} + + +static void +supplicant_iface_connection_error_cb (NMSupplicantInterface * iface, + const char * name, + const char * message, + NMDeviceWifi * self) +{ + NMDeviceWifiPrivate *priv; + struct iface_con_error_cb_data *cb_data; + guint id; + + g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + cb_data = g_slice_new0 (struct iface_con_error_cb_data); + cb_data->self = self; + cb_data->name = g_strdup (name); + cb_data->message = g_strdup (message); + + if (priv->supplicant.iface_con_error_cb_id) + g_source_remove (priv->supplicant.iface_con_error_cb_id); + + id = g_idle_add (supplicant_iface_connection_error_cb_handler, cb_data); + priv->supplicant.iface_con_error_cb_id = id; +} + +static void +supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface, + GParamSpec *pspec, + NMDeviceWifi *self) +{ + NMDeviceState state; + gboolean scanning; + + scanning = nm_supplicant_interface_get_scanning (iface); + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): now %s", + nm_device_get_iface (NM_DEVICE (self)), + scanning ? "scanning" : "idle"); + + g_object_notify (G_OBJECT (self), "scanning"); + + /* Run a quick update of current AP when coming out of a scan */ + state = nm_device_get_state (NM_DEVICE (self)); + if (!scanning && state == NM_DEVICE_STATE_ACTIVATED) + periodic_update (self, NULL); +} + +static void +remove_supplicant_connection_timeout (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + + g_return_if_fail (self != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* Remove any pending timeouts on the request */ + if (priv->supplicant.con_timeout_id) { + g_source_remove (priv->supplicant.con_timeout_id); + priv->supplicant.con_timeout_id = 0; + } +} + +static NMActStageReturn +handle_auth_or_fail (NMDeviceWifi *self, + NMActRequest *req, + gboolean new_secrets) +{ + const char *setting_name; + guint32 tries; + NMConnection *connection; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE); + + if (!req) { + req = nm_device_get_act_request (NM_DEVICE (self)); + g_assert (req); + } + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES)); + if (tries > 3) + return NM_ACT_STAGE_RETURN_FAILURE; + + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + + nm_connection_clear_secrets (connection); + setting_name = nm_connection_need_secrets (connection, NULL); + if (setting_name) { + NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION; + + if (new_secrets) + flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW; + nm_act_request_get_secrets (req, setting_name, flags, NULL, wifi_secrets_cb, self); + + g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else + nm_log_warn (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); + + return ret; +} + +/* + * supplicant_connection_timeout_cb + * + * Called when the supplicant has been unable to connect to an access point + * within a specified period of time. + */ +static gboolean +supplicant_connection_timeout_cb (gpointer user_data) +{ + NMDevice *dev = NM_DEVICE (user_data); + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + + cleanup_association_attempt (self, TRUE); + + if (!nm_device_is_activating (dev)) + return FALSE; + + /* Timed out waiting for a successful connection to the AP; if the AP's + * security requires network-side authentication (like WPA or 802.1x) + * and the connection attempt timed out then it's likely the authentication + * information (passwords, pin codes, etc) are wrong. + */ + + req = nm_device_get_act_request (dev); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + if ( priv->mode == NM_802_11_MODE_ADHOC + || priv->mode == NM_802_11_MODE_AP) { + /* In Ad-Hoc and AP modes there's nothing to check the encryption key + * (if any), so supplicant timeouts here are almost certainly the wifi + * driver being really stupid. + */ + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): %s network creation took " + "too long, failing activation.", + nm_device_get_iface (dev), + priv->mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc" : "Hotspot"); + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); + return FALSE; + } + + g_assert (priv->mode == NM_802_11_MODE_INFRA); + + if (priv->ssid_found && nm_connection_get_setting_wireless_security (connection)) { + guint64 timestamp = 0; + gboolean new_secrets = TRUE; + + /* Connection failed; either driver problems, the encryption key is + * wrong, or the passwords or certificates were wrong. + */ + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): association took too long.", + nm_device_get_iface (dev)); + + /* Ask for new secrets only if we've never activated this connection + * before. If we've connected before, don't bother the user with + * dialogs, just retry or fail, and if we never connect the user can + * fix the password somewhere else. + */ + if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), ×tamp)) + new_secrets = !timestamp; + + if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): asking for new secrets", + nm_device_get_iface (dev)); + } else { + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } + } else { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): association took too long, " + "failing activation.", + nm_device_get_iface (dev)); + nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, + priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT : + NM_DEVICE_STATE_REASON_SSID_NOT_FOUND); + } + + return FALSE; +} + + +static gboolean +start_supplicant_connection_timeout (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv; + guint id; + + g_return_val_if_fail (self != NULL, FALSE); + + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* Set up a timeout on the connection attempt to fail it after 25 seconds */ + id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self); + if (id == 0) { + nm_log_err (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): couldn't start supplicant " + "timeout timer.", + nm_device_get_iface (NM_DEVICE (self))); + return FALSE; + } + priv->supplicant.con_timeout_id = id; + return TRUE; +} + + +static void +remove_supplicant_timeouts (NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + remove_supplicant_connection_timeout (self); + remove_link_timeout (self); +} + +static NMSupplicantConfig * +build_supplicant_config (NMDeviceWifi *self, + NMConnection *connection, + guint32 fixed_freq) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMSupplicantConfig *config = NULL; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + + g_return_val_if_fail (self != NULL, NULL); + + s_wireless = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wireless != NULL, NULL); + + config = nm_supplicant_config_new (); + if (!config) + return NULL; + + /* Warn if AP mode may not be supported */ + if ( g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0 + && nm_supplicant_interface_get_ap_support (priv->supplicant.iface) == AP_SUPPORT_UNKNOWN) { + nm_log_warn (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out."); + } + + if (!nm_supplicant_config_add_setting_wireless (config, + s_wireless, + fixed_freq)) { + nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config."); + goto error; + } + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + if (s_wireless_sec) { + NMSetting8021x *s_8021x; + const char *con_uuid = nm_connection_get_uuid (connection); + + g_assert (con_uuid); + s_8021x = nm_connection_get_setting_802_1x (connection); + if (!nm_supplicant_config_add_setting_wireless_security (config, + s_wireless_sec, + s_8021x, + con_uuid)) { + nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to " + "supplicant config."); + goto error; + } + } else { + if (!nm_supplicant_config_add_no_security (config)) { + nm_log_err (LOGD_WIFI, "Couldn't add unsecured option to supplicant config."); + goto error; + } + } + + return config; + +error: + g_object_unref (config); + return NULL; +} + +/****************************************************************************/ + +static void +update_permanent_hw_address (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + struct ifreq req; + struct ethtool_perm_addr *epaddr = NULL; + int fd, ret; + + fd = socket (PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + nm_log_err (LOGD_HW, "could not open control socket."); + return; + } + + /* Get permanent MAC address */ + memset (&req, 0, sizeof (struct ifreq)); + strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ); + + epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN); + epaddr->cmd = ETHTOOL_GPERMADDR; + epaddr->size = ETH_ALEN; + req.ifr_data = (void *) epaddr; + + errno = 0; + ret = ioctl (fd, SIOCETHTOOL, &req); + if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) { + nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)", + nm_device_get_iface (dev), errno); + /* Fall back to current address */ + memcpy (epaddr->data, nm_device_get_hw_address (dev, NULL), ETH_ALEN); + } + + if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) { + memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN); + g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS); + } + + g_free (epaddr); + close (fd); +} + +static void +update_initial_hw_address (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + char *mac_str; + + /* This sets initial MAC address from current MAC address. It should only + * be called from NMDevice constructor() to really get the initial address. + */ + memcpy (priv->initial_hw_addr, nm_device_get_hw_address (dev, NULL), ETH_ALEN); + + mac_str = nm_utils_hwaddr_ntoa (priv->initial_hw_addr, ARPHRD_ETHER); + nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s", + nm_device_get_iface (dev), mac_str); + g_free (mac_str); +} + +static NMActStageReturn +act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMActStageReturn ret; + NMAccessPoint *ap = NULL; + NMActRequest *req; + NMConnection *connection; + NMSettingWireless *s_wireless; + const GByteArray *cloned_mac; + GSList *iter; + const char *mode; + const char *ap_path; + + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (dev, 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 != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + connection = nm_act_request_get_connection (req); + g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + s_wireless = nm_connection_get_setting_wireless (connection); + g_assert (s_wireless); + + mode = nm_setting_wireless_get_mode (s_wireless); + if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) + priv->mode = NM_802_11_MODE_INFRA; + else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) + priv->mode = NM_802_11_MODE_ADHOC; + else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) { + priv->mode = NM_802_11_MODE_AP; + + /* Scanning not done in AP mode; clear the scan list */ + remove_all_aps (self); + } + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE); + + /* The kernel doesn't support Ad-Hoc WPA connections well at this time, + * and turns them into open networks. It's been this way since at least + * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks. + */ + if (is_adhoc_wpa (connection)) { + nm_log_warn (LOGD_WIFI, "Ad-Hoc WPA disabled due to kernel bugs"); + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + /* Set spoof MAC to the interface */ + cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); + if (cloned_mac && (cloned_mac->len == ETH_ALEN)) + nm_device_set_hw_addr (dev, (const guint8 *) cloned_mac->data, "set", LOGD_WIFI); + + /* AP mode never uses a specific object or existing scanned AP */ + if (priv->mode != NM_802_11_MODE_AP) { + + 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) + goto done; + + /* Find a compatible AP in the scan list */ + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint *candidate = NM_AP (iter->data); + + if (nm_ap_check_compatible (candidate, connection)) { + ap = candidate; + break; + } + } + } + + if (ap) { + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap)); + goto done; + } + + /* If the user is trying to connect to an AP that NM doesn't yet know about + * (hidden network or something) or starting a Hotspot, create an fake AP + * from the security settings in the connection. This "fake" AP gets used + * until the real one is found in the scan list (Ad-Hoc or Hidden), or until + * the device is deactivated (Hotspot). + */ + ap = nm_ap_new_fake_from_connection (connection); + g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (nm_ap_get_mode (ap) == NM_802_11_MODE_INFRA) + nm_ap_set_broadcast (ap, FALSE); + else if (nm_ap_is_hotspot (ap)) + nm_ap_set_address (ap, (const struct ether_addr *) nm_device_get_hw_address (dev, NULL)); + + priv->ap_list = g_slist_prepend (priv->ap_list, ap); + nm_ap_export_to_dbus (ap); + g_object_freeze_notify (G_OBJECT (self)); + set_current_ap (self, ap, FALSE, FALSE); + emit_ap_added_removed (self, ACCESS_POINT_ADDED, ap, TRUE); + g_object_thaw_notify (G_OBJECT (self)); + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap)); + return NM_ACT_STAGE_RETURN_SUCCESS; + +done: + set_current_ap (self, ap, TRUE, FALSE); + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +ensure_hotspot_frequency (NMDeviceWifi *self, + NMSettingWireless *s_wifi, + NMAccessPoint *ap) +{ + const char *band = nm_setting_wireless_get_band (s_wifi); + const guint32 a_freqs[] = { 5180, 5200, 5220, 5745, 5765, 5785, 5805, 0 }; + const guint32 bg_freqs[] = { 2412, 2437, 2462, 2472, 0 }; + guint32 freq = 0; + + g_assert (ap); + + if (nm_ap_get_freq (ap)) + return; + + if (g_strcmp0 (band, "a") == 0) + freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), a_freqs); + else + freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs); + + if (!freq) + freq = (g_strcmp0 (band, "a") == 0) ? 5180 : 2462; + + nm_ap_set_freq (ap, freq); +} + +static NMActStageReturn +act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + const char *iface = nm_device_get_iface (dev); + NMSupplicantConfig *config = NULL; + gulong id = 0; + NMActRequest *req; + NMAccessPoint *ap; + NMConnection *connection; + const char *setting_name; + NMSettingWireless *s_wireless; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + remove_supplicant_timeouts (self); + + req = nm_device_get_act_request (dev); + g_assert (req); + + ap = priv->current_ap; + if (!ap) { + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED; + goto out; + } + + connection = nm_act_request_get_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) { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): access point '%s' has security," + " but secrets are required.", + iface, nm_connection_get_id (connection)); + + ret = handle_auth_or_fail (self, req, FALSE); + if (ret == NM_ACT_STAGE_RETURN_FAILURE) + *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; + goto out; + } + + /* have secrets, or no secrets required */ + if (nm_connection_get_setting_wireless_security (connection)) { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): connection '%s' has security" + ", and secrets exist. No new secrets needed.", + iface, nm_connection_get_id (connection)); + } else { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): connection '%s' requires no " + "security. No secrets needed.", + iface, nm_connection_get_id (connection)); + } + + priv->ssid_found = FALSE; + + /* Supplicant requires an initial frequency for Ad-Hoc and Hotspot; if the user + * didn't specify one and we didn't find an AP that matched the connection, + * just pick a frequency the device supports. + */ + if ((nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) || nm_ap_is_hotspot (ap)) + ensure_hotspot_frequency (self, s_wireless, ap); + + /* Build up the supplicant configuration */ + config = build_supplicant_config (self, connection, nm_ap_get_freq (ap)); + if (config == NULL) { + nm_log_err (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): couldn't build wireless configuration.", + iface); + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; + goto out; + } + + /* Hook up error signal handler to capture association errors */ + id = g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR, + G_CALLBACK (supplicant_iface_connection_error_cb), + self); + priv->supplicant.iface_error_id = id; + + if (!nm_supplicant_interface_set_config (priv->supplicant.iface, config)) { + nm_log_err (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): couldn't send wireless " + "configuration to the supplicant.", iface); + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; + goto out; + } + + if (!start_supplicant_connection_timeout (self)) { + *reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED; + goto out; + } + + if (!priv->periodic_source_id) + priv->periodic_source_id = g_timeout_add_seconds (6, periodic_update_cb, self); + + /* 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, TRUE); + + if (config) { + /* Supplicant interface object refs the config; we no longer care about + * it after this function. + */ + g_object_unref (config); + } + return ret; +} + +static NMActStageReturn +act_stage3_ip4_config_start (NMDevice *device, + NMIP4Config **out_config, + NMDeviceStateReason *reason) +{ + NMConnection *connection; + NMSettingIP4Config *s_ip4; + const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; + + connection = nm_device_get_connection (device); + g_assert (connection); + s_ip4 = nm_connection_get_setting_ip4_config (connection); + if (s_ip4) + method = nm_setting_ip4_config_get_method (s_ip4); + + /* Indicate that a critical protocol is about to start */ + if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) + nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE); + + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, reason); +} + +static NMActStageReturn +act_stage3_ip6_config_start (NMDevice *device, + NMIP6Config **out_config, + NMDeviceStateReason *reason) +{ + NMConnection *connection; + NMSettingIP6Config *s_ip6; + const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + + connection = nm_device_get_connection (device); + g_assert (connection); + s_ip6 = nm_connection_get_setting_ip6_config (connection); + if (s_ip6) + method = nm_setting_ip6_config_get_method (s_ip6); + + /* Indicate that a critical protocol is about to start */ + if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 || + strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) + nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE); + + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, reason); +} + +static void +ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) +{ + NMConnection *connection; + NMSettingWireless *s_wifi; + guint32 mtu; + + connection = nm_device_get_connection (device); + g_assert (connection); + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + + /* MTU override */ + mtu = nm_setting_wireless_get_mtu (s_wifi); + if (mtu) + nm_ip4_config_set_mtu (config, mtu); +} + +static gboolean +is_static_wep (NMConnection *connection) +{ + NMSettingWirelessSecurity *s_wsec; + const char *str; + + g_return_val_if_fail (connection != NULL, FALSE); + + s_wsec = nm_connection_get_setting_wireless_security (connection); + if (!s_wsec) + return FALSE; + + str = nm_setting_wireless_security_get_key_mgmt (s_wsec); + if (g_strcmp0 (str, "none") != 0) + return FALSE; + + str = nm_setting_wireless_security_get_auth_alg (s_wsec); + if (g_strcmp0 (str, "leap") == 0) + return FALSE; + + return TRUE; +} + +static NMActStageReturn +handle_ip_config_timeout (NMDeviceWifi *self, + NMConnection *connection, + gboolean may_fail, + gboolean *chain_up, + NMDeviceStateReason *reason) +{ + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + + g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) { + *chain_up = TRUE; + return ret; + } + + /* If IP configuration times out and it's a static WEP connection, that + * usually means the WEP key is wrong. WEP's Open System auth mode has + * no provision for figuring out if the WEP key is wrong, so you just have + * to wait for DHCP to fail to figure it out. For all other WiFi security + * types (open, WPA, 802.1x, etc) if the secrets/certs were wrong the + * connection would have failed before IP configuration. + */ + if (!may_fail && is_static_wep (connection)) { + /* Activation failed, we must have bad encryption key */ + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): could not get IP configuration for " + "connection '%s'.", + nm_device_get_iface (NM_DEVICE (self)), + nm_connection_get_id (connection)); + + ret = handle_auth_or_fail (self, NULL, TRUE); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { + nm_log_info (LOGD_DEVICE | LOGD_WIFI, + "Activation (%s/wireless): asking for new secrets", + nm_device_get_iface (NM_DEVICE (self))); + } else { + *reason = NM_DEVICE_STATE_REASON_NO_SECRETS; + } + } else { + /* Not static WEP or failure allowed; let superclass handle it */ + *chain_up = TRUE; + } + + return ret; +} + + +static NMActStageReturn +act_stage4_ip4_config_timeout (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMConnection *connection; + NMSettingIP4Config *s_ip4; + gboolean may_fail = FALSE, chain_up = FALSE; + NMActStageReturn ret; + + connection = nm_device_get_connection (dev); + g_assert (connection); + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + may_fail = nm_setting_ip4_config_get_may_fail (s_ip4); + + ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason); + if (chain_up) + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (dev, reason); + + return ret; +} + +static NMActStageReturn +act_stage4_ip6_config_timeout (NMDevice *dev, NMDeviceStateReason *reason) +{ + NMConnection *connection; + NMSettingIP6Config *s_ip6; + gboolean may_fail = FALSE, chain_up = FALSE; + NMActStageReturn ret; + + connection = nm_device_get_connection (dev); + g_assert (connection); + + s_ip6 = nm_connection_get_setting_ip6_config (connection); + may_fail = nm_setting_ip6_config_get_may_fail (s_ip6); + + ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason); + if (chain_up) + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (dev, reason); + + return ret; +} + +static void +activation_success_handler (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + int ifindex = nm_device_get_ifindex (dev); + NMAccessPoint *ap; + struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; + NMAccessPoint *tmp_ap = NULL; + NMActRequest *req; + NMConnection *connection; + + req = nm_device_get_act_request (dev); + g_assert (req); + + connection = nm_act_request_get_connection (req); + g_assert (connection); + + /* Clear any critical protocol notification in the wifi stack */ + nm_platform_wifi_indicate_addressing_running (ifindex, FALSE); + + /* Clear wireless secrets tries on success */ + g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); + + ap = priv->current_ap; + + /* If the AP isn't fake, it was found in the scan list and all its + * details are known. + */ + if (!ap || !nm_ap_get_fake (ap)){ + ap = NULL; + goto done; + } + + /* If the activate AP was fake, it probably won't have a BSSID at all. + * But if activation was successful, the card will know the BSSID. Grab + * the BSSID off the card and fill in the BSSID of the activation AP. + */ + nm_platform_wifi_get_bssid (ifindex, &bssid); + if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap))) + nm_ap_set_address (ap, &bssid); + if (!nm_ap_get_freq (ap)) + nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (ifindex)); + if (!nm_ap_get_max_bitrate (ap)) + nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (ifindex)); + + tmp_ap = find_active_ap (self, ap, TRUE); + if (tmp_ap) { + const GByteArray *ssid = nm_ap_get_ssid (tmp_ap); + + /* Found a better match in the scan list than the fake AP. Use it + * instead. + */ + + /* If the better match was a hidden AP, update it's SSID */ + if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) + nm_ap_set_ssid (tmp_ap, nm_ap_get_ssid (ap)); + + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), + nm_ap_get_dbus_path (tmp_ap)); + } + +done: + periodic_update (self, ap); + + /* ap might be already unrefed, because it was a fake_ap. But we don't touch it... */ + if (tmp_ap && ap == priv->current_ap) { + /* Strange, we would expect periodic_update() to find a better AP + * then the fake one and reset it. Reset the fake current_ap to NULL + * now, which will remove the fake ap. + **/ + set_current_ap (self, NULL, TRUE, FALSE); + } + + /* No need to update seen BSSIDs cache, that is done by set_current_ap() already */ + + /* Reset scan interval to something reasonable */ + priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2); +} + +static void +activation_failure_handler (NMDevice *dev) +{ + NMConnection *connection; + + connection = nm_device_get_connection (dev); + g_assert (connection); + + /* Clear wireless secrets tries on failure */ + g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL); + + /* Clear any critical protocol notification in the wifi stack */ + nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (dev), FALSE); +} + +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); + gboolean clear_aps = FALSE; + + if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { + /* Clean up the supplicant interface because in these states the + * device cannot be used. + */ + if (priv->supplicant.iface) + supplicant_interface_release (self); + + if (priv->periodic_source_id) { + g_source_remove (priv->periodic_source_id); + priv->periodic_source_id = 0; + } + + cleanup_association_attempt (self, TRUE); + remove_all_aps (self); + } + + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + clear_aps = TRUE; + break; + case NM_DEVICE_STATE_UNAVAILABLE: + /* If the device is enabled and the supplicant manager is ready, + * acquire a supplicant interface and transition to DISCONNECTED because + * the device is now ready to use. + */ + if (priv->enabled && (nm_device_get_firmware_missing (device) == FALSE)) { + if (!priv->supplicant.iface) + supplicant_interface_acquire (self); + } + clear_aps = TRUE; + break; + case NM_DEVICE_STATE_NEED_AUTH: + if (priv->supplicant.iface) + nm_supplicant_interface_disconnect (priv->supplicant.iface); + break; + case NM_DEVICE_STATE_IP_CHECK: + /* Clear any critical protocol notification in the wifi stack */ + nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE); + 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: + /* Kick off a scan to get latest results */ + priv->scan_interval = SCAN_INTERVAL_MIN; + cancel_pending_scan (self); + request_wireless_scan (self); + break; + default: + break; + } + + if (clear_aps) + remove_all_aps (self); +} + +static void +set_enabled (NMDevice *device, gboolean enabled) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDeviceState state; + + if (priv->enabled == enabled) + return; + + priv->enabled = enabled; + + nm_log_dbg (LOGD_WIFI, "(%s): device now %s", + nm_device_get_iface (NM_DEVICE (device)), + enabled ? "enabled" : "disabled"); + + state = nm_device_get_state (NM_DEVICE (self)); + if (state < NM_DEVICE_STATE_UNAVAILABLE) { + nm_log_dbg (LOGD_WIFI, "(%s): %s blocked by UNMANAGED state", + enabled ? "enable" : "disable", + nm_device_get_iface (NM_DEVICE (device))); + return; + } + + if (enabled) { + gboolean no_firmware = FALSE; + + if (state != NM_DEVICE_STATE_UNAVAILABLE) + nm_log_warn (LOGD_CORE, "not in expected unavailable state!"); + + if (!nm_device_bring_up (NM_DEVICE (self), TRUE, &no_firmware)) { + nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up", + nm_device_get_iface (NM_DEVICE (device))); + + if (no_firmware) + nm_device_set_firmware_missing (NM_DEVICE (device), TRUE); + else { + /* The device sucks, or the kernel was lying to us about the killswitch state */ + priv->enabled = FALSE; + } + return; + } + + /* Re-initialize the supplicant interface and wait for it to be ready */ + if (priv->supplicant.iface) + supplicant_interface_release (self); + supplicant_interface_acquire (self); + + nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state", + nm_device_get_iface (NM_DEVICE (device))); + } else { + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_NONE); + nm_device_take_down (NM_DEVICE (self), TRUE); + } +} + +/********************************************************************/ + +NMDevice * +nm_device_wifi_new (NMPlatformLink *platform_device) +{ + g_return_val_if_fail (platform_device != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIFI, + NM_DEVICE_PLATFORM_DEVICE, platform_device, + NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, + NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, + NULL); +} + +static void +nm_device_wifi_init (NMDeviceWifi *self) +{ + NM_DEVICE_WIFI_GET_PRIVATE (self)->mode = NM_802_11_MODE_INFRA; +} + +static void +dispose (GObject *object) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (object); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (priv->disposed) { + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); + return; + } + + priv->disposed = TRUE; + + if (priv->periodic_source_id) { + g_source_remove (priv->periodic_source_id); + priv->periodic_source_id = 0; + } + + cleanup_association_attempt (self, TRUE); + supplicant_interface_release (self); + + if (priv->supplicant.mgr) { + g_object_unref (priv->supplicant.mgr); + priv->supplicant.mgr = NULL; + } + + remove_all_aps (self); + + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceWifi *device = NM_DEVICE_WIFI (object); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + GPtrArray *array; + GSList *iter; + + switch (prop_id) { + case PROP_PERM_HW_ADDRESS: + g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->perm_hw_addr, ARPHRD_ETHER)); + break; + case PROP_MODE: + g_value_set_uint (value, priv->mode); + break; + case PROP_BITRATE: + g_value_set_uint (value, priv->rate); + break; + case PROP_CAPABILITIES: + g_value_set_uint (value, priv->capabilities); + break; + case PROP_ACCESS_POINTS: + array = g_ptr_array_sized_new (4); + for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) + g_ptr_array_add (array, g_strdup (nm_ap_get_dbus_path (NM_AP (iter->data)))); + g_value_take_boxed (value, array); + break; + case PROP_ACTIVE_ACCESS_POINT: + if (priv->current_ap) + g_value_set_boxed (value, nm_ap_get_dbus_path (priv->current_ap)); + else + g_value_set_boxed (value, "/"); + break; + case PROP_SCANNING: + g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->supplicant.iface)); + 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) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +nm_device_wifi_class_init (NMDeviceWifiClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate)); + + object_class->constructor = constructor; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + + parent_class->bring_up = bring_up; + parent_class->update_permanent_hw_address = update_permanent_hw_address; + parent_class->update_initial_hw_address = update_initial_hw_address; + 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->check_connection_available_wifi_hidden = check_connection_available_wifi_hidden; + parent_class->complete_connection = complete_connection; + parent_class->set_enabled = set_enabled; + + parent_class->act_stage1_prepare = act_stage1_prepare; + parent_class->act_stage2_config = act_stage2_config; + parent_class->ip4_config_pre_commit = ip4_config_pre_commit; + parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + parent_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; + parent_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; + parent_class->deactivate = deactivate; + + parent_class->state_changed = device_state_changed; + + klass->scanning_allowed = scanning_allowed; + + /* Properties */ + g_object_class_install_property (object_class, PROP_PERM_HW_ADDRESS, + g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS, + "Permanent MAC Address", + "Permanent hardware MAC address", + NULL, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_MODE, + g_param_spec_uint (NM_DEVICE_WIFI_MODE, + "Mode", + "Mode", + NM_802_11_MODE_UNKNOWN, + NM_802_11_MODE_AP, + NM_802_11_MODE_INFRA, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_BITRATE, + g_param_spec_uint (NM_DEVICE_WIFI_BITRATE, + "Bitrate", + "Bitrate", + 0, G_MAXUINT32, 0, + G_PARAM_READABLE)); + + g_object_class_install_property + (object_class, PROP_ACCESS_POINTS, + g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS, + "Access points", + "Access points", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_ACTIVE_ACCESS_POINT, + g_param_spec_boxed (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT, + "Active access point", + "Currently active access point", + DBUS_TYPE_G_OBJECT_PATH, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_CAPABILITIES, + g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES, + "Wireless Capabilities", + "Wireless Capabilities", + 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, PROP_SCANNING, + g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING, + "Scanning", + "Scanning", + FALSE, + G_PARAM_READABLE)); + + /* Signals */ + signals[ACCESS_POINT_ADDED] = + g_signal_new ("access-point-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[ACCESS_POINT_REMOVED] = + g_signal_new ("access-point-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[SCANNING_ALLOWED] = + g_signal_new ("scanning-allowed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed), + scanning_allowed_accumulator, NULL, NULL, + G_TYPE_BOOLEAN, 0); + + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_wifi_object_info); + + dbus_g_error_domain_register (NM_WIFI_ERROR, NULL, NM_TYPE_WIFI_ERROR); +} + + diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h new file mode 100644 index 0000000000..f0a1beacde --- /dev/null +++ b/src/devices/wifi/nm-device-wifi.h @@ -0,0 +1,94 @@ +/* -*- 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 - 2010 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_DEVICE_WIFI_H +#define NM_DEVICE_WIFI_H + +#include +#include +#include + +#include "nm-device.h" +#include "nm-wifi-ap.h" + +struct NMAccessPointList; + +G_BEGIN_DECLS + +#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)) + +typedef enum { + NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, /*< nick=ConnectionNotWireless >*/ + NM_WIFI_ERROR_CONNECTION_INVALID, /*< nick=ConnectionInvalid >*/ + NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/ + NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, /*< nick=AccessPointNotFound >*/ + NM_WIFI_ERROR_SCAN_NOT_ALLOWED, /*< nick=ScanNotAllowed >*/ + NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, /*< nick=ApModeUnsupported >*/ + NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED, /*< nick=AdhocModeUnsupported >*/ +} NMWifiError; + +#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address" +#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" + +#ifndef NM_DEVICE_WIFI_DEFINED +#define NM_DEVICE_WIFI_DEFINED +typedef struct _NMDeviceWifi NMDeviceWifi; +#endif + +typedef struct _NMDeviceWifiClass NMDeviceWifiClass; +typedef struct _NMDeviceWifiPrivate NMDeviceWifiPrivate; + +struct _NMDeviceWifi +{ + NMDevice parent; + + /*< private >*/ + NMDeviceWifiPrivate *priv; +}; + +struct _NMDeviceWifiClass +{ + NMDeviceClass parent; + + /* Signals */ + void (*access_point_added) (NMDeviceWifi *device, NMAccessPoint *ap); + void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap); + gboolean (*scanning_allowed) (NMDeviceWifi *device); +}; + + +GType nm_device_wifi_get_type (void); + +NMDevice *nm_device_wifi_new (NMPlatformLink *platform_device); + +G_END_DECLS + +#endif /* NM_DEVICE_WIFI_H */ diff --git a/src/devices/wifi/nm-wifi-ap-utils.c b/src/devices/wifi/nm-wifi-ap-utils.c new file mode 100644 index 0000000000..9b03cbd45f --- /dev/null +++ b/src/devices/wifi/nm-wifi-ap-utils.c @@ -0,0 +1,721 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2011 Red Hat, Inc. + */ + +#include +#include +#include + +#include "nm-wifi-ap-utils.h" + +static gboolean +verify_no_wep (NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error) +{ + if ( nm_setting_wireless_security_get_wep_key (s_wsec, 0) + || nm_setting_wireless_security_get_wep_key (s_wsec, 1) + || nm_setting_wireless_security_get_wep_key (s_wsec, 2) + || nm_setting_wireless_security_get_wep_key (s_wsec, 3) + || nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + || nm_setting_wireless_security_get_wep_key_type (s_wsec)) { + /* Dynamic WEP cannot have any WEP keys set */ + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with static WEP keys", tag); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_leap (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + GError **error) +{ + const char *key_mgmt, *auth_alg, *leap_username; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + + /* One (or both) of two things indicates we want LEAP: + * 1) auth_alg == 'leap' + * 2) valid leap_username + * + * LEAP always requires a LEAP username. + */ + + if (auth_alg) { + if (!strcmp (auth_alg, "leap")) { + /* LEAP authentication requires at least a LEAP username */ + if (!leap_username) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, + "LEAP requires a LEAP username"); + return FALSE; + } + } else if (leap_username) { + /* Leap username requires 'leap' auth */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "LEAP requires 'leap' authentication"); + return FALSE; + } + } + + if (leap_username) { + if (key_mgmt && strcmp (key_mgmt, "ieee8021x")) { + /* LEAP requires ieee8021x key management */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X, + "LEAP requires IEEE 802.1x key management"); + return FALSE; + } + } + + /* At this point if auth_alg is set it must be 'leap', and if key_mgmt + * is set it must be 'ieee8021x'. + */ + if (leap_username) { + if (auth_alg) + g_assert (strcmp (auth_alg, "leap") == 0); + if (key_mgmt) + g_assert (strcmp (key_mgmt, "ieee8021x") == 0); + + if (adhoc) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "LEAP incompatible with Ad-Hoc mode"); + return FALSE; + } + + if (!verify_no_wep (s_wsec, "LEAP", error)) + return FALSE; + + if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, + "LEAP incompatible with 802.1x setting"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +verify_no_wpa (NMSettingWirelessSecurity *s_wsec, + const char *tag, + GError **error) +{ + const char *key_mgmt; + int n, i; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + if (key_mgmt && !strncmp (key_mgmt, "wpa", 3)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s incompatible with any WPA key management", tag); + return FALSE; + } + + if (nm_setting_wireless_security_get_num_protos (s_wsec)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s incompatible with any 'proto' setting", tag); + return FALSE; + } + + n = nm_setting_wireless_security_get_num_pairwise (s_wsec); + for (i = 0; i < n; i++) { + const char *pw; + + pw = nm_setting_wireless_security_get_pairwise (s_wsec, i); + if (!strcmp (pw, "tkip") || !strcmp (pw, "ccmp")) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with WPA pairwise ciphers", tag); + return FALSE; + } + } + + n = nm_setting_wireless_security_get_num_groups (s_wsec); + for (i = 0; i < n; i++) { + const char *gr; + + gr = nm_setting_wireless_security_get_group (s_wsec, i); + if (strcmp (gr, "wep40") && strcmp (gr, "wep104")) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with WPA group ciphers", tag); + return FALSE; + } + } + + if (nm_setting_wireless_security_get_psk (s_wsec)) { + g_set_error (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "%s is incompatible with a WPA Pre-Shared Key", tag); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_dynamic_wep (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + GError **error) +{ + const char *key_mgmt, *auth_alg, *leap_username; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + + g_return_val_if_fail (leap_username == NULL, TRUE); + + if (key_mgmt) { + if (!strcmp (key_mgmt, "ieee8021x")) { + if (!s_8021x) { + /* 802.1x key management requires an 802.1x setting */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires an 802.1x setting"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + /* 802.1x key management must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires 'open' authentication"); + return FALSE; + } + + /* Dynamic WEP incompatible with anything static WEP related */ + if (!verify_no_wep (s_wsec, "Dynamic WEP", error)) + return FALSE; + } else if (!strcmp (key_mgmt, "none")) { + if (s_8021x) { + /* 802.1x setting requires 802.1x key management */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires 'ieee8021x' key management"); + return FALSE; + } + } + } else if (s_8021x) { + /* 802.1x setting incompatible with anything but 'open' auth */ + if (auth_alg && strcmp (auth_alg, "open")) { + /* 802.1x key management must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Dynamic WEP requires 'open' authentication"); + return FALSE; + } + + /* Dynamic WEP incompatible with anything static WEP related */ + if (!verify_no_wep (s_wsec, "Dynamic WEP", error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_wpa_psk (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + guint32 wpa_flags, + guint32 rsn_flags, + GError **error) +{ + const char *key_mgmt, *auth_alg, *tmp; + int n; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + + if (key_mgmt) { + if (!strcmp (key_mgmt, "wpa-psk") || !strcmp (key_mgmt, "wpa-none")) { + if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-PSK incompatible with 802.1x"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + /* WPA must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-PSK requires 'open' authentication"); + return FALSE; + } + } + + if (!strcmp (key_mgmt, "wpa-none")) { + if (!adhoc) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires an Ad-Hoc mode AP"); + return FALSE; + } + + /* Ad-Hoc WPA requires 'wpa' proto, 'none' pairwise, and 'tkip' group */ + n = nm_setting_wireless_security_get_num_protos (s_wsec); + tmp = (n > 0) ? nm_setting_wireless_security_get_proto (s_wsec, 0) : NULL; + if (n > 1 || !tmp || strcmp (tmp, "wpa")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires 'wpa' proto"); + return FALSE; + } + + n = nm_setting_wireless_security_get_num_pairwise (s_wsec); + tmp = (n > 0) ? nm_setting_wireless_security_get_pairwise (s_wsec, 0) : NULL; + if (n > 1 || g_strcmp0 (tmp, "none")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires 'none' pairwise cipher"); + return FALSE; + } + + n = nm_setting_wireless_security_get_num_groups (s_wsec); + tmp = (n > 0) ? nm_setting_wireless_security_get_group (s_wsec, 0) : NULL; + if (n > 1 || !tmp || strcmp (tmp, "tkip")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA Ad-Hoc requires 'tkip' group cipher"); + return FALSE; + } + } + + if (!strcmp (key_mgmt, "wpa-psk")) { + /* Make sure the AP's capabilities support WPA-PSK */ + if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP does not support PSK but setting requires it"); + return FALSE; + } + } + } + + return TRUE; +} + +static gboolean +verify_wpa_eap (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + guint32 wpa_flags, + guint32 rsn_flags, + GError **error) +{ + const char *key_mgmt, *auth_alg; + gboolean is_wpa_eap = FALSE; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + + if (key_mgmt) { + if (!strcmp (key_mgmt, "wpa-eap")) { + if (!s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-EAP requires an 802.1x setting"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + /* WPA must use "open" authentication */ + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA-EAP requires 'open' authentication"); + return FALSE; + } + + is_wpa_eap = TRUE; + } else if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Setting requires 802.1x but does not use 'wpa-eap' key management"); + return FALSE; + } + } + + if (is_wpa_eap || s_8021x) { + /* Make sure the AP's capabilities support WPA-EAP */ + if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP does not support 802.1x but setting requires it"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +verify_adhoc (NMSettingWirelessSecurity *s_wsec, + NMSetting8021x *s_8021x, + gboolean adhoc, + GError **error) +{ + const char *key_mgmt = NULL, *leap_username = NULL, *auth_alg = NULL; + + if (s_wsec) { + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + } + + if (adhoc) { + if (key_mgmt && strcmp (key_mgmt, "wpa-none") && strcmp (key_mgmt, "none")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP mode is Ad-Hoc but setting requires Infrastructure security"); + return FALSE; + } + + if (s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Ad-Hoc mode incompatible with 802.1x security"); + return FALSE; + } + + if (leap_username) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Ad-Hoc mode incompatible with LEAP security"); + return FALSE; + } + + if (auth_alg && strcmp (auth_alg, "open")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Ad-Hoc mode requires 'open' authentication"); + return FALSE; + } + } else { + if (key_mgmt && !strcmp (key_mgmt, "wpa-none")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP mode is Infrastructure but setting requires Ad-Hoc security"); + return FALSE; + } + } + + return TRUE; +} + +gboolean +nm_ap_utils_complete_connection (const GByteArray *ap_ssid, + const guint8 ap_bssid[ETH_ALEN], + NM80211Mode ap_mode, + guint32 ap_flags, + guint32 ap_wpa_flags, + guint32 ap_rsn_flags, + NMConnection *connection, + gboolean lock_bssid, + GError **error) +{ + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSetting8021x *s_8021x; + const GByteArray *ssid; + const char *mode, *key_mgmt, *auth_alg, *leap_username; + gboolean adhoc = FALSE; + + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); + s_wsec = nm_connection_get_setting_wireless_security (connection); + s_8021x = nm_connection_get_setting_802_1x (connection); + + /* Fill in missing SSID */ + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid) + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid, NULL); + else if ( ssid->len != ap_ssid->len + || memcmp (ssid->data, ap_ssid->data, ssid->len)) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + "Setting SSID did not match AP SSID"); + return FALSE; + } + + if (lock_bssid && !nm_setting_wireless_get_bssid (s_wifi)) { + GByteArray *bssid; + + bssid = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (bssid, ap_bssid, ETH_ALEN); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL); + g_byte_array_free (bssid, TRUE); + } + + /* And mode */ + mode = nm_setting_wireless_get_mode (s_wifi); + if (mode) { + gboolean valid = FALSE; + + /* Make sure the supplied mode matches the AP's */ + if ( !strcmp (mode, NM_SETTING_WIRELESS_MODE_INFRA) + || !strcmp (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (ap_mode == NM_802_11_MODE_INFRA) + valid = TRUE; + } else if (!strcmp (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + if (ap_mode == NM_802_11_MODE_ADHOC) + valid = TRUE; + adhoc = TRUE; + } + + if (valid == FALSE) { + g_set_error (error, + NM_SETTING_WIRELESS_ERROR, + NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRELESS_MODE); + return FALSE; + } + } else { + mode = NM_SETTING_WIRELESS_MODE_INFRA; + if (ap_mode == NM_802_11_MODE_ADHOC) { + mode = NM_SETTING_WIRELESS_MODE_ADHOC; + adhoc = TRUE; + } + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, mode, NULL); + } + + /* Security */ + + /* Open */ + if ( !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY) + && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) + && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { + /* Make sure the connection doesn't specify security */ + if (s_wsec || s_8021x) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "AP is unencrypted but setting specifies security"); + return FALSE; + } + return TRUE; + } + + /* Everything else requires security */ + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + } + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); + leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); + + /* Ad-Hoc checks */ + if (!verify_adhoc (s_wsec, s_8021x, adhoc, error)) + return FALSE; + + /* Static WEP, Dynamic WEP, or LEAP */ + if ( (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) + && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) + && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { + const char *tag = "WEP"; + gboolean is_dynamic_wep = FALSE; + + if (!verify_leap (s_wsec, s_8021x, adhoc, error)) + return FALSE; + + if (leap_username) { + tag = "LEAP"; + } else { + /* Static or Dynamic WEP */ + if (!verify_dynamic_wep (s_wsec, s_8021x, adhoc, error)) + return FALSE; + + if (s_8021x || (key_mgmt && !strcmp (key_mgmt, "ieee8021x"))) { + is_dynamic_wep = TRUE; + tag = "Dynamic WEP"; + } + } + + /* Nothing WPA-related can be set */ + if (!verify_no_wpa (s_wsec, tag, error)) + return FALSE; + + if (leap_username) { + /* LEAP */ + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", + NULL); + } else if (is_dynamic_wep) { + /* Dynamic WEP */ + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); + + if (s_8021x) { + /* Dynamic WEP requires a valid 802.1x setting since we can't + * autocomplete 802.1x. + */ + if (!nm_setting_verify (NM_SETTING (s_8021x), NULL, error)) + return FALSE; + } + } else { + /* Static WEP */ + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NULL); + } + + return TRUE; + } + + /* WPA/RSN */ + g_assert (ap_wpa_flags || ap_rsn_flags); + + /* Ensure key management is valid for WPA */ + if ((key_mgmt && !strcmp (key_mgmt, "ieee8021x")) || leap_username) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA incompatible with non-EAP (original) LEAP or Dynamic WEP"); + return FALSE; + } + + /* 'shared' auth incompatible with any type of WPA */ + if (auth_alg && strcmp (auth_alg, "open")) { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "WPA incompatible with Shared Key authentication"); + return FALSE; + } + + if (!verify_no_wep (s_wsec, "WPA", error)) + return FALSE; + + if (!verify_wpa_psk (s_wsec, s_8021x, adhoc, ap_wpa_flags, ap_rsn_flags, error)) + return FALSE; + + if (!adhoc && !verify_wpa_eap (s_wsec, s_8021x, ap_wpa_flags, ap_rsn_flags, error)) + return FALSE; + + if (adhoc) { + g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL); + /* Ad-Hoc does not support RSN/WPA2 */ + nm_setting_wireless_security_add_proto (s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise (s_wsec, "none"); + nm_setting_wireless_security_add_group (s_wsec, "tkip"); + } else if (s_8021x) { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); + /* Leave proto/pairwise/group as client set them; if they are unset the + * supplicant will figure out the best combination at connect time. + */ + + /* 802.1x also requires the client to completely fill in the 8021x + * setting. Since there's so much configuration required for it, there's + * no way it can be automatically completed. + */ + } else if ( (key_mgmt && !strcmp (key_mgmt, "wpa-psk")) + || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); + /* Leave proto/pairwise/group as client set them; if they are unset the + * supplicant will figure out the best combination at connect time. + */ + } else { + g_set_error_literal (error, + NM_SETTING_WIRELESS_SECURITY_ERROR, + NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, + "Failed to determine AP security information"); + return FALSE; + } + + return TRUE; +} + +guint32 +nm_ap_utils_level_to_quality (gint val) +{ + if (val < 0) { + /* Assume dBm already; rough conversion: best = -40, worst = -100 */ + val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */ + val = 100 - (int) ((100.0 * (double) val) / 60.0); + } else if (val > 110 && val < 256) { + /* assume old-style WEXT 8-bit unsigned signal level */ + val -= 256; /* subtract 256 to convert to dBm */ + val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */ + val = 100 - (int) ((100.0 * (double) val) / 60.0); + } else { + /* Assume signal is a "quality" percentage */ + val = CLAMP (val, 0, 100); + } + g_assert (val >= 0); + + return (guint32) val; +} + diff --git a/src/devices/wifi/nm-wifi-ap-utils.h b/src/devices/wifi/nm-wifi-ap-utils.h new file mode 100644 index 0000000000..992b839d52 --- /dev/null +++ b/src/devices/wifi/nm-wifi-ap-utils.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2011 Red Hat, Inc. + */ + +#ifndef NM_WIFI_AP_UTILS_H +#define NM_WIFI_AP_UTILS_H + +#include + +#include +#include +#include +#include +#include + +gboolean nm_ap_utils_complete_connection (const GByteArray *ssid, + const guint8 bssid[ETH_ALEN], + NM80211Mode mode, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + NMConnection *connection, + gboolean lock_bssid, + GError **error); + +guint32 nm_ap_utils_level_to_quality (gint val); + +#endif /* NM_WIFI_AP_UTILS_H */ + diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c new file mode 100644 index 0000000000..363be2e326 --- /dev/null +++ b/src/devices/wifi/nm-wifi-ap.c @@ -0,0 +1,1293 @@ +/* -*- 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) 2004 - 2011 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#include +#include +#include + +#include "nm-wifi-ap.h" +#include "nm-wifi-ap-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-logging.h" +#include "nm-dbus-manager.h" + +#include "nm-setting-wireless.h" +#include "nm-glib-compat.h" + +#include "nm-access-point-glue.h" + +/* + * Encapsulates Access Point information + */ +typedef struct +{ + char *dbus_path; + char *supplicant_path; /* D-Bus object path of this AP from wpa_supplicant */ + + /* Scanned or cached values */ + GByteArray * ssid; + struct ether_addr address; + NM80211Mode mode; + gint8 strength; + guint32 freq; /* Frequency in MHz; ie 2412 (== 2.412 GHz) */ + guint32 max_bitrate;/* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */ + + NM80211ApFlags flags; /* General flags */ + NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */ + NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */ + + /* Non-scanned attributes */ + gboolean fake; /* Whether or not the AP is from a scan */ + gboolean hotspot; /* Whether the AP is a local device's hotspot network */ + gboolean broadcast; /* Whether or not the AP is broadcasting (hidden) */ + gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ +} NMAccessPointPrivate; + +#define NM_AP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AP, NMAccessPointPrivate)) + +G_DEFINE_TYPE (NMAccessPoint, nm_ap, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_FLAGS, + PROP_WPA_FLAGS, + PROP_RSN_FLAGS, + PROP_SSID, + PROP_FREQUENCY, + PROP_HW_ADDRESS, + PROP_MODE, + PROP_MAX_BITRATE, + PROP_STRENGTH, + LAST_PROP +}; + +static void +nm_ap_init (NMAccessPoint *ap) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap); + + priv->dbus_path = NULL; + priv->mode = NM_802_11_MODE_INFRA; + priv->flags = NM_802_11_AP_FLAGS_NONE; + priv->wpa_flags = NM_802_11_AP_SEC_NONE; + priv->rsn_flags = NM_802_11_AP_SEC_NONE; + priv->broadcast = TRUE; +} + +static void +finalize (GObject *object) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object); + + g_free (priv->dbus_path); + g_free (priv->supplicant_path); + if (priv->ssid) + g_byte_array_free (priv->ssid, TRUE); + + G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object); +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMAccessPoint *ap = NM_AP (object); + + switch (prop_id) { + case PROP_FLAGS: + nm_ap_set_flags (ap, g_value_get_uint (value)); + break; + case PROP_WPA_FLAGS: + nm_ap_set_wpa_flags (ap, g_value_get_uint (value)); + break; + case PROP_RSN_FLAGS: + nm_ap_set_rsn_flags (ap, g_value_get_uint (value)); + break; + case PROP_SSID: + nm_ap_set_ssid (ap, (GByteArray *) g_value_get_boxed (value)); + break; + case PROP_FREQUENCY: + nm_ap_set_freq (ap, g_value_get_uint (value)); + break; + case PROP_MODE: + nm_ap_set_mode (ap, g_value_get_uint (value)); + break; + case PROP_MAX_BITRATE: + nm_ap_set_max_bitrate (ap, g_value_get_uint (value)); + break; + case PROP_STRENGTH: + nm_ap_set_strength (ap, g_value_get_schar (value)); + break; + case PROP_HW_ADDRESS: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object); + GArray * ssid; + int len; + int i; + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_WPA_FLAGS: + g_value_set_uint (value, priv->wpa_flags); + break; + case PROP_RSN_FLAGS: + g_value_set_uint (value, priv->rsn_flags); + break; + case PROP_SSID: + len = priv->ssid ? priv->ssid->len : 0; + ssid = g_array_sized_new (FALSE, TRUE, sizeof (unsigned char), len); + for (i = 0; i < len; i++) + g_array_append_val (ssid, priv->ssid->data[i]); + g_value_set_boxed (value, ssid); + g_array_free (ssid, TRUE); + break; + case PROP_FREQUENCY: + g_value_set_uint (value, priv->freq); + break; + case PROP_HW_ADDRESS: + g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->address, ARPHRD_ETHER)); + break; + case PROP_MODE: + g_value_set_uint (value, priv->mode); + break; + case PROP_MAX_BITRATE: + g_value_set_uint (value, priv->max_bitrate); + break; + case PROP_STRENGTH: + g_value_set_schar (value, priv->strength); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_ap_class_init (NMAccessPointClass *ap_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ap_class); + const NM80211ApSecurityFlags all_sec_flags = NM_802_11_AP_SEC_NONE + | NM_802_11_AP_SEC_PAIR_WEP40 + | NM_802_11_AP_SEC_PAIR_WEP104 + | NM_802_11_AP_SEC_PAIR_TKIP + | NM_802_11_AP_SEC_PAIR_CCMP + | NM_802_11_AP_SEC_GROUP_WEP40 + | NM_802_11_AP_SEC_GROUP_WEP104 + | NM_802_11_AP_SEC_GROUP_TKIP + | NM_802_11_AP_SEC_GROUP_CCMP + | NM_802_11_AP_SEC_KEY_MGMT_PSK + | NM_802_11_AP_SEC_KEY_MGMT_802_1X; + + g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + /* properties */ + g_object_class_install_property + (object_class, PROP_FLAGS, + g_param_spec_uint (NM_AP_FLAGS, + "Flags", + "Flags", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_WPA_FLAGS, + g_param_spec_uint (NM_AP_WPA_FLAGS, + "WPA Flags", + "WPA Flags", + NM_802_11_AP_SEC_NONE, + all_sec_flags, + NM_802_11_AP_SEC_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_RSN_FLAGS, + g_param_spec_uint (NM_AP_RSN_FLAGS, + "RSN Flags", + "RSN Flags", + NM_802_11_AP_SEC_NONE, + all_sec_flags, + NM_802_11_AP_SEC_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_SSID, + g_param_spec_boxed (NM_AP_SSID, + "SSID", + "SSID", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_FREQUENCY, + g_param_spec_uint (NM_AP_FREQUENCY, + "Frequency", + "Frequency", + 0, 10000, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_HW_ADDRESS, + g_param_spec_string (NM_AP_HW_ADDRESS, + "MAC Address", + "Hardware MAC address", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_MODE, + g_param_spec_uint (NM_AP_MODE, + "Mode", + "Mode", + NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_MAX_BITRATE, + g_param_spec_uint (NM_AP_MAX_BITRATE, + "Max Bitrate", + "Max Bitrate", + 0, G_MAXUINT16, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property + (object_class, PROP_STRENGTH, + g_param_spec_char (NM_AP_STRENGTH, + "Strength", + "Strength", + G_MININT8, G_MAXINT8, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), + G_TYPE_FROM_CLASS (ap_class), + &dbus_glib_nm_access_point_object_info); +} + +void +nm_ap_export_to_dbus (NMAccessPoint *ap) +{ + NMAccessPointPrivate *priv; + static guint32 counter = 0; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->dbus_path) { + nm_log_err (LOGD_CORE, "Tried to export AP %s twice.", priv->dbus_path); + return; + } + + priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", counter++); + nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->dbus_path, ap); +} + +/* + * nm_ap_new + * + * Create a new, blank user access point info structure + * + */ +static NMAccessPoint * +nm_ap_new (void) +{ + return (NMAccessPoint *) g_object_new (NM_TYPE_AP, NULL); +} + +static NM80211ApSecurityFlags +pair_to_flags (const char *str) +{ + g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE); + + if (strcmp (str, "tkip") == 0) + return NM_802_11_AP_SEC_PAIR_TKIP; + if (strcmp (str, "ccmp") == 0) + return NM_802_11_AP_SEC_PAIR_CCMP; + return NM_802_11_AP_SEC_NONE; +} + +static NM80211ApSecurityFlags +group_to_flags (const char *str) +{ + g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE); + + if (strcmp (str, "wep40") == 0) + return NM_802_11_AP_SEC_GROUP_WEP40; + if (strcmp (str, "wep104") == 0) + return NM_802_11_AP_SEC_GROUP_WEP104; + if (strcmp (str, "tkip") == 0) + return NM_802_11_AP_SEC_GROUP_TKIP; + if (strcmp (str, "ccmp") == 0) + return NM_802_11_AP_SEC_GROUP_CCMP; + return NM_802_11_AP_SEC_NONE; +} + +static NM80211ApSecurityFlags +security_from_dict (GHashTable *security) +{ + GValue *value; + NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; + const char **items, **iter; + + value = g_hash_table_lookup (security, "KeyMgmt"); + if (value) { + items = g_value_get_boxed (value); + for (iter = items; iter && *iter; iter++) { + if (strcmp (*iter, "wpa-psk") == 0) + flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (strcmp (*iter, "wpa-eap") == 0) + flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; + } + } + + value = g_hash_table_lookup (security, "Pairwise"); + if (value) { + items = g_value_get_boxed (value); + for (iter = items; iter && *iter; iter++) + flags |= pair_to_flags (*iter); + } + + value = g_hash_table_lookup (security, "Group"); + if (value) + flags |= group_to_flags (g_value_get_string (value)); + + return flags; +} + +static void +foreach_property_cb (gpointer key, gpointer value, gpointer user_data) +{ + GValue *variant = (GValue *) value; + NMAccessPoint *ap = (NMAccessPoint *) user_data; + + if (G_VALUE_HOLDS_BOXED (variant)) { + GArray *array = g_value_get_boxed (variant); + + if (!strcmp (key, "SSID")) { + guint32 len = MIN (32, array->len); + GByteArray *ssid; + + /* Stupid ieee80211 layer uses */ + if (((len == 8) || (len == 9)) + && (memcmp (array->data, "", 8) == 0)) + return; + + if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len)) + return; + + ssid = g_byte_array_sized_new (len); + g_byte_array_append (ssid, (const guint8 *) array->data, len); + nm_ap_set_ssid (ap, ssid); + g_byte_array_free (ssid, TRUE); + } else if (!strcmp (key, "BSSID")) { + struct ether_addr addr; + + if (array->len != ETH_ALEN) + return; + memset (&addr, 0, sizeof (struct ether_addr)); + memcpy (&addr, array->data, ETH_ALEN); + nm_ap_set_address (ap, &addr); + } else if (!strcmp (key, "Rates")) { + guint32 maxrate = 0; + int i; + + /* Find the max AP rate */ + for (i = 0; i < array->len; i++) { + guint32 r = g_array_index (array, guint32, i); + + if (r > maxrate) { + maxrate = r; + nm_ap_set_max_bitrate (ap, r / 1000); + } + } + } else if (!strcmp (key, "WPA")) { + NM80211ApSecurityFlags flags = nm_ap_get_wpa_flags (ap); + + flags |= security_from_dict (g_value_get_boxed (variant)); + nm_ap_set_wpa_flags (ap, flags); + } else if (!strcmp (key, "RSN")) { + NM80211ApSecurityFlags flags = nm_ap_get_rsn_flags (ap); + + flags |= security_from_dict (g_value_get_boxed (variant)); + nm_ap_set_rsn_flags (ap, flags); + } + } else if (G_VALUE_HOLDS_UINT (variant)) { + guint32 val = g_value_get_uint (variant); + + if (!strcmp (key, "Frequency")) + nm_ap_set_freq (ap, val); + } else if (G_VALUE_HOLDS_INT (variant)) { + gint val = g_value_get_int (variant); + + if (!strcmp (key, "Signal")) + nm_ap_set_strength (ap, nm_ap_utils_level_to_quality (val)); + } else if (G_VALUE_HOLDS_STRING (variant)) { + const char *val = g_value_get_string (variant); + + if (val && !strcmp (key, "Mode")) { + if (strcmp (val, "infrastructure") == 0) + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + else if (strcmp (val, "ad-hoc") == 0) + nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); + } + } else if (G_VALUE_HOLDS_BOOLEAN (variant)) { + gboolean val = g_value_get_boolean (variant); + + if (strcmp (key, "Privacy") == 0) { + if (val) { + NM80211ApFlags flags = nm_ap_get_flags (ap); + nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY); + } + } + } +} + +NMAccessPoint * +nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties) +{ + NMAccessPoint *ap; + const struct ether_addr * addr; + const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + g_return_val_if_fail (properties != NULL, NULL); + + ap = nm_ap_new (); + + g_object_freeze_notify (G_OBJECT (ap)); + g_hash_table_foreach (properties, foreach_property_cb, ap); + + nm_ap_set_supplicant_path (ap, supplicant_path); + + /* ignore APs with invalid BSSIDs */ + addr = nm_ap_get_address (ap); + if ( !(memcmp (addr->ether_addr_octet, bad_bssid1, ETH_ALEN)) + || !(memcmp (addr->ether_addr_octet, bad_bssid2, ETH_ALEN))) { + g_object_unref (ap); + return NULL; + } + + nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ()); + + if (!nm_ap_get_ssid (ap)) + nm_ap_set_broadcast (ap, FALSE); + + g_object_thaw_notify (G_OBJECT (ap)); + + return ap; +} + +#define PROTO_WPA "wpa" +#define PROTO_RSN "rsn" + +static gboolean +has_proto (NMSettingWirelessSecurity *sec, const char *proto) +{ + guint32 num_protos = nm_setting_wireless_security_get_num_protos (sec); + guint32 i; + + if (num_protos == 0) + return TRUE; /* interpret no protos as "all" */ + + for (i = 0; i < num_protos; i++) { + if (!strcmp (nm_setting_wireless_security_get_proto (sec, i), proto)) + return TRUE; + } + return FALSE; +} + +static void +add_pair_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec) +{ + guint32 num = nm_setting_wireless_security_get_num_pairwise (sec); + NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; + guint32 i; + + /* If no ciphers are specified, that means "all" WPA ciphers */ + if (num == 0) { + flags |= NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP; + } else { + for (i = 0; i < num; i++) { + const char *cipher = nm_setting_wireless_security_get_pairwise (sec, i); + + if (!strcmp (cipher, "tkip")) + flags |= NM_802_11_AP_SEC_PAIR_TKIP; + else if (!strcmp (cipher, "ccmp")) + flags |= NM_802_11_AP_SEC_PAIR_CCMP; + } + } + + if (has_proto (sec, PROTO_WPA)) + nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags); + if (has_proto (sec, PROTO_RSN)) + nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags); +} + +static void +add_group_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec) +{ + guint32 num = nm_setting_wireless_security_get_num_groups (sec); + NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; + guint32 i; + + /* If no ciphers are specified, that means "all" WPA ciphers */ + if (num == 0) { + flags |= NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_GROUP_CCMP; + } else { + for (i = 0; i < num; i++) { + const char *cipher = nm_setting_wireless_security_get_group (sec, i); + + if (!strcmp (cipher, "wep40")) + flags |= NM_802_11_AP_SEC_GROUP_WEP40; + else if (!strcmp (cipher, "wep104")) + flags |= NM_802_11_AP_SEC_GROUP_WEP104; + else if (!strcmp (cipher, "tkip")) + flags |= NM_802_11_AP_SEC_GROUP_TKIP; + else if (!strcmp (cipher, "ccmp")) + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + } + } + + if (has_proto (sec, PROTO_WPA)) + nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags); + if (has_proto (sec, PROTO_RSN)) + nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags); +} + +NMAccessPoint * +nm_ap_new_fake_from_connection (NMConnection *connection) +{ + NMAccessPoint *ap; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + const GByteArray *ssid; + const char *mode, *band, *key_mgmt; + guint32 channel; + NM80211ApSecurityFlags flags; + gboolean psk = FALSE, eap = FALSE; + + g_return_val_if_fail (connection != NULL, NULL); + + s_wireless = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wireless != NULL, NULL); + + ssid = nm_setting_wireless_get_ssid (s_wireless); + g_return_val_if_fail (ssid != NULL, NULL); + g_return_val_if_fail (ssid->len > 0, NULL); + + ap = nm_ap_new (); + nm_ap_set_fake (ap, TRUE); + nm_ap_set_ssid (ap, ssid); + + // FIXME: bssid too? + + mode = nm_setting_wireless_get_mode (s_wireless); + if (mode) { + if (!strcmp (mode, "infrastructure")) + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + else if (!strcmp (mode, "adhoc")) + nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); + else if (!strcmp (mode, "ap")) { + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + NM_AP_GET_PRIVATE (ap)->hotspot = TRUE; + } else + goto error; + } else { + nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); + } + + band = nm_setting_wireless_get_band (s_wireless); + channel = nm_setting_wireless_get_channel (s_wireless); + + if (band && channel) { + guint32 freq = nm_utils_wifi_channel_to_freq (channel, band); + + if (freq == 0) + goto error; + + nm_ap_set_freq (ap, freq); + } + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + /* Assume presence of a security setting means the AP is encrypted */ + if (!s_wireless_sec) + goto done; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); + + /* Everything below here uses encryption */ + nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY); + + /* Static & Dynamic WEP */ + if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x")) + goto done; + + psk = !strcmp (key_mgmt, "wpa-psk"); + eap = !strcmp (key_mgmt, "wpa-eap"); + if (psk || eap) { + if (has_proto (s_wireless_sec, PROTO_WPA)) { + flags = nm_ap_get_wpa_flags (ap); + flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK; + nm_ap_set_wpa_flags (ap, flags); + } + if (has_proto (s_wireless_sec, PROTO_RSN)) { + flags = nm_ap_get_rsn_flags (ap); + flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK; + nm_ap_set_rsn_flags (ap, flags); + } + + add_pair_ciphers (ap, s_wireless_sec); + add_group_ciphers (ap, s_wireless_sec); + } else if (!strcmp (key_mgmt, "wpa-none")) { + guint32 i; + + /* Ad-Hoc has special requirements: proto=WPA, pairwise=(none), and + * group=TKIP/CCMP (but not both). + */ + + flags = nm_ap_get_wpa_flags (ap); + flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; + + /* Clear ciphers; pairwise must be unset anyway, and group gets set below */ + flags &= ~( NM_802_11_AP_SEC_PAIR_WEP40 + | NM_802_11_AP_SEC_PAIR_WEP104 + | NM_802_11_AP_SEC_PAIR_TKIP + | NM_802_11_AP_SEC_PAIR_CCMP + | NM_802_11_AP_SEC_GROUP_WEP40 + | NM_802_11_AP_SEC_GROUP_WEP104 + | NM_802_11_AP_SEC_GROUP_TKIP + | NM_802_11_AP_SEC_GROUP_CCMP); + + for (i = 0; i < nm_setting_wireless_security_get_num_groups (s_wireless_sec); i++) { + if (!strcmp (nm_setting_wireless_security_get_group (s_wireless_sec, i), "ccmp")) { + flags |= NM_802_11_AP_SEC_GROUP_CCMP; + break; + } + } + + /* Default to TKIP since not all WPA-capable cards can do CCMP */ + if (!(flags & NM_802_11_AP_SEC_GROUP_CCMP)) + flags |= NM_802_11_AP_SEC_GROUP_TKIP; + + nm_ap_set_wpa_flags (ap, flags); + + /* Don't use Ad-Hoc RSN yet */ + nm_ap_set_rsn_flags (ap, NM_802_11_AP_SEC_NONE); + } + +done: + return ap; + +error: + g_object_unref (ap); + return NULL; +} + + +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5] + +void +nm_ap_dump (NMAccessPoint *ap, const char *prefix) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + nm_log_dbg (LOGD_WIFI_SCAN, "%s'%s' (%p)", + prefix, + priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)", + ap); + nm_log_dbg (LOGD_WIFI_SCAN, " BSSID " MAC_FMT, MAC_ARG (priv->address.ether_addr_octet)); + nm_log_dbg (LOGD_WIFI_SCAN, " mode %d", priv->mode); + nm_log_dbg (LOGD_WIFI_SCAN, " flags 0x%X", priv->flags); + nm_log_dbg (LOGD_WIFI_SCAN, " wpa flags 0x%X", priv->wpa_flags); + nm_log_dbg (LOGD_WIFI_SCAN, " rsn flags 0x%X", priv->rsn_flags); + nm_log_dbg (LOGD_WIFI_SCAN, " quality %d", priv->strength); + nm_log_dbg (LOGD_WIFI_SCAN, " frequency %d", priv->freq); + nm_log_dbg (LOGD_WIFI_SCAN, " max rate %d", priv->max_bitrate); + nm_log_dbg (LOGD_WIFI_SCAN, " last-seen %d", (int) priv->last_seen); +} + +const char * +nm_ap_get_dbus_path (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + return NM_AP_GET_PRIVATE (ap)->dbus_path; +} + +const char * +nm_ap_get_supplicant_path (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + return NM_AP_GET_PRIVATE (ap)->supplicant_path; +} + +void +nm_ap_set_supplicant_path (NMAccessPoint *ap, const char *path) +{ + g_return_if_fail (NM_IS_AP (ap)); + g_return_if_fail (path != NULL); + + g_free (NM_AP_GET_PRIVATE (ap)->supplicant_path); + NM_AP_GET_PRIVATE (ap)->supplicant_path = g_strdup (path); +} + +/* + * Get/set functions for ssid + * + */ +const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + return NM_AP_GET_PRIVATE (ap)->ssid; +} + +void +nm_ap_set_ssid (NMAccessPoint *ap, const GByteArray * ssid) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (ssid == priv->ssid) + return; + + /* same SSID */ + if ((ssid && priv->ssid) && (ssid->len == priv->ssid->len)) { + if (!memcmp (ssid->data, priv->ssid->data, ssid->len)) + return; + } + + if (priv->ssid) { + g_byte_array_free (priv->ssid, TRUE); + priv->ssid = NULL; + } + + if (ssid) { + /* Should never get zero-length SSIDs */ + g_warn_if_fail (ssid->len > 0); + + if (ssid->len) { + priv->ssid = g_byte_array_sized_new (ssid->len); + priv->ssid->len = ssid->len; + memcpy (priv->ssid->data, ssid->data, ssid->len); + } + } + + g_object_notify (G_OBJECT (ap), NM_AP_SSID); +} + + +NM80211ApFlags +nm_ap_get_flags (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); + + return NM_AP_GET_PRIVATE (ap)->flags; +} + + +void +nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->flags != flags) { + priv->flags = flags; + g_object_notify (G_OBJECT (ap), NM_AP_FLAGS); + } +} + +NM80211ApSecurityFlags +nm_ap_get_wpa_flags (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); + + return NM_AP_GET_PRIVATE (ap)->wpa_flags; +} + + +void +nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + if (priv->wpa_flags != flags) { + priv->wpa_flags = flags; + g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS); + } +} + +NM80211ApSecurityFlags +nm_ap_get_rsn_flags (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); + + return NM_AP_GET_PRIVATE (ap)->rsn_flags; +} + + +void +nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + if (priv->rsn_flags != flags) { + priv->rsn_flags = flags; + g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS); + } +} + +/* + * Get/set functions for address + * + */ +const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), NULL); + + return &NM_AP_GET_PRIVATE (ap)->address; +} + +void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr * addr) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + g_return_if_fail (addr != NULL); + + priv = NM_AP_GET_PRIVATE (ap); + + if (memcmp (addr, &priv->address, sizeof (priv->address))) { + memcpy (&NM_AP_GET_PRIVATE (ap)->address, addr, sizeof (struct ether_addr)); + g_object_notify (G_OBJECT (ap), NM_AP_HW_ADDRESS); + } +} + + +/* + * Get/set functions for mode (ie Ad-Hoc, Infrastructure, etc) + * + */ +NM80211Mode nm_ap_get_mode (NMAccessPoint *ap) +{ + NM80211Mode mode; + + g_return_val_if_fail (NM_IS_AP (ap), -1); + + g_object_get (ap, NM_AP_MODE, &mode, NULL); + + return mode; +} + +void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + g_return_if_fail ( mode == NM_802_11_MODE_ADHOC + || mode == NM_802_11_MODE_INFRA); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->mode != mode) { + priv->mode = mode; + g_object_notify (G_OBJECT (ap), NM_AP_MODE); + } +} + +gboolean +nm_ap_is_hotspot (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), FALSE); + + return NM_AP_GET_PRIVATE (ap)->hotspot; +} + +/* + * Get/set functions for strength + * + */ +gint8 nm_ap_get_strength (NMAccessPoint *ap) +{ + gint8 strength; + + g_return_val_if_fail (NM_IS_AP (ap), 0); + + g_object_get (ap, NM_AP_STRENGTH, &strength, NULL); + + return strength; +} + +void nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->strength != strength) { + priv->strength = strength; + g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH); + } +} + + +/* + * Get/set functions for frequency + * + */ +guint32 +nm_ap_get_freq (NMAccessPoint *ap) +{ + guint32 freq; + + g_return_val_if_fail (NM_IS_AP (ap), 0); + + g_object_get (ap, NM_AP_FREQUENCY, &freq, NULL); + + return freq; +} + +void +nm_ap_set_freq (NMAccessPoint *ap, + const guint32 freq) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->freq != freq) { + priv->freq = freq; + g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY); + } +} + + +/* + * Get/set functions for max bitrate (in kbit/s) + * + */ +guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap) +{ + guint32 rate; + + g_return_val_if_fail (NM_IS_AP (ap), 0); + + g_object_get (ap, NM_AP_MAX_BITRATE, &rate, NULL); + + return rate; +} + +void +nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate) +{ + NMAccessPointPrivate *priv; + + g_return_if_fail (NM_IS_AP (ap)); + + priv = NM_AP_GET_PRIVATE (ap); + + if (priv->max_bitrate != bitrate) { + priv->max_bitrate = bitrate; + g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE); + } +} + +/* + * Get/Set functions to indicate that an access point is 'fake', ie whether + * or not it was created from scan results + */ +gboolean nm_ap_get_fake (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), FALSE); + + return NM_AP_GET_PRIVATE (ap)->fake; +} + +void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->fake = fake; +} + + +/* + * Get/Set functions to indicate whether an AP broadcasts its SSID. + */ +gboolean nm_ap_get_broadcast (NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), TRUE); + + return NM_AP_GET_PRIVATE (ap)->broadcast; +} + + +void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->broadcast = broadcast; +} + + +/* + * Get/Set functions for how long ago the AP was last seen in a scan. + * APs older than a certain date are dropped from the list. + * + */ +gint32 +nm_ap_get_last_seen (const NMAccessPoint *ap) +{ + g_return_val_if_fail (NM_IS_AP (ap), FALSE); + + return NM_AP_GET_PRIVATE (ap)->last_seen; +} + +void +nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen) +{ + g_return_if_fail (NM_IS_AP (ap)); + + NM_AP_GET_PRIVATE (ap)->last_seen = last_seen; +} + +gboolean +nm_ap_check_compatible (NMAccessPoint *self, + NMConnection *connection) +{ + NMAccessPointPrivate *priv; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + const char *mode; + const char *band; + const GByteArray *bssid; + guint32 channel; + + g_return_val_if_fail (NM_IS_AP (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + priv = NM_AP_GET_PRIVATE (self); + + s_wireless = nm_connection_get_setting_wireless (connection); + if (s_wireless == NULL) + return FALSE; + + if (!nm_utils_same_ssid (nm_setting_wireless_get_ssid (s_wireless), priv->ssid, TRUE)) + return FALSE; + + bssid = nm_setting_wireless_get_bssid (s_wireless); + if (bssid && memcmp (bssid->data, &priv->address, ETH_ALEN)) + return FALSE; + + mode = nm_setting_wireless_get_mode (s_wireless); + if (mode) { + if (!strcmp (mode, "infrastructure") && (priv->mode != NM_802_11_MODE_INFRA)) + return FALSE; + if (!strcmp (mode, "adhoc") && (priv->mode != NM_802_11_MODE_ADHOC)) + return FALSE; + if ( !strcmp (mode, "ap") + && (priv->mode != NM_802_11_MODE_INFRA || priv->hotspot != TRUE)) + return FALSE; + } + + band = nm_setting_wireless_get_band (s_wireless); + if (band) { + if (!strcmp (band, "a")) { + if (priv->freq < 4915 || priv->freq > 5825) + return FALSE; + } else if (!strcmp (band, "bg")) { + if (priv->freq < 2412 || priv->freq > 2484) + return FALSE; + } + } + + channel = nm_setting_wireless_get_channel (s_wireless); + if (channel) { + guint32 ap_chan = nm_utils_wifi_freq_to_channel (priv->freq); + + if (channel != ap_chan) + return FALSE; + } + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + + return nm_setting_wireless_ap_security_compatible (s_wireless, + s_wireless_sec, + nm_ap_get_flags (self), + nm_ap_get_wpa_flags (self), + nm_ap_get_rsn_flags (self), + nm_ap_get_mode (self)); +} + +gboolean +nm_ap_complete_connection (NMAccessPoint *self, + NMConnection *connection, + gboolean lock_bssid, + GError **error) +{ + NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (self); + + g_return_val_if_fail (connection != NULL, FALSE); + + return nm_ap_utils_complete_connection (priv->ssid, + priv->address.ether_addr_octet, + priv->mode, + priv->flags, + priv->wpa_flags, + priv->rsn_flags, + connection, + lock_bssid, + error); +} + +static gboolean +capabilities_compatible (NM80211ApSecurityFlags a_flags, NM80211ApSecurityFlags b_flags) +{ + if (a_flags == b_flags) + return TRUE; + + /* Make sure there's a common key management method */ + if (!((a_flags & 0x300) & (b_flags & 0x300))) + return FALSE; + + /* Ensure common pairwise ciphers */ + if (!((a_flags & 0xF) & (b_flags & 0xF))) + return FALSE; + + /* Ensure common group ciphers */ + if (!((a_flags & 0xF0) & (b_flags & 0xF0))) + return FALSE; + + return TRUE; +} + +NMAccessPoint * +nm_ap_match_in_list (NMAccessPoint *find_ap, + GSList *ap_list, + gboolean strict_match) +{ + GSList *iter; + + g_return_val_if_fail (find_ap != NULL, NULL); + + for (iter = ap_list; iter; iter = g_slist_next (iter)) { + NMAccessPoint * list_ap = NM_AP (iter->data); + const GByteArray * list_ssid = nm_ap_get_ssid (list_ap); + const struct ether_addr * list_addr = nm_ap_get_address (list_ap); + + const GByteArray * find_ssid = nm_ap_get_ssid (find_ap); + const struct ether_addr * find_addr = nm_ap_get_address (find_ap); + + /* SSID match; if both APs are hiding their SSIDs, + * let matching continue on BSSID and other properties + */ + if ( (!list_ssid && find_ssid) + || (list_ssid && !find_ssid) + || !nm_utils_same_ssid (list_ssid, find_ssid, TRUE)) + continue; + + /* BSSID match */ + if ( (strict_match || nm_ethernet_address_is_valid (find_addr)) + && nm_ethernet_address_is_valid (list_addr) + && memcmp (list_addr->ether_addr_octet, + find_addr->ether_addr_octet, + ETH_ALEN) != 0) { + continue; + } + + /* mode match */ + if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap)) + continue; + + /* Frequency match */ + if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap)) + continue; + + /* AP flags */ + if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap)) + continue; + + if (strict_match) { + if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap)) + continue; + + if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap)) + continue; + } else { + NM80211ApSecurityFlags list_wpa_flags = nm_ap_get_wpa_flags (list_ap); + NM80211ApSecurityFlags find_wpa_flags = nm_ap_get_wpa_flags (find_ap); + NM80211ApSecurityFlags list_rsn_flags = nm_ap_get_rsn_flags (list_ap); + NM80211ApSecurityFlags find_rsn_flags = nm_ap_get_rsn_flags (find_ap); + + /* Just ensure that there is overlap in the capabilities */ + if ( !capabilities_compatible (list_wpa_flags, find_wpa_flags) + && !capabilities_compatible (list_rsn_flags, find_rsn_flags)) + continue; + } + + return list_ap; + } + + return NULL; +} + diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h new file mode 100644 index 0000000000..f51fa078ee --- /dev/null +++ b/src/devices/wifi/nm-wifi-ap.h @@ -0,0 +1,121 @@ +/* -*- 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) 2004 - 2011 Red Hat, Inc. + * Copyright (C) 2006 - 2008 Novell, Inc. + */ + +#ifndef NM_ACCESS_POINT_H +#define NM_ACCESS_POINT_H + +#include +#include +#include "NetworkManager.h" +#include "nm-connection.h" + +#define NM_TYPE_AP (nm_ap_get_type ()) +#define NM_AP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AP, NMAccessPoint)) +#define NM_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AP, NMAccessPointClass)) +#define NM_IS_AP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AP)) +#define NM_IS_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AP)) +#define NM_AP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AP, NMAccessPointClass)) + +#define NM_AP_FLAGS "flags" +#define NM_AP_WPA_FLAGS "wpa-flags" +#define NM_AP_RSN_FLAGS "rsn-flags" +#define NM_AP_SSID "ssid" +#define NM_AP_FREQUENCY "frequency" +#define NM_AP_HW_ADDRESS "hw-address" +#define NM_AP_MODE "mode" +#define NM_AP_MAX_BITRATE "max-bitrate" +#define NM_AP_STRENGTH "strength" + +typedef struct { + GObject parent; +} NMAccessPoint; + +typedef struct { + GObjectClass parent; + +} NMAccessPointClass; + +GType nm_ap_get_type (void); + +NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path, + GHashTable *properties); +NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection); +void nm_ap_export_to_dbus (NMAccessPoint *ap); + +const char * nm_ap_get_dbus_path (NMAccessPoint *ap); + +const char * nm_ap_get_supplicant_path (NMAccessPoint *ap); +void nm_ap_set_supplicant_path (NMAccessPoint *ap, + const char *path); + +const GByteArray * nm_ap_get_ssid (const NMAccessPoint * ap); +void nm_ap_set_ssid (NMAccessPoint * ap, const GByteArray * ssid); + +NM80211ApFlags nm_ap_get_flags (NMAccessPoint *ap); +void nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags); + +NM80211ApSecurityFlags nm_ap_get_wpa_flags (NMAccessPoint *ap); +void nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags); + +NM80211ApSecurityFlags nm_ap_get_rsn_flags (NMAccessPoint *ap); +void nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags); + +const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap); +void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr *addr); + +NM80211Mode nm_ap_get_mode (NMAccessPoint *ap); +void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode); + +gboolean nm_ap_is_hotspot (NMAccessPoint *ap); + +gint8 nm_ap_get_strength (NMAccessPoint *ap); +void nm_ap_set_strength (NMAccessPoint *ap, gint8 strength); + +guint32 nm_ap_get_freq (NMAccessPoint *ap); +void nm_ap_set_freq (NMAccessPoint *ap, guint32 freq); + +guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap); +void nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate); + +gboolean nm_ap_get_fake (const NMAccessPoint *ap); +void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake); + +gboolean nm_ap_get_broadcast (NMAccessPoint *ap); +void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast); + +gint32 nm_ap_get_last_seen (const NMAccessPoint *ap); +void nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen); + +gboolean nm_ap_check_compatible (NMAccessPoint *self, + NMConnection *connection); + +gboolean nm_ap_complete_connection (NMAccessPoint *self, + NMConnection *connection, + gboolean lock_bssid, + GError **error); + +NMAccessPoint * nm_ap_match_in_list (NMAccessPoint *find_ap, + GSList *ap_list, + gboolean strict_match); + +void nm_ap_dump (NMAccessPoint *ap, const char *prefix); + +#endif /* NM_ACCESS_POINT_H */ diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c new file mode 100644 index 0000000000..e4a140a324 --- /dev/null +++ b/src/devices/wifi/nm-wifi-factory.c @@ -0,0 +1,114 @@ +/* -*- 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) 2011 - 2014 Red Hat, Inc. + */ + +#include + +#include "nm-device-factory.h" +#include "nm-device-wifi.h" +#include "nm-device-olpc-mesh.h" +#include "nm-settings-connection.h" + +#define NM_TYPE_WIFI_FACTORY (nm_wifi_factory_get_type ()) +#define NM_WIFI_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIFI_FACTORY, NMWifiFactory)) + +typedef struct { + GObject parent; +} NMWifiFactory; + +typedef struct { + GObjectClass parent; +} NMWifiFactoryClass; + +static GType nm_wifi_factory_get_type (void); + +static void device_factory_interface_init (NMDeviceFactory *factory_iface); + +G_DEFINE_TYPE_EXTENDED (NMWifiFactory, nm_wifi_factory, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init)) + +enum { + PROP_0, + PROP_DEVICE_TYPE, +}; + +/**************************************************************************/ + +#define PLUGIN_TYPE NM_DEVICE_TYPE_WIFI + +G_MODULE_EXPORT NMDeviceFactory * +nm_device_factory_create (GError **error) +{ + return (NMDeviceFactory *) g_object_new (NM_TYPE_WIFI_FACTORY, NULL); +} + +G_MODULE_EXPORT NMDeviceType +nm_device_factory_get_device_type (void) +{ + return PLUGIN_TYPE; +} + +/**************************************************************************/ + +static NMDevice * +new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error) +{ + if (plink->type == NM_LINK_TYPE_WIFI) + return nm_device_wifi_new (plink); + else if (plink->type == NM_LINK_TYPE_OLPC_MESH) + return nm_device_olpc_mesh_new (plink); + return NULL; +} + +static void +get_property (GObject *object, guint prop, GValue *value, GParamSpec *pspec) +{ + switch (prop) { + case PROP_DEVICE_TYPE: + g_value_set_uint (value, PLUGIN_TYPE); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop, pspec); + break; + } +} + +static void +device_factory_interface_init (NMDeviceFactory *factory_iface) +{ + factory_iface->new_link = new_link; +} + +static void +nm_wifi_factory_init (NMWifiFactory *self) +{ +} + +static void +nm_wifi_factory_class_init (NMWifiFactoryClass *wf_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (wf_class); + + object_class->get_property = get_property; + + g_object_class_override_property (object_class, + PROP_DEVICE_TYPE, + NM_DEVICE_FACTORY_DEVICE_TYPE); +} + diff --git a/src/devices/wifi/tests/Makefile.am b/src/devices/wifi/tests/Makefile.am new file mode 100644 index 0000000000..2667c5f12d --- /dev/null +++ b/src/devices/wifi/tests/Makefile.am @@ -0,0 +1,28 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_builddir)/libnm-util \ + -I$(top_srcdir)/src/platform \ + -I$(top_srcdir)/src/logging \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/devices/wifi \ + -I$(top_builddir)/src \ + -DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \ + -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) + +noinst_PROGRAMS = test-wifi-ap-utils + +test_wifi_ap_utils_SOURCES = \ + test-wifi-ap-utils.c \ + $(srcdir)/../nm-wifi-ap.c \ + $(srcdir)/../nm-wifi-ap.h \ + $(srcdir)/../nm-wifi-ap-utils.c \ + $(srcdir)/../nm-wifi-ap-utils.h + +test_wifi_ap_utils_LDADD = $(top_builddir)/src/libNetworkManager.la + +TESTS = test-wifi-ap-utils + diff --git a/src/devices/wifi/tests/test-wifi-ap-utils.c b/src/devices/wifi/tests/test-wifi-ap-utils.c new file mode 100644 index 0000000000..b828aa5f8f --- /dev/null +++ b/src/devices/wifi/tests/test-wifi-ap-utils.c @@ -0,0 +1,1542 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * 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, 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) 2011 Red Hat, Inc. + * + */ + +#include +#include + +#include "nm-wifi-ap-utils.h" +#include "nm-dbus-glib-types.h" + +#include "nm-setting-connection.h" +#include "nm-setting-wireless.h" +#include "nm-setting-wireless-security.h" +#include "nm-setting-8021x.h" + +#define DEBUG 1 + +/*******************************************/ + +#define COMPARE(src, expected, success, error, edomain, ecode) \ +{ \ + if (expected) { \ + if (!success) { \ + g_assert (error != NULL); \ + g_warning ("Failed to complete connection: (%d) %s", error->code, error->message); \ + } \ + g_assert (success == TRUE); \ + g_assert (error == NULL); \ +\ + success = nm_connection_compare (src, expected, NM_SETTING_COMPARE_FLAG_EXACT); \ + if (success == FALSE && DEBUG) { \ + g_message ("\n- COMPLETED ---------------------------------\n"); \ + nm_connection_dump (src); \ + g_message ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \ + nm_connection_dump (expected); \ + g_message ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \ + } \ + g_assert (success == TRUE); \ + } else { \ + if (success) { \ + g_message ("\n- COMPLETED ---------------------------------\n"); \ + nm_connection_dump (src); \ + } \ + g_assert (success == FALSE); \ + g_assert_error (error, edomain, ecode); \ + } \ + \ + g_clear_error (&error); \ +} + +static gboolean +complete_connection (const char *ssid, + const guint8 bssid[ETH_ALEN], + NM80211Mode mode, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + gboolean lock_bssid, + NMConnection *src, + GError **error) +{ + GByteArray *tmp; + gboolean success; + NMSettingWireless *s_wifi; + + /* Add a wifi setting if one doesn't exist */ + s_wifi = nm_connection_get_setting_wireless (src); + if (!s_wifi) { + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (src, NM_SETTING (s_wifi)); + } + + tmp = g_byte_array_sized_new (strlen (ssid)); + g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid)); + + success = nm_ap_utils_complete_connection (tmp, + bssid, + mode, + flags, + wpa_flags, + rsn_flags, + src, + lock_bssid, + error); + g_byte_array_free (tmp, TRUE); + return success; +} + +typedef struct { + const char *key; + const char *str; + guint32 uint; +} KeyData; + +static void +set_items (NMSetting *setting, const KeyData *items) +{ + const KeyData *item; + GParamSpec *pspec; + GByteArray *tmp; + + for (item = items; item && item->key; item++) { + g_assert (item->key); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), item->key); + g_assert (pspec); + + if (pspec->value_type == G_TYPE_STRING) { + g_assert (item->uint == 0); + if (item->str) + g_object_set (G_OBJECT (setting), item->key, item->str, NULL); + } else if (pspec->value_type == G_TYPE_UINT) { + g_assert (item->str == NULL); + g_object_set (G_OBJECT (setting), item->key, item->uint, NULL); + } else if (pspec->value_type == G_TYPE_INT) { + gint foo = (gint) item->uint; + + g_assert (item->str == NULL); + g_object_set (G_OBJECT (setting), item->key, foo, NULL); + } else if (pspec->value_type == G_TYPE_BOOLEAN) { + gboolean foo = !! (item->uint); + + g_assert (item->str == NULL); + g_object_set (G_OBJECT (setting), item->key, foo, NULL); + } else if (pspec->value_type == DBUS_TYPE_G_UCHAR_ARRAY) { + g_assert (item->str); + tmp = g_byte_array_sized_new (strlen (item->str)); + g_byte_array_append (tmp, (const guint8 *) item->str, strlen (item->str)); + g_object_set (G_OBJECT (setting), item->key, tmp, NULL); + g_byte_array_free (tmp, TRUE); + } else { + /* Special types, check based on property name */ + if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_PROTO)) + nm_setting_wireless_security_add_proto (NM_SETTING_WIRELESS_SECURITY (setting), item->str); + else if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_PAIRWISE)) + nm_setting_wireless_security_add_pairwise (NM_SETTING_WIRELESS_SECURITY (setting), item->str); + else if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_GROUP)) + nm_setting_wireless_security_add_group (NM_SETTING_WIRELESS_SECURITY (setting), item->str); + else if (!strcmp (item->key, NM_SETTING_802_1X_EAP)) + nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), item->str); + } + } +} + +static NMSettingWireless * +fill_wifi_empty (NMConnection *connection) +{ + NMSettingWireless *s_wifi; + + s_wifi = nm_connection_get_setting_wireless (connection); + if (!s_wifi) { + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + } + return s_wifi; +} + +static NMSettingWireless * +fill_wifi (NMConnection *connection, const KeyData items[]) +{ + NMSettingWireless *s_wifi; + + s_wifi = nm_connection_get_setting_wireless (connection); + if (!s_wifi) { + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + } + + set_items (NM_SETTING (s_wifi), items); + return s_wifi; +} + +static NMSettingWirelessSecurity * +fill_wsec (NMConnection *connection, const KeyData items[]) +{ + NMSettingWirelessSecurity *s_wsec; + + s_wsec = nm_connection_get_setting_wireless_security (connection); + if (!s_wsec) { + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + } + + set_items (NM_SETTING (s_wsec), items); + return s_wsec; +} + +static NMSetting8021x * +fill_8021x (NMConnection *connection, const KeyData items[]) +{ + NMSetting8021x *s_8021x; + + s_8021x = nm_connection_get_setting_802_1x (connection); + if (!s_8021x) { + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + nm_connection_add_setting (connection, NM_SETTING (s_8021x)); + } + + set_items (NM_SETTING (s_8021x), items); + return s_8021x; +} + +static NMConnection * +create_basic (const char *ssid, + const guint8 *bssid, + NM80211Mode mode) +{ + NMConnection *connection; + NMSettingWireless *s_wifi = NULL; + GByteArray *tmp; + + connection = nm_connection_new (); + + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + + /* SSID */ + tmp = g_byte_array_sized_new (strlen (ssid)); + g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid)); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, tmp, NULL); + g_byte_array_free (tmp, TRUE); + + /* BSSID */ + if (bssid) { + tmp = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (tmp, bssid, ETH_ALEN); + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, tmp, NULL); + g_byte_array_free (tmp, TRUE); + } + + if (mode == NM_802_11_MODE_INFRA) + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "infrastructure", NULL); + else if (mode == NM_802_11_MODE_ADHOC) + g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "adhoc", NULL); + else + g_assert_not_reached (); + + return connection; +} + +/*******************************************/ + +static void +test_lock_bssid (void) +{ + NMConnection *src, *expected; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *ssid = "blahblah"; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + TRUE, + src, &error); + expected = create_basic (ssid, bssid, NM_802_11_MODE_INFRA); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_open_ap_empty_connection (void) +{ + NMConnection *src, *expected; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *ssid = "blahblah"; + gboolean success; + GError *error = NULL; + + /* Test that an empty source connection is correctly filled with the + * SSID and Infra modes of the given AP details. + */ + + src = nm_connection_new (); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_open_ap_leap_connection_1 (gconstpointer add_wifi) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "Bill Smith", 0 }, { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that a basic connection filled with a LEAP username is + * rejected when completion is attempted with an open AP. LEAP requires + * the AP to have the Privacy bit set. + */ + + src = nm_connection_new (); + if (add_wifi) + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + /* We expect failure */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_open_ap_leap_connection_2 (void) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that a basic connection specifying IEEE8021x security (ie, Dynamic + * WEP or LEAP) is rejected when completion is attempted with an open AP. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + /* We expect failure */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_open_ap_wep_connection (gconstpointer add_wifi) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "11111111111111111111111111", 0 }, + { NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, NULL, 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that a static WEP connection is rejected when completion is + * attempted with an open AP. + */ + + src = nm_connection_new (); + if (add_wifi) + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + /* We expect failure */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_ap_wpa_psk_connection_base (const char *key_mgmt, + const char *auth_alg, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + gboolean add_wifi, + NMConnection *expected) +{ + NMConnection *src; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData exp_wifi[] = { + { NM_SETTING_WIRELESS_SSID, ssid, 0 }, + { NM_SETTING_WIRELESS_MODE, "infrastructure", 0 }, + { NULL } }; + const KeyData both_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0 }, + { NM_SETTING_WIRELESS_SECURITY_PSK, "asdfasdfasdfasdfasdfafs", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + if (add_wifi) + fill_wifi_empty (src); + fill_wsec (src, both_wsec); + success = complete_connection (ssid, bssid, NM_802_11_MODE_INFRA, + flags, wpa_flags, rsn_flags, + FALSE, src, &error); + if (expected) { + fill_wifi (expected, exp_wifi); + fill_wsec (expected, both_wsec); + } + COMPARE (src, expected, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +static void +test_open_ap_wpa_psk_connection_1 (void) +{ + /* Test that a WPA-PSK connection filling only the PSK itself and *not* + * filling the wifi setting is rejected when completion is attempted with + * an open AP. + */ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +static void +test_open_ap_wpa_psk_connection_2 (void) +{ + /* Test that a WPA-PSK connection filling only the PSK itself and also + * filling the wifi setting is rejected when completion is attempted with + * an open AP. + */ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + TRUE, NULL); +} + +static void +test_open_ap_wpa_psk_connection_3 (void) +{ + /* Test that a WPA-PSK connection filling the PSK and setting the auth alg + * to 'open' is rejected when completion is attempted with an open AP. + */ + test_ap_wpa_psk_connection_base (NULL, "open", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +static void +test_open_ap_wpa_psk_connection_4 (void) +{ + /* Test that a WPA-PSK connection filling the PSK and setting the auth alg + * to 'shared' is rejected when completion is attempted with an open AP. + * Shared auth cannot be used with WPA. + */ + test_ap_wpa_psk_connection_base (NULL, "shared", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +static void +test_open_ap_wpa_psk_connection_5 (void) +{ + /* Test that a WPA-PSK connection filling the PSK, the auth algorithm, and + * key management is rejected when completion is attempted with an open AP. + */ + test_ap_wpa_psk_connection_base ("wpa-psk", "open", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +/*******************************************/ + +static void +test_ap_wpa_eap_connection_base (const char *key_mgmt, + const char *auth_alg, + guint32 flags, + guint32 wpa_flags, + guint32 rsn_flags, + gboolean add_wifi, + guint error_domain, + guint error_code) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_empty[] = { { NULL } }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + src = nm_connection_new (); + if (add_wifi) + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + fill_8021x (src, src_empty); + success = complete_connection ("blahblah", bssid, NM_802_11_MODE_INFRA, + flags, wpa_flags, rsn_flags, + FALSE, src, &error); + /* Failure expected */ + COMPARE (src, NULL, success, error, error_domain, error_code); + + g_object_unref (src); +} + +enum { + IDX_NONE = 0, + IDX_OPEN, + IDX_PRIV, + IDX_WPA_PSK_PTKIP_GTKIP, + IDX_WPA_PSK_PTKIP_PCCMP_GTKIP, + IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP, + IDX_WPA_RSN_PSK_PCCMP_GCCMP, + IDX_RSN_PSK_PCCMP_GCCMP, + IDX_RSN_PSK_PTKIP_PCCMP_GTKIP, + IDX_WPA_8021X, + IDX_RSN_8021X, +}; + +static guint32 +flags_for_idx (guint32 idx) +{ + if (idx == IDX_OPEN) + return NM_802_11_AP_FLAGS_NONE; + else if ( idx == IDX_PRIV + || idx == IDX_WPA_PSK_PTKIP_GTKIP + || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP + || idx == IDX_RSN_PSK_PCCMP_GCCMP + || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP + || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP + || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP + || idx == IDX_WPA_8021X + || idx == IDX_RSN_8021X) + return NM_802_11_AP_FLAGS_PRIVACY; + else + g_assert_not_reached (); +} + +static guint32 +wpa_flags_for_idx (guint32 idx) +{ + if (idx == IDX_OPEN || idx == IDX_PRIV || idx == IDX_RSN_8021X + || idx == IDX_RSN_PSK_PCCMP_GCCMP || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP) + return NM_802_11_AP_SEC_NONE; + else if (idx == IDX_WPA_PSK_PTKIP_GTKIP) + return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP) + return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (IDX_WPA_RSN_PSK_PCCMP_GCCMP) + return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (idx == IDX_WPA_8021X) + return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_802_1X; + else + g_assert_not_reached (); +} + +static guint32 +rsn_flags_for_idx (guint32 idx) +{ + if (idx == IDX_OPEN || idx == IDX_PRIV || idx == IDX_WPA_8021X + || idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP) + return NM_802_11_AP_SEC_NONE; + else if (idx == IDX_RSN_PSK_PCCMP_GCCMP) + return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP) + return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP) + return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP) + return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK; + else if (idx == IDX_RSN_8021X) + return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_802_1X; + else + g_assert_not_reached (); +} + +static guint32 +error_domain_for_idx (guint32 idx, guint num) +{ + if (idx == IDX_OPEN) + return NM_SETTING_WIRELESS_SECURITY_ERROR; + else if (idx == IDX_PRIV) { + if (num <= 3) + return NM_SETTING_802_1X_ERROR; + else + return NM_SETTING_WIRELESS_SECURITY_ERROR; + } else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP + || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP + || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP) + return NM_SETTING_WIRELESS_SECURITY_ERROR; + else + g_assert_not_reached (); +} + +static guint32 +error_code_for_idx (guint32 idx, guint num) +{ + if (idx == IDX_OPEN) + return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY; + else if (idx == IDX_PRIV) { + if (num <= 3) + return NM_SETTING_802_1X_ERROR_MISSING_PROPERTY; + else + return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY; + } else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP + || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP + || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP) + return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY; + else + g_assert_not_reached (); +} + +static void +test_ap_wpa_eap_connection_1 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + + test_ap_wpa_eap_connection_base (NULL, NULL, + flags_for_idx (idx), + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, + error_domain_for_idx (idx, 1), + error_code_for_idx (idx, 1)); +} + +static void +test_ap_wpa_eap_connection_2 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + + test_ap_wpa_eap_connection_base (NULL, NULL, + flags_for_idx (idx), + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + TRUE, + error_domain_for_idx (idx, 2), + error_code_for_idx (idx, 2)); +} + +static void +test_ap_wpa_eap_connection_3 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + + test_ap_wpa_eap_connection_base (NULL, "open", + flags_for_idx (idx), + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, + error_domain_for_idx (idx, 3), + error_code_for_idx (idx, 3)); +} + +static void +test_ap_wpa_eap_connection_4 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + + test_ap_wpa_eap_connection_base (NULL, "shared", + flags_for_idx (idx), + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, + error_domain_for_idx (idx, 4), + error_code_for_idx (idx, 4)); +} + +static void +test_ap_wpa_eap_connection_5 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + + test_ap_wpa_eap_connection_base ("wpa-eap", "open", + flags_for_idx (idx), + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, + error_domain_for_idx (idx, 5), + error_code_for_idx (idx, 5)); +} + +/*******************************************/ + +static void +test_priv_ap_empty_connection (void) +{ + NMConnection *src, *expected; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *ssid = "blahblah"; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that an empty connection is completed to a valid Static WEP + * connection when completed with an AP with the Privacy bit set. + */ + + src = nm_connection_new (); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + + /* Static WEP connection expected */ + expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); + fill_wsec (expected, exp_wsec); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_priv_ap_leap_connection_1 (gconstpointer add_wifi) +{ + NMConnection *src, *expected; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *leap_username = "Bill Smith"; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 }, + { NULL } }; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 }, + { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that an minimal LEAP connection specifying only key management and + * the LEAP username is completed to a full LEAP connection when completed + * with an AP with the Privacy bit set. + */ + + src = nm_connection_new (); + if (add_wifi) + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + /* We expect success here; since LEAP APs just set the 'privacy' flag + * there's no way to determine from the AP's beacon whether it's static WEP, + * dynamic WEP, or LEAP. + */ + expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); + fill_wsec (expected, exp_wsec); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_leap_connection_2 (void) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that an minimal LEAP connection specifying only key management and + * the LEAP auth alg is completed to a full LEAP connection when completed + * with an AP with the Privacy bit set. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + /* We expect failure here, we need a LEAP username */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_dynamic_wep_1 (void) +{ + NMConnection *src, *expected; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + const KeyData both_8021x[] = { + { NM_SETTING_802_1X_EAP, "peap", 0 }, + { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 }, + { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 }, + { NULL } }; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that an minimal Dynamic WEP connection specifying key management, + * the auth algorithm, and valid 802.1x setting is completed to a valid + * Dynamic WEP connection when completed with an AP with the Privacy bit set. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + fill_8021x (src, both_8021x); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + + /* We expect a completed Dynamic WEP connection */ + expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); + fill_wsec (expected, exp_wsec); + fill_8021x (expected, both_8021x); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_dynamic_wep_2 (void) +{ + NMConnection *src, *expected; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + const KeyData both_8021x[] = { + { NM_SETTING_802_1X_EAP, "peap", 0 }, + { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 }, + { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 }, + { NULL } }; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that an minimal Dynamic WEP connection specifying only the auth + * algorithm and a valid 802.1x setting is completed to a valid Dynamic + * WEP connection when completed with an AP with the Privacy bit set. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + fill_8021x (src, both_8021x); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + + /* We expect a completed Dynamic WEP connection */ + expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); + fill_wsec (expected, exp_wsec); + fill_8021x (expected, both_8021x); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_dynamic_wep_3 (void) +{ + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", 0 }, + { NULL } }; + const KeyData src_8021x[] = { + { NM_SETTING_802_1X_EAP, "peap", 0 }, + { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 }, + { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Ensure that a basic connection specifying 'shared' auth and an 802.1x + * setting is rejected, as 802.1x is incompatible with 'shared' auth. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + fill_8021x (src, src_8021x); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, + FALSE, + src, &error); + /* Expect failure; shared is not compatible with dynamic WEP */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_priv_ap_wpa_psk_connection_1 (void) +{ + /* Test that a basic WPA-PSK connection is rejected when completion is + * attempted with an AP with just the Privacy bit set. Lack of WPA/RSN + * flags means the AP provides Static/Dynamic WEP or LEAP, not WPA. + */ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +static void +test_priv_ap_wpa_psk_connection_2 (void) +{ + /* Test that a basic WPA-PSK connection is rejected when completion is + * attempted with an AP with just the Privacy bit set. Lack of WPA/RSN + * flags means the AP provides Static/Dynamic WEP or LEAP, not WPA. + */ + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + TRUE, NULL); +} + +static void +test_priv_ap_wpa_psk_connection_3 (void) +{ + /* Test that a basic WPA-PSK connection specifying only the auth algorithm + * is rejected when completion is attempted with an AP with just the Privacy + * bit set. Lack of WPA/RSN flags means the AP provides Static/Dynamic WEP + * or LEAP, not WPA. + */ + test_ap_wpa_psk_connection_base (NULL, "open", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +static void +test_priv_ap_wpa_psk_connection_4 (void) +{ + /* Test that a basic WPA-PSK connection specifying only the auth algorithm + * is rejected when completion is attempted with an AP with just the Privacy + * bit set. Lack of WPA/RSN flags means the AP provides Static/Dynamic WEP + * or LEAP, not WPA. Second, 'shared' auth is incompatible with WPA. + */ + test_ap_wpa_psk_connection_base (NULL, "shared", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +static void +test_priv_ap_wpa_psk_connection_5 (void) +{ + /* Test that a WPA-PSK connection specifying both the key management and + * auth algorithm is rejected when completion is attempted with an AP with + * just the Privacy bit set. Lack of WPA/RSN flags means the AP provides + * Static/Dynamic WEP or LEAP, not WPA. + */ + test_ap_wpa_psk_connection_base ("wpa-psk", "open", + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_SEC_NONE, + NM_802_11_AP_SEC_NONE, + FALSE, NULL); +} + +/*******************************************/ + +static void +test_wpa_ap_empty_connection (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *src, *expected; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *ssid = "blahblah"; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that a basic WPA-PSK connection specifying just key management and + * the auth algorithm is completed successfully when given an AP with WPA + * or RSN flags. + */ + + src = nm_connection_new (); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, src, &error); + + /* WPA connection expected */ + expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); + fill_wsec (expected, exp_wsec); + COMPARE (src, expected, success, error, 0, 0); + + g_object_unref (src); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_wpa_ap_leap_connection_1 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *src; + const char *ssid = "blahblah"; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const char *leap_username = "Bill Smith"; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that completion of a LEAP connection with a WPA-enabled AP is + * rejected since WPA APs (usually) do not support LEAP. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + success = complete_connection (ssid, bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, + src, &error); + /* Expect failure here; WPA APs don't support old-school LEAP */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_wpa_ap_leap_connection_2 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that completion of a LEAP connection with a WPA-enabled AP is + * rejected since WPA APs (usually) do not support LEAP. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, + src, &error); + /* We expect failure here, we need a LEAP username */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_wpa_ap_dynamic_wep_connection (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *src; + const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + const KeyData src_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, + { NULL } }; + gboolean success; + GError *error = NULL; + + /* Test that completion of a Dynamic WEP connection with a WPA-enabled AP is + * rejected since WPA APs (usually) do not support Dynamic WEP. + */ + + src = nm_connection_new (); + fill_wifi_empty (src); + fill_wsec (src, src_wsec); + success = complete_connection ("blahblah", bssid, + NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, + src, &error); + /* We expect failure here since Dynamic WEP is incompatible with WPA */ + COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); + + g_object_unref (src); +} + +/*******************************************/ + +static void +test_wpa_ap_wpa_psk_connection_1 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *expected; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + + expected = nm_connection_new (); + fill_wsec (expected, exp_wsec); + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, expected); + g_object_unref (expected); +} + +static void +test_wpa_ap_wpa_psk_connection_2 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *expected; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + + expected = nm_connection_new (); + fill_wsec (expected, exp_wsec); + test_ap_wpa_psk_connection_base (NULL, NULL, + NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + TRUE, expected); + g_object_unref (expected); +} + +static void +test_wpa_ap_wpa_psk_connection_3 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *expected; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + + expected = nm_connection_new (); + fill_wsec (expected, exp_wsec); + test_ap_wpa_psk_connection_base (NULL, "open", + NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, expected); + g_object_unref (expected); +} + +static void +test_wpa_ap_wpa_psk_connection_4 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + test_ap_wpa_psk_connection_base (NULL, "shared", + NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, NULL); +} + +static void +test_wpa_ap_wpa_psk_connection_5 (gconstpointer data) +{ + guint idx = GPOINTER_TO_UINT (data); + NMConnection *expected; + const KeyData exp_wsec[] = { + { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, + { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, + { NULL } }; + + expected = nm_connection_new (); + fill_wsec (expected, exp_wsec); + test_ap_wpa_psk_connection_base ("wpa-psk", "open", + NM_802_11_AP_FLAGS_PRIVACY, + wpa_flags_for_idx (idx), + rsn_flags_for_idx (idx), + FALSE, expected); + g_object_unref (expected); +} + +/*******************************************/ + +static void +test_strength_dbm (void) +{ + /* boundary conditions first */ + g_assert_cmpint (nm_ap_utils_level_to_quality (-1), ==, 100); + g_assert_cmpint (nm_ap_utils_level_to_quality (-40), ==, 100); + g_assert_cmpint (nm_ap_utils_level_to_quality (-30), ==, 100); + g_assert_cmpint (nm_ap_utils_level_to_quality (-100), ==, 0); + g_assert_cmpint (nm_ap_utils_level_to_quality (-200), ==, 0); + + g_assert_cmpint (nm_ap_utils_level_to_quality (-81), ==, 32); + g_assert_cmpint (nm_ap_utils_level_to_quality (-92), ==, 14); + g_assert_cmpint (nm_ap_utils_level_to_quality (-74), ==, 44); + g_assert_cmpint (nm_ap_utils_level_to_quality (-81), ==, 32); + g_assert_cmpint (nm_ap_utils_level_to_quality (-66), ==, 57); +} + +static void +test_strength_percent (void) +{ + int i; + + /* boundary conditions first */ + g_assert_cmpint (nm_ap_utils_level_to_quality (0), ==, 0); + g_assert_cmpint (nm_ap_utils_level_to_quality (100), ==, 100); + g_assert_cmpint (nm_ap_utils_level_to_quality (110), ==, 100); + + for (i = 0; i <= 100; i++) + g_assert_cmpint (nm_ap_utils_level_to_quality (i), ==, i); +} + +static void +test_strength_wext (void) +{ + /* boundary conditions that we assume aren't WEXT first */ + g_assert_cmpint (nm_ap_utils_level_to_quality (256), ==, 100); + g_assert_cmpint (nm_ap_utils_level_to_quality (110), ==, 100); + + /* boundary conditions that we assume are WEXT */ + g_assert_cmpint (nm_ap_utils_level_to_quality (111), ==, 0); + g_assert_cmpint (nm_ap_utils_level_to_quality (150), ==, 0); + g_assert_cmpint (nm_ap_utils_level_to_quality (225), ==, 100); + g_assert_cmpint (nm_ap_utils_level_to_quality (255), ==, 100); + + g_assert_cmpint (nm_ap_utils_level_to_quality (157), ==, 2); + g_assert_cmpint (nm_ap_utils_level_to_quality (200), ==, 74); + g_assert_cmpint (nm_ap_utils_level_to_quality (215), ==, 99); +} + +/*******************************************/ + +int +main (int argc, char **argv) +{ + gsize i; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/wifi/lock_bssid", + test_lock_bssid); + + /* Open AP tests; make sure that connections to be completed that have + * various security-related settings already set cause the completion + * to fail. + */ + g_test_add_func ("/wifi/open_ap/empty_connection", + test_open_ap_empty_connection); + g_test_add_data_func ("/wifi/open_ap/leap_connection/1", + (gconstpointer) TRUE, + test_open_ap_leap_connection_1); + g_test_add_data_func ("/wifi/open_ap/leap_connection/1_no_add_wifi", + (gconstpointer) FALSE, + test_open_ap_leap_connection_1); + g_test_add_func ("/wifi/open_ap/leap_connection/2", + test_open_ap_leap_connection_2); + g_test_add_data_func ("/wifi/open_ap/wep_connection", + (gconstpointer) TRUE, + test_open_ap_wep_connection); + g_test_add_data_func ("/wifi/open_ap/wep_connection", + (gconstpointer) FALSE, + test_open_ap_wep_connection); + + g_test_add_func ("/wifi/open_ap/wpa_psk_connection/1", + test_open_ap_wpa_psk_connection_1); + g_test_add_func ("/wifi/open_ap/wpa_psk_connection/2", + test_open_ap_wpa_psk_connection_2); + g_test_add_func ("/wifi/open_ap/wpa_psk_connection/3", + test_open_ap_wpa_psk_connection_3); + g_test_add_func ("/wifi/open_ap/wpa_psk_connection/4", + test_open_ap_wpa_psk_connection_4); + g_test_add_func ("/wifi/open_ap/wpa_psk_connection/5", + test_open_ap_wpa_psk_connection_5); + + g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/1", + (gconstpointer) IDX_OPEN, + test_ap_wpa_eap_connection_1); + g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/2", + (gconstpointer) IDX_OPEN, + test_ap_wpa_eap_connection_2); + g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/3", + (gconstpointer) IDX_OPEN, + test_ap_wpa_eap_connection_3); + g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/4", + (gconstpointer) IDX_OPEN, + test_ap_wpa_eap_connection_4); + g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/5", + (gconstpointer) IDX_OPEN, + test_ap_wpa_eap_connection_5); + + /* WEP AP tests */ + g_test_add_func ("/wifi/priv_ap/empty_connection", + test_priv_ap_empty_connection); + g_test_add_data_func ("/wifi/priv_ap/leap_connection/1", + (gconstpointer) FALSE, + test_priv_ap_leap_connection_1); + g_test_add_func ("/wifi/priv_ap/leap_connection/2", + test_priv_ap_leap_connection_2); + + g_test_add_func ("/wifi/priv_ap/dynamic_wep/1", + test_priv_ap_dynamic_wep_1); + g_test_add_func ("/wifi/priv_ap/dynamic_wep/2", + test_priv_ap_dynamic_wep_2); + g_test_add_func ("/wifi/priv_ap/dynamic_wep/3", + test_priv_ap_dynamic_wep_3); + + g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/1", + test_priv_ap_wpa_psk_connection_1); + g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/2", + test_priv_ap_wpa_psk_connection_2); + g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/3", + test_priv_ap_wpa_psk_connection_3); + g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/4", + test_priv_ap_wpa_psk_connection_4); + g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/5", + test_priv_ap_wpa_psk_connection_5); + + g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/1", + (gconstpointer) IDX_PRIV, + test_ap_wpa_eap_connection_1); + g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/2", + (gconstpointer) IDX_PRIV, + test_ap_wpa_eap_connection_2); + g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/3", + (gconstpointer) IDX_PRIV, + test_ap_wpa_eap_connection_3); + g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/4", + (gconstpointer) IDX_PRIV, + test_ap_wpa_eap_connection_4); + g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/5", + (gconstpointer) IDX_PRIV, + test_ap_wpa_eap_connection_5); + + /* WPA-PSK tests */ + for (i = IDX_WPA_PSK_PTKIP_GTKIP; i <= IDX_WPA_RSN_PSK_PCCMP_GCCMP; i++) { + g_test_add_data_func ("/wifi/wpa_psk/empty_connection", + (gconstpointer) i, + test_wpa_ap_empty_connection); + g_test_add_data_func ("/wifi/wpa_psk/leap_connection/1", + (gconstpointer) i, + test_wpa_ap_leap_connection_1); + g_test_add_data_func ("/wifi/wpa_psk/leap_connection/2", + (gconstpointer) i, + test_wpa_ap_leap_connection_2); + + g_test_add_data_func ("/wifi/wpa_psk/dynamic_wep_connection", + (gconstpointer) i, + test_wpa_ap_dynamic_wep_connection); + + g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/1", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_1); + g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/2", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_2); + g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/3", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_3); + g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/4", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_4); + g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/5", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_5); + + g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/1", + (gconstpointer) i, + test_ap_wpa_eap_connection_1); + g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/2", + (gconstpointer) i, + test_ap_wpa_eap_connection_2); + g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/3", + (gconstpointer) i, + test_ap_wpa_eap_connection_3); + g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/4", + (gconstpointer) i, + test_ap_wpa_eap_connection_4); + g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/5", + (gconstpointer) i, + test_ap_wpa_eap_connection_5); + } + + /* RSN-PSK tests */ + for (i = IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP; i <= IDX_RSN_PSK_PTKIP_PCCMP_GTKIP; i++) { + g_test_add_data_func ("/wifi/rsn_psk/empty_connection", + (gconstpointer) i, + test_wpa_ap_empty_connection); + g_test_add_data_func ("/wifi/rsn_psk/leap_connection/1", + (gconstpointer) i, + test_wpa_ap_leap_connection_1); + g_test_add_data_func ("/wifi/rsn_psk/leap_connection/2", + (gconstpointer) i, + test_wpa_ap_leap_connection_2); + + g_test_add_data_func ("/wifi/rsn_psk/dynamic_wep_connection", + (gconstpointer) i, + test_wpa_ap_dynamic_wep_connection); + + g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/1", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_1); + g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/2", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_2); + g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/3", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_3); + g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/4", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_4); + g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/5", + (gconstpointer) i, + test_wpa_ap_wpa_psk_connection_5); + + g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/1", + (gconstpointer) i, + test_ap_wpa_eap_connection_1); + g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/2", + (gconstpointer) i, + test_ap_wpa_eap_connection_2); + g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/3", + (gconstpointer) i, + test_ap_wpa_eap_connection_3); + g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/4", + (gconstpointer) i, + test_ap_wpa_eap_connection_4); + g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/5", + (gconstpointer) i, + test_ap_wpa_eap_connection_5); + } + + /* Scanned signal strength conversion tests */ + g_test_add_func ("/wifi/strength/dbm", + test_strength_dbm); + g_test_add_func ("/wifi/strength/percent", + test_strength_percent); + g_test_add_func ("/wifi/strength/wext", + test_strength_wext); + + return g_test_run (); +} diff --git a/src/nm-manager.c b/src/nm-manager.c index f82967094b..dde273923d 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -41,8 +41,6 @@ #include "nm-vpn-manager.h" #include "nm-device.h" #include "nm-device-ethernet.h" -#include "nm-device-wifi.h" -#include "nm-device-olpc-mesh.h" #include "nm-device-infiniband.h" #include "nm-device-bond.h" #include "nm-device-team.h" @@ -1309,39 +1307,6 @@ manager_update_radio_enabled (NMManager *self, } } -static void -manager_hidden_ap_found (NMDevice *device, - NMAccessPoint *ap, - gpointer user_data) -{ - NMManager *manager = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); - const struct ether_addr *bssid; - GSList *iter; - GSList *connections; - gboolean done = FALSE; - - g_return_if_fail (nm_ap_get_ssid (ap) == NULL); - - bssid = nm_ap_get_address (ap); - g_assert (bssid); - - /* Look for this AP's BSSID in the seen-bssids list of a connection, - * and if a match is found, copy over the SSID */ - connections = nm_settings_get_connections (priv->settings); - for (iter = connections; iter && !done; iter = g_slist_next (iter)) { - NMConnection *connection = NM_CONNECTION (iter->data); - NMSettingWireless *s_wifi; - - s_wifi = nm_connection_get_setting_wireless (connection); - if (s_wifi) { - if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid)) - nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi)); - } - } - g_slist_free (connections); -} - static void update_rstate_from_rfkill (NMRfkillManager *rfkill_mgr, RadioState *rstate) { @@ -1644,11 +1609,8 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con) NMConnection *connection = NULL; gboolean enabled = FALSE; RfKillType rtype; - NMDeviceType devtype; GSList *iter, *remove = NULL; - devtype = nm_device_get_device_type (device); - /* No duplicates */ if (nm_manager_get_device_by_udi (self, nm_device_get_udi (device))) return; @@ -1689,15 +1651,6 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con) self); } - if (devtype == NM_DEVICE_TYPE_WIFI) { - /* Attach to the access-point-added signal so that the manager can fill - * non-SSID-broadcasting APs with an SSID. - */ - g_signal_connect (device, "hidden-ap-found", - G_CALLBACK (manager_hidden_ap_found), - self); - } - /* Update global rfkill state for this device type with the device's * rfkill state, and then set this device's rfkill state based on the * global state. @@ -2007,12 +1960,6 @@ platform_link_added (NMManager *self, case NM_LINK_TYPE_INFINIBAND: device = nm_device_infiniband_new (plink); break; - case NM_LINK_TYPE_OLPC_MESH: - device = nm_device_olpc_mesh_new (plink); - break; - case NM_LINK_TYPE_WIFI: - device = nm_device_wifi_new (plink); - break; case NM_LINK_TYPE_BOND: device = nm_device_bond_new (plink); break; diff --git a/src/nm-policy.c b/src/nm-policy.c index 88bc31a161..d751606ed8 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -29,7 +29,6 @@ #include "nm-policy.h" #include "NetworkManagerUtils.h" -#include "nm-wifi-ap.h" #include "nm-activation-request.h" #include "nm-logging.h" #include "nm-device.h" diff --git a/src/nm-wifi-ap-utils.c b/src/nm-wifi-ap-utils.c deleted file mode 100644 index 9b03cbd45f..0000000000 --- a/src/nm-wifi-ap-utils.c +++ /dev/null @@ -1,721 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2011 Red Hat, Inc. - */ - -#include -#include -#include - -#include "nm-wifi-ap-utils.h" - -static gboolean -verify_no_wep (NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error) -{ - if ( nm_setting_wireless_security_get_wep_key (s_wsec, 0) - || nm_setting_wireless_security_get_wep_key (s_wsec, 1) - || nm_setting_wireless_security_get_wep_key (s_wsec, 2) - || nm_setting_wireless_security_get_wep_key (s_wsec, 3) - || nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) - || nm_setting_wireless_security_get_wep_key_type (s_wsec)) { - /* Dynamic WEP cannot have any WEP keys set */ - g_set_error (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "%s is incompatible with static WEP keys", tag); - return FALSE; - } - - return TRUE; -} - -static gboolean -verify_leap (NMSettingWirelessSecurity *s_wsec, - NMSetting8021x *s_8021x, - gboolean adhoc, - GError **error) -{ - const char *key_mgmt, *auth_alg, *leap_username; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); - leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); - - /* One (or both) of two things indicates we want LEAP: - * 1) auth_alg == 'leap' - * 2) valid leap_username - * - * LEAP always requires a LEAP username. - */ - - if (auth_alg) { - if (!strcmp (auth_alg, "leap")) { - /* LEAP authentication requires at least a LEAP username */ - if (!leap_username) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, - "LEAP requires a LEAP username"); - return FALSE; - } - } else if (leap_username) { - /* Leap username requires 'leap' auth */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "LEAP requires 'leap' authentication"); - return FALSE; - } - } - - if (leap_username) { - if (key_mgmt && strcmp (key_mgmt, "ieee8021x")) { - /* LEAP requires ieee8021x key management */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X, - "LEAP requires IEEE 802.1x key management"); - return FALSE; - } - } - - /* At this point if auth_alg is set it must be 'leap', and if key_mgmt - * is set it must be 'ieee8021x'. - */ - if (leap_username) { - if (auth_alg) - g_assert (strcmp (auth_alg, "leap") == 0); - if (key_mgmt) - g_assert (strcmp (key_mgmt, "ieee8021x") == 0); - - if (adhoc) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "LEAP incompatible with Ad-Hoc mode"); - return FALSE; - } - - if (!verify_no_wep (s_wsec, "LEAP", error)) - return FALSE; - - if (s_8021x) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME, - "LEAP incompatible with 802.1x setting"); - return FALSE; - } - } - - return TRUE; -} - -static gboolean -verify_no_wpa (NMSettingWirelessSecurity *s_wsec, - const char *tag, - GError **error) -{ - const char *key_mgmt; - int n, i; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - if (key_mgmt && !strncmp (key_mgmt, "wpa", 3)) { - g_set_error (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "%s incompatible with any WPA key management", tag); - return FALSE; - } - - if (nm_setting_wireless_security_get_num_protos (s_wsec)) { - g_set_error (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "%s incompatible with any 'proto' setting", tag); - return FALSE; - } - - n = nm_setting_wireless_security_get_num_pairwise (s_wsec); - for (i = 0; i < n; i++) { - const char *pw; - - pw = nm_setting_wireless_security_get_pairwise (s_wsec, i); - if (!strcmp (pw, "tkip") || !strcmp (pw, "ccmp")) { - g_set_error (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "%s is incompatible with WPA pairwise ciphers", tag); - return FALSE; - } - } - - n = nm_setting_wireless_security_get_num_groups (s_wsec); - for (i = 0; i < n; i++) { - const char *gr; - - gr = nm_setting_wireless_security_get_group (s_wsec, i); - if (strcmp (gr, "wep40") && strcmp (gr, "wep104")) { - g_set_error (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "%s is incompatible with WPA group ciphers", tag); - return FALSE; - } - } - - if (nm_setting_wireless_security_get_psk (s_wsec)) { - g_set_error (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "%s is incompatible with a WPA Pre-Shared Key", tag); - return FALSE; - } - - return TRUE; -} - -static gboolean -verify_dynamic_wep (NMSettingWirelessSecurity *s_wsec, - NMSetting8021x *s_8021x, - gboolean adhoc, - GError **error) -{ - const char *key_mgmt, *auth_alg, *leap_username; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); - leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); - - g_return_val_if_fail (leap_username == NULL, TRUE); - - if (key_mgmt) { - if (!strcmp (key_mgmt, "ieee8021x")) { - if (!s_8021x) { - /* 802.1x key management requires an 802.1x setting */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Dynamic WEP requires an 802.1x setting"); - return FALSE; - } - - if (auth_alg && strcmp (auth_alg, "open")) { - /* 802.1x key management must use "open" authentication */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Dynamic WEP requires 'open' authentication"); - return FALSE; - } - - /* Dynamic WEP incompatible with anything static WEP related */ - if (!verify_no_wep (s_wsec, "Dynamic WEP", error)) - return FALSE; - } else if (!strcmp (key_mgmt, "none")) { - if (s_8021x) { - /* 802.1x setting requires 802.1x key management */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Dynamic WEP requires 'ieee8021x' key management"); - return FALSE; - } - } - } else if (s_8021x) { - /* 802.1x setting incompatible with anything but 'open' auth */ - if (auth_alg && strcmp (auth_alg, "open")) { - /* 802.1x key management must use "open" authentication */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Dynamic WEP requires 'open' authentication"); - return FALSE; - } - - /* Dynamic WEP incompatible with anything static WEP related */ - if (!verify_no_wep (s_wsec, "Dynamic WEP", error)) - return FALSE; - } - - return TRUE; -} - -static gboolean -verify_wpa_psk (NMSettingWirelessSecurity *s_wsec, - NMSetting8021x *s_8021x, - gboolean adhoc, - guint32 wpa_flags, - guint32 rsn_flags, - GError **error) -{ - const char *key_mgmt, *auth_alg, *tmp; - int n; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); - - if (key_mgmt) { - if (!strcmp (key_mgmt, "wpa-psk") || !strcmp (key_mgmt, "wpa-none")) { - if (s_8021x) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA-PSK incompatible with 802.1x"); - return FALSE; - } - - if (auth_alg && strcmp (auth_alg, "open")) { - /* WPA must use "open" authentication */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA-PSK requires 'open' authentication"); - return FALSE; - } - } - - if (!strcmp (key_mgmt, "wpa-none")) { - if (!adhoc) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA Ad-Hoc requires an Ad-Hoc mode AP"); - return FALSE; - } - - /* Ad-Hoc WPA requires 'wpa' proto, 'none' pairwise, and 'tkip' group */ - n = nm_setting_wireless_security_get_num_protos (s_wsec); - tmp = (n > 0) ? nm_setting_wireless_security_get_proto (s_wsec, 0) : NULL; - if (n > 1 || !tmp || strcmp (tmp, "wpa")) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA Ad-Hoc requires 'wpa' proto"); - return FALSE; - } - - n = nm_setting_wireless_security_get_num_pairwise (s_wsec); - tmp = (n > 0) ? nm_setting_wireless_security_get_pairwise (s_wsec, 0) : NULL; - if (n > 1 || g_strcmp0 (tmp, "none")) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA Ad-Hoc requires 'none' pairwise cipher"); - return FALSE; - } - - n = nm_setting_wireless_security_get_num_groups (s_wsec); - tmp = (n > 0) ? nm_setting_wireless_security_get_group (s_wsec, 0) : NULL; - if (n > 1 || !tmp || strcmp (tmp, "tkip")) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA Ad-Hoc requires 'tkip' group cipher"); - return FALSE; - } - } - - if (!strcmp (key_mgmt, "wpa-psk")) { - /* Make sure the AP's capabilities support WPA-PSK */ - if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) - && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "AP does not support PSK but setting requires it"); - return FALSE; - } - } - } - - return TRUE; -} - -static gboolean -verify_wpa_eap (NMSettingWirelessSecurity *s_wsec, - NMSetting8021x *s_8021x, - guint32 wpa_flags, - guint32 rsn_flags, - GError **error) -{ - const char *key_mgmt, *auth_alg; - gboolean is_wpa_eap = FALSE; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); - - if (key_mgmt) { - if (!strcmp (key_mgmt, "wpa-eap")) { - if (!s_8021x) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA-EAP requires an 802.1x setting"); - return FALSE; - } - - if (auth_alg && strcmp (auth_alg, "open")) { - /* WPA must use "open" authentication */ - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA-EAP requires 'open' authentication"); - return FALSE; - } - - is_wpa_eap = TRUE; - } else if (s_8021x) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Setting requires 802.1x but does not use 'wpa-eap' key management"); - return FALSE; - } - } - - if (is_wpa_eap || s_8021x) { - /* Make sure the AP's capabilities support WPA-EAP */ - if ( !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) - && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "AP does not support 802.1x but setting requires it"); - return FALSE; - } - } - - return TRUE; -} - -static gboolean -verify_adhoc (NMSettingWirelessSecurity *s_wsec, - NMSetting8021x *s_8021x, - gboolean adhoc, - GError **error) -{ - const char *key_mgmt = NULL, *leap_username = NULL, *auth_alg = NULL; - - if (s_wsec) { - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); - leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); - } - - if (adhoc) { - if (key_mgmt && strcmp (key_mgmt, "wpa-none") && strcmp (key_mgmt, "none")) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "AP mode is Ad-Hoc but setting requires Infrastructure security"); - return FALSE; - } - - if (s_8021x) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Ad-Hoc mode incompatible with 802.1x security"); - return FALSE; - } - - if (leap_username) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Ad-Hoc mode incompatible with LEAP security"); - return FALSE; - } - - if (auth_alg && strcmp (auth_alg, "open")) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Ad-Hoc mode requires 'open' authentication"); - return FALSE; - } - } else { - if (key_mgmt && !strcmp (key_mgmt, "wpa-none")) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "AP mode is Infrastructure but setting requires Ad-Hoc security"); - return FALSE; - } - } - - return TRUE; -} - -gboolean -nm_ap_utils_complete_connection (const GByteArray *ap_ssid, - const guint8 ap_bssid[ETH_ALEN], - NM80211Mode ap_mode, - guint32 ap_flags, - guint32 ap_wpa_flags, - guint32 ap_rsn_flags, - NMConnection *connection, - gboolean lock_bssid, - GError **error) -{ - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - NMSetting8021x *s_8021x; - const GByteArray *ssid; - const char *mode, *key_mgmt, *auth_alg, *leap_username; - gboolean adhoc = FALSE; - - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - s_wsec = nm_connection_get_setting_wireless_security (connection); - s_8021x = nm_connection_get_setting_802_1x (connection); - - /* Fill in missing SSID */ - ssid = nm_setting_wireless_get_ssid (s_wifi); - if (!ssid) - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid, NULL); - else if ( ssid->len != ap_ssid->len - || memcmp (ssid->data, ap_ssid->data, ssid->len)) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_ERROR, - NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, - "Setting SSID did not match AP SSID"); - return FALSE; - } - - if (lock_bssid && !nm_setting_wireless_get_bssid (s_wifi)) { - GByteArray *bssid; - - bssid = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (bssid, ap_bssid, ETH_ALEN); - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL); - g_byte_array_free (bssid, TRUE); - } - - /* And mode */ - mode = nm_setting_wireless_get_mode (s_wifi); - if (mode) { - gboolean valid = FALSE; - - /* Make sure the supplied mode matches the AP's */ - if ( !strcmp (mode, NM_SETTING_WIRELESS_MODE_INFRA) - || !strcmp (mode, NM_SETTING_WIRELESS_MODE_AP)) { - if (ap_mode == NM_802_11_MODE_INFRA) - valid = TRUE; - } else if (!strcmp (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { - if (ap_mode == NM_802_11_MODE_ADHOC) - valid = TRUE; - adhoc = TRUE; - } - - if (valid == FALSE) { - g_set_error (error, - NM_SETTING_WIRELESS_ERROR, - NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, - NM_SETTING_WIRELESS_MODE); - return FALSE; - } - } else { - mode = NM_SETTING_WIRELESS_MODE_INFRA; - if (ap_mode == NM_802_11_MODE_ADHOC) { - mode = NM_SETTING_WIRELESS_MODE_ADHOC; - adhoc = TRUE; - } - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, mode, NULL); - } - - /* Security */ - - /* Open */ - if ( !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY) - && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) - && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { - /* Make sure the connection doesn't specify security */ - if (s_wsec || s_8021x) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "AP is unencrypted but setting specifies security"); - return FALSE; - } - return TRUE; - } - - /* Everything else requires security */ - if (!s_wsec) { - s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - } - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); - auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec); - leap_username = nm_setting_wireless_security_get_leap_username (s_wsec); - - /* Ad-Hoc checks */ - if (!verify_adhoc (s_wsec, s_8021x, adhoc, error)) - return FALSE; - - /* Static WEP, Dynamic WEP, or LEAP */ - if ( (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) - && (ap_wpa_flags == NM_802_11_AP_SEC_NONE) - && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) { - const char *tag = "WEP"; - gboolean is_dynamic_wep = FALSE; - - if (!verify_leap (s_wsec, s_8021x, adhoc, error)) - return FALSE; - - if (leap_username) { - tag = "LEAP"; - } else { - /* Static or Dynamic WEP */ - if (!verify_dynamic_wep (s_wsec, s_8021x, adhoc, error)) - return FALSE; - - if (s_8021x || (key_mgmt && !strcmp (key_mgmt, "ieee8021x"))) { - is_dynamic_wep = TRUE; - tag = "Dynamic WEP"; - } - } - - /* Nothing WPA-related can be set */ - if (!verify_no_wpa (s_wsec, tag, error)) - return FALSE; - - if (leap_username) { - /* LEAP */ - g_object_set (s_wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", - NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", - NULL); - } else if (is_dynamic_wep) { - /* Dynamic WEP */ - g_object_set (s_wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", - NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", - NULL); - - if (s_8021x) { - /* Dynamic WEP requires a valid 802.1x setting since we can't - * autocomplete 802.1x. - */ - if (!nm_setting_verify (NM_SETTING (s_8021x), NULL, error)) - return FALSE; - } - } else { - /* Static WEP */ - g_object_set (s_wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", - NULL); - } - - return TRUE; - } - - /* WPA/RSN */ - g_assert (ap_wpa_flags || ap_rsn_flags); - - /* Ensure key management is valid for WPA */ - if ((key_mgmt && !strcmp (key_mgmt, "ieee8021x")) || leap_username) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA incompatible with non-EAP (original) LEAP or Dynamic WEP"); - return FALSE; - } - - /* 'shared' auth incompatible with any type of WPA */ - if (auth_alg && strcmp (auth_alg, "open")) { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "WPA incompatible with Shared Key authentication"); - return FALSE; - } - - if (!verify_no_wep (s_wsec, "WPA", error)) - return FALSE; - - if (!verify_wpa_psk (s_wsec, s_8021x, adhoc, ap_wpa_flags, ap_rsn_flags, error)) - return FALSE; - - if (!adhoc && !verify_wpa_eap (s_wsec, s_8021x, ap_wpa_flags, ap_rsn_flags, error)) - return FALSE; - - if (adhoc) { - g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL); - /* Ad-Hoc does not support RSN/WPA2 */ - nm_setting_wireless_security_add_proto (s_wsec, "wpa"); - nm_setting_wireless_security_add_pairwise (s_wsec, "none"); - nm_setting_wireless_security_add_group (s_wsec, "tkip"); - } else if (s_8021x) { - g_object_set (s_wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", - NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", - NULL); - /* Leave proto/pairwise/group as client set them; if they are unset the - * supplicant will figure out the best combination at connect time. - */ - - /* 802.1x also requires the client to completely fill in the 8021x - * setting. Since there's so much configuration required for it, there's - * no way it can be automatically completed. - */ - } else if ( (key_mgmt && !strcmp (key_mgmt, "wpa-psk")) - || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) - || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { - g_object_set (s_wsec, - NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", - NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", - NULL); - /* Leave proto/pairwise/group as client set them; if they are unset the - * supplicant will figure out the best combination at connect time. - */ - } else { - g_set_error_literal (error, - NM_SETTING_WIRELESS_SECURITY_ERROR, - NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY, - "Failed to determine AP security information"); - return FALSE; - } - - return TRUE; -} - -guint32 -nm_ap_utils_level_to_quality (gint val) -{ - if (val < 0) { - /* Assume dBm already; rough conversion: best = -40, worst = -100 */ - val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */ - val = 100 - (int) ((100.0 * (double) val) / 60.0); - } else if (val > 110 && val < 256) { - /* assume old-style WEXT 8-bit unsigned signal level */ - val -= 256; /* subtract 256 to convert to dBm */ - val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */ - val = 100 - (int) ((100.0 * (double) val) / 60.0); - } else { - /* Assume signal is a "quality" percentage */ - val = CLAMP (val, 0, 100); - } - g_assert (val >= 0); - - return (guint32) val; -} - diff --git a/src/nm-wifi-ap-utils.h b/src/nm-wifi-ap-utils.h deleted file mode 100644 index 992b839d52..0000000000 --- a/src/nm-wifi-ap-utils.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - * (C) Copyright 2011 Red Hat, Inc. - */ - -#ifndef NM_WIFI_AP_UTILS_H -#define NM_WIFI_AP_UTILS_H - -#include - -#include -#include -#include -#include -#include - -gboolean nm_ap_utils_complete_connection (const GByteArray *ssid, - const guint8 bssid[ETH_ALEN], - NM80211Mode mode, - guint32 flags, - guint32 wpa_flags, - guint32 rsn_flags, - NMConnection *connection, - gboolean lock_bssid, - GError **error); - -guint32 nm_ap_utils_level_to_quality (gint val); - -#endif /* NM_WIFI_AP_UTILS_H */ - diff --git a/src/nm-wifi-ap.c b/src/nm-wifi-ap.c deleted file mode 100644 index 363be2e326..0000000000 --- a/src/nm-wifi-ap.c +++ /dev/null @@ -1,1293 +0,0 @@ -/* -*- 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) 2004 - 2011 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#include -#include -#include - -#include "nm-wifi-ap.h" -#include "nm-wifi-ap-utils.h" -#include "NetworkManagerUtils.h" -#include "nm-utils.h" -#include "nm-logging.h" -#include "nm-dbus-manager.h" - -#include "nm-setting-wireless.h" -#include "nm-glib-compat.h" - -#include "nm-access-point-glue.h" - -/* - * Encapsulates Access Point information - */ -typedef struct -{ - char *dbus_path; - char *supplicant_path; /* D-Bus object path of this AP from wpa_supplicant */ - - /* Scanned or cached values */ - GByteArray * ssid; - struct ether_addr address; - NM80211Mode mode; - gint8 strength; - guint32 freq; /* Frequency in MHz; ie 2412 (== 2.412 GHz) */ - guint32 max_bitrate;/* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */ - - NM80211ApFlags flags; /* General flags */ - NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */ - NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */ - - /* Non-scanned attributes */ - gboolean fake; /* Whether or not the AP is from a scan */ - gboolean hotspot; /* Whether the AP is a local device's hotspot network */ - gboolean broadcast; /* Whether or not the AP is broadcasting (hidden) */ - gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ -} NMAccessPointPrivate; - -#define NM_AP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AP, NMAccessPointPrivate)) - -G_DEFINE_TYPE (NMAccessPoint, nm_ap, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_FLAGS, - PROP_WPA_FLAGS, - PROP_RSN_FLAGS, - PROP_SSID, - PROP_FREQUENCY, - PROP_HW_ADDRESS, - PROP_MODE, - PROP_MAX_BITRATE, - PROP_STRENGTH, - LAST_PROP -}; - -static void -nm_ap_init (NMAccessPoint *ap) -{ - NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap); - - priv->dbus_path = NULL; - priv->mode = NM_802_11_MODE_INFRA; - priv->flags = NM_802_11_AP_FLAGS_NONE; - priv->wpa_flags = NM_802_11_AP_SEC_NONE; - priv->rsn_flags = NM_802_11_AP_SEC_NONE; - priv->broadcast = TRUE; -} - -static void -finalize (GObject *object) -{ - NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object); - - g_free (priv->dbus_path); - g_free (priv->supplicant_path); - if (priv->ssid) - g_byte_array_free (priv->ssid, TRUE); - - G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object); -} - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMAccessPoint *ap = NM_AP (object); - - switch (prop_id) { - case PROP_FLAGS: - nm_ap_set_flags (ap, g_value_get_uint (value)); - break; - case PROP_WPA_FLAGS: - nm_ap_set_wpa_flags (ap, g_value_get_uint (value)); - break; - case PROP_RSN_FLAGS: - nm_ap_set_rsn_flags (ap, g_value_get_uint (value)); - break; - case PROP_SSID: - nm_ap_set_ssid (ap, (GByteArray *) g_value_get_boxed (value)); - break; - case PROP_FREQUENCY: - nm_ap_set_freq (ap, g_value_get_uint (value)); - break; - case PROP_MODE: - nm_ap_set_mode (ap, g_value_get_uint (value)); - break; - case PROP_MAX_BITRATE: - nm_ap_set_max_bitrate (ap, g_value_get_uint (value)); - break; - case PROP_STRENGTH: - nm_ap_set_strength (ap, g_value_get_schar (value)); - break; - case PROP_HW_ADDRESS: - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object); - GArray * ssid; - int len; - int i; - - switch (prop_id) { - case PROP_FLAGS: - g_value_set_uint (value, priv->flags); - break; - case PROP_WPA_FLAGS: - g_value_set_uint (value, priv->wpa_flags); - break; - case PROP_RSN_FLAGS: - g_value_set_uint (value, priv->rsn_flags); - break; - case PROP_SSID: - len = priv->ssid ? priv->ssid->len : 0; - ssid = g_array_sized_new (FALSE, TRUE, sizeof (unsigned char), len); - for (i = 0; i < len; i++) - g_array_append_val (ssid, priv->ssid->data[i]); - g_value_set_boxed (value, ssid); - g_array_free (ssid, TRUE); - break; - case PROP_FREQUENCY: - g_value_set_uint (value, priv->freq); - break; - case PROP_HW_ADDRESS: - g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->address, ARPHRD_ETHER)); - break; - case PROP_MODE: - g_value_set_uint (value, priv->mode); - break; - case PROP_MAX_BITRATE: - g_value_set_uint (value, priv->max_bitrate); - break; - case PROP_STRENGTH: - g_value_set_schar (value, priv->strength); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -nm_ap_class_init (NMAccessPointClass *ap_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (ap_class); - const NM80211ApSecurityFlags all_sec_flags = NM_802_11_AP_SEC_NONE - | NM_802_11_AP_SEC_PAIR_WEP40 - | NM_802_11_AP_SEC_PAIR_WEP104 - | NM_802_11_AP_SEC_PAIR_TKIP - | NM_802_11_AP_SEC_PAIR_CCMP - | NM_802_11_AP_SEC_GROUP_WEP40 - | NM_802_11_AP_SEC_GROUP_WEP104 - | NM_802_11_AP_SEC_GROUP_TKIP - | NM_802_11_AP_SEC_GROUP_CCMP - | NM_802_11_AP_SEC_KEY_MGMT_PSK - | NM_802_11_AP_SEC_KEY_MGMT_802_1X; - - g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate)); - - /* virtual methods */ - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->finalize = finalize; - - /* properties */ - g_object_class_install_property - (object_class, PROP_FLAGS, - g_param_spec_uint (NM_AP_FLAGS, - "Flags", - "Flags", - NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_FLAGS_NONE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_WPA_FLAGS, - g_param_spec_uint (NM_AP_WPA_FLAGS, - "WPA Flags", - "WPA Flags", - NM_802_11_AP_SEC_NONE, - all_sec_flags, - NM_802_11_AP_SEC_NONE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_RSN_FLAGS, - g_param_spec_uint (NM_AP_RSN_FLAGS, - "RSN Flags", - "RSN Flags", - NM_802_11_AP_SEC_NONE, - all_sec_flags, - NM_802_11_AP_SEC_NONE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_SSID, - g_param_spec_boxed (NM_AP_SSID, - "SSID", - "SSID", - DBUS_TYPE_G_UCHAR_ARRAY, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_FREQUENCY, - g_param_spec_uint (NM_AP_FREQUENCY, - "Frequency", - "Frequency", - 0, 10000, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_HW_ADDRESS, - g_param_spec_string (NM_AP_HW_ADDRESS, - "MAC Address", - "Hardware MAC address", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_MODE, - g_param_spec_uint (NM_AP_MODE, - "Mode", - "Mode", - NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_MAX_BITRATE, - g_param_spec_uint (NM_AP_MAX_BITRATE, - "Max Bitrate", - "Max Bitrate", - 0, G_MAXUINT16, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property - (object_class, PROP_STRENGTH, - g_param_spec_char (NM_AP_STRENGTH, - "Strength", - "Strength", - G_MININT8, G_MAXINT8, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), - G_TYPE_FROM_CLASS (ap_class), - &dbus_glib_nm_access_point_object_info); -} - -void -nm_ap_export_to_dbus (NMAccessPoint *ap) -{ - NMAccessPointPrivate *priv; - static guint32 counter = 0; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - - if (priv->dbus_path) { - nm_log_err (LOGD_CORE, "Tried to export AP %s twice.", priv->dbus_path); - return; - } - - priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", counter++); - nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->dbus_path, ap); -} - -/* - * nm_ap_new - * - * Create a new, blank user access point info structure - * - */ -static NMAccessPoint * -nm_ap_new (void) -{ - return (NMAccessPoint *) g_object_new (NM_TYPE_AP, NULL); -} - -static NM80211ApSecurityFlags -pair_to_flags (const char *str) -{ - g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE); - - if (strcmp (str, "tkip") == 0) - return NM_802_11_AP_SEC_PAIR_TKIP; - if (strcmp (str, "ccmp") == 0) - return NM_802_11_AP_SEC_PAIR_CCMP; - return NM_802_11_AP_SEC_NONE; -} - -static NM80211ApSecurityFlags -group_to_flags (const char *str) -{ - g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE); - - if (strcmp (str, "wep40") == 0) - return NM_802_11_AP_SEC_GROUP_WEP40; - if (strcmp (str, "wep104") == 0) - return NM_802_11_AP_SEC_GROUP_WEP104; - if (strcmp (str, "tkip") == 0) - return NM_802_11_AP_SEC_GROUP_TKIP; - if (strcmp (str, "ccmp") == 0) - return NM_802_11_AP_SEC_GROUP_CCMP; - return NM_802_11_AP_SEC_NONE; -} - -static NM80211ApSecurityFlags -security_from_dict (GHashTable *security) -{ - GValue *value; - NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; - const char **items, **iter; - - value = g_hash_table_lookup (security, "KeyMgmt"); - if (value) { - items = g_value_get_boxed (value); - for (iter = items; iter && *iter; iter++) { - if (strcmp (*iter, "wpa-psk") == 0) - flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (strcmp (*iter, "wpa-eap") == 0) - flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; - } - } - - value = g_hash_table_lookup (security, "Pairwise"); - if (value) { - items = g_value_get_boxed (value); - for (iter = items; iter && *iter; iter++) - flags |= pair_to_flags (*iter); - } - - value = g_hash_table_lookup (security, "Group"); - if (value) - flags |= group_to_flags (g_value_get_string (value)); - - return flags; -} - -static void -foreach_property_cb (gpointer key, gpointer value, gpointer user_data) -{ - GValue *variant = (GValue *) value; - NMAccessPoint *ap = (NMAccessPoint *) user_data; - - if (G_VALUE_HOLDS_BOXED (variant)) { - GArray *array = g_value_get_boxed (variant); - - if (!strcmp (key, "SSID")) { - guint32 len = MIN (32, array->len); - GByteArray *ssid; - - /* Stupid ieee80211 layer uses */ - if (((len == 8) || (len == 9)) - && (memcmp (array->data, "", 8) == 0)) - return; - - if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len)) - return; - - ssid = g_byte_array_sized_new (len); - g_byte_array_append (ssid, (const guint8 *) array->data, len); - nm_ap_set_ssid (ap, ssid); - g_byte_array_free (ssid, TRUE); - } else if (!strcmp (key, "BSSID")) { - struct ether_addr addr; - - if (array->len != ETH_ALEN) - return; - memset (&addr, 0, sizeof (struct ether_addr)); - memcpy (&addr, array->data, ETH_ALEN); - nm_ap_set_address (ap, &addr); - } else if (!strcmp (key, "Rates")) { - guint32 maxrate = 0; - int i; - - /* Find the max AP rate */ - for (i = 0; i < array->len; i++) { - guint32 r = g_array_index (array, guint32, i); - - if (r > maxrate) { - maxrate = r; - nm_ap_set_max_bitrate (ap, r / 1000); - } - } - } else if (!strcmp (key, "WPA")) { - NM80211ApSecurityFlags flags = nm_ap_get_wpa_flags (ap); - - flags |= security_from_dict (g_value_get_boxed (variant)); - nm_ap_set_wpa_flags (ap, flags); - } else if (!strcmp (key, "RSN")) { - NM80211ApSecurityFlags flags = nm_ap_get_rsn_flags (ap); - - flags |= security_from_dict (g_value_get_boxed (variant)); - nm_ap_set_rsn_flags (ap, flags); - } - } else if (G_VALUE_HOLDS_UINT (variant)) { - guint32 val = g_value_get_uint (variant); - - if (!strcmp (key, "Frequency")) - nm_ap_set_freq (ap, val); - } else if (G_VALUE_HOLDS_INT (variant)) { - gint val = g_value_get_int (variant); - - if (!strcmp (key, "Signal")) - nm_ap_set_strength (ap, nm_ap_utils_level_to_quality (val)); - } else if (G_VALUE_HOLDS_STRING (variant)) { - const char *val = g_value_get_string (variant); - - if (val && !strcmp (key, "Mode")) { - if (strcmp (val, "infrastructure") == 0) - nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); - else if (strcmp (val, "ad-hoc") == 0) - nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); - } - } else if (G_VALUE_HOLDS_BOOLEAN (variant)) { - gboolean val = g_value_get_boolean (variant); - - if (strcmp (key, "Privacy") == 0) { - if (val) { - NM80211ApFlags flags = nm_ap_get_flags (ap); - nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY); - } - } - } -} - -NMAccessPoint * -nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties) -{ - NMAccessPoint *ap; - const struct ether_addr * addr; - const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - - g_return_val_if_fail (properties != NULL, NULL); - - ap = nm_ap_new (); - - g_object_freeze_notify (G_OBJECT (ap)); - g_hash_table_foreach (properties, foreach_property_cb, ap); - - nm_ap_set_supplicant_path (ap, supplicant_path); - - /* ignore APs with invalid BSSIDs */ - addr = nm_ap_get_address (ap); - if ( !(memcmp (addr->ether_addr_octet, bad_bssid1, ETH_ALEN)) - || !(memcmp (addr->ether_addr_octet, bad_bssid2, ETH_ALEN))) { - g_object_unref (ap); - return NULL; - } - - nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ()); - - if (!nm_ap_get_ssid (ap)) - nm_ap_set_broadcast (ap, FALSE); - - g_object_thaw_notify (G_OBJECT (ap)); - - return ap; -} - -#define PROTO_WPA "wpa" -#define PROTO_RSN "rsn" - -static gboolean -has_proto (NMSettingWirelessSecurity *sec, const char *proto) -{ - guint32 num_protos = nm_setting_wireless_security_get_num_protos (sec); - guint32 i; - - if (num_protos == 0) - return TRUE; /* interpret no protos as "all" */ - - for (i = 0; i < num_protos; i++) { - if (!strcmp (nm_setting_wireless_security_get_proto (sec, i), proto)) - return TRUE; - } - return FALSE; -} - -static void -add_pair_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec) -{ - guint32 num = nm_setting_wireless_security_get_num_pairwise (sec); - NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; - guint32 i; - - /* If no ciphers are specified, that means "all" WPA ciphers */ - if (num == 0) { - flags |= NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP; - } else { - for (i = 0; i < num; i++) { - const char *cipher = nm_setting_wireless_security_get_pairwise (sec, i); - - if (!strcmp (cipher, "tkip")) - flags |= NM_802_11_AP_SEC_PAIR_TKIP; - else if (!strcmp (cipher, "ccmp")) - flags |= NM_802_11_AP_SEC_PAIR_CCMP; - } - } - - if (has_proto (sec, PROTO_WPA)) - nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags); - if (has_proto (sec, PROTO_RSN)) - nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags); -} - -static void -add_group_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec) -{ - guint32 num = nm_setting_wireless_security_get_num_groups (sec); - NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE; - guint32 i; - - /* If no ciphers are specified, that means "all" WPA ciphers */ - if (num == 0) { - flags |= NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_GROUP_CCMP; - } else { - for (i = 0; i < num; i++) { - const char *cipher = nm_setting_wireless_security_get_group (sec, i); - - if (!strcmp (cipher, "wep40")) - flags |= NM_802_11_AP_SEC_GROUP_WEP40; - else if (!strcmp (cipher, "wep104")) - flags |= NM_802_11_AP_SEC_GROUP_WEP104; - else if (!strcmp (cipher, "tkip")) - flags |= NM_802_11_AP_SEC_GROUP_TKIP; - else if (!strcmp (cipher, "ccmp")) - flags |= NM_802_11_AP_SEC_GROUP_CCMP; - } - } - - if (has_proto (sec, PROTO_WPA)) - nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags); - if (has_proto (sec, PROTO_RSN)) - nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags); -} - -NMAccessPoint * -nm_ap_new_fake_from_connection (NMConnection *connection) -{ - NMAccessPoint *ap; - NMSettingWireless *s_wireless; - NMSettingWirelessSecurity *s_wireless_sec; - const GByteArray *ssid; - const char *mode, *band, *key_mgmt; - guint32 channel; - NM80211ApSecurityFlags flags; - gboolean psk = FALSE, eap = FALSE; - - g_return_val_if_fail (connection != NULL, NULL); - - s_wireless = nm_connection_get_setting_wireless (connection); - g_return_val_if_fail (s_wireless != NULL, NULL); - - ssid = nm_setting_wireless_get_ssid (s_wireless); - g_return_val_if_fail (ssid != NULL, NULL); - g_return_val_if_fail (ssid->len > 0, NULL); - - ap = nm_ap_new (); - nm_ap_set_fake (ap, TRUE); - nm_ap_set_ssid (ap, ssid); - - // FIXME: bssid too? - - mode = nm_setting_wireless_get_mode (s_wireless); - if (mode) { - if (!strcmp (mode, "infrastructure")) - nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); - else if (!strcmp (mode, "adhoc")) - nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC); - else if (!strcmp (mode, "ap")) { - nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); - NM_AP_GET_PRIVATE (ap)->hotspot = TRUE; - } else - goto error; - } else { - nm_ap_set_mode (ap, NM_802_11_MODE_INFRA); - } - - band = nm_setting_wireless_get_band (s_wireless); - channel = nm_setting_wireless_get_channel (s_wireless); - - if (band && channel) { - guint32 freq = nm_utils_wifi_channel_to_freq (channel, band); - - if (freq == 0) - goto error; - - nm_ap_set_freq (ap, freq); - } - - s_wireless_sec = nm_connection_get_setting_wireless_security (connection); - /* Assume presence of a security setting means the AP is encrypted */ - if (!s_wireless_sec) - goto done; - - key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec); - - /* Everything below here uses encryption */ - nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY); - - /* Static & Dynamic WEP */ - if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x")) - goto done; - - psk = !strcmp (key_mgmt, "wpa-psk"); - eap = !strcmp (key_mgmt, "wpa-eap"); - if (psk || eap) { - if (has_proto (s_wireless_sec, PROTO_WPA)) { - flags = nm_ap_get_wpa_flags (ap); - flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK; - nm_ap_set_wpa_flags (ap, flags); - } - if (has_proto (s_wireless_sec, PROTO_RSN)) { - flags = nm_ap_get_rsn_flags (ap); - flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK; - nm_ap_set_rsn_flags (ap, flags); - } - - add_pair_ciphers (ap, s_wireless_sec); - add_group_ciphers (ap, s_wireless_sec); - } else if (!strcmp (key_mgmt, "wpa-none")) { - guint32 i; - - /* Ad-Hoc has special requirements: proto=WPA, pairwise=(none), and - * group=TKIP/CCMP (but not both). - */ - - flags = nm_ap_get_wpa_flags (ap); - flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK; - - /* Clear ciphers; pairwise must be unset anyway, and group gets set below */ - flags &= ~( NM_802_11_AP_SEC_PAIR_WEP40 - | NM_802_11_AP_SEC_PAIR_WEP104 - | NM_802_11_AP_SEC_PAIR_TKIP - | NM_802_11_AP_SEC_PAIR_CCMP - | NM_802_11_AP_SEC_GROUP_WEP40 - | NM_802_11_AP_SEC_GROUP_WEP104 - | NM_802_11_AP_SEC_GROUP_TKIP - | NM_802_11_AP_SEC_GROUP_CCMP); - - for (i = 0; i < nm_setting_wireless_security_get_num_groups (s_wireless_sec); i++) { - if (!strcmp (nm_setting_wireless_security_get_group (s_wireless_sec, i), "ccmp")) { - flags |= NM_802_11_AP_SEC_GROUP_CCMP; - break; - } - } - - /* Default to TKIP since not all WPA-capable cards can do CCMP */ - if (!(flags & NM_802_11_AP_SEC_GROUP_CCMP)) - flags |= NM_802_11_AP_SEC_GROUP_TKIP; - - nm_ap_set_wpa_flags (ap, flags); - - /* Don't use Ad-Hoc RSN yet */ - nm_ap_set_rsn_flags (ap, NM_802_11_AP_SEC_NONE); - } - -done: - return ap; - -error: - g_object_unref (ap); - return NULL; -} - - -#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" -#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5] - -void -nm_ap_dump (NMAccessPoint *ap, const char *prefix) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - - nm_log_dbg (LOGD_WIFI_SCAN, "%s'%s' (%p)", - prefix, - priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)", - ap); - nm_log_dbg (LOGD_WIFI_SCAN, " BSSID " MAC_FMT, MAC_ARG (priv->address.ether_addr_octet)); - nm_log_dbg (LOGD_WIFI_SCAN, " mode %d", priv->mode); - nm_log_dbg (LOGD_WIFI_SCAN, " flags 0x%X", priv->flags); - nm_log_dbg (LOGD_WIFI_SCAN, " wpa flags 0x%X", priv->wpa_flags); - nm_log_dbg (LOGD_WIFI_SCAN, " rsn flags 0x%X", priv->rsn_flags); - nm_log_dbg (LOGD_WIFI_SCAN, " quality %d", priv->strength); - nm_log_dbg (LOGD_WIFI_SCAN, " frequency %d", priv->freq); - nm_log_dbg (LOGD_WIFI_SCAN, " max rate %d", priv->max_bitrate); - nm_log_dbg (LOGD_WIFI_SCAN, " last-seen %d", (int) priv->last_seen); -} - -const char * -nm_ap_get_dbus_path (NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), NULL); - - return NM_AP_GET_PRIVATE (ap)->dbus_path; -} - -const char * -nm_ap_get_supplicant_path (NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), NULL); - - return NM_AP_GET_PRIVATE (ap)->supplicant_path; -} - -void -nm_ap_set_supplicant_path (NMAccessPoint *ap, const char *path) -{ - g_return_if_fail (NM_IS_AP (ap)); - g_return_if_fail (path != NULL); - - g_free (NM_AP_GET_PRIVATE (ap)->supplicant_path); - NM_AP_GET_PRIVATE (ap)->supplicant_path = g_strdup (path); -} - -/* - * Get/set functions for ssid - * - */ -const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), NULL); - - return NM_AP_GET_PRIVATE (ap)->ssid; -} - -void -nm_ap_set_ssid (NMAccessPoint *ap, const GByteArray * ssid) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - - if (ssid == priv->ssid) - return; - - /* same SSID */ - if ((ssid && priv->ssid) && (ssid->len == priv->ssid->len)) { - if (!memcmp (ssid->data, priv->ssid->data, ssid->len)) - return; - } - - if (priv->ssid) { - g_byte_array_free (priv->ssid, TRUE); - priv->ssid = NULL; - } - - if (ssid) { - /* Should never get zero-length SSIDs */ - g_warn_if_fail (ssid->len > 0); - - if (ssid->len) { - priv->ssid = g_byte_array_sized_new (ssid->len); - priv->ssid->len = ssid->len; - memcpy (priv->ssid->data, ssid->data, ssid->len); - } - } - - g_object_notify (G_OBJECT (ap), NM_AP_SSID); -} - - -NM80211ApFlags -nm_ap_get_flags (NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); - - return NM_AP_GET_PRIVATE (ap)->flags; -} - - -void -nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - - if (priv->flags != flags) { - priv->flags = flags; - g_object_notify (G_OBJECT (ap), NM_AP_FLAGS); - } -} - -NM80211ApSecurityFlags -nm_ap_get_wpa_flags (NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); - - return NM_AP_GET_PRIVATE (ap)->wpa_flags; -} - - -void -nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - if (priv->wpa_flags != flags) { - priv->wpa_flags = flags; - g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS); - } -} - -NM80211ApSecurityFlags -nm_ap_get_rsn_flags (NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE); - - return NM_AP_GET_PRIVATE (ap)->rsn_flags; -} - - -void -nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - if (priv->rsn_flags != flags) { - priv->rsn_flags = flags; - g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS); - } -} - -/* - * Get/set functions for address - * - */ -const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), NULL); - - return &NM_AP_GET_PRIVATE (ap)->address; -} - -void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr * addr) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - g_return_if_fail (addr != NULL); - - priv = NM_AP_GET_PRIVATE (ap); - - if (memcmp (addr, &priv->address, sizeof (priv->address))) { - memcpy (&NM_AP_GET_PRIVATE (ap)->address, addr, sizeof (struct ether_addr)); - g_object_notify (G_OBJECT (ap), NM_AP_HW_ADDRESS); - } -} - - -/* - * Get/set functions for mode (ie Ad-Hoc, Infrastructure, etc) - * - */ -NM80211Mode nm_ap_get_mode (NMAccessPoint *ap) -{ - NM80211Mode mode; - - g_return_val_if_fail (NM_IS_AP (ap), -1); - - g_object_get (ap, NM_AP_MODE, &mode, NULL); - - return mode; -} - -void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - g_return_if_fail ( mode == NM_802_11_MODE_ADHOC - || mode == NM_802_11_MODE_INFRA); - - priv = NM_AP_GET_PRIVATE (ap); - - if (priv->mode != mode) { - priv->mode = mode; - g_object_notify (G_OBJECT (ap), NM_AP_MODE); - } -} - -gboolean -nm_ap_is_hotspot (NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), FALSE); - - return NM_AP_GET_PRIVATE (ap)->hotspot; -} - -/* - * Get/set functions for strength - * - */ -gint8 nm_ap_get_strength (NMAccessPoint *ap) -{ - gint8 strength; - - g_return_val_if_fail (NM_IS_AP (ap), 0); - - g_object_get (ap, NM_AP_STRENGTH, &strength, NULL); - - return strength; -} - -void nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - - if (priv->strength != strength) { - priv->strength = strength; - g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH); - } -} - - -/* - * Get/set functions for frequency - * - */ -guint32 -nm_ap_get_freq (NMAccessPoint *ap) -{ - guint32 freq; - - g_return_val_if_fail (NM_IS_AP (ap), 0); - - g_object_get (ap, NM_AP_FREQUENCY, &freq, NULL); - - return freq; -} - -void -nm_ap_set_freq (NMAccessPoint *ap, - const guint32 freq) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - - if (priv->freq != freq) { - priv->freq = freq; - g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY); - } -} - - -/* - * Get/set functions for max bitrate (in kbit/s) - * - */ -guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap) -{ - guint32 rate; - - g_return_val_if_fail (NM_IS_AP (ap), 0); - - g_object_get (ap, NM_AP_MAX_BITRATE, &rate, NULL); - - return rate; -} - -void -nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate) -{ - NMAccessPointPrivate *priv; - - g_return_if_fail (NM_IS_AP (ap)); - - priv = NM_AP_GET_PRIVATE (ap); - - if (priv->max_bitrate != bitrate) { - priv->max_bitrate = bitrate; - g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE); - } -} - -/* - * Get/Set functions to indicate that an access point is 'fake', ie whether - * or not it was created from scan results - */ -gboolean nm_ap_get_fake (const NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), FALSE); - - return NM_AP_GET_PRIVATE (ap)->fake; -} - -void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake) -{ - g_return_if_fail (NM_IS_AP (ap)); - - NM_AP_GET_PRIVATE (ap)->fake = fake; -} - - -/* - * Get/Set functions to indicate whether an AP broadcasts its SSID. - */ -gboolean nm_ap_get_broadcast (NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), TRUE); - - return NM_AP_GET_PRIVATE (ap)->broadcast; -} - - -void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast) -{ - g_return_if_fail (NM_IS_AP (ap)); - - NM_AP_GET_PRIVATE (ap)->broadcast = broadcast; -} - - -/* - * Get/Set functions for how long ago the AP was last seen in a scan. - * APs older than a certain date are dropped from the list. - * - */ -gint32 -nm_ap_get_last_seen (const NMAccessPoint *ap) -{ - g_return_val_if_fail (NM_IS_AP (ap), FALSE); - - return NM_AP_GET_PRIVATE (ap)->last_seen; -} - -void -nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen) -{ - g_return_if_fail (NM_IS_AP (ap)); - - NM_AP_GET_PRIVATE (ap)->last_seen = last_seen; -} - -gboolean -nm_ap_check_compatible (NMAccessPoint *self, - NMConnection *connection) -{ - NMAccessPointPrivate *priv; - NMSettingWireless *s_wireless; - NMSettingWirelessSecurity *s_wireless_sec; - const char *mode; - const char *band; - const GByteArray *bssid; - guint32 channel; - - g_return_val_if_fail (NM_IS_AP (self), FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); - - priv = NM_AP_GET_PRIVATE (self); - - s_wireless = nm_connection_get_setting_wireless (connection); - if (s_wireless == NULL) - return FALSE; - - if (!nm_utils_same_ssid (nm_setting_wireless_get_ssid (s_wireless), priv->ssid, TRUE)) - return FALSE; - - bssid = nm_setting_wireless_get_bssid (s_wireless); - if (bssid && memcmp (bssid->data, &priv->address, ETH_ALEN)) - return FALSE; - - mode = nm_setting_wireless_get_mode (s_wireless); - if (mode) { - if (!strcmp (mode, "infrastructure") && (priv->mode != NM_802_11_MODE_INFRA)) - return FALSE; - if (!strcmp (mode, "adhoc") && (priv->mode != NM_802_11_MODE_ADHOC)) - return FALSE; - if ( !strcmp (mode, "ap") - && (priv->mode != NM_802_11_MODE_INFRA || priv->hotspot != TRUE)) - return FALSE; - } - - band = nm_setting_wireless_get_band (s_wireless); - if (band) { - if (!strcmp (band, "a")) { - if (priv->freq < 4915 || priv->freq > 5825) - return FALSE; - } else if (!strcmp (band, "bg")) { - if (priv->freq < 2412 || priv->freq > 2484) - return FALSE; - } - } - - channel = nm_setting_wireless_get_channel (s_wireless); - if (channel) { - guint32 ap_chan = nm_utils_wifi_freq_to_channel (priv->freq); - - if (channel != ap_chan) - return FALSE; - } - - s_wireless_sec = nm_connection_get_setting_wireless_security (connection); - - return nm_setting_wireless_ap_security_compatible (s_wireless, - s_wireless_sec, - nm_ap_get_flags (self), - nm_ap_get_wpa_flags (self), - nm_ap_get_rsn_flags (self), - nm_ap_get_mode (self)); -} - -gboolean -nm_ap_complete_connection (NMAccessPoint *self, - NMConnection *connection, - gboolean lock_bssid, - GError **error) -{ - NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (self); - - g_return_val_if_fail (connection != NULL, FALSE); - - return nm_ap_utils_complete_connection (priv->ssid, - priv->address.ether_addr_octet, - priv->mode, - priv->flags, - priv->wpa_flags, - priv->rsn_flags, - connection, - lock_bssid, - error); -} - -static gboolean -capabilities_compatible (NM80211ApSecurityFlags a_flags, NM80211ApSecurityFlags b_flags) -{ - if (a_flags == b_flags) - return TRUE; - - /* Make sure there's a common key management method */ - if (!((a_flags & 0x300) & (b_flags & 0x300))) - return FALSE; - - /* Ensure common pairwise ciphers */ - if (!((a_flags & 0xF) & (b_flags & 0xF))) - return FALSE; - - /* Ensure common group ciphers */ - if (!((a_flags & 0xF0) & (b_flags & 0xF0))) - return FALSE; - - return TRUE; -} - -NMAccessPoint * -nm_ap_match_in_list (NMAccessPoint *find_ap, - GSList *ap_list, - gboolean strict_match) -{ - GSList *iter; - - g_return_val_if_fail (find_ap != NULL, NULL); - - for (iter = ap_list; iter; iter = g_slist_next (iter)) { - NMAccessPoint * list_ap = NM_AP (iter->data); - const GByteArray * list_ssid = nm_ap_get_ssid (list_ap); - const struct ether_addr * list_addr = nm_ap_get_address (list_ap); - - const GByteArray * find_ssid = nm_ap_get_ssid (find_ap); - const struct ether_addr * find_addr = nm_ap_get_address (find_ap); - - /* SSID match; if both APs are hiding their SSIDs, - * let matching continue on BSSID and other properties - */ - if ( (!list_ssid && find_ssid) - || (list_ssid && !find_ssid) - || !nm_utils_same_ssid (list_ssid, find_ssid, TRUE)) - continue; - - /* BSSID match */ - if ( (strict_match || nm_ethernet_address_is_valid (find_addr)) - && nm_ethernet_address_is_valid (list_addr) - && memcmp (list_addr->ether_addr_octet, - find_addr->ether_addr_octet, - ETH_ALEN) != 0) { - continue; - } - - /* mode match */ - if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap)) - continue; - - /* Frequency match */ - if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap)) - continue; - - /* AP flags */ - if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap)) - continue; - - if (strict_match) { - if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap)) - continue; - - if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap)) - continue; - } else { - NM80211ApSecurityFlags list_wpa_flags = nm_ap_get_wpa_flags (list_ap); - NM80211ApSecurityFlags find_wpa_flags = nm_ap_get_wpa_flags (find_ap); - NM80211ApSecurityFlags list_rsn_flags = nm_ap_get_rsn_flags (list_ap); - NM80211ApSecurityFlags find_rsn_flags = nm_ap_get_rsn_flags (find_ap); - - /* Just ensure that there is overlap in the capabilities */ - if ( !capabilities_compatible (list_wpa_flags, find_wpa_flags) - && !capabilities_compatible (list_rsn_flags, find_rsn_flags)) - continue; - } - - return list_ap; - } - - return NULL; -} - diff --git a/src/nm-wifi-ap.h b/src/nm-wifi-ap.h deleted file mode 100644 index f51fa078ee..0000000000 --- a/src/nm-wifi-ap.h +++ /dev/null @@ -1,121 +0,0 @@ -/* -*- 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) 2004 - 2011 Red Hat, Inc. - * Copyright (C) 2006 - 2008 Novell, Inc. - */ - -#ifndef NM_ACCESS_POINT_H -#define NM_ACCESS_POINT_H - -#include -#include -#include "NetworkManager.h" -#include "nm-connection.h" - -#define NM_TYPE_AP (nm_ap_get_type ()) -#define NM_AP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AP, NMAccessPoint)) -#define NM_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AP, NMAccessPointClass)) -#define NM_IS_AP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AP)) -#define NM_IS_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AP)) -#define NM_AP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AP, NMAccessPointClass)) - -#define NM_AP_FLAGS "flags" -#define NM_AP_WPA_FLAGS "wpa-flags" -#define NM_AP_RSN_FLAGS "rsn-flags" -#define NM_AP_SSID "ssid" -#define NM_AP_FREQUENCY "frequency" -#define NM_AP_HW_ADDRESS "hw-address" -#define NM_AP_MODE "mode" -#define NM_AP_MAX_BITRATE "max-bitrate" -#define NM_AP_STRENGTH "strength" - -typedef struct { - GObject parent; -} NMAccessPoint; - -typedef struct { - GObjectClass parent; - -} NMAccessPointClass; - -GType nm_ap_get_type (void); - -NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path, - GHashTable *properties); -NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection); -void nm_ap_export_to_dbus (NMAccessPoint *ap); - -const char * nm_ap_get_dbus_path (NMAccessPoint *ap); - -const char * nm_ap_get_supplicant_path (NMAccessPoint *ap); -void nm_ap_set_supplicant_path (NMAccessPoint *ap, - const char *path); - -const GByteArray * nm_ap_get_ssid (const NMAccessPoint * ap); -void nm_ap_set_ssid (NMAccessPoint * ap, const GByteArray * ssid); - -NM80211ApFlags nm_ap_get_flags (NMAccessPoint *ap); -void nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags); - -NM80211ApSecurityFlags nm_ap_get_wpa_flags (NMAccessPoint *ap); -void nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags); - -NM80211ApSecurityFlags nm_ap_get_rsn_flags (NMAccessPoint *ap); -void nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags); - -const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap); -void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr *addr); - -NM80211Mode nm_ap_get_mode (NMAccessPoint *ap); -void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode); - -gboolean nm_ap_is_hotspot (NMAccessPoint *ap); - -gint8 nm_ap_get_strength (NMAccessPoint *ap); -void nm_ap_set_strength (NMAccessPoint *ap, gint8 strength); - -guint32 nm_ap_get_freq (NMAccessPoint *ap); -void nm_ap_set_freq (NMAccessPoint *ap, guint32 freq); - -guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap); -void nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate); - -gboolean nm_ap_get_fake (const NMAccessPoint *ap); -void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake); - -gboolean nm_ap_get_broadcast (NMAccessPoint *ap); -void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast); - -gint32 nm_ap_get_last_seen (const NMAccessPoint *ap); -void nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen); - -gboolean nm_ap_check_compatible (NMAccessPoint *self, - NMConnection *connection); - -gboolean nm_ap_complete_connection (NMAccessPoint *self, - NMConnection *connection, - gboolean lock_bssid, - GError **error); - -NMAccessPoint * nm_ap_match_in_list (NMAccessPoint *find_ap, - GSList *ap_list, - gboolean strict_match); - -void nm_ap_dump (NMAccessPoint *ap, const char *prefix); - -#endif /* NM_ACCESS_POINT_H */ diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index e23ad68761..ee5be17d7b 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -16,7 +16,6 @@ AM_CPPFLAGS = \ noinst_PROGRAMS = \ test-dhcp-options \ test-general \ - test-wifi-ap-utils \ test-ip4-config \ test-ip6-config \ test-dcb \ @@ -35,14 +34,6 @@ test_dhcp_options_CPPFLAGS = \ test_dhcp_options_LDADD = \ $(top_builddir)/src/libNetworkManager.la -####### wifi ap utils test ####### - -test_wifi_ap_utils_SOURCES = \ - test-wifi-ap-utils.c - -test_wifi_ap_utils_LDADD = \ - $(top_builddir)/src/libNetworkManager.la - ####### ip4 config test ####### test_ip4_config_SOURCES = \ @@ -89,5 +80,5 @@ EXTRA_DIST = test-secret-agent.py ########################################### -TESTS = test-dhcp-options test-wifi-ap-utils test-ip4-config test-ip6-config test-dcb test-resolvconf-capture test-general +TESTS = test-dhcp-options test-ip4-config test-ip6-config test-dcb test-resolvconf-capture test-general diff --git a/src/tests/test-wifi-ap-utils.c b/src/tests/test-wifi-ap-utils.c deleted file mode 100644 index b828aa5f8f..0000000000 --- a/src/tests/test-wifi-ap-utils.c +++ /dev/null @@ -1,1542 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * 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, 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) 2011 Red Hat, Inc. - * - */ - -#include -#include - -#include "nm-wifi-ap-utils.h" -#include "nm-dbus-glib-types.h" - -#include "nm-setting-connection.h" -#include "nm-setting-wireless.h" -#include "nm-setting-wireless-security.h" -#include "nm-setting-8021x.h" - -#define DEBUG 1 - -/*******************************************/ - -#define COMPARE(src, expected, success, error, edomain, ecode) \ -{ \ - if (expected) { \ - if (!success) { \ - g_assert (error != NULL); \ - g_warning ("Failed to complete connection: (%d) %s", error->code, error->message); \ - } \ - g_assert (success == TRUE); \ - g_assert (error == NULL); \ -\ - success = nm_connection_compare (src, expected, NM_SETTING_COMPARE_FLAG_EXACT); \ - if (success == FALSE && DEBUG) { \ - g_message ("\n- COMPLETED ---------------------------------\n"); \ - nm_connection_dump (src); \ - g_message ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \ - nm_connection_dump (expected); \ - g_message ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \ - } \ - g_assert (success == TRUE); \ - } else { \ - if (success) { \ - g_message ("\n- COMPLETED ---------------------------------\n"); \ - nm_connection_dump (src); \ - } \ - g_assert (success == FALSE); \ - g_assert_error (error, edomain, ecode); \ - } \ - \ - g_clear_error (&error); \ -} - -static gboolean -complete_connection (const char *ssid, - const guint8 bssid[ETH_ALEN], - NM80211Mode mode, - guint32 flags, - guint32 wpa_flags, - guint32 rsn_flags, - gboolean lock_bssid, - NMConnection *src, - GError **error) -{ - GByteArray *tmp; - gboolean success; - NMSettingWireless *s_wifi; - - /* Add a wifi setting if one doesn't exist */ - s_wifi = nm_connection_get_setting_wireless (src); - if (!s_wifi) { - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (src, NM_SETTING (s_wifi)); - } - - tmp = g_byte_array_sized_new (strlen (ssid)); - g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid)); - - success = nm_ap_utils_complete_connection (tmp, - bssid, - mode, - flags, - wpa_flags, - rsn_flags, - src, - lock_bssid, - error); - g_byte_array_free (tmp, TRUE); - return success; -} - -typedef struct { - const char *key; - const char *str; - guint32 uint; -} KeyData; - -static void -set_items (NMSetting *setting, const KeyData *items) -{ - const KeyData *item; - GParamSpec *pspec; - GByteArray *tmp; - - for (item = items; item && item->key; item++) { - g_assert (item->key); - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), item->key); - g_assert (pspec); - - if (pspec->value_type == G_TYPE_STRING) { - g_assert (item->uint == 0); - if (item->str) - g_object_set (G_OBJECT (setting), item->key, item->str, NULL); - } else if (pspec->value_type == G_TYPE_UINT) { - g_assert (item->str == NULL); - g_object_set (G_OBJECT (setting), item->key, item->uint, NULL); - } else if (pspec->value_type == G_TYPE_INT) { - gint foo = (gint) item->uint; - - g_assert (item->str == NULL); - g_object_set (G_OBJECT (setting), item->key, foo, NULL); - } else if (pspec->value_type == G_TYPE_BOOLEAN) { - gboolean foo = !! (item->uint); - - g_assert (item->str == NULL); - g_object_set (G_OBJECT (setting), item->key, foo, NULL); - } else if (pspec->value_type == DBUS_TYPE_G_UCHAR_ARRAY) { - g_assert (item->str); - tmp = g_byte_array_sized_new (strlen (item->str)); - g_byte_array_append (tmp, (const guint8 *) item->str, strlen (item->str)); - g_object_set (G_OBJECT (setting), item->key, tmp, NULL); - g_byte_array_free (tmp, TRUE); - } else { - /* Special types, check based on property name */ - if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_PROTO)) - nm_setting_wireless_security_add_proto (NM_SETTING_WIRELESS_SECURITY (setting), item->str); - else if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_PAIRWISE)) - nm_setting_wireless_security_add_pairwise (NM_SETTING_WIRELESS_SECURITY (setting), item->str); - else if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_GROUP)) - nm_setting_wireless_security_add_group (NM_SETTING_WIRELESS_SECURITY (setting), item->str); - else if (!strcmp (item->key, NM_SETTING_802_1X_EAP)) - nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), item->str); - } - } -} - -static NMSettingWireless * -fill_wifi_empty (NMConnection *connection) -{ - NMSettingWireless *s_wifi; - - s_wifi = nm_connection_get_setting_wireless (connection); - if (!s_wifi) { - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - } - return s_wifi; -} - -static NMSettingWireless * -fill_wifi (NMConnection *connection, const KeyData items[]) -{ - NMSettingWireless *s_wifi; - - s_wifi = nm_connection_get_setting_wireless (connection); - if (!s_wifi) { - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - } - - set_items (NM_SETTING (s_wifi), items); - return s_wifi; -} - -static NMSettingWirelessSecurity * -fill_wsec (NMConnection *connection, const KeyData items[]) -{ - NMSettingWirelessSecurity *s_wsec; - - s_wsec = nm_connection_get_setting_wireless_security (connection); - if (!s_wsec) { - s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wsec)); - } - - set_items (NM_SETTING (s_wsec), items); - return s_wsec; -} - -static NMSetting8021x * -fill_8021x (NMConnection *connection, const KeyData items[]) -{ - NMSetting8021x *s_8021x; - - s_8021x = nm_connection_get_setting_802_1x (connection); - if (!s_8021x) { - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - nm_connection_add_setting (connection, NM_SETTING (s_8021x)); - } - - set_items (NM_SETTING (s_8021x), items); - return s_8021x; -} - -static NMConnection * -create_basic (const char *ssid, - const guint8 *bssid, - NM80211Mode mode) -{ - NMConnection *connection; - NMSettingWireless *s_wifi = NULL; - GByteArray *tmp; - - connection = nm_connection_new (); - - s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); - nm_connection_add_setting (connection, NM_SETTING (s_wifi)); - - /* SSID */ - tmp = g_byte_array_sized_new (strlen (ssid)); - g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid)); - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, tmp, NULL); - g_byte_array_free (tmp, TRUE); - - /* BSSID */ - if (bssid) { - tmp = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (tmp, bssid, ETH_ALEN); - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, tmp, NULL); - g_byte_array_free (tmp, TRUE); - } - - if (mode == NM_802_11_MODE_INFRA) - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "infrastructure", NULL); - else if (mode == NM_802_11_MODE_ADHOC) - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "adhoc", NULL); - else - g_assert_not_reached (); - - return connection; -} - -/*******************************************/ - -static void -test_lock_bssid (void) -{ - NMConnection *src, *expected; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const char *ssid = "blahblah"; - gboolean success; - GError *error = NULL; - - src = nm_connection_new (); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - TRUE, - src, &error); - expected = create_basic (ssid, bssid, NM_802_11_MODE_INFRA); - COMPARE (src, expected, success, error, 0, 0); - - g_object_unref (src); - g_object_unref (expected); -} - -/*******************************************/ - -static void -test_open_ap_empty_connection (void) -{ - NMConnection *src, *expected; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const char *ssid = "blahblah"; - gboolean success; - GError *error = NULL; - - /* Test that an empty source connection is correctly filled with the - * SSID and Infra modes of the given AP details. - */ - - src = nm_connection_new (); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); - COMPARE (src, expected, success, error, 0, 0); - - g_object_unref (src); - g_object_unref (expected); -} - -/*******************************************/ - -static void -test_open_ap_leap_connection_1 (gconstpointer add_wifi) -{ - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "Bill Smith", 0 }, { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that a basic connection filled with a LEAP username is - * rejected when completion is attempted with an open AP. LEAP requires - * the AP to have the Privacy bit set. - */ - - src = nm_connection_new (); - if (add_wifi) - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - - success = complete_connection ("blahblah", bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - /* We expect failure */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_open_ap_leap_connection_2 (void) -{ - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that a basic connection specifying IEEE8021x security (ie, Dynamic - * WEP or LEAP) is rejected when completion is attempted with an open AP. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - - success = complete_connection ("blahblah", bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - /* We expect failure */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_open_ap_wep_connection (gconstpointer add_wifi) -{ - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "11111111111111111111111111", 0 }, - { NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, NULL, 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that a static WEP connection is rejected when completion is - * attempted with an open AP. - */ - - src = nm_connection_new (); - if (add_wifi) - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - success = complete_connection ("blahblah", bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - /* We expect failure */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_ap_wpa_psk_connection_base (const char *key_mgmt, - const char *auth_alg, - guint32 flags, - guint32 wpa_flags, - guint32 rsn_flags, - gboolean add_wifi, - NMConnection *expected) -{ - NMConnection *src; - const char *ssid = "blahblah"; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData exp_wifi[] = { - { NM_SETTING_WIRELESS_SSID, ssid, 0 }, - { NM_SETTING_WIRELESS_MODE, "infrastructure", 0 }, - { NULL } }; - const KeyData both_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0 }, - { NM_SETTING_WIRELESS_SECURITY_PSK, "asdfasdfasdfasdfasdfafs", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - src = nm_connection_new (); - if (add_wifi) - fill_wifi_empty (src); - fill_wsec (src, both_wsec); - success = complete_connection (ssid, bssid, NM_802_11_MODE_INFRA, - flags, wpa_flags, rsn_flags, - FALSE, src, &error); - if (expected) { - fill_wifi (expected, exp_wifi); - fill_wsec (expected, both_wsec); - } - COMPARE (src, expected, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -static void -test_open_ap_wpa_psk_connection_1 (void) -{ - /* Test that a WPA-PSK connection filling only the PSK itself and *not* - * filling the wifi setting is rejected when completion is attempted with - * an open AP. - */ - test_ap_wpa_psk_connection_base (NULL, NULL, - NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -static void -test_open_ap_wpa_psk_connection_2 (void) -{ - /* Test that a WPA-PSK connection filling only the PSK itself and also - * filling the wifi setting is rejected when completion is attempted with - * an open AP. - */ - test_ap_wpa_psk_connection_base (NULL, NULL, - NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - TRUE, NULL); -} - -static void -test_open_ap_wpa_psk_connection_3 (void) -{ - /* Test that a WPA-PSK connection filling the PSK and setting the auth alg - * to 'open' is rejected when completion is attempted with an open AP. - */ - test_ap_wpa_psk_connection_base (NULL, "open", - NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -static void -test_open_ap_wpa_psk_connection_4 (void) -{ - /* Test that a WPA-PSK connection filling the PSK and setting the auth alg - * to 'shared' is rejected when completion is attempted with an open AP. - * Shared auth cannot be used with WPA. - */ - test_ap_wpa_psk_connection_base (NULL, "shared", - NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -static void -test_open_ap_wpa_psk_connection_5 (void) -{ - /* Test that a WPA-PSK connection filling the PSK, the auth algorithm, and - * key management is rejected when completion is attempted with an open AP. - */ - test_ap_wpa_psk_connection_base ("wpa-psk", "open", - NM_802_11_AP_FLAGS_NONE, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -/*******************************************/ - -static void -test_ap_wpa_eap_connection_base (const char *key_mgmt, - const char *auth_alg, - guint32 flags, - guint32 wpa_flags, - guint32 rsn_flags, - gboolean add_wifi, - guint error_domain, - guint error_code) -{ - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_empty[] = { { NULL } }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - src = nm_connection_new (); - if (add_wifi) - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - fill_8021x (src, src_empty); - success = complete_connection ("blahblah", bssid, NM_802_11_MODE_INFRA, - flags, wpa_flags, rsn_flags, - FALSE, src, &error); - /* Failure expected */ - COMPARE (src, NULL, success, error, error_domain, error_code); - - g_object_unref (src); -} - -enum { - IDX_NONE = 0, - IDX_OPEN, - IDX_PRIV, - IDX_WPA_PSK_PTKIP_GTKIP, - IDX_WPA_PSK_PTKIP_PCCMP_GTKIP, - IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP, - IDX_WPA_RSN_PSK_PCCMP_GCCMP, - IDX_RSN_PSK_PCCMP_GCCMP, - IDX_RSN_PSK_PTKIP_PCCMP_GTKIP, - IDX_WPA_8021X, - IDX_RSN_8021X, -}; - -static guint32 -flags_for_idx (guint32 idx) -{ - if (idx == IDX_OPEN) - return NM_802_11_AP_FLAGS_NONE; - else if ( idx == IDX_PRIV - || idx == IDX_WPA_PSK_PTKIP_GTKIP - || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP - || idx == IDX_RSN_PSK_PCCMP_GCCMP - || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP - || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP - || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP - || idx == IDX_WPA_8021X - || idx == IDX_RSN_8021X) - return NM_802_11_AP_FLAGS_PRIVACY; - else - g_assert_not_reached (); -} - -static guint32 -wpa_flags_for_idx (guint32 idx) -{ - if (idx == IDX_OPEN || idx == IDX_PRIV || idx == IDX_RSN_8021X - || idx == IDX_RSN_PSK_PCCMP_GCCMP || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP) - return NM_802_11_AP_SEC_NONE; - else if (idx == IDX_WPA_PSK_PTKIP_GTKIP) - return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP) - return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (IDX_WPA_RSN_PSK_PCCMP_GCCMP) - return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (idx == IDX_WPA_8021X) - return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_802_1X; - else - g_assert_not_reached (); -} - -static guint32 -rsn_flags_for_idx (guint32 idx) -{ - if (idx == IDX_OPEN || idx == IDX_PRIV || idx == IDX_WPA_8021X - || idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP) - return NM_802_11_AP_SEC_NONE; - else if (idx == IDX_RSN_PSK_PCCMP_GCCMP) - return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP) - return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP) - return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP) - return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK; - else if (idx == IDX_RSN_8021X) - return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_802_1X; - else - g_assert_not_reached (); -} - -static guint32 -error_domain_for_idx (guint32 idx, guint num) -{ - if (idx == IDX_OPEN) - return NM_SETTING_WIRELESS_SECURITY_ERROR; - else if (idx == IDX_PRIV) { - if (num <= 3) - return NM_SETTING_802_1X_ERROR; - else - return NM_SETTING_WIRELESS_SECURITY_ERROR; - } else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP - || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP - || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP) - return NM_SETTING_WIRELESS_SECURITY_ERROR; - else - g_assert_not_reached (); -} - -static guint32 -error_code_for_idx (guint32 idx, guint num) -{ - if (idx == IDX_OPEN) - return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY; - else if (idx == IDX_PRIV) { - if (num <= 3) - return NM_SETTING_802_1X_ERROR_MISSING_PROPERTY; - else - return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY; - } else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP - || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP - || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP) - return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY; - else - g_assert_not_reached (); -} - -static void -test_ap_wpa_eap_connection_1 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - - test_ap_wpa_eap_connection_base (NULL, NULL, - flags_for_idx (idx), - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, - error_domain_for_idx (idx, 1), - error_code_for_idx (idx, 1)); -} - -static void -test_ap_wpa_eap_connection_2 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - - test_ap_wpa_eap_connection_base (NULL, NULL, - flags_for_idx (idx), - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - TRUE, - error_domain_for_idx (idx, 2), - error_code_for_idx (idx, 2)); -} - -static void -test_ap_wpa_eap_connection_3 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - - test_ap_wpa_eap_connection_base (NULL, "open", - flags_for_idx (idx), - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, - error_domain_for_idx (idx, 3), - error_code_for_idx (idx, 3)); -} - -static void -test_ap_wpa_eap_connection_4 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - - test_ap_wpa_eap_connection_base (NULL, "shared", - flags_for_idx (idx), - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, - error_domain_for_idx (idx, 4), - error_code_for_idx (idx, 4)); -} - -static void -test_ap_wpa_eap_connection_5 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - - test_ap_wpa_eap_connection_base ("wpa-eap", "open", - flags_for_idx (idx), - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, - error_domain_for_idx (idx, 5), - error_code_for_idx (idx, 5)); -} - -/*******************************************/ - -static void -test_priv_ap_empty_connection (void) -{ - NMConnection *src, *expected; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const char *ssid = "blahblah"; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that an empty connection is completed to a valid Static WEP - * connection when completed with an AP with the Privacy bit set. - */ - - src = nm_connection_new (); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - - /* Static WEP connection expected */ - expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); - fill_wsec (expected, exp_wsec); - COMPARE (src, expected, success, error, 0, 0); - - g_object_unref (src); - g_object_unref (expected); -} - -/*******************************************/ - -static void -test_priv_ap_leap_connection_1 (gconstpointer add_wifi) -{ - NMConnection *src, *expected; - const char *ssid = "blahblah"; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const char *leap_username = "Bill Smith"; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 }, - { NULL } }; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 }, - { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that an minimal LEAP connection specifying only key management and - * the LEAP username is completed to a full LEAP connection when completed - * with an AP with the Privacy bit set. - */ - - src = nm_connection_new (); - if (add_wifi) - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - /* We expect success here; since LEAP APs just set the 'privacy' flag - * there's no way to determine from the AP's beacon whether it's static WEP, - * dynamic WEP, or LEAP. - */ - expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); - fill_wsec (expected, exp_wsec); - COMPARE (src, expected, success, error, 0, 0); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_priv_ap_leap_connection_2 (void) -{ - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that an minimal LEAP connection specifying only key management and - * the LEAP auth alg is completed to a full LEAP connection when completed - * with an AP with the Privacy bit set. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - success = complete_connection ("blahblah", bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - /* We expect failure here, we need a LEAP username */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_priv_ap_dynamic_wep_1 (void) -{ - NMConnection *src, *expected; - const char *ssid = "blahblah"; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - const KeyData both_8021x[] = { - { NM_SETTING_802_1X_EAP, "peap", 0 }, - { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 }, - { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 }, - { NULL } }; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that an minimal Dynamic WEP connection specifying key management, - * the auth algorithm, and valid 802.1x setting is completed to a valid - * Dynamic WEP connection when completed with an AP with the Privacy bit set. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - fill_8021x (src, both_8021x); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - - /* We expect a completed Dynamic WEP connection */ - expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); - fill_wsec (expected, exp_wsec); - fill_8021x (expected, both_8021x); - COMPARE (src, expected, success, error, 0, 0); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_priv_ap_dynamic_wep_2 (void) -{ - NMConnection *src, *expected; - const char *ssid = "blahblah"; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - const KeyData both_8021x[] = { - { NM_SETTING_802_1X_EAP, "peap", 0 }, - { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 }, - { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 }, - { NULL } }; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that an minimal Dynamic WEP connection specifying only the auth - * algorithm and a valid 802.1x setting is completed to a valid Dynamic - * WEP connection when completed with an AP with the Privacy bit set. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - fill_8021x (src, both_8021x); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - - /* We expect a completed Dynamic WEP connection */ - expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); - fill_wsec (expected, exp_wsec); - fill_8021x (expected, both_8021x); - COMPARE (src, expected, success, error, 0, 0); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_priv_ap_dynamic_wep_3 (void) -{ - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", 0 }, - { NULL } }; - const KeyData src_8021x[] = { - { NM_SETTING_802_1X_EAP, "peap", 0 }, - { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 }, - { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Ensure that a basic connection specifying 'shared' auth and an 802.1x - * setting is rejected, as 802.1x is incompatible with 'shared' auth. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - fill_8021x (src, src_8021x); - success = complete_connection ("blahblah", bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE, - FALSE, - src, &error); - /* Expect failure; shared is not compatible with dynamic WEP */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_priv_ap_wpa_psk_connection_1 (void) -{ - /* Test that a basic WPA-PSK connection is rejected when completion is - * attempted with an AP with just the Privacy bit set. Lack of WPA/RSN - * flags means the AP provides Static/Dynamic WEP or LEAP, not WPA. - */ - test_ap_wpa_psk_connection_base (NULL, NULL, - NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -static void -test_priv_ap_wpa_psk_connection_2 (void) -{ - /* Test that a basic WPA-PSK connection is rejected when completion is - * attempted with an AP with just the Privacy bit set. Lack of WPA/RSN - * flags means the AP provides Static/Dynamic WEP or LEAP, not WPA. - */ - test_ap_wpa_psk_connection_base (NULL, NULL, - NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - TRUE, NULL); -} - -static void -test_priv_ap_wpa_psk_connection_3 (void) -{ - /* Test that a basic WPA-PSK connection specifying only the auth algorithm - * is rejected when completion is attempted with an AP with just the Privacy - * bit set. Lack of WPA/RSN flags means the AP provides Static/Dynamic WEP - * or LEAP, not WPA. - */ - test_ap_wpa_psk_connection_base (NULL, "open", - NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -static void -test_priv_ap_wpa_psk_connection_4 (void) -{ - /* Test that a basic WPA-PSK connection specifying only the auth algorithm - * is rejected when completion is attempted with an AP with just the Privacy - * bit set. Lack of WPA/RSN flags means the AP provides Static/Dynamic WEP - * or LEAP, not WPA. Second, 'shared' auth is incompatible with WPA. - */ - test_ap_wpa_psk_connection_base (NULL, "shared", - NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -static void -test_priv_ap_wpa_psk_connection_5 (void) -{ - /* Test that a WPA-PSK connection specifying both the key management and - * auth algorithm is rejected when completion is attempted with an AP with - * just the Privacy bit set. Lack of WPA/RSN flags means the AP provides - * Static/Dynamic WEP or LEAP, not WPA. - */ - test_ap_wpa_psk_connection_base ("wpa-psk", "open", - NM_802_11_AP_FLAGS_PRIVACY, - NM_802_11_AP_SEC_NONE, - NM_802_11_AP_SEC_NONE, - FALSE, NULL); -} - -/*******************************************/ - -static void -test_wpa_ap_empty_connection (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *src, *expected; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const char *ssid = "blahblah"; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that a basic WPA-PSK connection specifying just key management and - * the auth algorithm is completed successfully when given an AP with WPA - * or RSN flags. - */ - - src = nm_connection_new (); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, src, &error); - - /* WPA connection expected */ - expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA); - fill_wsec (expected, exp_wsec); - COMPARE (src, expected, success, error, 0, 0); - - g_object_unref (src); - g_object_unref (expected); -} - -/*******************************************/ - -static void -test_wpa_ap_leap_connection_1 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *src; - const char *ssid = "blahblah"; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const char *leap_username = "Bill Smith"; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that completion of a LEAP connection with a WPA-enabled AP is - * rejected since WPA APs (usually) do not support LEAP. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - success = complete_connection (ssid, bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, - src, &error); - /* Expect failure here; WPA APs don't support old-school LEAP */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_wpa_ap_leap_connection_2 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that completion of a LEAP connection with a WPA-enabled AP is - * rejected since WPA APs (usually) do not support LEAP. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - success = complete_connection ("blahblah", bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, - src, &error); - /* We expect failure here, we need a LEAP username */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_wpa_ap_dynamic_wep_connection (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *src; - const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; - const KeyData src_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, - { NULL } }; - gboolean success; - GError *error = NULL; - - /* Test that completion of a Dynamic WEP connection with a WPA-enabled AP is - * rejected since WPA APs (usually) do not support Dynamic WEP. - */ - - src = nm_connection_new (); - fill_wifi_empty (src); - fill_wsec (src, src_wsec); - success = complete_connection ("blahblah", bssid, - NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, - src, &error); - /* We expect failure here since Dynamic WEP is incompatible with WPA */ - COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY); - - g_object_unref (src); -} - -/*******************************************/ - -static void -test_wpa_ap_wpa_psk_connection_1 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *expected; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - - expected = nm_connection_new (); - fill_wsec (expected, exp_wsec); - test_ap_wpa_psk_connection_base (NULL, NULL, - NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, expected); - g_object_unref (expected); -} - -static void -test_wpa_ap_wpa_psk_connection_2 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *expected; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - - expected = nm_connection_new (); - fill_wsec (expected, exp_wsec); - test_ap_wpa_psk_connection_base (NULL, NULL, - NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - TRUE, expected); - g_object_unref (expected); -} - -static void -test_wpa_ap_wpa_psk_connection_3 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *expected; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - - expected = nm_connection_new (); - fill_wsec (expected, exp_wsec); - test_ap_wpa_psk_connection_base (NULL, "open", - NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, expected); - g_object_unref (expected); -} - -static void -test_wpa_ap_wpa_psk_connection_4 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - test_ap_wpa_psk_connection_base (NULL, "shared", - NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, NULL); -} - -static void -test_wpa_ap_wpa_psk_connection_5 (gconstpointer data) -{ - guint idx = GPOINTER_TO_UINT (data); - NMConnection *expected; - const KeyData exp_wsec[] = { - { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 }, - { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 }, - { NULL } }; - - expected = nm_connection_new (); - fill_wsec (expected, exp_wsec); - test_ap_wpa_psk_connection_base ("wpa-psk", "open", - NM_802_11_AP_FLAGS_PRIVACY, - wpa_flags_for_idx (idx), - rsn_flags_for_idx (idx), - FALSE, expected); - g_object_unref (expected); -} - -/*******************************************/ - -static void -test_strength_dbm (void) -{ - /* boundary conditions first */ - g_assert_cmpint (nm_ap_utils_level_to_quality (-1), ==, 100); - g_assert_cmpint (nm_ap_utils_level_to_quality (-40), ==, 100); - g_assert_cmpint (nm_ap_utils_level_to_quality (-30), ==, 100); - g_assert_cmpint (nm_ap_utils_level_to_quality (-100), ==, 0); - g_assert_cmpint (nm_ap_utils_level_to_quality (-200), ==, 0); - - g_assert_cmpint (nm_ap_utils_level_to_quality (-81), ==, 32); - g_assert_cmpint (nm_ap_utils_level_to_quality (-92), ==, 14); - g_assert_cmpint (nm_ap_utils_level_to_quality (-74), ==, 44); - g_assert_cmpint (nm_ap_utils_level_to_quality (-81), ==, 32); - g_assert_cmpint (nm_ap_utils_level_to_quality (-66), ==, 57); -} - -static void -test_strength_percent (void) -{ - int i; - - /* boundary conditions first */ - g_assert_cmpint (nm_ap_utils_level_to_quality (0), ==, 0); - g_assert_cmpint (nm_ap_utils_level_to_quality (100), ==, 100); - g_assert_cmpint (nm_ap_utils_level_to_quality (110), ==, 100); - - for (i = 0; i <= 100; i++) - g_assert_cmpint (nm_ap_utils_level_to_quality (i), ==, i); -} - -static void -test_strength_wext (void) -{ - /* boundary conditions that we assume aren't WEXT first */ - g_assert_cmpint (nm_ap_utils_level_to_quality (256), ==, 100); - g_assert_cmpint (nm_ap_utils_level_to_quality (110), ==, 100); - - /* boundary conditions that we assume are WEXT */ - g_assert_cmpint (nm_ap_utils_level_to_quality (111), ==, 0); - g_assert_cmpint (nm_ap_utils_level_to_quality (150), ==, 0); - g_assert_cmpint (nm_ap_utils_level_to_quality (225), ==, 100); - g_assert_cmpint (nm_ap_utils_level_to_quality (255), ==, 100); - - g_assert_cmpint (nm_ap_utils_level_to_quality (157), ==, 2); - g_assert_cmpint (nm_ap_utils_level_to_quality (200), ==, 74); - g_assert_cmpint (nm_ap_utils_level_to_quality (215), ==, 99); -} - -/*******************************************/ - -int -main (int argc, char **argv) -{ - gsize i; - - g_type_init (); - g_test_init (&argc, &argv, NULL); - - g_test_add_func ("/wifi/lock_bssid", - test_lock_bssid); - - /* Open AP tests; make sure that connections to be completed that have - * various security-related settings already set cause the completion - * to fail. - */ - g_test_add_func ("/wifi/open_ap/empty_connection", - test_open_ap_empty_connection); - g_test_add_data_func ("/wifi/open_ap/leap_connection/1", - (gconstpointer) TRUE, - test_open_ap_leap_connection_1); - g_test_add_data_func ("/wifi/open_ap/leap_connection/1_no_add_wifi", - (gconstpointer) FALSE, - test_open_ap_leap_connection_1); - g_test_add_func ("/wifi/open_ap/leap_connection/2", - test_open_ap_leap_connection_2); - g_test_add_data_func ("/wifi/open_ap/wep_connection", - (gconstpointer) TRUE, - test_open_ap_wep_connection); - g_test_add_data_func ("/wifi/open_ap/wep_connection", - (gconstpointer) FALSE, - test_open_ap_wep_connection); - - g_test_add_func ("/wifi/open_ap/wpa_psk_connection/1", - test_open_ap_wpa_psk_connection_1); - g_test_add_func ("/wifi/open_ap/wpa_psk_connection/2", - test_open_ap_wpa_psk_connection_2); - g_test_add_func ("/wifi/open_ap/wpa_psk_connection/3", - test_open_ap_wpa_psk_connection_3); - g_test_add_func ("/wifi/open_ap/wpa_psk_connection/4", - test_open_ap_wpa_psk_connection_4); - g_test_add_func ("/wifi/open_ap/wpa_psk_connection/5", - test_open_ap_wpa_psk_connection_5); - - g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/1", - (gconstpointer) IDX_OPEN, - test_ap_wpa_eap_connection_1); - g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/2", - (gconstpointer) IDX_OPEN, - test_ap_wpa_eap_connection_2); - g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/3", - (gconstpointer) IDX_OPEN, - test_ap_wpa_eap_connection_3); - g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/4", - (gconstpointer) IDX_OPEN, - test_ap_wpa_eap_connection_4); - g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/5", - (gconstpointer) IDX_OPEN, - test_ap_wpa_eap_connection_5); - - /* WEP AP tests */ - g_test_add_func ("/wifi/priv_ap/empty_connection", - test_priv_ap_empty_connection); - g_test_add_data_func ("/wifi/priv_ap/leap_connection/1", - (gconstpointer) FALSE, - test_priv_ap_leap_connection_1); - g_test_add_func ("/wifi/priv_ap/leap_connection/2", - test_priv_ap_leap_connection_2); - - g_test_add_func ("/wifi/priv_ap/dynamic_wep/1", - test_priv_ap_dynamic_wep_1); - g_test_add_func ("/wifi/priv_ap/dynamic_wep/2", - test_priv_ap_dynamic_wep_2); - g_test_add_func ("/wifi/priv_ap/dynamic_wep/3", - test_priv_ap_dynamic_wep_3); - - g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/1", - test_priv_ap_wpa_psk_connection_1); - g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/2", - test_priv_ap_wpa_psk_connection_2); - g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/3", - test_priv_ap_wpa_psk_connection_3); - g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/4", - test_priv_ap_wpa_psk_connection_4); - g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/5", - test_priv_ap_wpa_psk_connection_5); - - g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/1", - (gconstpointer) IDX_PRIV, - test_ap_wpa_eap_connection_1); - g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/2", - (gconstpointer) IDX_PRIV, - test_ap_wpa_eap_connection_2); - g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/3", - (gconstpointer) IDX_PRIV, - test_ap_wpa_eap_connection_3); - g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/4", - (gconstpointer) IDX_PRIV, - test_ap_wpa_eap_connection_4); - g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/5", - (gconstpointer) IDX_PRIV, - test_ap_wpa_eap_connection_5); - - /* WPA-PSK tests */ - for (i = IDX_WPA_PSK_PTKIP_GTKIP; i <= IDX_WPA_RSN_PSK_PCCMP_GCCMP; i++) { - g_test_add_data_func ("/wifi/wpa_psk/empty_connection", - (gconstpointer) i, - test_wpa_ap_empty_connection); - g_test_add_data_func ("/wifi/wpa_psk/leap_connection/1", - (gconstpointer) i, - test_wpa_ap_leap_connection_1); - g_test_add_data_func ("/wifi/wpa_psk/leap_connection/2", - (gconstpointer) i, - test_wpa_ap_leap_connection_2); - - g_test_add_data_func ("/wifi/wpa_psk/dynamic_wep_connection", - (gconstpointer) i, - test_wpa_ap_dynamic_wep_connection); - - g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/1", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_1); - g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/2", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_2); - g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/3", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_3); - g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/4", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_4); - g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/5", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_5); - - g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/1", - (gconstpointer) i, - test_ap_wpa_eap_connection_1); - g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/2", - (gconstpointer) i, - test_ap_wpa_eap_connection_2); - g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/3", - (gconstpointer) i, - test_ap_wpa_eap_connection_3); - g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/4", - (gconstpointer) i, - test_ap_wpa_eap_connection_4); - g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/5", - (gconstpointer) i, - test_ap_wpa_eap_connection_5); - } - - /* RSN-PSK tests */ - for (i = IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP; i <= IDX_RSN_PSK_PTKIP_PCCMP_GTKIP; i++) { - g_test_add_data_func ("/wifi/rsn_psk/empty_connection", - (gconstpointer) i, - test_wpa_ap_empty_connection); - g_test_add_data_func ("/wifi/rsn_psk/leap_connection/1", - (gconstpointer) i, - test_wpa_ap_leap_connection_1); - g_test_add_data_func ("/wifi/rsn_psk/leap_connection/2", - (gconstpointer) i, - test_wpa_ap_leap_connection_2); - - g_test_add_data_func ("/wifi/rsn_psk/dynamic_wep_connection", - (gconstpointer) i, - test_wpa_ap_dynamic_wep_connection); - - g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/1", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_1); - g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/2", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_2); - g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/3", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_3); - g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/4", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_4); - g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/5", - (gconstpointer) i, - test_wpa_ap_wpa_psk_connection_5); - - g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/1", - (gconstpointer) i, - test_ap_wpa_eap_connection_1); - g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/2", - (gconstpointer) i, - test_ap_wpa_eap_connection_2); - g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/3", - (gconstpointer) i, - test_ap_wpa_eap_connection_3); - g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/4", - (gconstpointer) i, - test_ap_wpa_eap_connection_4); - g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/5", - (gconstpointer) i, - test_ap_wpa_eap_connection_5); - } - - /* Scanned signal strength conversion tests */ - g_test_add_func ("/wifi/strength/dbm", - test_strength_dbm); - g_test_add_func ("/wifi/strength/percent", - test_strength_percent); - g_test_add_func ("/wifi/strength/wext", - test_strength_wext); - - return g_test_run (); -} -- cgit v1.2.1