nm-device-wifi.c 122 KB
Newer Older
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 3 4 5 6 7 8 9 10 11 12 13
/* 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.
 *
14 15 16
 * 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.
17
 *
18
 * Copyright (C) 2005 - 2011 Red Hat, Inc.
19
 * Copyright (C) 2006 - 2008 Novell, Inc.
20 21 22 23 24 25 26 27 28
 */

#include <glib.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
#include <netinet/in.h>
#include <string.h>
#include <net/ethernet.h>
#include <iwlib.h>
29 30
#include <sys/stat.h>
#include <sys/wait.h>
31
#include <signal.h>
32
#include <unistd.h>
33 34 35
#include <linux/sockios.h>
#include <linux/ethtool.h>
#include <sys/ioctl.h>
36
#include <netinet/ether.h>
37

38
#include "nm-glib-compat.h"
39
#include "nm-device.h"
40
#include "nm-device-wifi.h"
41
#include "nm-device-interface.h"
42 43
#include "nm-device-private.h"
#include "nm-utils.h"
Dan Williams's avatar
Dan Williams committed
44
#include "nm-logging.h"
45
#include "nm-marshal.h"
46 47
#include "NetworkManagerUtils.h"
#include "nm-activation-request.h"
48 49
#include "nm-supplicant-manager.h"
#include "nm-supplicant-interface.h"
50
#include "nm-supplicant-config.h"
51
#include "nm-properties-changed-signal.h"
52 53
#include "nm-setting-connection.h"
#include "nm-setting-wireless.h"
54 55
#include "nm-setting-wireless-security.h"
#include "nm-setting-8021x.h"
56 57
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
58
#include "nm-system.h"
59
#include "nm-settings-connection.h"
60

61
static gboolean impl_device_get_access_points (NMDeviceWifi *device,
62 63
                                               GPtrArray **aps,
                                               GError **err);
64

65
#include "nm-device-wifi-glue.h"
66 67


68 69 70 71 72
/* All of these are in seconds */
#define SCAN_INTERVAL_MIN 0
#define SCAN_INTERVAL_STEP 20
#define SCAN_INTERVAL_MAX 120

73 74
#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"

75
static void device_interface_init (NMDeviceInterface *iface_class);
76

77 78
G_DEFINE_TYPE_EXTENDED (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE, 0,
                        G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE, device_interface_init))
79

80
#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate))
81

82 83 84 85

enum {
	PROP_0,
	PROP_HW_ADDRESS,
86
	PROP_PERM_HW_ADDRESS,
87 88
	PROP_MODE,
	PROP_BITRATE,
89
	PROP_ACTIVE_ACCESS_POINT,
90
	PROP_CAPABILITIES,
91
	PROP_SCANNING,
92
	PROP_IPW_RFKILL_STATE,
93 94 95 96

	LAST_PROP
};

97
enum {
98 99
	ACCESS_POINT_ADDED,
	ACCESS_POINT_REMOVED,
100
	HIDDEN_AP_FOUND,
101
	PROPERTIES_CHANGED,
102
	SCANNING_ALLOWED,
103 104 105 106 107 108

	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

109 110
#define SUP_SIG_ID_LEN 5

111
typedef struct Supplicant {
112 113
	NMSupplicantManager *mgr;
	NMSupplicantInterface *iface;
114

115
	guint sig_ids[SUP_SIG_ID_LEN];
116 117 118 119 120
	guint iface_error_id;

	/* Timeouts and idles */
	guint iface_con_error_cb_id;
	guint con_timeout_id;
121
} Supplicant;
122

123
struct _NMDeviceWifiPrivate {
124
	gboolean          disposed;
125

126 127 128
	guint8            hw_addr[ETH_ALEN];         /* Currently set MAC address */
	guint8            perm_hw_addr[ETH_ALEN];    /* Permanent MAC address */
	guint8            initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
129

130 131 132 133 134
	/* Legacy rfkill for ipw2x00; will be fixed with 2.6.33 kernel */
	char *            ipw_rfkill_path;
	guint             ipw_rfkill_id;
	RfKillState       ipw_rfkill_state;

135 136 137
	GByteArray *      ssid;
	gint8             invalid_strength_counter;
	iwqual            max_qual;
138

139 140
	gint8             num_freqs;
	guint32           freqs[IW_MAX_FREQUENCIES];
141

142 143 144 145
	GSList *          ap_list;
	NMAccessPoint *   current_ap;
	guint32           rate;
	gboolean          enabled; /* rfkilled or not */
146
	
147 148 149
	glong             scheduled_scan_time;
	guint8            scan_interval; /* seconds */
	guint             pending_scan_id;
150

151
	Supplicant        supplicant;
152

153 154 155
	guint32           failed_link_count;
	guint             periodic_source_id;
	guint             link_timeout_id;
156

157
	/* Static options from driver */
158 159
	guint32           capabilities;
	gboolean          has_scan_capa_ssid;
160 161
};

162 163 164
static guint32 nm_device_wifi_get_frequency (NMDeviceWifi *self);

static gboolean request_wireless_scan (gpointer user_data);
165

166
static void schedule_scan (NMDeviceWifi *self, gboolean backoff);
167

168
static void cancel_pending_scan (NMDeviceWifi *self);
169

170
static void cleanup_association_attempt (NMDeviceWifi * self,
171
                                         gboolean disconnect);
172

173
static void remove_supplicant_timeouts (NMDeviceWifi *self);
174

175
static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
176 177
                                       guint32 new_state,
                                       guint32 old_state,
178
                                       gpointer user_data);
179

180 181 182
static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
                                         GHashTable *properties,
                                         NMDeviceWifi * self);
183

184 185 186
static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface,
                                           gboolean success,
                                           NMDeviceWifi * self);
187

188 189 190 191
static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
                                                 GParamSpec * pspec,
                                                 NMDeviceWifi * self);

192
static guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *self);
193

194
static void cull_scan_list (NMDeviceWifi *self);
195

196 197 198 199 200 201 202 203 204 205 206 207
/*****************************************************************/

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 ())

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
static GQuark
nm_wifi_error_quark (void)
{
	static GQuark quark = 0;
	if (!quark)
		quark = g_quark_from_static_string ("nm-wifi-error");
	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"),
233 234
			/* Given access point was not in this device's scan list. */
			ENUM_ENTRY (NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, "AccessPointNotFound"),
235 236 237 238 239 240 241
			{ 0, 0, 0 }
		};
		etype = g_enum_register_static ("NMWifiError", values);
	}
	return etype;
}

242 243
/*****************************************************************/

244 245 246
/* IPW rfkill handling (until 2.6.33) */
RfKillState
nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self)
247
{
248 249 250
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	char *contents = NULL;
	RfKillState state = RFKILL_UNBLOCKED;
251
	const char *str_state = NULL;
252 253 254 255 256 257 258 259 260 261 262 263 264

	if (   priv->ipw_rfkill_path
	    && g_file_get_contents (priv->ipw_rfkill_path, &contents, NULL, NULL)) {
		contents = g_strstrip (contents);

		/* 0 - RF kill not enabled
		 * 1 - SW based RF kill active (sysfs)
		 * 2 - HW based RF kill active
		 * 3 - Both HW and SW baed RF kill active
		 */
		switch (contents[0]) {
		case '1':
			state = RFKILL_SOFT_BLOCKED;
265
			str_state = "soft-blocked";
266 267 268 269
			break;
		case '2':
		case '3':
			state = RFKILL_HARD_BLOCKED;
270
			str_state = "hard-blocked";
271 272
			break;
		case '0':
273
			str_state = "unblocked";
274 275 276 277
		default:
			break;
		}
		g_free (contents);
278 279 280 281

		nm_log_dbg (LOGD_RFKILL, "(%s): ipw rfkill state '%s'",
		            nm_device_get_iface (NM_DEVICE (self)),
		            str_state ? str_state : "(unknown)");
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	}

	return state;
}

static gboolean
ipw_rfkill_state_work (gpointer user_data)
{
	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	RfKillState old_state;

	old_state = priv->ipw_rfkill_state;
	priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self);
	if (priv->ipw_rfkill_state != old_state)
		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_IPW_RFKILL_STATE);

	return TRUE;
300 301
}

302 303
/*****************************************************************/

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
/*
 * 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));
}


405
/*
406
 * nm_device_wifi_update_signal_strength
407 408 409 410 411
 *
 * Update the device's idea of the strength of its connection to the
 * current access point.
 *
 */
412
static void
413
nm_device_wifi_update_signal_strength (NMDeviceWifi *self,
414
                                       NMAccessPoint *ap)
415
{
416
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
417
	const char *iface = nm_device_get_iface (NM_DEVICE (self));
418
	int fd, percent = -1;
419

420 421
	fd = socket (PF_INET, SOCK_DGRAM, 0);
	if (fd >= 0) {
422 423
		struct iwreq wrq;
		struct iw_statistics stats;
424

425
		memset (&stats, 0, sizeof (stats));
426

427 428
		wrq.u.data.pointer = &stats;
		wrq.u.data.length = sizeof (stats);
429
		wrq.u.data.flags = 1;  /* Clear updated flag */
430 431
		strncpy (wrq.ifr_name, iface, IFNAMSIZ);

432 433
		if (ioctl (fd, SIOCGIWSTATS, &wrq) == 0)
			percent = wireless_qual_to_percent (&stats.qual, &priv->max_qual);
434
		close (fd);
435 436 437 438 439
	}

	/* Try to smooth out the strength.  Atmel cards, for example, will give no strength
	 * one second and normal strength the next.
	 */
440
	if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
441
		nm_ap_set_strength (ap, (gint8) percent);
442
		priv->invalid_strength_counter = 0;
443 444 445
	}
}

446

447 448 449 450
static gboolean
wireless_get_range (NMDeviceWifi *self,
                    struct iw_range *range,
                    guint32 *response_len)
451
{
452
	int fd, i = 26;
453 454
	gboolean success = FALSE;
	const char *iface;
455
	struct iwreq wrq;
456

457 458 459 460
	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));
461

462 463
	fd = socket (PF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
Dan Williams's avatar
Dan Williams committed
464
		nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface);
465
		return FALSE;
466
	}
467

468 469
	memset (&wrq, 0, sizeof (struct iwreq));
	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
470
	wrq.u.data.pointer = (caddr_t) range;
471 472
	wrq.u.data.length = sizeof (struct iw_range);

473 474 475 476 477
	/* 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) {
478
		if (ioctl (fd, SIOCGIWRANGE, &wrq) == 0) {
479 480 481 482 483
			if (response_len)
				*response_len = wrq.u.data.length;
			success = TRUE;
			break;
		} else if (errno != EAGAIN) {
484 485
			nm_log_err (LOGD_HW | LOGD_WIFI,
			            "(%s): couldn't get driver range information (%d).",
Dan Williams's avatar
Dan Williams committed
486
			            iface, errno);
487 488 489 490
			break;
		}

		g_usleep (G_USEC_PER_SEC / 4);
491
	}
492

Dan Williams's avatar
Dan Williams committed
493
	if (i <= 0) {
494
		nm_log_warn (LOGD_HW | LOGD_WIFI,
Dan Williams's avatar
Dan Williams committed
495 496 497
		             "(%s): driver took too long to respond to IWRANGE query.",
		             iface);
	}
498 499 500 501 502 503 504 505 506

	close (fd);
	return success;
}

static guint32
real_get_generic_capabilities (NMDevice *dev)
{
	int fd, err;
507
	guint32 caps = NM_DEVICE_CAP_NONE;
508 509 510 511 512
	struct iwreq wrq;
	const char *iface = nm_device_get_iface (dev);

	fd = socket (PF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
Dan Williams's avatar
Dan Williams committed
513
		nm_log_err (LOGD_HW, "(%s): couldn't open control socket.", iface);
514
		return NM_DEVICE_CAP_NONE;
515
	}
516

517
	/* Cards that don't scan aren't supported */
518
	memset (&wrq, 0, sizeof (struct iwreq));
519 520
	strncpy (wrq.ifr_name, iface, IFNAMSIZ);
	err = ioctl (fd, SIOCSIWSCAN, &wrq);
521
	close (fd);
522
	if ((err < 0) && (errno == EOPNOTSUPP))
523
		caps = NM_DEVICE_CAP_NONE;
524 525
	else
		caps |= NM_DEVICE_CAP_NM_SUPPORTED;
526 527 528 529

	return caps;
}

530 531 532 533
#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)
534

535
static guint32
536
get_wireless_capabilities (NMDeviceWifi *self, iwrange *range)
537
{
538
	guint32 caps = NM_WIFI_DEVICE_CAP_NONE;
539
	const char * iface;
540

541 542
	g_return_val_if_fail (self != NULL, NM_WIFI_DEVICE_CAP_NONE);
	g_return_val_if_fail (range != NULL, NM_WIFI_DEVICE_CAP_NONE);
543

544 545
	iface = nm_device_get_iface (NM_DEVICE (self));

546
	/* All drivers should support WEP by default */
547
	caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40 | NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
548

549 550
	if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
		caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP;
551

552 553
	if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
		caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP;
554

555 556
	if (range->enc_capa & IW_ENC_CAPA_WPA)
		caps |= NM_WIFI_DEVICE_CAP_WPA;
557

558 559
	if (range->enc_capa & IW_ENC_CAPA_WPA2)
		caps |= NM_WIFI_DEVICE_CAP_RSN;
560

561 562 563 564 565 566 567
	/* 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;
	}
568

569 570 571 572 573 574
	/* 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;
575 576 577 578 579 580
	}

	return caps;
}


581 582 583 584 585 586 587 588 589 590 591 592
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;
	}

Dan Williams's avatar
Dan Williams committed
593
	return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000);
594 595
}

596 597 598 599 600

/* 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 */
601 602
#define NM_IW_SCAN_CAPA_NONE    0x00
#define NM_IW_SCAN_CAPA_ESSID   0x01
603 604 605

struct iw_range_with_scan_capa
{
606 607 608 609 610
	guint32 throughput;
	guint32 min_nwid;
	guint32 max_nwid;
	guint16 old_num_channels;
	guint8  old_num_frequency;
611

612
	guint8  scan_capa;
613 614 615 616
/* don't need the rest... */
};


617 618
static GObject*
constructor (GType type,
619 620
             guint n_construct_params,
             GObjectConstructParam *construct_params)
621 622
{
	GObject *object;
623
	GObjectClass *klass;
624 625
	NMDeviceWifi *self;
	NMDeviceWifiPrivate *priv;
626
	struct iw_range range;
627
	struct iw_range_with_scan_capa *scan_capa_range;
628
	guint32 response_len = 0;
629 630
	gboolean success;
	int i;
631

632
	klass = G_OBJECT_CLASS (nm_device_wifi_parent_class);
633
	object = klass->constructor (type, n_construct_params, construct_params);
634 635 636
	if (!object)
		return NULL;

637 638
	self = NM_DEVICE_WIFI (object);
	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
639

640
	nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d",
641 642
	            nm_device_get_iface (NM_DEVICE (self)),
	            nm_device_get_ifindex (NM_DEVICE (self)));
643

644
	memset (&range, 0, sizeof (struct iw_range));
645
	success = wireless_get_range (NM_DEVICE_WIFI (object), &range, &response_len);
646 647 648
	if (!success) {
		nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver WEXT range request failed",
		             nm_device_get_iface (NM_DEVICE (self)));
649
		goto error;
650 651 652 653 654 655 656 657 658
	}

	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;
	}
659

660 661 662 663
	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;
664

665 666
	priv->num_freqs = MIN (range.num_frequency, IW_MAX_FREQUENCIES);
	for (i = 0; i < priv->num_freqs; i++)
667
		priv->freqs[i] = iw_freq_to_uint32 (&range.freq[i]);
668

669 670 671 672 673
	/* 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 *) &range;
Dan Williams's avatar
Dan Williams committed
674
	if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) {
675
		priv->has_scan_capa_ssid = TRUE;
676 677
		nm_log_info (LOGD_HW | LOGD_WIFI,
		             "(%s): driver supports SSID scans (scan_capa 0x%02X).",
Dan Williams's avatar
Dan Williams committed
678 679
		             nm_device_get_iface (NM_DEVICE (self)),
		             scan_capa_range->scan_capa);
Dan Williams's avatar
Dan Williams committed
680
	} else {
681 682
		nm_log_info (LOGD_HW | LOGD_WIFI,
		             "(%s): driver does not support SSID scans (scan_capa 0x%02X).",
Dan Williams's avatar
Dan Williams committed
683 684
		             nm_device_get_iface (NM_DEVICE (self)),
		             scan_capa_range->scan_capa);
Dan Williams's avatar
Dan Williams committed
685
	}
686

687
	/* 802.11 wireless-specific capabilities */
688
	priv->capabilities = get_wireless_capabilities (self, &range);
689

690 691
	/* Connect to the supplicant manager */
	priv->supplicant.mgr = nm_supplicant_manager_get ();
692
	g_assert (priv->supplicant.mgr);
693

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
	/* The ipw2x00 drivers don't integrate with the kernel rfkill subsystem until
	 * 2.6.33.  Thus all our nice libgudev magic is useless.  So we get to poll.
	 *
	 * FIXME: when 2.6.33 comes lands, we can do some sysfs parkour to figure out
	 * if we need to poll or not by matching /sys/class/net/ethX/device to one
	 * of the /sys/class/rfkill/rfkillX/device links.  If there's a match, we
	 * don't have to poll.
	 */
	priv->ipw_rfkill_path = g_strdup_printf ("/sys/class/net/%s/device/rf_kill",
	                                         nm_device_get_iface (NM_DEVICE (self)));
	if (!g_file_test (priv->ipw_rfkill_path, G_FILE_TEST_IS_REGULAR)) {
		g_free (priv->ipw_rfkill_path);
		priv->ipw_rfkill_path = NULL;
	}
	priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self);

710
	return object;
711 712 713 714

error:
	g_object_unref (object);
	return NULL;
715 716
}

717 718
static gboolean
supplicant_interface_acquire (NMDeviceWifi *self)
719
{
720
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
721
	guint id, i = 0;
722

723 724 725
	g_return_val_if_fail (self != NULL, FALSE);
	/* interface already acquired? */
	g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE);
726

727
	priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr,
728 729 730
	                                                          nm_device_get_iface (NM_DEVICE (self)),
	                                                          TRUE);
	if (priv->supplicant.iface == NULL) {
Dan Williams's avatar
Dan Williams committed
731
		nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.",
732
		            nm_device_get_iface (NM_DEVICE (self)));
733 734 735
		return FALSE;
	}

736 737
	memset (priv->supplicant.sig_ids, 0, sizeof (priv->supplicant.sig_ids));

738
	id = g_signal_connect (priv->supplicant.iface,
739
	                       NM_SUPPLICANT_INTERFACE_STATE,
740 741
	                       G_CALLBACK (supplicant_iface_state_cb),
	                       self);
742
	priv->supplicant.sig_ids[i++] = id;
743 744

	id = g_signal_connect (priv->supplicant.iface,
745 746
	                       NM_SUPPLICANT_INTERFACE_NEW_BSS,
	                       G_CALLBACK (supplicant_iface_new_bss_cb),
747
	                       self);
748
	priv->supplicant.sig_ids[i++] = id;
749 750

	id = g_signal_connect (priv->supplicant.iface,
751 752
	                       NM_SUPPLICANT_INTERFACE_SCAN_DONE,
	                       G_CALLBACK (supplicant_iface_scan_done_cb),
753
	                       self);
754
	priv->supplicant.sig_ids[i++] = id;
755

756 757 758 759
	id = g_signal_connect (priv->supplicant.iface,
	                       "notify::scanning",
	                       G_CALLBACK (supplicant_iface_notify_scanning_cb),
	                       self);
760
	priv->supplicant.sig_ids[i++] = id;
761

762 763 764
	return TRUE;
}

765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
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;
	}
}

784 785 786 787
static void
supplicant_interface_release (NMDeviceWifi *self)
{
	NMDeviceWifiPrivate *priv;
788
	guint i;
789 790 791 792 793 794 795 796 797

	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;
798
	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds",
799 800
	            nm_device_get_iface (NM_DEVICE (self)),
	            priv->scan_interval);
801

802 803
	remove_supplicant_interface_error_handler (self);

804 805 806 807
	/* Clear supplicant interface signal handlers */
	for (i = 0; i < SUP_SIG_ID_LEN; i++) {
		if (priv->supplicant.sig_ids[i] > 0)
			g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.sig_ids[i]);
808
	}
809
	memset (priv->supplicant.sig_ids, 0, sizeof (priv->supplicant.sig_ids));
810

811 812 813
	if (priv->supplicant.iface) {
		/* Tell the supplicant to disconnect from the current AP */
		nm_supplicant_interface_disconnect (priv->supplicant.iface);
814

815
		nm_supplicant_manager_iface_release (priv->supplicant.mgr, priv->supplicant.iface);
816
		priv->supplicant.iface = NULL;
817 818 819
	}
}

820 821 822 823 824 825 826 827 828 829 830 831 832 833

static NMAccessPoint *
get_ap_by_path (NMDeviceWifi *self, const char *path)
{
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	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)
			return NM_AP (iter->data);
	}
	return NULL;
}

834
static NMAccessPoint *
835
get_active_ap (NMDeviceWifi *self,
836 837
               NMAccessPoint *ignore_ap,
               gboolean match_hidden)
838
{
839
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
840
	const char *iface = nm_device_get_iface (NM_DEVICE (self));
841
	struct ether_addr bssid;
842 843
	const GByteArray *ssid;
	GSList *iter;
844
	int i = 0;
845 846 847 848 849
	NMAccessPoint *match_nofreq = NULL;
	gboolean found_a_band = FALSE;
	gboolean found_bg_band = FALSE;
	NM80211Mode devmode;
	guint32 devfreq;
850

851
	nm_device_wifi_get_bssid (self, &bssid);
Dan Williams's avatar
Dan Williams committed
852 853 854 855 856 857
	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]);

858 859 860
	if (!nm_ethernet_address_is_valid (&bssid))
		return NULL;

861
	ssid = nm_device_wifi_get_ssid (self);
Dan Williams's avatar
Dan Williams committed
862 863 864 865 866
	nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s",
	            iface,
	            ssid ? "'" : "",
	            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
	            ssid ? "'" : "");
867

868 869 870
	devmode = nm_device_wifi_get_mode (self);
	devfreq = nm_device_wifi_get_frequency (self);

871 872 873 874 875
	/* 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)) {
Dan Williams's avatar
Dan Williams committed
876
		nm_log_dbg (LOGD_WIFI, "  Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : "");
877

878
		/* Find this SSID + BSSID in the device's AP list */
879
		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
880 881 882
			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);
883 884
			NM80211Mode apmode;
			guint32 apfreq;
885

Dan Williams's avatar
Dan Williams committed
886 887 888 889 890 891 892
			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]);
893 894

			if (ignore_ap && (ap == ignore_ap)) {
Dan Williams's avatar
Dan Williams committed
895
				nm_log_dbg (LOGD_WIFI, "      ignored");
896
				continue;
897
			}
898

899
			if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) {
Dan Williams's avatar
Dan Williams committed
900
				nm_log_dbg (LOGD_WIFI, "      BSSID mismatch");
901
				continue;
902
			}
903

904
			if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) {
Dan Williams's avatar
Dan Williams committed
905
				nm_log_dbg (LOGD_WIFI, "      SSID mismatch");
906
				continue;
907 908 909 910
			}

			apmode = nm_ap_get_mode (ap);
			if (devmode != apmode) {
Dan Williams's avatar
Dan Williams committed
911 912
				nm_log_dbg (LOGD_WIFI, "      mode mismatch (device %d, ap %d)",
				            devmode, apmode);
913
				continue;
914 915 916 917
			}

			apfreq = nm_ap_get_freq (ap);
			if (devfreq != apfreq) {
Dan Williams's avatar
Dan Williams committed
918 919
				nm_log_dbg (LOGD_WIFI, "      frequency mismatch (device %u, ap %u)",
				            devfreq, apfreq);
920 921 922 923 924 925 926 927

				if (match_nofreq == NULL)
					match_nofreq = ap;

				if (apfreq > 4000)
					found_a_band = TRUE;
				else if (apfreq > 2000)
					found_bg_band = TRUE;
928
				continue;
929
			}
930

931
			// FIXME: handle security settings here too
Dan Williams's avatar
Dan Williams committed
932
			nm_log_dbg (LOGD_WIFI, "      matched");
933 934
			return ap;
		}
935 936
	}

937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
	/* 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.
	 */
	if (match_nofreq && (found_a_band != found_bg_band)) {
		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]);

		return match_nofreq;
	}

Dan Williams's avatar
Dan Williams committed
963
	nm_log_dbg (LOGD_WIFI, "  No matching AP found.");
964 965
	return NULL;
}
966

967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
static void
update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
{
	NMActRequest *req;
	NMConnection *connection;

	g_return_if_fail (ap != NULL);

	/* 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));
		}
	}
}

989
static void
990
set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap)
991
{
992
	NMDeviceWifiPrivate *priv;
993
	char *old_path = NULL;
994
	NMAccessPoint *old_ap;
995

996
	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
997

998 999 1000 1001 1002
	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	old_ap = priv->current_ap;

	if (old_ap) {
		old_path = g_strdup (nm_ap_get_dbus_path (old_ap));
1003 1004 1005
		priv->current_ap = NULL;
	}

1006
	if (new_ap) {
1007 1008
		priv->current_ap = g_object_ref (new_ap);

1009 1010 1011 1012 1013 1014
		/* 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);
1015 1016 1017

		/* Update seen BSSIDs cache */
		update_seen_bssids_cache (self, priv->current_ap);
1018 1019 1020 1021 1022 1023
	}

	/* Unref old AP here to ensure object lives if new_ap == old_ap */
	if (old_ap)
		g_object_unref (old_ap);

1024
	/* Only notify if it's really changed */
1025 1026 1027
	if (   (!old_path && new_ap)
	    || (old_path && !new_ap)
	    || (old_path && new_ap && strcmp (old_path, nm_ap_get_dbus_path (new_ap))))
1028
		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT);
1029 1030 1031 1032

	g_free (old_path);
}

1033
static void
1034
periodic_update (NMDeviceWifi *self)
1035
{
1036
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
1037
	NMAccessPoint *new_ap;
1038
	guint32 new_rate;
1039

1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
	/* 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 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_device_wifi_get_bssid (self, &bssid);
1052 1053 1054 1055
		/* 0x02 means "locally administered" and should be OR-ed into
		 * the first byte of IBSS BSSIDs.
		 */
		if (   (bssid.ether_addr_octet[0] & 0x02)
1056 1057 1058 1059
		    && nm_ethernet_address_is_valid (&bssid))
			nm_ap_set_address (priv->current_ap, &bssid);
	}

1060
	new_ap = get_active_ap (self, NULL, FALSE);
1061
	if (new_ap)
1062
		nm_device_wifi_update_signal_strength (self, new_ap);
1063

1064
	if ((new_ap || priv->current_ap) && (new_ap != priv->current_ap)) {
1065 1066 1067 1068
		const struct ether_addr *new_bssid = NULL;
		const GByteArray *new_ssid = NULL;
		const struct ether_addr *old_bssid = NULL;
		const GByteArray *old_ssid = NULL;
1069
		char *old_addr = NULL, *new_addr = NULL;
1070 1071 1072

		if (new_ap) {
			new_bssid = nm_ap_get_address (new_ap);
1073
			new_addr = nm_ether_ntop (new_bssid);
1074 1075 1076
			new_ssid = nm_ap_get_ssid (new_ap);
		}

1077 1078
		if (priv->current_ap) {
			old_bssid = nm_ap_get_address (priv->current_ap);
1079
			old_addr = nm_ether_ntop (old_bssid);
1080
			old_ssid = nm_ap_get_ssid (priv->current_ap);
1081 1082
		}

Dan Williams's avatar
Dan Williams committed
1083 1084 1085 1086 1087 1088
		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)");
1089 1090
		g_free (old_addr);
		g_free (new_addr);
1091

1092
		set_current_ap (self, new_ap);
1093
	}
1094

1095
	new_rate = nm_device_wifi_get_bitrate (self);
1096 1097
	if (new_rate != priv->rate) {
		priv->rate = new_rate;
1098
		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE);
1099 1100 1101 1102
	}
}

/*
1103
 * nm_device_wifi_periodic_update
1104 1105 1106 1107 1108
 *
 * Periodically update device statistics.
 *
 */
static gboolean
1109
nm_device_wifi_periodic_update (gpointer data)
1110
{
1111 1112
	NMDeviceWifi *self = NM_DEVICE_WIFI (data);
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
1113 1114 1115 1116 1117 1118 1119 1120
	NMDeviceState 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)
		goto out;

1121
	if (nm_supplicant_interface_get_scanning (priv->supplicant.iface))
1122 1123 1124
		goto out;

	periodic_update (self);
1125