nm-device-wifi.c 119 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 - 2012 Red Hat, Inc.
19
 * Copyright (C) 2006 - 2008 Novell, Inc.
20
21
22
23
24
25
26
27
 */

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

38
#include "nm-glib-compat.h"
39
#include "nm-dbus-manager.h"
40
#include "nm-device.h"
41
#include "nm-device-wifi.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
46
#include "NetworkManagerUtils.h"
#include "nm-activation-request.h"
47
48
#include "nm-supplicant-manager.h"
#include "nm-supplicant-interface.h"
49
#include "nm-supplicant-config.h"
50
51
#include "nm-setting-connection.h"
#include "nm-setting-wireless.h"
52
53
#include "nm-setting-wireless-security.h"
#include "nm-setting-8021x.h"
54
55
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
56
#include "nm-platform.h"
57
#include "nm-manager-auth.h"
58
#include "nm-settings-connection.h"
59
#include "nm-enum-types.h"
60
#include "wifi-utils.h"
61
62
#include "nm-dbus-glib-types.h"

63

64
static gboolean impl_device_get_access_points (NMDeviceWifi *device,
65
66
                                               GPtrArray **aps,
                                               GError **err);
67

68
69
70
71
static gboolean impl_device_get_all_access_points (NMDeviceWifi *device,
                                                   GPtrArray **aps,
                                                   GError **err);

72
73
74
75
static void impl_device_request_scan (NMDeviceWifi *device,
                                      GHashTable *options,
                                      DBusGMethodInvocation *context);

76
#include "nm-device-wifi-glue.h"
77
78


79
/* All of these are in seconds */
80
#define SCAN_INTERVAL_MIN 3
81
82
83
#define SCAN_INTERVAL_STEP 20
#define SCAN_INTERVAL_MAX 120

84
85
#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"

86
G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
87

88
#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate))
89

90
91
92

enum {
	PROP_0,
93
	PROP_PERM_HW_ADDRESS,
94
95
	PROP_MODE,
	PROP_BITRATE,
96
	PROP_ACCESS_POINTS,
97
	PROP_ACTIVE_ACCESS_POINT,
98
	PROP_CAPABILITIES,
99
	PROP_SCANNING,
100
	PROP_IPW_RFKILL_STATE,
101
102
103
104

	LAST_PROP
};

105
enum {
106
107
	ACCESS_POINT_ADDED,
	ACCESS_POINT_REMOVED,
108
	HIDDEN_AP_FOUND,
109
	SCANNING_ALLOWED,
110
111
112
113
114
115

	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

116
typedef struct Supplicant {
117
118
	NMSupplicantManager *mgr;
	NMSupplicantInterface *iface;
119

120
121
122
123
124
	guint iface_error_id;

	/* Timeouts and idles */
	guint iface_con_error_cb_id;
	guint con_timeout_id;
125
} Supplicant;
126

127
struct _NMDeviceWifiPrivate {
128
	gboolean          disposed;
129

130
131
	guint8            perm_hw_addr[ETH_ALEN];    /* Permanent MAC address */
	guint8            initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
132

133
134
135
136
137
	/* Legacy rfkill for ipw2x00; will be fixed with 2.6.33 kernel */
	char *            ipw_rfkill_path;
	guint             ipw_rfkill_id;
	RfKillState       ipw_rfkill_state;

138
	gint8             invalid_strength_counter;
139

140
141
142
143
	GSList *          ap_list;
	NMAccessPoint *   current_ap;
	guint32           rate;
	gboolean          enabled; /* rfkilled or not */
Thomas Haller's avatar
Thomas Haller committed
144

145
	gint32            scheduled_scan_time;
146
147
	guint8            scan_interval; /* seconds */
	guint             pending_scan_id;
148
	guint             scanlist_cull_id;
149
	gboolean          requested_scan;
150

151
	Supplicant        supplicant;
152
	WifiData *        wifi_data;
153
	gboolean          ssid_found;
Dan Williams's avatar
Dan Williams committed
154
	NM80211Mode       mode;
155

156
157
158
	guint32           failed_link_count;
	guint             periodic_source_id;
	guint             link_timeout_id;
159

160
	NMDeviceWifiCapabilities capabilities;
161
162
};

163
164
static gboolean check_scanning_allowed (NMDeviceWifi *self);

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

167
static void cancel_pending_scan (NMDeviceWifi *self);
168

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

172
static void remove_supplicant_timeouts (NMDeviceWifi *self);
173

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

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

185
186
187
188
189
static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
                                             const char *object_path,
                                             GHashTable *properties,
                                             NMDeviceWifi *self);

190
191
192
193
static void supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
                                             const char *object_path,
                                             NMDeviceWifi *self);

194
195
196
static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface,
                                           gboolean success,
                                           NMDeviceWifi * self);
197

198
199
200
201
static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
                                                 GParamSpec * pspec,
                                                 NMDeviceWifi * self);

202
static void schedule_scanlist_cull (NMDeviceWifi *self);
203

204
205
static gboolean request_wireless_scan (gpointer user_data);

206
207
static void remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap);

208
209
210
211
/*****************************************************************/

#define NM_WIFI_ERROR (nm_wifi_error_quark ())

212
213
214
215
216
217
218
219
220
static GQuark
nm_wifi_error_quark (void)
{
	static GQuark quark = 0;
	if (!quark)
		quark = g_quark_from_static_string ("nm-wifi-error");
	return quark;
}

221
222
/*****************************************************************/

223
224
225
/* IPW rfkill handling (until 2.6.33) */
RfKillState
nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self)
226
{
227
228
229
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	char *contents = NULL;
	RfKillState state = RFKILL_UNBLOCKED;
230
	const char *str_state = NULL;
231
232
233
234
235
236
237
238
239
240
241
242
243

	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;
244
			str_state = "soft-blocked";
245
246
247
248
			break;
		case '2':
		case '3':
			state = RFKILL_HARD_BLOCKED;
249
			str_state = "hard-blocked";
250
251
			break;
		case '0':
252
			str_state = "unblocked";
253
254
255
256
		default:
			break;
		}
		g_free (contents);
257
258
259
260

		nm_log_dbg (LOGD_RFKILL, "(%s): ipw rfkill state '%s'",
		            nm_device_get_iface (NM_DEVICE (self)),
		            str_state ? str_state : "(unknown)");
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
	}

	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;
279
280
}

281
282
/*****************************************************************/

283
284
static GObject*
constructor (GType type,
285
286
             guint n_construct_params,
             GObjectConstructParam *construct_params)
287
288
{
	GObject *object;
289
	GObjectClass *klass;
290
291
	NMDeviceWifi *self;
	NMDeviceWifiPrivate *priv;
292

293
	klass = G_OBJECT_CLASS (nm_device_wifi_parent_class);
294
	object = klass->constructor (type, n_construct_params, construct_params);
295
296
297
	if (!object)
		return NULL;

298
299
	self = NM_DEVICE_WIFI (object);
	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
300

301
	nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d",
302
303
	            nm_device_get_iface (NM_DEVICE (self)),
	            nm_device_get_ifindex (NM_DEVICE (self)));
304

305
	priv->wifi_data = wifi_utils_init (nm_device_get_iface (NM_DEVICE (self)),
306
307
	                                   nm_device_get_ifindex (NM_DEVICE (self)),
	                                   TRUE);
308
309
	if (priv->wifi_data == NULL) {
		nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): failed to initialize WiFi driver",
310
		             nm_device_get_iface (NM_DEVICE (self)));
311
312
		g_object_unref (object);
		return NULL;
Dan Williams's avatar
Dan Williams committed
313
	}
314
	priv->capabilities = wifi_utils_get_caps (priv->wifi_data);
315

316
	if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP) {
317
		nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver supports Access Point (AP) mode",
318
319
320
		             nm_device_get_iface (NM_DEVICE (self)));
	}

321
322
	/* Connect to the supplicant manager */
	priv->supplicant.mgr = nm_supplicant_manager_get ();
323
	g_assert (priv->supplicant.mgr);
324

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
	/* 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);

341
342
343
	return object;
}

344
345
static gboolean
supplicant_interface_acquire (NMDeviceWifi *self)
346
{
347
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
348

349
350
351
	g_return_val_if_fail (self != NULL, FALSE);
	/* interface already acquired? */
	g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE);
352

353
	priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr,
354
355
356
	                                                          nm_device_get_iface (NM_DEVICE (self)),
	                                                          TRUE);
	if (priv->supplicant.iface == NULL) {
Dan Williams's avatar
Dan Williams committed
357
		nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.",
358
		            nm_device_get_iface (NM_DEVICE (self)));
359
360
361
		return FALSE;
	}

362
363
364
	if (nm_supplicant_interface_get_state (priv->supplicant.iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
		nm_device_add_pending_action (NM_DEVICE (self), "waiting for supplicant");

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
	g_signal_connect (priv->supplicant.iface,
	                  NM_SUPPLICANT_INTERFACE_STATE,
	                  G_CALLBACK (supplicant_iface_state_cb),
	                  self);
	g_signal_connect (priv->supplicant.iface,
	                  NM_SUPPLICANT_INTERFACE_NEW_BSS,
	                  G_CALLBACK (supplicant_iface_new_bss_cb),
	                  self);
	g_signal_connect (priv->supplicant.iface,
	                  NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
	                  G_CALLBACK (supplicant_iface_bss_updated_cb),
	                  self);
	g_signal_connect (priv->supplicant.iface,
	                  NM_SUPPLICANT_INTERFACE_BSS_REMOVED,
	                  G_CALLBACK (supplicant_iface_bss_removed_cb),
	                  self);
	g_signal_connect (priv->supplicant.iface,
	                  NM_SUPPLICANT_INTERFACE_SCAN_DONE,
	                  G_CALLBACK (supplicant_iface_scan_done_cb),
	                  self);
	g_signal_connect (priv->supplicant.iface,
	                  "notify::scanning",
	                  G_CALLBACK (supplicant_iface_notify_scanning_cb),
	                  self);
389

390
391
392
	return TRUE;
}

393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
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;
	}
}

412
413
414
415
416
417
418
419
420
421
422
423
424
static void
supplicant_interface_release (NMDeviceWifi *self)
{
	NMDeviceWifiPrivate *priv;

	g_return_if_fail (self != NULL);

	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);

	cancel_pending_scan (self);

	/* Reset the scan interval to be pretty frequent when disconnected */
	priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP;
Dan Williams's avatar
Dan Williams committed
425
	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds",
426
427
	            nm_device_get_iface (NM_DEVICE (self)),
	            priv->scan_interval);
428

429
430
	remove_supplicant_interface_error_handler (self);

431
432
433
434
435
	if (priv->scanlist_cull_id) {
		g_source_remove (priv->scanlist_cull_id);
		priv->scanlist_cull_id = 0;
	}

436
	if (priv->supplicant.iface) {
437
438
439
440
		/* Clear supplicant interface signal handlers */
		g_signal_handlers_disconnect_matched (priv->supplicant.iface, G_SIGNAL_MATCH_DATA,
		                                      0, 0, NULL, NULL, self);

441
442
		/* Tell the supplicant to disconnect from the current AP */
		nm_supplicant_interface_disconnect (priv->supplicant.iface);
443

444
		nm_supplicant_manager_iface_release (priv->supplicant.mgr, priv->supplicant.iface);
445
		priv->supplicant.iface = NULL;
446
447
448
	}
}

449
450
451
452
453
454
static NMAccessPoint *
get_ap_by_path (NMDeviceWifi *self, const char *path)
{
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	GSList *iter;

455
456
457
	if (!path)
		return NULL;

458
	for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
459
		if (g_strcmp0 (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0)
460
461
462
463
464
			return NM_AP (iter->data);
	}
	return NULL;
}

465
466
467
468
469
470
static NMAccessPoint *
get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path)
{
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	GSList *iter;

471
472
473
474
	if (!path)
		return NULL;

	for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
475
		if (g_strcmp0 (path, nm_ap_get_supplicant_path (NM_AP (iter->data))) == 0)
476
477
478
479
480
			return NM_AP (iter->data);
	}
	return NULL;
}

481
static NMAccessPoint *
482
483
484
find_active_ap (NMDeviceWifi *self,
                NMAccessPoint *ignore_ap,
                gboolean match_hidden)
485
{
486
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
487
	const char *iface = nm_device_get_iface (NM_DEVICE (self));
488
	struct ether_addr bssid;
489
	GByteArray *ssid;
490
	GSList *iter;
491
	int i = 0;
492
	NMAccessPoint *match_nofreq = NULL, *active_ap = NULL;
493
494
495
496
	gboolean found_a_band = FALSE;
	gboolean found_bg_band = FALSE;
	NM80211Mode devmode;
	guint32 devfreq;
497

498
	wifi_utils_get_bssid (priv->wifi_data, &bssid);
Dan Williams's avatar
Dan Williams committed
499
500
501
502
503
504
	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]);

505
506
507
	if (!nm_ethernet_address_is_valid (&bssid))
		return NULL;

508
	ssid = wifi_utils_get_ssid (priv->wifi_data);
Dan Williams's avatar
Dan Williams committed
509
510
511
512
513
	nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s",
	            iface,
	            ssid ? "'" : "",
	            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
	            ssid ? "'" : "");
514

515
516
	devmode = wifi_utils_get_mode (priv->wifi_data);
	devfreq = wifi_utils_get_freq (priv->wifi_data);
517

518
519
520
521
522
	/* 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
523
		nm_log_dbg (LOGD_WIFI, "  Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : "");
524

525
		/* Find this SSID + BSSID in the device's AP list */
526
		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
527
528
529
			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);
530
531
			NM80211Mode apmode;
			guint32 apfreq;
532

Dan Williams's avatar
Dan Williams committed
533
534
535
536
537
538
539
			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]);
540

541
			if (ap == ignore_ap) {
Dan Williams's avatar
Dan Williams committed
542
				nm_log_dbg (LOGD_WIFI, "      ignored");
543
				continue;
544
			}
545

546
			if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) {
Dan Williams's avatar
Dan Williams committed
547
				nm_log_dbg (LOGD_WIFI, "      BSSID mismatch");
548
				continue;
549
			}
550

551
			if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) {
Dan Williams's avatar
Dan Williams committed
552
				nm_log_dbg (LOGD_WIFI, "      SSID mismatch");
553
				continue;
554
555
556
557
			}

			apmode = nm_ap_get_mode (ap);
			if (devmode != apmode) {
Dan Williams's avatar
Dan Williams committed
558
559
				nm_log_dbg (LOGD_WIFI, "      mode mismatch (device %d, ap %d)",
				            devmode, apmode);
560
				continue;
561
562
563
564
			}

			apfreq = nm_ap_get_freq (ap);
			if (devfreq != apfreq) {
Dan Williams's avatar
Dan Williams committed
565
566
				nm_log_dbg (LOGD_WIFI, "      frequency mismatch (device %u, ap %u)",
				            devfreq, apfreq);
567
568
569
570
571
572
573
574

				if (match_nofreq == NULL)
					match_nofreq = ap;

				if (apfreq > 4000)
					found_a_band = TRUE;
				else if (apfreq > 2000)
					found_bg_band = TRUE;
575
				continue;
576
			}
577

578
			// FIXME: handle security settings here too
Dan Williams's avatar
Dan Williams committed
579
			nm_log_dbg (LOGD_WIFI, "      matched");
580
581
			active_ap = ap;
			goto done;
582
		}
583
584
	}

585
586
587
588
589
590
591
592
593
	/* 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
594
595
	 * 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.
596
	 */
597
	if (match_nofreq && ((found_a_band != found_bg_band) || (devfreq == 0))) {
598
599
600
601
602
603
604
605
606
607
608
		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]);

609
		active_ap = match_nofreq;
610
		goto done;
611
612
	}

Dan Williams's avatar
Dan Williams committed
613
	nm_log_dbg (LOGD_WIFI, "  No matching AP found.");
614
615
616
617
618

done:
	if (ssid)
		g_byte_array_free (ssid, TRUE);
	return active_ap;
619
}
620

621
622
623
624
625
626
static void
update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
{
	NMActRequest *req;
	NMConnection *connection;

627
	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
Thomas Haller's avatar
Thomas Haller committed
628

629
630
	if (ap == NULL)
		return;
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645

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

646
static void
647
set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_available_connections, gboolean force_remove_old_ap)
648
{
649
650
	NMDeviceWifiPrivate *priv;
	NMAccessPoint *old_ap;
651

652
	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
653

654
655
656
	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
	old_ap = priv->current_ap;

657
658
	if (old_ap == new_ap)
		return;
659

660
	if (new_ap) {
661
662
		priv->current_ap = g_object_ref (new_ap);

663
664
665
666
667
668
		/* 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);
669
670
671

		/* Update seen BSSIDs cache */
		update_seen_bssids_cache (self, priv->current_ap);
672
673
	} else
		priv->current_ap = NULL;
674

675
676
	if (old_ap) {
		NM80211Mode mode = nm_ap_get_mode (old_ap);
677

678
679
680
681
682
683
684
		if (force_remove_old_ap || mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_ap_get_fake (old_ap)) {
			remove_access_point (self, old_ap);
			if (recheck_available_connections)
				nm_device_recheck_available_connections (NM_DEVICE (self));
		}
		g_object_unref (old_ap);
	}
685

686
	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT);
687
688
}

689
690
static void
periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
691
{
692
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
693
	NMAccessPoint *new_ap;
Thomas Haller's avatar
Thomas Haller committed
694
695
	guint32 new_rate;
	int percent;
Dan Williams's avatar
Dan Williams committed
696
	NMDeviceState state;
697
	guint32 supplicant_state;
Dan Williams's avatar
Dan Williams committed
698
699
700
701
702
703

	/* 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)
704
		return;
Dan Williams's avatar
Dan Williams committed
705

706
707
708
709
710
711
712
713
	/* Only update current AP if we're actually talking to something, otherwise
	 * assume the old one (if any) is still valid until we're told otherwise or
	 * the connection fails.
	 */
	supplicant_state = nm_supplicant_interface_get_state (priv->supplicant.iface);
	if (   supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING
	    || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED
	    || nm_supplicant_interface_get_scanning (priv->supplicant.iface))
714
		return;
715

716
	/* In AP mode we currently have nothing to do. */
Dan Williams's avatar
Dan Williams committed
717
	if (priv->mode == NM_802_11_MODE_AP)
718
		return;
719

720
721
722
	/* 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,
Yuri Chornoivan's avatar
Yuri Chornoivan committed
723
	 * the card's reported BSSID will change, but the new BSS may not
724
725
726
727
728
729
730
	 * 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} };

731
		wifi_utils_get_bssid (priv->wifi_data, &bssid);
732
733
734
735
		/* 0x02 means "locally administered" and should be OR-ed into
		 * the first byte of IBSS BSSIDs.
		 */
		if (   (bssid.ether_addr_octet[0] & 0x02)
736
737
738
739
		    && nm_ethernet_address_is_valid (&bssid))
			nm_ap_set_address (priv->current_ap, &bssid);
	}

740
	new_ap = find_active_ap (self, ignore_ap, FALSE);
741
742
743
744
745
746
747
748
749
750
	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;
		}
	}
751

752
	if (new_ap != priv->current_ap) {
753
754
755
756
		const struct ether_addr *new_bssid = NULL;
		const GByteArray *new_ssid = NULL;
		const struct ether_addr *old_bssid = NULL;
		const GByteArray *old_ssid = NULL;
757
		char *old_addr = NULL, *new_addr = NULL;
758
759
760

		if (new_ap) {
			new_bssid = nm_ap_get_address (new_ap);
761
			new_addr = nm_utils_hwaddr_ntoa (new_bssid, ARPHRD_ETHER);
762
763
764
			new_ssid = nm_ap_get_ssid (new_ap);
		}

765
766
		if (priv->current_ap) {
			old_bssid = nm_ap_get_address (priv->current_ap);
767
			old_addr = nm_utils_hwaddr_ntoa (old_bssid, ARPHRD_ETHER);
768
			old_ssid = nm_ap_get_ssid (priv->current_ap);
769
770
		}

Dan Williams's avatar
Dan Williams committed
771
772
773
774
775
776
		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)");
777
778
		g_free (old_addr);
		g_free (new_addr);
779

780
		set_current_ap (self, new_ap, TRUE, FALSE);
781
	}
782

783
	new_rate = wifi_utils_get_rate (priv->wifi_data);
784
785
	if (new_rate != priv->rate) {
		priv->rate = new_rate;
786
		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE);
787
	}
788
}
789

790
791
792
793
static gboolean
periodic_update_cb (gpointer user_data)
{
	periodic_update (NM_DEVICE_WIFI (user_data), NULL);
794
795
796
	return TRUE;
}

797
static gboolean
798
bring_up (NMDevice *device, gboolean *no_firmware)
799
{
800
801
802
	if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled)
		return FALSE;

803
	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->bring_up (device, no_firmware);
804
805
}

806
807
808
809
810
811
812
813
814
815
816
817
818
static void
emit_ap_added_removed (NMDeviceWifi *self,
                       guint signum,
                       NMAccessPoint *ap,
                       gboolean recheck_available_connections)
{
	g_signal_emit (self, signals[signum], 0, ap);
	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACCESS_POINTS);
	nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
	if (recheck_available_connections)
		nm_device_recheck_available_connections (NM_DEVICE (self));
}

819
static void
820
remove_access_point (NMDeviceWifi *device,
821
                     NMAccessPoint *ap)
822
{
823
824
	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
Dan Williams's avatar
Dan Williams committed
825

826
827
828
829
	g_return_if_fail (ap);
	g_return_if_fail (ap != priv->current_ap);
	g_return_if_fail (g_slist_find (priv->ap_list, ap));

830
	priv->ap_list = g_slist_remove (priv->ap_list, ap);
831
	emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE);
Dan Williams's avatar
Dan Williams committed
832
	g_object_unref (ap);
833
834
}

835
static void
836
remove_all_aps (NMDeviceWifi *self)
837
{
838
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
839

840
841
	if (priv->ap_list) {
		set_current_ap (self, NULL, FALSE, FALSE);
842

843
844
845
846
847
		while (priv->ap_list)
			remove_access_point (self, NM_AP (priv->ap_list->data));

		nm_device_recheck_available_connections (NM_DEVICE (self));
	}
848
849
}

850
static void
851
deactivate (NMDevice *dev)
852
{
853
	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
854
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
855
856
	NMActRequest *req;
	NMConnection *connection;
857
	NM80211Mode old_mode = priv->mode;
858
859
860
861
862
863
864

	req = nm_device_get_act_request (dev);
	if (req) {
		connection = nm_act_request_get_connection (req);
		/* Clear wireless secrets tries when deactivating */
		g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
	}
865

866
867
868
869
870
	if (priv->periodic_source_id) {
		g_source_remove (priv->periodic_source_id);
		priv->periodic_source_id = 0;
	}

871
	cleanup_association_attempt (self, TRUE);
872

873
874
	priv->rate = 0;

875
876
	/* If the AP is 'fake', i.e. it wasn't actually found from
	 * a scan but the user tried to connect to it manually (maybe it
877
	 * was non-broadcasting or something) get rid of it, because 'fake'
878
879
880
	 * APs should only live for as long as we're connected to them.
	 **/
	set_current_ap (self, NULL, TRUE, FALSE);
881

882
883
884
	/* Clear any critical protocol notification in the Wi-Fi stack */
	wifi_utils_indicate_addressing_running (priv->wifi_data, FALSE);

885
	/* Reset MAC address back to initial address */
886
	nm_device_set_hw_addr (dev, priv->initial_hw_addr, "reset", LOGD_WIFI);
887

888
889
890
	/* Ensure we're in infrastructure mode after deactivation; some devices
	 * (usually older ones) don't scan well in adhoc mode.
	 */
891
	if (wifi_utils_get_mode (priv->wifi_data) != NM_802_11_MODE_INFRA) {
892
		nm_device_take_down (NM_DEVICE (self), TRUE);
893
		wifi_utils_set_mode (priv->wifi_data, NM_802_11_MODE_INFRA);
894
		nm_device_bring_up (NM_DEVICE (self), TRUE, NULL);
895
896
897
898
	}

	if (priv->mode != NM_802_11_MODE_INFRA) {
		priv->mode = NM_802_11_MODE_INFRA;
899
		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE);
900
	}
901
902
903
904
905
906

	/* Ensure we trigger a scan after deactivating a Hotspot */
	if (old_mode == NM_802_11_MODE_AP) {
		cancel_pending_scan (self);
		request_wireless_scan (self);
	}
907
908
}

909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
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;
}

939
static gboolean
940
941
942
check_connection_compatible (NMDevice *device,
                             NMConnection *connection,
                             GError **error)
943
{
944
945
	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
946
947
	NMSettingConnection *s_con;
	NMSettingWireless *s_wireless;
948
	const GByteArray *mac;
949
	const GSList *mac_blacklist, *mac_blacklist_iter;
950
	const char *mode;
951

952
953
954
	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->check_connection_compatible (device, connection, error))
		return FALSE;

955
	s_con = nm_connection_get_setting_connection (connection);
956
957
	g_assert (s_con);

958
	if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME)) {
959
960
961
		g_set_error (error,
		             NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS,
		             "The connection was not a WiFi connection.");
962
		return FALSE;
963
	}
964

965
	s_wireless = nm_connection_get_setting_wireless (connection);
966
967
968
969
	if (!s_wireless) {
		g_set_error (error,
		             NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INVALID,
		             "The connection was not a valid WiFi connection.");
970
		return FALSE;
971
	}
972

973

974
	mac = nm_setting_wireless_get_mac_address (s_wireless);
975
	if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) {
976
977
978
		g_set_error (error,
		             NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE,
		             "The connection's MAC address did not match this device.");
979
980
981
		return FALSE;
	}

982
983
984
985
986
987
988
989
990
991
992