diff options
| author | Michael Biebl <biebl@debian.org> | 2012-03-24 01:37:02 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2012-03-24 01:37:02 +0100 |
| commit | de06e5715e780baade318f3490ac7a4c9ce84e32 (patch) | |
| tree | 23fbc3fafc12072476eff98bee60100eb54c29db /src/nm-device-wifi.c | |
| parent | b436a68a20ff3114ded32a7a3d70cdd4954039f9 (diff) | |
Imported Upstream version 0.9.4.0 upstream/0.9.4.0
Diffstat (limited to 'src/nm-device-wifi.c')
| -rw-r--r-- | src/nm-device-wifi.c | 1205 |
1 files changed, 355 insertions, 850 deletions
diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index 9695c077..ed964a57 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -15,7 +15,7 @@ * 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 - 2011 Red Hat, Inc. + * Copyright (C) 2005 - 2012 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -25,7 +25,6 @@ #include <netinet/in.h> #include <string.h> #include <net/ethernet.h> -#include <iwlib.h> #include <sys/stat.h> #include <sys/wait.h> #include <signal.h> @@ -34,11 +33,12 @@ #include <linux/ethtool.h> #include <sys/ioctl.h> #include <netinet/ether.h> +#include <net/if.h> +#include <errno.h> #include "nm-glib-compat.h" #include "nm-device.h" #include "nm-device-wifi.h" -#include "nm-device-interface.h" #include "nm-device-private.h" #include "nm-utils.h" #include "nm-logging.h" @@ -57,6 +57,8 @@ #include "nm-setting-ip6-config.h" #include "nm-system.h" #include "nm-settings-connection.h" +#include "nm-enum-types.h" +#include "wifi-utils.h" static gboolean impl_device_get_access_points (NMDeviceWifi *device, GPtrArray **aps, @@ -72,10 +74,7 @@ static gboolean impl_device_get_access_points (NMDeviceWifi *device, #define WIRELESS_SECRETS_TRIES "wireless-secrets-tries" -static void device_interface_init (NMDeviceInterface *iface_class); - -G_DEFINE_TYPE_EXTENDED (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE, 0, - G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init)) +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)) @@ -106,7 +105,7 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -#define SUP_SIG_ID_LEN 5 +#define SUP_SIG_ID_LEN 6 typedef struct Supplicant { NMSupplicantManager *mgr; @@ -132,12 +131,7 @@ struct _NMDeviceWifiPrivate { guint ipw_rfkill_id; RfKillState ipw_rfkill_state; - GByteArray * ssid; gint8 invalid_strength_counter; - iwqual max_qual; - - gint8 num_freqs; - guint32 freqs[IW_MAX_FREQUENCIES]; GSList * ap_list; NMAccessPoint * current_ap; @@ -147,20 +141,18 @@ struct _NMDeviceWifiPrivate { glong scheduled_scan_time; guint8 scan_interval; /* seconds */ guint pending_scan_id; + guint scanlist_cull_id; Supplicant supplicant; + WifiData * wifi_data; guint32 failed_link_count; guint periodic_source_id; guint link_timeout_id; - /* Static options from driver */ - guint32 capabilities; - gboolean has_scan_capa_ssid; + NMDeviceWifiCapabilities capabilities; }; -static guint32 nm_device_wifi_get_frequency (NMDeviceWifi *self); - static gboolean request_wireless_scan (gpointer user_data); static void schedule_scan (NMDeviceWifi *self, gboolean backoff); @@ -178,9 +170,19 @@ static void supplicant_iface_state_cb (NMSupplicantInterface *iface, 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); @@ -189,21 +191,11 @@ static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, GParamSpec * pspec, NMDeviceWifi * self); -static guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *self); - -static void cull_scan_list (NMDeviceWifi *self); +static void schedule_scanlist_cull (NMDeviceWifi *self); /*****************************************************************/ -typedef enum { - NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, - NM_WIFI_ERROR_CONNECTION_INVALID, - NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, - NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, -} NMWifiError; - #define NM_WIFI_ERROR (nm_wifi_error_quark ()) -#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) static GQuark nm_wifi_error_quark (void) @@ -214,31 +206,6 @@ nm_wifi_error_quark (void) return quark; } -/* This should really be standard. */ -#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } - -static GType -nm_wifi_error_get_type (void) -{ - static GType etype = 0; - - if (etype == 0) { - static const GEnumValue values[] = { - /* Connection was not a wireless connection. */ - ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, "ConnectionNotWireless"), - /* Connection was not a valid wireless connection. */ - ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), - /* Connection does not apply to this device. */ - ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), - /* Given access point was not in this device's scan list. */ - ENUM_ENTRY (NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, "AccessPointNotFound"), - { 0, 0, 0 } - }; - etype = g_enum_register_static ("NMWifiError", values); - } - return etype; -} - /*****************************************************************/ /* IPW rfkill handling (until 2.6.33) */ @@ -301,319 +268,12 @@ ipw_rfkill_state_work (gpointer user_data) /*****************************************************************/ -/* - * wireless_qual_to_percent - * - * Convert an iw_quality structure from SIOCGIWSTATS into a magical signal - * strength percentage. - * - */ -static int -wireless_qual_to_percent (const struct iw_quality *qual, - const struct iw_quality *max_qual) -{ - int percent = -1; - int level_percent = -1; - - g_return_val_if_fail (qual != NULL, -1); - g_return_val_if_fail (max_qual != NULL, -1); - - nm_log_dbg (LOGD_WIFI, - "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X", - (__s8) qual->qual, qual->qual, qual->qual, - (__s8) qual->level, qual->level, qual->level, - (__s8) qual->noise, qual->noise, qual->noise, - qual->updated, - (__s8) max_qual->qual, max_qual->qual, max_qual->qual, - (__s8) max_qual->level, max_qual->level, max_qual->level, - (__s8) max_qual->noise, max_qual->noise, max_qual->noise, - max_qual->updated); - - /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is. - * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be - * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers - * are free to use whatever they want to calculate "Link Quality". - */ - if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID)) - percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual)); - - /* If the driver doesn't specify a complete and valid quality, we have two options: - * - * 1) dBm: driver must specify max_qual->level = 0, and have valid values for - * qual->level and (qual->noise OR max_qual->noise) - * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for - * qual->level and max_qual->level - * - * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise, - * If drivers don't conform to it, they are wrong and need to be fixed. - */ - - if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */ - && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */ - && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */ - || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */ - ) { - /* Absolute power values (dBm) */ - - /* Reasonable fallbacks for dumb drivers that don't specify either level. */ - #define FALLBACK_NOISE_FLOOR_DBM -90 - #define FALLBACK_SIGNAL_MAX_DBM -20 - int max_level = FALLBACK_SIGNAL_MAX_DBM; - int noise = FALLBACK_NOISE_FLOOR_DBM; - int level = qual->level - 0x100; - - level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); - - if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID)) - noise = qual->noise - 0x100; - else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) - noise = max_qual->noise - 0x100; - noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); - - /* A sort of signal-to-noise ratio calculation */ - level_percent = (int)(100 - 70 *( - ((double)max_level - (double)level) / - ((double)max_level - (double)noise))); - nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.", - level_percent, max_level, level, noise); - } else if ( (max_qual->level != 0) - && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */ - && !(qual->updated & IW_QUAL_LEVEL_INVALID)) { - /* Relative power values (RSSI) */ - - int level = qual->level; - - /* Signal level is relavtive (0 -> max_qual->level) */ - level = CLAMP (level, 0, max_qual->level); - level_percent = (int)(100 * ((double)level / (double)max_qual->level)); - nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d. max_level %d, level %d.", - level_percent, max_qual->level, level); - } else if (percent == -1) { - nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver. Driver is probably buggy."); - } - - /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */ - if ((percent < 1) && (level_percent >= 0)) - percent = level_percent; - - nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).", - percent, CLAMP (percent, 0, 100)); - return (CLAMP (percent, 0, 100)); -} - - -/* - * nm_device_wifi_update_signal_strength - * - * Update the device's idea of the strength of its connection to the - * current access point. - * - */ -static void -nm_device_wifi_update_signal_strength (NMDeviceWifi *self, - NMAccessPoint *ap) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - const char *iface = nm_device_get_iface (NM_DEVICE (self)); - int fd, percent = -1; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd >= 0) { - struct iwreq wrq; - struct iw_statistics stats; - - memset (&stats, 0, sizeof (stats)); - - wrq.u.data.pointer = &stats; - wrq.u.data.length = sizeof (stats); - wrq.u.data.flags = 1; /* Clear updated flag */ - strncpy (wrq.ifr_name, iface, IFNAMSIZ); - - if (ioctl (fd, SIOCGIWSTATS, &wrq) == 0) - percent = wireless_qual_to_percent (&stats.qual, &priv->max_qual); - close (fd); - } - - /* Try to smooth out the strength. Atmel cards, for example, will give no strength - * one second and normal strength the next. - */ - if (percent >= 0 || ++priv->invalid_strength_counter > 3) { - nm_ap_set_strength (ap, (gint8) percent); - priv->invalid_strength_counter = 0; - } -} - - -static gboolean -wireless_get_range (NMDeviceWifi *self, - struct iw_range *range, - guint32 *response_len) -{ - int fd, i = 26; - gboolean success = FALSE; - const char *iface; - struct iwreq wrq; - - g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE); - g_return_val_if_fail (range != NULL, FALSE); - - iface = nm_device_get_iface (NM_DEVICE (self)); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface); - return FALSE; - } - - memset (&wrq, 0, sizeof (struct iwreq)); - strncpy (wrq.ifr_name, iface, IFNAMSIZ); - wrq.u.data.pointer = (caddr_t) range; - wrq.u.data.length = sizeof (struct iw_range); - - /* Need to give some drivers time to recover after suspend/resume - * (ex ipw3945 takes a few seconds to talk to its regulatory daemon; - * see rh bz#362421) - */ - while (i-- > 0) { - if (ioctl (fd, SIOCGIWRANGE, &wrq) == 0) { - if (response_len) - *response_len = wrq.u.data.length; - success = TRUE; - break; - } else if (errno != EAGAIN) { - nm_log_err (LOGD_HW | LOGD_WIFI, - "(%s): couldn't get driver range information (%d).", - iface, errno); - break; - } - - g_usleep (G_USEC_PER_SEC / 4); - } - - if (i <= 0) { - nm_log_warn (LOGD_HW | LOGD_WIFI, - "(%s): driver took too long to respond to IWRANGE query.", - iface); - } - - close (fd); - return success; -} - static guint32 real_get_generic_capabilities (NMDevice *dev) { - int fd, err; - guint32 caps = NM_DEVICE_CAP_NONE; - struct iwreq wrq; - const char *iface = nm_device_get_iface (dev); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface); - return NM_DEVICE_CAP_NONE; - } - - /* Cards that don't scan aren't supported */ - memset (&wrq, 0, sizeof (struct iwreq)); - strncpy (wrq.ifr_name, iface, IFNAMSIZ); - err = ioctl (fd, SIOCSIWSCAN, &wrq); - close (fd); - if ((err < 0) && (errno == EOPNOTSUPP)) - caps = NM_DEVICE_CAP_NONE; - else - caps |= NM_DEVICE_CAP_NM_SUPPORTED; - - return caps; -} - -#define WPA_CAPS (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | \ - NM_WIFI_DEVICE_CAP_CIPHER_CCMP | \ - NM_WIFI_DEVICE_CAP_WPA | \ - NM_WIFI_DEVICE_CAP_RSN) - -static guint32 -get_wireless_capabilities (NMDeviceWifi *self, iwrange *range) -{ - guint32 caps = NM_WIFI_DEVICE_CAP_NONE; - const char * iface; - - g_return_val_if_fail (self != NULL, NM_WIFI_DEVICE_CAP_NONE); - g_return_val_if_fail (range != NULL, NM_WIFI_DEVICE_CAP_NONE); - - iface = nm_device_get_iface (NM_DEVICE (self)); - - /* All drivers should support WEP by default */ - caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104; - - if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) - caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP; - - if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) - caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP; - - if (range->enc_capa & IW_ENC_CAPA_WPA) - caps |= NM_WIFI_DEVICE_CAP_WPA; - - if (range->enc_capa & IW_ENC_CAPA_WPA2) - caps |= NM_WIFI_DEVICE_CAP_RSN; - - /* Check for cipher support but not WPA support */ - if ( (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP)) - && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) { - nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; " - "WPA unavailable.", iface); - caps &= ~WPA_CAPS; - } - - /* Check for WPA support but not cipher support */ - if ( (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN)) - && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) { - nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; " - "WPA unavailable.", iface); - caps &= ~WPA_CAPS; - } - - return caps; -} - - -static guint32 iw_freq_to_uint32 (struct iw_freq *freq) -{ - if (freq->e == 0) { - /* Some drivers report channel not frequency. Convert to a - * frequency; but this assumes that the device is in b/g mode. - */ - if ((freq->m >= 1) && (freq->m <= 13)) - return 2407 + (5 * freq->m); - else if (freq->m == 14) - return 2484; - } - - return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000); + return NM_DEVICE_CAP_NM_SUPPORTED; } - -/* Until a new wireless-tools comes out that has the defs and the structure, - * need to copy them here. - */ -/* Scan capability flags - in (struct iw_range *)->scan_capa */ -#define NM_IW_SCAN_CAPA_NONE 0x00 -#define NM_IW_SCAN_CAPA_ESSID 0x01 - -struct iw_range_with_scan_capa -{ - guint32 throughput; - guint32 min_nwid; - guint32 max_nwid; - guint16 old_num_channels; - guint8 old_num_frequency; - - guint8 scan_capa; -/* don't need the rest... */ -}; - - static GObject* constructor (GType type, guint n_construct_params, @@ -623,11 +283,6 @@ constructor (GType type, GObjectClass *klass; NMDeviceWifi *self; NMDeviceWifiPrivate *priv; - struct iw_range range; - struct iw_range_with_scan_capa *scan_capa_range; - guint32 response_len = 0; - gboolean success; - int i; klass = G_OBJECT_CLASS (nm_device_wifi_parent_class); object = klass->constructor (type, n_construct_params, construct_params); @@ -641,52 +296,22 @@ constructor (GType type, nm_device_get_iface (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); - memset (&range, 0, sizeof (struct iw_range)); - success = wireless_get_range (NM_DEVICE_WIFI (object), &range, &response_len); - if (!success) { - nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed", + priv->wifi_data = wifi_utils_init (nm_device_get_iface (NM_DEVICE (self)), + nm_device_get_ifindex (NM_DEVICE (self)), + TRUE); + if (priv->wifi_data == NULL) { + nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): failed to initialize WiFi driver", nm_device_get_iface (NM_DEVICE (self))); - goto error; - } - - if ((response_len < 300) || (range.we_version_compiled < 21)) { - nm_log_info (LOGD_HW | LOGD_WIFI, - "(%s): driver WEXT version too old (got %d, expected >= 21)", - nm_device_get_iface (NM_DEVICE (self)), - range.we_version_compiled); - goto error; + g_object_unref (object); + return NULL; } + priv->capabilities = wifi_utils_get_caps (priv->wifi_data); - priv->max_qual.qual = range.max_qual.qual; - priv->max_qual.level = range.max_qual.level; - priv->max_qual.noise = range.max_qual.noise; - priv->max_qual.updated = range.max_qual.updated; - - priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES); - for (i = 0; i < priv->num_freqs; i++) - priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]); - - /* Check for the ability to scan specific SSIDs. Until the scan_capa - * field gets added to wireless-tools, need to work around that by casting - * to the custom structure. - */ - scan_capa_range = (struct iw_range_with_scan_capa *) ⦥ - if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) { - priv->has_scan_capa_ssid = TRUE; - nm_log_info (LOGD_HW | LOGD_WIFI, - "(%s): driver supports SSID scans (scan_capa 0x%02X).", - nm_device_get_iface (NM_DEVICE (self)), - scan_capa_range->scan_capa); - } else { - nm_log_info (LOGD_HW | LOGD_WIFI, - "(%s): driver does not support SSID scans (scan_capa 0x%02X).", - nm_device_get_iface (NM_DEVICE (self)), - scan_capa_range->scan_capa); + if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP) { + nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): driver supports Access Point (AP) mode", + nm_device_get_iface (NM_DEVICE (self))); } - /* 802.11 wireless-specific capabilities */ - priv->capabilities = get_wireless_capabilities (self, &range); - /* Connect to the supplicant manager */ priv->supplicant.mgr = nm_supplicant_manager_get (); g_assert (priv->supplicant.mgr); @@ -708,10 +333,6 @@ constructor (GType type, priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self); return object; - -error: - g_object_unref (object); - return NULL; } static gboolean @@ -748,6 +369,18 @@ supplicant_interface_acquire (NMDeviceWifi *self) priv->supplicant.sig_ids[i++] = id; id = g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_BSS_UPDATED, + G_CALLBACK (supplicant_iface_bss_updated_cb), + self); + priv->supplicant.sig_ids[i++] = id; + + id = g_signal_connect (priv->supplicant.iface, + NM_SUPPLICANT_INTERFACE_BSS_REMOVED, + G_CALLBACK (supplicant_iface_bss_removed_cb), + self); + priv->supplicant.sig_ids[i++] = id; + + id = g_signal_connect (priv->supplicant.iface, NM_SUPPLICANT_INTERFACE_SCAN_DONE, G_CALLBACK (supplicant_iface_scan_done_cb), self); @@ -808,6 +441,11 @@ supplicant_interface_release (NMDeviceWifi *self) } memset (priv->supplicant.sig_ids, 0, sizeof (priv->supplicant.sig_ids)); + if (priv->scanlist_cull_id) { + g_source_remove (priv->scanlist_cull_id); + priv->scanlist_cull_id = 0; + } + if (priv->supplicant.iface) { /* Tell the supplicant to disconnect from the current AP */ nm_supplicant_interface_disconnect (priv->supplicant.iface); @@ -817,7 +455,6 @@ supplicant_interface_release (NMDeviceWifi *self) } } - static NMAccessPoint * get_ap_by_path (NMDeviceWifi *self, const char *path) { @@ -825,7 +462,20 @@ get_ap_by_path (NMDeviceWifi *self, const char *path) GSList *iter; for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - if (strcmp (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0) + 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; + + for (iter = priv->ap_list; iter && path; 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; @@ -839,16 +489,16 @@ get_active_ap (NMDeviceWifi *self, NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); const char *iface = nm_device_get_iface (NM_DEVICE (self)); struct ether_addr bssid; - const GByteArray *ssid; + GByteArray *ssid; GSList *iter; int i = 0; - NMAccessPoint *match_nofreq = NULL; + NMAccessPoint *match_nofreq = NULL, *active_ap = NULL; gboolean found_a_band = FALSE; gboolean found_bg_band = FALSE; NM80211Mode devmode; guint32 devfreq; - nm_device_wifi_get_bssid (self, &bssid); + wifi_utils_get_bssid (priv->wifi_data, &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], @@ -858,15 +508,15 @@ get_active_ap (NMDeviceWifi *self, if (!nm_ethernet_address_is_valid (&bssid)) return NULL; - ssid = nm_device_wifi_get_ssid (self); + ssid = wifi_utils_get_ssid (priv->wifi_data); 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_device_wifi_get_mode (self); - devfreq = nm_device_wifi_get_frequency (self); + devmode = wifi_utils_get_mode (priv->wifi_data); + devfreq = wifi_utils_get_freq (priv->wifi_data); /* 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 @@ -930,7 +580,8 @@ get_active_ap (NMDeviceWifi *self, // FIXME: handle security settings here too nm_log_dbg (LOGD_WIFI, " matched"); - return ap; + active_ap = ap; + goto done; } } @@ -943,9 +594,10 @@ get_active_ap (NMDeviceWifi *self, * 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. + * 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)) { + 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); @@ -957,11 +609,15 @@ get_active_ap (NMDeviceWifi *self, ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3], ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]); - return match_nofreq; + active_ap = match_nofreq; } nm_log_dbg (LOGD_WIFI, " No matching AP found."); - return NULL; + +done: + if (ssid) + g_byte_array_free (ssid, TRUE); + return active_ap; } static void @@ -1038,7 +694,7 @@ periodic_update (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMAccessPoint *new_ap; - guint32 new_rate; + guint32 new_rate, percent; /* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where * multiple IBSS stations using the same SSID will eventually switch to @@ -1051,7 +707,7 @@ periodic_update (NMDeviceWifi *self) 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_device_wifi_get_bssid (self, &bssid); + wifi_utils_get_bssid (priv->wifi_data, &bssid); /* 0x02 means "locally administered" and should be OR-ed into * the first byte of IBSS BSSIDs. */ @@ -1061,8 +717,16 @@ periodic_update (NMDeviceWifi *self) } new_ap = get_active_ap (self, NULL, FALSE); - if (new_ap) - nm_device_wifi_update_signal_strength (self, new_ap); + 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 = wifi_utils_get_qual (priv->wifi_data); + 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) && (new_ap != priv->current_ap)) { const struct ether_addr *new_bssid = NULL; @@ -1073,13 +737,13 @@ periodic_update (NMDeviceWifi *self) if (new_ap) { new_bssid = nm_ap_get_address (new_ap); - new_addr = nm_ether_ntop (new_bssid); + 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_ether_ntop (old_bssid); + old_addr = nm_utils_hwaddr_ntoa (old_bssid, ARPHRD_ETHER); old_ssid = nm_ap_get_ssid (priv->current_ap); } @@ -1095,7 +759,7 @@ periodic_update (NMDeviceWifi *self) set_current_ap (self, new_ap); } - new_rate = nm_device_wifi_get_bitrate (self); + new_rate = wifi_utils_get_rate (priv->wifi_data); if (new_rate != priv->rate) { priv->rate = new_rate; g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE); @@ -1305,7 +969,37 @@ real_deactivate (NMDevice *dev) /* Ensure we're in infrastructure mode after deactivation; some devices * (usually older ones) don't scan well in adhoc mode. */ - nm_device_wifi_set_mode (self, NM_802_11_MODE_INFRA); + wifi_utils_set_mode (priv->wifi_data, NM_802_11_MODE_INFRA); +} + +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 @@ -1320,7 +1014,15 @@ real_check_connection_compatible (NMDevice *device, const GByteArray *mac; const GSList *mac_blacklist, *mac_blacklist_iter; - s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + 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; + } + + 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)) { @@ -1330,7 +1032,7 @@ real_check_connection_compatible (NMDevice *device, return FALSE; } - s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); + s_wireless = nm_connection_get_setting_wireless (connection); if (!s_wireless) { g_set_error (error, NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INVALID, @@ -1525,6 +1227,18 @@ real_complete_connection (NMDevice *device, 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); @@ -1552,8 +1266,11 @@ real_complete_connection (NMDevice *device, GByteArray *mac; const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - /* Lock the connection to this device by default */ - if (memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) { + /* 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); @@ -1614,7 +1331,7 @@ real_get_best_auto_connection (NMDevice *dev, NMSettingIP4Config *s_ip4; const char *method = NULL; - s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + s_con = nm_connection_get_setting_connection (connection); if (s_con == NULL) continue; if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME)) @@ -1622,7 +1339,7 @@ real_get_best_auto_connection (NMDevice *dev, if (!nm_setting_connection_get_autoconnect (s_con)) continue; - s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_connection_get_setting_wireless (connection); if (!s_wireless) continue; @@ -1650,7 +1367,7 @@ real_get_best_auto_connection (NMDevice *dev, continue; /* Use the connection if it's a shared connection */ - s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (s_ip4) method = nm_setting_ip4_config_get_method (s_ip4); @@ -1725,251 +1442,6 @@ impl_device_get_access_points (NMDeviceWifi *self, return TRUE; } -/* - * nm_device_get_mode - * - * Get managed/infrastructure/adhoc mode on a device - * - */ -NM80211Mode -nm_device_wifi_get_mode (NMDeviceWifi *self) -{ - int fd; - NM80211Mode mode = NM_802_11_MODE_UNKNOWN; - const char *iface; - struct iwreq wrq; - - g_return_val_if_fail (self != NULL, NM_802_11_MODE_UNKNOWN); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) - goto out; - - memset (&wrq, 0, sizeof (struct iwreq)); - iface = nm_device_get_iface (NM_DEVICE (self)); - strncpy (wrq.ifr_name, iface, IFNAMSIZ); - - if (ioctl (fd, SIOCGIWMODE, &wrq) == 0) { - switch (wrq.u.mode) { - case IW_MODE_ADHOC: - mode = NM_802_11_MODE_ADHOC; - break; - case IW_MODE_INFRA: - mode = NM_802_11_MODE_INFRA; - break; - default: - break; - } - } else { - if (errno != ENODEV) - nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): error %d getting card mode", iface, errno); - } - close (fd); - -out: - return mode; -} - - -/* - * nm_device_wifi_set_mode - * - * Set managed/infrastructure/adhoc mode on a device - * - */ -gboolean -nm_device_wifi_set_mode (NMDeviceWifi *self, const NM80211Mode mode) -{ - int fd; - const char *iface; - gboolean success = FALSE; - struct iwreq wrq; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail ((mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_ADHOC), FALSE); - - if (nm_device_wifi_get_mode (self) == mode) - return TRUE; - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) - goto out; - - memset (&wrq, 0, sizeof (struct iwreq)); - switch (mode) { - case NM_802_11_MODE_ADHOC: - wrq.u.mode = IW_MODE_ADHOC; - break; - case NM_802_11_MODE_INFRA: - wrq.u.mode = IW_MODE_INFRA; - break; - default: - goto out; - } - - iface = nm_device_get_iface (NM_DEVICE (self)); - strncpy (wrq.ifr_name, iface, IFNAMSIZ); - - if (ioctl (fd, SIOCSIWMODE, &wrq) < 0) { - if (errno != ENODEV) - nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): error setting mode %d", iface, mode); - } else - success = TRUE; - close (fd); - -out: - return success; -} - - -/* - * nm_device_wifi_get_frequency - * - * Get current frequency - * - */ -static guint32 -nm_device_wifi_get_frequency (NMDeviceWifi *self) -{ - int fd; - guint32 freq = 0; - const char *iface; - struct iwreq wrq; - - g_return_val_if_fail (self != NULL, 0); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) - return 0; - - memset (&wrq, 0, sizeof (struct iwreq)); - iface = nm_device_get_iface (NM_DEVICE (self)); - strncpy (wrq.ifr_name, iface, IFNAMSIZ); - - if (ioctl (fd, SIOCGIWFREQ, &wrq) < 0) { - nm_log_warn (LOGD_HW | LOGD_WIFI, - "(%s): error getting frequency: %s", - iface, strerror (errno)); - } else - freq = iw_freq_to_uint32 (&wrq.u.freq); - - close (fd); - return freq; -} - -/* - * nm_device_wifi_get_ssid - * - * If a device is wireless, return the ssid that it is attempting - * to use. - */ -const GByteArray * -nm_device_wifi_get_ssid (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv; - int sk; - struct iwreq wrq; - char ssid[IW_ESSID_MAX_SIZE + 2]; - guint32 len; - - g_return_val_if_fail (self != NULL, NULL); - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - sk = socket (AF_INET, SOCK_DGRAM, 0); - if (sk == -1) { - nm_log_err (LOGD_HW, "couldn't create socket: %d.", errno); - return NULL; - } - - memset (ssid, 0, sizeof (ssid)); - wrq.u.essid.pointer = (caddr_t) &ssid; - wrq.u.essid.length = sizeof (ssid); - wrq.u.essid.flags = 0; - strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); - - if (ioctl (sk, SIOCGIWESSID, &wrq) < 0) { - nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): couldn't get SSID: %d", - nm_device_get_iface (NM_DEVICE (self)), errno); - goto out; - } - - if (priv->ssid) { - g_byte_array_free (priv->ssid, TRUE); - priv->ssid = NULL; - } - - len = wrq.u.essid.length; - if (!nm_utils_is_empty_ssid ((guint8 *) ssid, len)) { - priv->ssid = g_byte_array_sized_new (len); - g_byte_array_append (priv->ssid, (const guint8 *) ssid, len); - } - -out: - close (sk); - return priv->ssid; -} - - -/* - * nm_device_wifi_get_bitrate - * - * For wireless devices, get the bitrate to broadcast/receive at. - * Returned value is rate in Kb/s. - * - */ -static guint32 -nm_device_wifi_get_bitrate (NMDeviceWifi *self) -{ - int err = -1, fd; - struct iwreq wrq; - - g_return_val_if_fail (self != NULL, 0); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) - return 0; - - memset (&wrq, 0, sizeof (wrq)); - strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); - err = ioctl (fd, SIOCGIWRATE, &wrq); - close (fd); - - return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0); -} - -/* - * nm_device_get_bssid - * - * If a device is wireless, get the access point's ethernet address - * that the card is associated with. - */ -void -nm_device_wifi_get_bssid (NMDeviceWifi *self, - struct ether_addr *bssid) -{ - int fd; - struct iwreq wrq; - - g_return_if_fail (self != NULL); - g_return_if_fail (bssid != NULL); - - memset (bssid, 0, sizeof (struct ether_addr)); - - fd = socket (PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - nm_log_err (LOGD_WIFI, "failed to open control socket."); - return; - } - - memset (&wrq, 0, sizeof (wrq)); - strncpy (wrq.ifr_name, nm_device_get_iface (NM_DEVICE (self)), IFNAMSIZ); - if (ioctl (fd, SIOCGIWAP, &wrq) == 0) - memcpy (bssid->ether_addr_octet, &(wrq.u.ap_addr.sa_data), ETH_ALEN); - - close (fd); -} - - static gboolean scanning_allowed (NMDeviceWifi *self) { @@ -2020,7 +1492,7 @@ scanning_allowed (NMDeviceWifi *self) /* Don't scan when a shared connection is active; it makes drivers mad */ connection = nm_act_request_get_connection (req); - s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (s_ip4) ip4_method = nm_setting_ip4_config_get_method (s_ip4); @@ -2031,7 +1503,7 @@ scanning_allowed (NMDeviceWifi *self) * intra-ESS roaming (which requires periodic scanning) isn't being * used due to the specific AP lock. (bgo #513820) */ - s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + 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) @@ -2174,15 +1646,10 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, if (check_scanning_allowed (self)) schedule_scan (self, TRUE); -#if 0 - if (num_results == 0) { - /* ensure that old APs get culled, which otherwise only - * happens when there are actual scan results to process. - */ - cull_scan_list (self); - ap_list_dump (self); - } -#endif + /* Ensure that old APs get removed, which otherwise only + * happens when there are new BSSes. + */ + schedule_scanlist_cull (self); } @@ -2217,9 +1684,7 @@ merge_scanned_ap (NMDeviceWifi *self, gboolean strict_match = TRUE; NMAccessPoint *current_ap = NULL; - /* Let the manager try to fill in the SSID from seen-bssids lists - * if it can - */ + /* 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)) { @@ -2252,7 +1717,9 @@ merge_scanned_ap (NMDeviceWifi *self, if (current_ap && nm_ap_get_fake (current_ap)) strict_match = FALSE; - found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match); + 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)), @@ -2261,6 +1728,7 @@ merge_scanned_ap (NMDeviceWifi *self, 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)); @@ -2289,25 +1757,18 @@ merge_scanned_ap (NMDeviceWifi *self, } } -static void +#define WPAS_REMOVED_TAG "supplicant-removed" + +static gboolean cull_scan_list (NMDeviceWifi *self) { - NMDeviceWifiPrivate *priv; - GTimeVal cur_time; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + GTimeVal now; GSList *outdated_list = NULL; GSList *elt; - NMActRequest *req; - const char *cur_ap_path = NULL; guint32 removed = 0, total = 0; - g_return_if_fail (self != NULL); - priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - g_get_current_time (&cur_time); - - req = nm_device_get_act_request (NM_DEVICE (self)); - if (req) - cur_ap_path = nm_act_request_get_specific_object (req); + 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))); @@ -2315,19 +1776,27 @@ cull_scan_list (NMDeviceWifi *self) /* Walk the access point list and remove any access points older than * three times the inactive scan interval. */ + g_get_current_time (&now); for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) { - NMAccessPoint * ap = NM_AP (elt->data); - const glong ap_time = nm_ap_get_last_seen (ap); - gboolean keep = FALSE; - const guint prune_interval_s = SCAN_INTERVAL_MAX * 3; + NMAccessPoint *ap = elt->data; + const guint prune_interval_s = SCAN_INTERVAL_MAX * 3; - /* Don't ever prune the AP we're currently associated with */ - if (cur_ap_path && !strcmp (cur_ap_path, nm_ap_get_dbus_path (ap))) - keep = TRUE; - if (nm_ap_get_fake (ap)) - keep = TRUE; + /* Don't cull the associated AP or manually created APs */ + if (ap == priv->current_ap || nm_ap_get_fake (ap)) + continue; + + /* 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; - if (!keep && (ap_time + prune_interval_s < cur_time.tv_sec)) + if (nm_ap_get_last_seen (ap) + prune_interval_s < now.tv_sec) outdated_list = g_slist_append (outdated_list, ap); } @@ -2358,10 +1827,26 @@ cull_scan_list (NMDeviceWifi *self) 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); + + 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) { @@ -2377,22 +1862,65 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, if (state <= NM_DEVICE_STATE_UNAVAILABLE) return; - ap = nm_ap_new_from_properties (properties); + 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); - - /* Remove outdated access points */ - cull_scan_list (self); - - ap_list_dump (self); } 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; + GTimeVal now; + + 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) { + g_get_current_time (&now); + nm_ap_set_last_seen (ap, now.tv_sec); + } + + /* 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)); } @@ -2921,23 +2449,6 @@ remove_supplicant_timeouts (NMDeviceWifi *self) remove_link_timeout (self); } -static guint32 -find_supported_frequency (NMDeviceWifi *self, const guint32 *freqs) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - int i; - - for (i = 0; i < priv->num_freqs; i++) { - while (*freqs) { - if (priv->freqs[i] == *freqs) - return *freqs; - freqs++; - } - } - - return 0; -} - static NMSupplicantConfig * build_supplicant_config (NMDeviceWifi *self, NMConnection *connection, @@ -2951,7 +2462,7 @@ build_supplicant_config (NMDeviceWifi *self, g_return_val_if_fail (self != NULL, NULL); - s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless != NULL, NULL); config = nm_supplicant_config_new (); @@ -2970,9 +2481,9 @@ build_supplicant_config (NMDeviceWifi *self, adhoc_freq = nm_ap_get_freq (ap); if (!adhoc_freq) { if (g_strcmp0 (band, "a") == 0) - adhoc_freq = find_supported_frequency (self, a_freqs); + adhoc_freq = wifi_utils_find_freq (priv->wifi_data, a_freqs); else - adhoc_freq = find_supported_frequency (self, bg_freqs); + adhoc_freq = wifi_utils_find_freq (priv->wifi_data, bg_freqs); } if (!adhoc_freq) { @@ -2987,22 +2498,22 @@ build_supplicant_config (NMDeviceWifi *self, s_wireless, nm_ap_get_broadcast (ap), adhoc_freq, - priv->has_scan_capa_ssid)) { + wifi_utils_can_scan_ssid (priv->wifi_data))) { nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config."); goto error; } - s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); if (s_wireless_sec) { NMSetting8021x *s_8021x; - const char *con_path = nm_connection_get_path (connection); + const char *con_uuid = nm_connection_get_uuid (connection); - g_assert (con_path); - s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); + 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_path)) { + con_uuid)) { nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to " "supplicant config."); goto error; @@ -3135,8 +2646,18 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) connection = nm_act_request_get_connection (req); g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + /* 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 */ - s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_connection_get_setting_wireless (connection); g_assert (s_wireless); cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless); @@ -3177,7 +2698,7 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, ap); } - nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (ap)); + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap)); done: set_current_ap (self, ap); @@ -3212,7 +2733,7 @@ real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) connection = nm_act_request_get_connection (req); g_assert (connection); - s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_connection_get_setting_wireless (connection); g_assert (s_wireless); /* If we need secrets, get them */ @@ -3287,38 +2808,22 @@ out: return ret; } -static NMActStageReturn -real_act_stage4_get_ip4_config (NMDevice *dev, - NMIP4Config **config, - NMDeviceStateReason *reason) +static void +real_ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) { - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - NMDeviceClass *parent_class; - - g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); - g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); - - /* Chain up to parent */ - parent_class = NM_DEVICE_CLASS (nm_device_wifi_parent_class); - ret = parent_class->act_stage4_get_ip4_config (dev, config, reason); - - if ((ret == NM_ACT_STAGE_RETURN_SUCCESS) && *config) { - NMConnection *connection; - NMSettingWireless *s_wireless; - guint32 mtu; - - connection = nm_act_request_get_connection (nm_device_get_act_request (dev)); - g_assert (connection); - s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS)); - g_assert (s_wireless); + NMConnection *connection; + NMSettingWireless *s_wifi; + guint32 mtu; - /* MTU override */ - mtu = nm_setting_wireless_get_mtu (s_wireless); - if (mtu) - nm_ip4_config_set_mtu (*config, mtu); - } + connection = nm_device_get_connection (device); + g_assert (connection); + s_wifi = nm_connection_get_setting_wireless (connection); + g_assert (s_wifi); - return ret; + /* MTU override */ + mtu = nm_setting_wireless_get_mtu (s_wifi); + if (mtu) + nm_ip4_config_set_mtu (config, mtu); } static gboolean @@ -3398,55 +2903,45 @@ handle_ip_config_timeout (NMDeviceWifi *self, static NMActStageReturn -real_act_stage4_ip4_config_timeout (NMDevice *dev, - NMIP4Config **config, - NMDeviceStateReason *reason) +real_act_stage4_ip4_config_timeout (NMDevice *dev, NMDeviceStateReason *reason) { - NMActRequest *req; NMConnection *connection; NMSettingIP4Config *s_ip4; gboolean may_fail = FALSE, chain_up = FALSE; NMActStageReturn ret; - req = nm_device_get_act_request (dev); - g_assert (req); - connection = nm_act_request_get_connection (req); + connection = nm_device_get_connection (dev); g_assert (connection); - s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + s_ip4 = nm_connection_get_setting_ip4_config (connection); if (s_ip4) 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, config, reason); + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (dev, reason); return ret; } static NMActStageReturn -real_act_stage4_ip6_config_timeout (NMDevice *dev, - NMIP6Config **config, - NMDeviceStateReason *reason) +real_act_stage4_ip6_config_timeout (NMDevice *dev, NMDeviceStateReason *reason) { - NMActRequest *req; NMConnection *connection; NMSettingIP6Config *s_ip6; gboolean may_fail = FALSE, chain_up = FALSE; NMActStageReturn ret; - req = nm_device_get_act_request (dev); - g_assert (req); - connection = nm_act_request_get_connection (req); + connection = nm_device_get_connection (dev); g_assert (connection); - s_ip6 = (NMSettingIP6Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_connection_get_setting_ip6_config (connection); if (s_ip6) 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, config, reason); + ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (dev, reason); return ret; } @@ -3483,13 +2978,13 @@ activation_success_handler (NMDevice *dev) * 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_device_wifi_get_bssid (self, &bssid); + wifi_utils_get_bssid (priv->wifi_data, &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_device_wifi_get_frequency (self)); + nm_ap_set_freq (ap, wifi_utils_get_freq (priv->wifi_data)); if (!nm_ap_get_max_bitrate (ap)) - nm_ap_set_max_bitrate (ap, nm_device_wifi_get_bitrate (self)); + nm_ap_set_max_bitrate (ap, wifi_utils_get_rate (priv->wifi_data)); tmp_ap = get_active_ap (self, ap, TRUE); if (tmp_ap) { @@ -3503,7 +2998,8 @@ activation_success_handler (NMDevice *dev) if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) nm_ap_set_ssid (tmp_ap, nm_ap_get_ssid (ap)); - nm_act_request_set_specific_object (req, nm_ap_get_dbus_path (tmp_ap)); + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), + nm_ap_get_dbus_path (tmp_ap)); priv->ap_list = g_slist_remove (priv->ap_list, ap); g_object_unref (ap); @@ -3526,13 +3022,9 @@ activation_failure_handler (NMDevice *dev) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMAccessPoint *ap; const GByteArray * ssid; - NMActRequest *req; NMConnection *connection; - req = nm_device_get_act_request (dev); - g_assert (req); - - connection = nm_act_request_get_connection (req); + connection = nm_device_get_connection (dev); g_assert (connection); /* Clear wireless secrets tries on failure */ @@ -3583,13 +3075,42 @@ spec_match_list (NMDevice *device, const GSList *specs) char *hwaddr; gboolean matched; - hwaddr = nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr); + hwaddr = nm_utils_hwaddr_ntoa (&priv->perm_hw_addr, ARPHRD_ETHER); matched = nm_match_spec_hwaddr (specs, hwaddr); g_free (hwaddr); return matched; } +static gboolean +hwaddr_matches (NMDevice *device, + NMConnection *connection, + const guint8 *other_hwaddr, + guint other_hwaddr_len, + gboolean fail_if_no_hwaddr) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); + NMSettingWireless *s_wifi; + const GByteArray *mac = NULL; + + s_wifi = nm_connection_get_setting_wireless (connection); + if (s_wifi) + mac = nm_setting_wireless_get_mac_address (s_wifi); + + if (mac) { + g_return_val_if_fail (mac->len == ETH_ALEN, FALSE); + if (other_hwaddr) { + g_return_val_if_fail (other_hwaddr_len == ETH_ALEN, FALSE); + if (memcmp (mac->data, other_hwaddr, mac->len) == 0) + return TRUE; + } else if (memcmp (mac->data, priv->hw_addr, mac->len) == 0) + return TRUE; + } else if (fail_if_no_hwaddr == FALSE) + return TRUE; + + return FALSE; +} + static void device_state_changed (NMDevice *device, NMDeviceState new_state, @@ -3632,12 +3153,6 @@ device_state_changed (NMDevice *device, * the device is now ready to use. */ if (priv->enabled && (nm_device_get_firmware_missing (device) == FALSE)) { - gboolean success; - struct iw_range range; - - /* Wait for some drivers like ipw3945 to come back to life */ - success = wireless_get_range (self, &range, NULL); - if (!priv->supplicant.iface) supplicant_interface_acquire (self); } @@ -3676,13 +3191,13 @@ nm_device_wifi_get_activation_ap (NMDeviceWifi *self) if (!req) return NULL; - ap_path = nm_act_request_get_specific_object (req); + ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req)); return ap_path ? get_ap_by_path (self, ap_path) : NULL; } static void -real_set_enabled (NMDeviceInterface *device, gboolean enabled) +real_set_enabled (NMDevice *device, gboolean enabled) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); @@ -3697,7 +3212,7 @@ real_set_enabled (NMDeviceInterface *device, gboolean enabled) nm_device_get_iface (NM_DEVICE (device)), enabled ? "enabled" : "disabled"); - state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); + 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", @@ -3706,8 +3221,7 @@ real_set_enabled (NMDeviceInterface *device, gboolean enabled) } if (enabled) { - gboolean no_firmware = FALSE, success; - struct iw_range range; + gboolean no_firmware = FALSE; if (state != NM_DEVICE_STATE_UNAVAILABLE) nm_log_warn (LOGD_CORE, "not in expected unavailable state!"); @@ -3725,9 +3239,6 @@ real_set_enabled (NMDeviceInterface *device, gboolean enabled) return; } - /* Wait for some drivers like ipw3945 to come back to life */ - success = wireless_get_range (self, &range, NULL); - /* Re-initialize the supplicant interface and wait for it to be ready */ if (priv->supplicant.iface) supplicant_interface_release (self); @@ -3755,22 +3266,16 @@ nm_device_wifi_new (const char *udi, g_return_val_if_fail (driver != NULL, NULL); return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIFI, - NM_DEVICE_INTERFACE_UDI, udi, - NM_DEVICE_INTERFACE_IFACE, iface, - NM_DEVICE_INTERFACE_DRIVER, driver, - NM_DEVICE_INTERFACE_TYPE_DESC, "802.11 WiFi", - NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, - NM_DEVICE_INTERFACE_RFKILL_TYPE, RFKILL_TYPE_WLAN, + NM_DEVICE_UDI, udi, + NM_DEVICE_IFACE, iface, + NM_DEVICE_DRIVER, driver, + 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 -device_interface_init (NMDeviceInterface *iface_class) -{ - iface_class->set_enabled = real_set_enabled; -} - -static void nm_device_wifi_init (NMDeviceWifi * self) { g_signal_connect (self, "state-changed", G_CALLBACK (device_state_changed), NULL); @@ -3802,14 +3307,12 @@ dispose (GObject *object) priv->supplicant.mgr = NULL; } - if (priv->ssid) { - g_byte_array_free (priv->ssid, TRUE); - priv->ssid = NULL; - } - set_current_ap (self, NULL); remove_all_aps (self); + if (priv->wifi_data) + wifi_utils_deinit (priv->wifi_data); + g_free (priv->ipw_rfkill_path); if (priv->ipw_rfkill_id) { g_source_remove (priv->ipw_rfkill_id); @@ -3828,13 +3331,13 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_HW_ADDRESS: - g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->hw_addr)); + g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->hw_addr, ARPHRD_ETHER)); break; case PROP_PERM_HW_ADDRESS: - g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr)); + g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->perm_hw_addr, ARPHRD_ETHER)); break; case PROP_MODE: - g_value_set_uint (value, nm_device_wifi_get_mode (device)); + g_value_set_uint (value, wifi_utils_get_mode (priv->wifi_data)); break; case PROP_BITRATE: g_value_set_uint (value, priv->rate); @@ -3906,15 +3409,17 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->is_available = real_is_available; parent_class->check_connection_compatible = real_check_connection_compatible; parent_class->complete_connection = real_complete_connection; + parent_class->set_enabled = real_set_enabled; parent_class->act_stage1_prepare = real_act_stage1_prepare; parent_class->act_stage2_config = real_act_stage2_config; - parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + parent_class->ip4_config_pre_commit = real_ip4_config_pre_commit; parent_class->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout; parent_class->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout; parent_class->deactivate = real_deactivate; parent_class->can_interrupt_activation = real_can_interrupt_activation; parent_class->spec_match_list = spec_match_list; + parent_class->hwaddr_matches = hwaddr_matches; klass->scanning_allowed = scanning_allowed; |