sme.c 23.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
 * SME code for cfg80211's connect emulation.
 *
 * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
 * Copyright (C) 2009   Intel Corporation. All rights reserved.
 */

#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/workqueue.h>
11
12
#include <linux/wireless.h>
#include <net/iw_handler.h>
13
14
15
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include "nl80211.h"
16
#include "reg.h"
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct cfg80211_conn {
	struct cfg80211_connect_params params;
	/* these are sub-states of the _CONNECTING sme_state */
	enum {
		CFG80211_CONN_IDLE,
		CFG80211_CONN_SCANNING,
		CFG80211_CONN_SCAN_AGAIN,
		CFG80211_CONN_AUTHENTICATE_NEXT,
		CFG80211_CONN_AUTHENTICATING,
		CFG80211_CONN_ASSOCIATE_NEXT,
		CFG80211_CONN_ASSOCIATING,
	} state;
	u8 bssid[ETH_ALEN];
	u8 *ie;
	size_t ie_len;
	bool auto_auth;
};


static int cfg80211_conn_scan(struct wireless_dev *wdev)
{
39
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
40
41
42
43
	struct cfg80211_scan_request *request;
	int n_channels, err;

	ASSERT_RTNL();
44
	ASSERT_RDEV_LOCK(rdev);
Johannes Berg's avatar
Johannes Berg committed
45
	ASSERT_WDEV_LOCK(wdev);
46

47
	if (rdev->scan_req)
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
		return -EBUSY;

	if (wdev->conn->params.channel) {
		n_channels = 1;
	} else {
		enum ieee80211_band band;
		n_channels = 0;

		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
			if (!wdev->wiphy->bands[band])
				continue;
			n_channels += wdev->wiphy->bands[band]->n_channels;
		}
	}
	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
			  sizeof(request->channels[0]) * n_channels,
			  GFP_KERNEL);
	if (!request)
		return -ENOMEM;

	request->channels = (void *)((char *)request + sizeof(*request));
	if (wdev->conn->params.channel)
		request->channels[0] = wdev->conn->params.channel;
	else {
		int i = 0, j;
		enum ieee80211_band band;

		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
			if (!wdev->wiphy->bands[band])
				continue;
			for (j = 0; j < wdev->wiphy->bands[band]->n_channels;
			     i++, j++)
				request->channels[i] =
					&wdev->wiphy->bands[band]->channels[j];
		}
	}
	request->n_channels = n_channels;
	request->ssids = (void *)(request->channels + n_channels);
	request->n_ssids = 1;

	memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
		wdev->conn->params.ssid_len);
	request->ssids[0].ssid_len = wdev->conn->params.ssid_len;

92
	request->dev = wdev->netdev;
93
	request->wiphy = &rdev->wiphy;
94

95
	rdev->scan_req = request;
96

97
	err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
98
99
	if (!err) {
		wdev->conn->state = CFG80211_CONN_SCANNING;
100
		nl80211_send_scan_start(rdev, wdev->netdev);
101
		dev_hold(wdev->netdev);
102
	} else {
103
		rdev->scan_req = NULL;
104
105
106
107
108
109
110
		kfree(request);
	}
	return err;
}

static int cfg80211_conn_do_work(struct wireless_dev *wdev)
{
111
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
Johannes Berg's avatar
Johannes Berg committed
112
113
	struct cfg80211_connect_params *params;
	int err;
114

Johannes Berg's avatar
Johannes Berg committed
115
116
	ASSERT_WDEV_LOCK(wdev);

117
118
119
	if (!wdev->conn)
		return 0;

Johannes Berg's avatar
Johannes Berg committed
120
121
	params = &wdev->conn->params;

122
123
124
125
	switch (wdev->conn->state) {
	case CFG80211_CONN_SCAN_AGAIN:
		return cfg80211_conn_scan(wdev);
	case CFG80211_CONN_AUTHENTICATE_NEXT:
126
		BUG_ON(!rdev->ops->auth);
Johannes Berg's avatar
Johannes Berg committed
127
		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
128
		return __cfg80211_mlme_auth(rdev, wdev->netdev,
Johannes Berg's avatar
Johannes Berg committed
129
130
131
					    params->channel, params->auth_type,
					    params->bssid,
					    params->ssid, params->ssid_len,
Johannes Berg's avatar
Johannes Berg committed
132
133
134
					    NULL, 0,
					    params->key, params->key_len,
					    params->key_idx);
135
	case CFG80211_CONN_ASSOCIATE_NEXT:
136
		BUG_ON(!rdev->ops->assoc);
Johannes Berg's avatar
Johannes Berg committed
137
		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
138
139
140
141
142
143
		/*
		 * We could, later, implement roaming here and then actually
		 * set prev_bssid to non-NULL. But then we need to be aware
		 * that some APs don't like that -- so we'd need to retry
		 * the association.
		 */
144
		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
Johannes Berg's avatar
Johannes Berg committed
145
146
147
148
149
					    params->channel, params->bssid,
					    NULL,
					    params->ssid, params->ssid_len,
					    params->ie, params->ie_len,
					    false, &params->crypto);
Johannes Berg's avatar
Johannes Berg committed
150
		if (err)
151
			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
Johannes Berg's avatar
Johannes Berg committed
152
153
					       NULL, 0,
					       WLAN_REASON_DEAUTH_LEAVING);
Johannes Berg's avatar
Johannes Berg committed
154
		return err;
155
156
157
158
159
160
161
	default:
		return 0;
	}
}

void cfg80211_conn_work(struct work_struct *work)
{
162
	struct cfg80211_registered_device *rdev =
163
164
165
166
		container_of(work, struct cfg80211_registered_device, conn_work);
	struct wireless_dev *wdev;

	rtnl_lock();
167
168
	cfg80211_lock_rdev(rdev);
	mutex_lock(&rdev->devlist_mtx);
169

170
	list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Berg's avatar
Johannes Berg committed
171
172
173
		wdev_lock(wdev);
		if (!netif_running(wdev->netdev)) {
			wdev_unlock(wdev);
174
			continue;
Johannes Berg's avatar
Johannes Berg committed
175
176
177
		}
		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
			wdev_unlock(wdev);
178
			continue;
Johannes Berg's avatar
Johannes Berg committed
179
		}
180
		if (cfg80211_conn_do_work(wdev))
Johannes Berg's avatar
Johannes Berg committed
181
182
183
184
185
			__cfg80211_connect_result(
					wdev->netdev,
					wdev->conn->params.bssid,
					NULL, 0, NULL, 0,
					WLAN_STATUS_UNSPECIFIED_FAILURE,
186
					false, NULL);
Johannes Berg's avatar
Johannes Berg committed
187
		wdev_unlock(wdev);
188
189
	}

190
191
	mutex_unlock(&rdev->devlist_mtx);
	cfg80211_unlock_rdev(rdev);
192
193
194
195
196
	rtnl_unlock();
}

static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
{
197
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
198
199
200
	struct cfg80211_bss *bss;
	u16 capa = WLAN_CAPABILITY_ESS;

Johannes Berg's avatar
Johannes Berg committed
201
202
	ASSERT_WDEV_LOCK(wdev);

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
	if (wdev->conn->params.privacy)
		capa |= WLAN_CAPABILITY_PRIVACY;

	bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid,
			       wdev->conn->params.ssid,
			       wdev->conn->params.ssid_len,
			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
			       capa);
	if (!bss)
		return false;

	memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
	wdev->conn->params.bssid = wdev->conn->bssid;
	wdev->conn->params.channel = bss->channel;
	wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
218
	schedule_work(&rdev->conn_work);
219
220
221
222
223

	cfg80211_put_bss(bss);
	return true;
}

Johannes Berg's avatar
Johannes Berg committed
224
static void __cfg80211_sme_scan_done(struct net_device *dev)
225
226
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
227
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
228

Johannes Berg's avatar
Johannes Berg committed
229
230
	ASSERT_WDEV_LOCK(wdev);

231
232
233
	if (wdev->sme_state != CFG80211_SME_CONNECTING)
		return;

234
	if (!wdev->conn)
235
236
237
238
239
240
241
242
243
		return;

	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
		return;

	if (!cfg80211_get_conn_bss(wdev)) {
		/* not found */
		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
244
			schedule_work(&rdev->conn_work);
245
		else
Johannes Berg's avatar
Johannes Berg committed
246
247
248
249
250
			__cfg80211_connect_result(
					wdev->netdev,
					wdev->conn->params.bssid,
					NULL, 0, NULL, 0,
					WLAN_STATUS_UNSPECIFIED_FAILURE,
251
					false, NULL);
252
253
254
	}
}

Johannes Berg's avatar
Johannes Berg committed
255
256
257
258
void cfg80211_sme_scan_done(struct net_device *dev)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;

259
	mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
260
261
262
	wdev_lock(wdev);
	__cfg80211_sme_scan_done(dev);
	wdev_unlock(wdev);
263
	mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
264
265
266
267
}

void cfg80211_sme_rx_auth(struct net_device *dev,
			  const u8 *buf, size_t len)
268
269
270
271
272
273
274
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct wiphy *wiphy = wdev->wiphy;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);

Johannes Berg's avatar
Johannes Berg committed
275
276
	ASSERT_WDEV_LOCK(wdev);

277
278
279
280
281
282
283
284
285
286
287
288
289
	/* should only RX auth frames when connecting */
	if (wdev->sme_state != CFG80211_SME_CONNECTING)
		return;

	if (WARN_ON(!wdev->conn))
		return;

	if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
	    wdev->conn->auto_auth &&
	    wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
		/* select automatically between only open, shared, leap */
		switch (wdev->conn->params.auth_type) {
		case NL80211_AUTHTYPE_OPEN_SYSTEM:
Johannes Berg's avatar
Johannes Berg committed
290
291
292
293
294
295
			if (wdev->connect_keys)
				wdev->conn->params.auth_type =
					NL80211_AUTHTYPE_SHARED_KEY;
			else
				wdev->conn->params.auth_type =
					NL80211_AUTHTYPE_NETWORK_EAP;
296
297
298
299
300
301
302
303
304
305
306
307
308
			break;
		case NL80211_AUTHTYPE_SHARED_KEY:
			wdev->conn->params.auth_type =
				NL80211_AUTHTYPE_NETWORK_EAP;
			break;
		default:
			/* huh? */
			wdev->conn->params.auth_type =
				NL80211_AUTHTYPE_OPEN_SYSTEM;
			break;
		}
		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
		schedule_work(&rdev->conn_work);
Johannes Berg's avatar
Johannes Berg committed
309
	} else if (status_code != WLAN_STATUS_SUCCESS) {
Johannes Berg's avatar
Johannes Berg committed
310
		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
311
					  status_code, false, NULL);
Johannes Berg's avatar
Johannes Berg committed
312
	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
313
314
315
316
317
		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
		schedule_work(&rdev->conn_work);
	}
}
318

Johannes Berg's avatar
Johannes Berg committed
319
320
321
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
			       const u8 *req_ie, size_t req_ie_len,
			       const u8 *resp_ie, size_t resp_ie_len,
322
323
			       u16 status, bool wextev,
			       struct cfg80211_bss *bss)
324
325
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
326
	u8 *country_ie;
327
328
329
330
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

Johannes Berg's avatar
Johannes Berg committed
331
332
	ASSERT_WDEV_LOCK(wdev);

333
334
335
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
		return;

336
337
338
	if (wdev->sme_state == CFG80211_SME_CONNECTED)
		nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev,
				    bssid, req_ie, req_ie_len,
Johannes Berg's avatar
Johannes Berg committed
339
				    resp_ie, resp_ie_len, GFP_KERNEL);
340
341
342
343
	else
		nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
					    bssid, req_ie, req_ie_len,
					    resp_ie, resp_ie_len,
Johannes Berg's avatar
Johannes Berg committed
344
					    status, GFP_KERNEL);
345
346
347
348
349
350

#ifdef CONFIG_WIRELESS_EXT
	if (wextev) {
		if (req_ie && status == WLAN_STATUS_SUCCESS) {
			memset(&wrqu, 0, sizeof(wrqu));
			wrqu.data.length = req_ie_len;
Zhu Yi's avatar
Zhu Yi committed
351
			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
		}

		if (resp_ie && status == WLAN_STATUS_SUCCESS) {
			memset(&wrqu, 0, sizeof(wrqu));
			wrqu.data.length = resp_ie_len;
			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
		}

		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
		if (bssid && status == WLAN_STATUS_SUCCESS)
			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
	}
#endif

368
369
370
371
372
373
	if (wdev->current_bss) {
		cfg80211_unhold_bss(wdev->current_bss);
		cfg80211_put_bss(&wdev->current_bss->pub);
		wdev->current_bss = NULL;
	}

374
	if (status == WLAN_STATUS_SUCCESS &&
Johannes Berg's avatar
Johannes Berg committed
375
376
	    wdev->sme_state == CFG80211_SME_IDLE)
		goto success;
377

378
	if (wdev->sme_state != CFG80211_SME_CONNECTING)
379
380
		return;

Johannes Berg's avatar
Johannes Berg committed
381
382
383
	if (wdev->conn)
		wdev->conn->state = CFG80211_CONN_IDLE;

Johannes Berg's avatar
Johannes Berg committed
384
	if (status != WLAN_STATUS_SUCCESS) {
385
		wdev->sme_state = CFG80211_SME_IDLE;
Johannes Berg's avatar
Johannes Berg committed
386
387
		kfree(wdev->conn);
		wdev->conn = NULL;
Johannes Berg's avatar
Johannes Berg committed
388
389
		kfree(wdev->connect_keys);
		wdev->connect_keys = NULL;
390
		wdev->ssid_len = 0;
Johannes Berg's avatar
Johannes Berg committed
391
		return;
392
	}
Johannes Berg's avatar
Johannes Berg committed
393

394
395
396
397
398
399
 success:
	if (!bss)
		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
				       wdev->ssid, wdev->ssid_len,
				       WLAN_CAPABILITY_ESS,
				       WLAN_CAPABILITY_ESS);
Johannes Berg's avatar
Johannes Berg committed
400
401
402
403
404
405
406
407
408

	if (WARN_ON(!bss))
		return;

	cfg80211_hold_bss(bss_from_pub(bss));
	wdev->current_bss = bss_from_pub(bss);

	wdev->sme_state = CFG80211_SME_CONNECTED;
	cfg80211_upload_connect_keys(wdev);
409
410
411
412
413
414
415
416
417
418
419
420
421
422

	country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);

	if (!country_ie)
		return;

	/*
	 * ieee80211_bss_get_ie() ensures we can access:
	 * - country_ie + 2, the start of the country ie data, and
	 * - and country_ie[1] which is the IE length
	 */
	regulatory_hint_11d(wdev->wiphy,
			    country_ie + 2,
			    country_ie[1]);
423
}
424
425
426
427
428
429

void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
			     const u8 *req_ie, size_t req_ie_len,
			     const u8 *resp_ie, size_t resp_ie_len,
			     u16 status, gfp_t gfp)
{
Johannes Berg's avatar
Johannes Berg committed
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	struct cfg80211_event *ev;
	unsigned long flags;

	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
	if (!ev)
		return;

	ev->type = EVENT_CONNECT_RESULT;
	memcpy(ev->cr.bssid, bssid, ETH_ALEN);
	ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
	ev->cr.req_ie_len = req_ie_len;
	memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
	ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
	ev->cr.resp_ie_len = resp_ie_len;
	memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
	ev->cr.status = status;

	spin_lock_irqsave(&wdev->event_lock, flags);
	list_add_tail(&ev->list, &wdev->event_list);
	spin_unlock_irqrestore(&wdev->event_lock, flags);
	schedule_work(&rdev->event_work);
453
}
454
455
EXPORT_SYMBOL(cfg80211_connect_result);

Johannes Berg's avatar
Johannes Berg committed
456
457
458
void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
		       const u8 *req_ie, size_t req_ie_len,
		       const u8 *resp_ie, size_t resp_ie_len)
459
460
461
462
463
464
{
	struct cfg80211_bss *bss;
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

Johannes Berg's avatar
Johannes Berg committed
465
466
	ASSERT_WDEV_LOCK(wdev);

467
468
469
470
471
472
473
474
475
476
477
478
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
		return;

	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
		return;

	/* internal error -- how did we get to CONNECTED w/o BSS? */
	if (WARN_ON(!wdev->current_bss)) {
		return;
	}

	cfg80211_unhold_bss(wdev->current_bss);
Johannes Berg's avatar
Johannes Berg committed
479
	cfg80211_put_bss(&wdev->current_bss->pub);
480
481
482
483
484
485
486
487
488
	wdev->current_bss = NULL;

	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
			       wdev->ssid, wdev->ssid_len,
			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);

	if (WARN_ON(!bss))
		return;

Johannes Berg's avatar
Johannes Berg committed
489
490
	cfg80211_hold_bss(bss_from_pub(bss));
	wdev->current_bss = bss_from_pub(bss);
491

Johannes Berg's avatar
Johannes Berg committed
492
493
494
	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
			    req_ie, req_ie_len, resp_ie, resp_ie_len,
			    GFP_KERNEL);
495
496
497
498
499

#ifdef CONFIG_WIRELESS_EXT
	if (req_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = req_ie_len;
Zhu Yi's avatar
Zhu Yi committed
500
		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
Johannes Berg's avatar
Johannes Berg committed
501
				    &wrqu, req_ie);
502
503
504
505
506
	}

	if (resp_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = resp_ie_len;
Johannes Berg's avatar
Johannes Berg committed
507
508
		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
				    &wrqu, resp_ie);
509
510
511
512
513
	}

	memset(&wrqu, 0, sizeof(wrqu));
	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
Johannes Berg's avatar
Johannes Berg committed
514
	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
515
516
#endif
}
Johannes Berg's avatar
Johannes Berg committed
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544

void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
		     const u8 *req_ie, size_t req_ie_len,
		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	struct cfg80211_event *ev;
	unsigned long flags;

	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
	if (!ev)
		return;

	ev->type = EVENT_ROAMED;
	memcpy(ev->rm.bssid, bssid, ETH_ALEN);
	ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
	ev->rm.req_ie_len = req_ie_len;
	memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
	ev->rm.resp_ie_len = resp_ie_len;
	memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);

	spin_lock_irqsave(&wdev->event_lock, flags);
	list_add_tail(&ev->list, &wdev->event_list);
	spin_unlock_irqrestore(&wdev->event_lock, flags);
	schedule_work(&rdev->event_work);
}
545
546
EXPORT_SYMBOL(cfg80211_roamed);

Johannes Berg's avatar
Johannes Berg committed
547
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
548
			     size_t ie_len, u16 reason, bool from_ap)
549
550
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg's avatar
Johannes Berg committed
551
552
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	int i;
553
554
555
556
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

Johannes Berg's avatar
Johannes Berg committed
557
558
	ASSERT_WDEV_LOCK(wdev);

559
560
561
562
563
564
565
566
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
		return;

	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
		return;

	if (wdev->current_bss) {
		cfg80211_unhold_bss(wdev->current_bss);
Johannes Berg's avatar
Johannes Berg committed
567
		cfg80211_put_bss(&wdev->current_bss->pub);
568
569
570
571
	}

	wdev->current_bss = NULL;
	wdev->sme_state = CFG80211_SME_IDLE;
572
	wdev->ssid_len = 0;
573

574
	if (wdev->conn) {
575
576
577
		const u8 *bssid;
		int ret;

578
579
		kfree(wdev->conn->ie);
		wdev->conn->ie = NULL;
Johannes Berg's avatar
Johannes Berg committed
580
581
		kfree(wdev->conn);
		wdev->conn = NULL;
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598

		/*
		 * If this disconnect was due to a disassoc, we
		 * we might still have an auth BSS around. For
		 * the userspace SME that's currently expected,
		 * but for the kernel SME (nl80211 CONNECT or
		 * wireless extensions) we want to clear up all
		 * state.
		 */
		for (i = 0; i < MAX_AUTH_BSSES; i++) {
			if (!wdev->auth_bsses[i])
				continue;
			bssid = wdev->auth_bsses[i]->pub.bssid;
			ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
						WLAN_REASON_DEAUTH_LEAVING);
			WARN(ret, "deauth failed: %d\n", ret);
		}
599
600
	}

Johannes Berg's avatar
Johannes Berg committed
601
602
603
604
605
606
607
608
609
	nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);

	/*
	 * Delete all the keys ... pairwise keys can't really
	 * exist any more anyway, but default keys might.
	 */
	if (rdev->ops->del_key)
		for (i = 0; i < 6; i++)
			rdev->ops->del_key(wdev->wiphy, dev, i, NULL);
610
611
612
613
614
615
616
617
618
619
620

#ifdef CONFIG_WIRELESS_EXT
	memset(&wrqu, 0, sizeof(wrqu));
	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
#endif
}

void cfg80211_disconnected(struct net_device *dev, u16 reason,
			   u8 *ie, size_t ie_len, gfp_t gfp)
{
Johannes Berg's avatar
Johannes Berg committed
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	struct cfg80211_event *ev;
	unsigned long flags;

	ev = kzalloc(sizeof(*ev) + ie_len, gfp);
	if (!ev)
		return;

	ev->type = EVENT_DISCONNECTED;
	ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
	ev->dc.ie_len = ie_len;
	memcpy((void *)ev->dc.ie, ie, ie_len);
	ev->dc.reason = reason;

	spin_lock_irqsave(&wdev->event_lock, flags);
	list_add_tail(&ev->list, &wdev->event_list);
	spin_unlock_irqrestore(&wdev->event_lock, flags);
	schedule_work(&rdev->event_work);
640
641
642
}
EXPORT_SYMBOL(cfg80211_disconnected);

Johannes Berg's avatar
Johannes Berg committed
643
644
int __cfg80211_connect(struct cfg80211_registered_device *rdev,
		       struct net_device *dev,
Johannes Berg's avatar
Johannes Berg committed
645
646
		       struct cfg80211_connect_params *connect,
		       struct cfg80211_cached_keys *connkeys)
647
648
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
649
	struct ieee80211_channel *chan;
Johannes Berg's avatar
Johannes Berg committed
650
651
652
	int err;

	ASSERT_WDEV_LOCK(wdev);
653
654
655
656

	if (wdev->sme_state != CFG80211_SME_IDLE)
		return -EALREADY;

657
658
659
660
	chan = rdev_fixed_channel(rdev, wdev);
	if (chan && chan != connect->channel)
		return -EBUSY;

Johannes Berg's avatar
Johannes Berg committed
661
662
663
664
665
666
667
	if (WARN_ON(wdev->connect_keys)) {
		kfree(wdev->connect_keys);
		wdev->connect_keys = NULL;
	}

	if (connkeys && connkeys->def >= 0) {
		int idx;
Samuel Ortiz's avatar
Samuel Ortiz committed
668
		u32 cipher;
Johannes Berg's avatar
Johannes Berg committed
669
670

		idx = connkeys->def;
Samuel Ortiz's avatar
Samuel Ortiz committed
671
		cipher = connkeys->params[idx].cipher;
Johannes Berg's avatar
Johannes Berg committed
672
		/* If given a WEP key we may need it for shared key auth */
Samuel Ortiz's avatar
Samuel Ortiz committed
673
674
		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
		    cipher == WLAN_CIPHER_SUITE_WEP104) {
Johannes Berg's avatar
Johannes Berg committed
675
676
677
			connect->key_idx = idx;
			connect->key = connkeys->params[idx].key;
			connect->key_len = connkeys->params[idx].key_len;
Samuel Ortiz's avatar
Samuel Ortiz committed
678
679
680
681
682
683
684
685
686
687
688
689

			/*
			 * If ciphers are not set (e.g. when going through
			 * iwconfig), we have to set them appropriately here.
			 */
			if (connect->crypto.cipher_group == 0)
				connect->crypto.cipher_group = cipher;

			if (connect->crypto.n_ciphers_pairwise == 0) {
				connect->crypto.n_ciphers_pairwise = 1;
				connect->crypto.ciphers_pairwise[0] = cipher;
			}
Johannes Berg's avatar
Johannes Berg committed
690
691
692
		}
	}

693
	if (!rdev->ops->connect) {
694
695
696
		if (!rdev->ops->auth || !rdev->ops->assoc)
			return -EOPNOTSUPP;

Johannes Berg's avatar
Johannes Berg committed
697
698
699
700
701
702
		if (WARN_ON(wdev->conn))
			return -EINPROGRESS;

		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
		if (!wdev->conn)
			return -ENOMEM;
703
704
705
706
707
708
709
710
711
712
713
714
715
716

		/*
		 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
		 */
		memcpy(&wdev->conn->params, connect, sizeof(*connect));
		if (connect->bssid) {
			wdev->conn->params.bssid = wdev->conn->bssid;
			memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
		}

		if (connect->ie) {
			wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
						GFP_KERNEL);
			wdev->conn->params.ie = wdev->conn->ie;
Johannes Berg's avatar
Johannes Berg committed
717
718
719
			if (!wdev->conn->ie) {
				kfree(wdev->conn);
				wdev->conn = NULL;
720
				return -ENOMEM;
Johannes Berg's avatar
Johannes Berg committed
721
			}
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
		}

		if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
			wdev->conn->auto_auth = true;
			/* start with open system ... should mostly work */
			wdev->conn->params.auth_type =
				NL80211_AUTHTYPE_OPEN_SYSTEM;
		} else {
			wdev->conn->auto_auth = false;
		}

		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
		wdev->ssid_len = connect->ssid_len;
		wdev->conn->params.ssid = wdev->ssid;
		wdev->conn->params.ssid_len = connect->ssid_len;

		/* don't care about result -- but fill bssid & channel */
		if (!wdev->conn->params.bssid || !wdev->conn->params.channel)
			cfg80211_get_conn_bss(wdev);

		wdev->sme_state = CFG80211_SME_CONNECTING;
Johannes Berg's avatar
Johannes Berg committed
743
		wdev->connect_keys = connkeys;
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761

		/* we're good if we have both BSSID and channel */
		if (wdev->conn->params.bssid && wdev->conn->params.channel) {
			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
			err = cfg80211_conn_do_work(wdev);
		} else {
			/* otherwise we'll need to scan for the AP first */
			err = cfg80211_conn_scan(wdev);
			/*
			 * If we can't scan right now, then we need to scan again
			 * after the current scan finished, since the parameters
			 * changed (unless we find a good AP anyway).
			 */
			if (err == -EBUSY) {
				err = 0;
				wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
			}
		}
Johannes Berg's avatar
Johannes Berg committed
762
763
764
		if (err) {
			kfree(wdev->conn);
			wdev->conn = NULL;
765
			wdev->sme_state = CFG80211_SME_IDLE;
Johannes Berg's avatar
Johannes Berg committed
766
			wdev->connect_keys = NULL;
767
			wdev->ssid_len = 0;
Johannes Berg's avatar
Johannes Berg committed
768
		}
769
770

		return err;
771
772
	} else {
		wdev->sme_state = CFG80211_SME_CONNECTING;
Johannes Berg's avatar
Johannes Berg committed
773
		wdev->connect_keys = connkeys;
774
775
		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
		if (err) {
Johannes Berg's avatar
Johannes Berg committed
776
			wdev->connect_keys = NULL;
777
778
779
780
			wdev->sme_state = CFG80211_SME_IDLE;
			return err;
		}

781
782
		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
		wdev->ssid_len = connect->ssid_len;
783

784
785
		return 0;
	}
786
787
}

Johannes Berg's avatar
Johannes Berg committed
788
789
int cfg80211_connect(struct cfg80211_registered_device *rdev,
		     struct net_device *dev,
Johannes Berg's avatar
Johannes Berg committed
790
791
		     struct cfg80211_connect_params *connect,
		     struct cfg80211_cached_keys *connkeys)
Johannes Berg's avatar
Johannes Berg committed
792
793
794
{
	int err;

795
	mutex_lock(&rdev->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
796
	wdev_lock(dev->ieee80211_ptr);
Johannes Berg's avatar
Johannes Berg committed
797
	err = __cfg80211_connect(rdev, dev, connect, connkeys);
Johannes Berg's avatar
Johannes Berg committed
798
	wdev_unlock(dev->ieee80211_ptr);
799
	mutex_unlock(&rdev->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
800
801
802
803
804
805

	return err;
}

int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
			  struct net_device *dev, u16 reason, bool wextev)
806
{
807
	struct wireless_dev *wdev = dev->ieee80211_ptr;
808
809
	int err;

Johannes Berg's avatar
Johannes Berg committed
810
811
	ASSERT_WDEV_LOCK(wdev);

812
813
814
	if (wdev->sme_state == CFG80211_SME_IDLE)
		return -EINVAL;

Johannes Berg's avatar
Johannes Berg committed
815
816
817
	kfree(wdev->connect_keys);
	wdev->connect_keys = NULL;

818
	if (!rdev->ops->disconnect) {
Johannes Berg's avatar
Johannes Berg committed
819
820
		if (!rdev->ops->deauth)
			return -EOPNOTSUPP;
821

Johannes Berg's avatar
Johannes Berg committed
822
823
824
825
826
		/* was it connected by userspace SME? */
		if (!wdev->conn) {
			cfg80211_mlme_down(rdev, dev);
			return 0;
		}
827
828
829
830
831

		if (wdev->sme_state == CFG80211_SME_CONNECTING &&
		    (wdev->conn->state == CFG80211_CONN_SCANNING ||
		     wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
			wdev->sme_state = CFG80211_SME_IDLE;
Johannes Berg's avatar
Johannes Berg committed
832
833
			kfree(wdev->conn);
			wdev->conn = NULL;
834
			wdev->ssid_len = 0;
835
836
837
838
			return 0;
		}

		/* wdev->conn->params.bssid must be set if > SCANNING */
Johannes Berg's avatar
Johannes Berg committed
839
840
841
		err = __cfg80211_mlme_deauth(rdev, dev,
					     wdev->conn->params.bssid,
					     NULL, 0, reason);
842
843
		if (err)
			return err;
844
845
846
847
848
849
	} else {
		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
		if (err)
			return err;
	}

850
	if (wdev->sme_state == CFG80211_SME_CONNECTED)
Johannes Berg's avatar
Johannes Berg committed
851
		__cfg80211_disconnected(dev, NULL, 0, 0, false);
852
	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
853
854
		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
					  WLAN_STATUS_UNSPECIFIED_FAILURE,
855
					  wextev, NULL);
856
857
858

	return 0;
}
Johannes Berg's avatar
Johannes Berg committed
859

Johannes Berg's avatar
Johannes Berg committed
860
861
862
863
864
865
866
867
868
869
870
871
872
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
			struct net_device *dev,
			u16 reason, bool wextev)
{
	int err;

	wdev_lock(dev->ieee80211_ptr);
	err = __cfg80211_disconnect(rdev, dev, reason, wextev);
	wdev_unlock(dev->ieee80211_ptr);

	return err;
}

Johannes Berg's avatar
Johannes Berg committed
873
874
875
876
877
878
void cfg80211_sme_disassoc(struct net_device *dev, int idx)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	u8 bssid[ETH_ALEN];

Johannes Berg's avatar
Johannes Berg committed
879
880
	ASSERT_WDEV_LOCK(wdev);

Johannes Berg's avatar
Johannes Berg committed
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
	if (!wdev->conn)
		return;

	if (wdev->conn->state == CFG80211_CONN_IDLE)
		return;

	/*
	 * Ok, so the association was made by this SME -- we don't
	 * want it any more so deauthenticate too.
	 */

	if (!wdev->auth_bsses[idx])
		return;

	memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
Johannes Berg's avatar
Johannes Berg committed
896
897
	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
Johannes Berg's avatar
Johannes Berg committed
898
899
900
901
902
903
		/* whatever -- assume gone anyway */
		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
		wdev->auth_bsses[idx] = NULL;
	}
}