sme.c 24.4 KB
Newer Older
S
Samuel Ortiz 已提交
1
/*
2 3 4
 * SME code for cfg80211
 * both driver SME event handling and the SME implementation
 * (for nl80211's connect() and wext)
S
Samuel Ortiz 已提交
5 6 7 8 9 10 11
 *
 * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
 * Copyright (C) 2009   Intel Corporation. All rights reserved.
 */

#include <linux/etherdevice.h>
#include <linux/if_arp.h>
12
#include <linux/slab.h>
S
Samuel Ortiz 已提交
13
#include <linux/workqueue.h>
14
#include <linux/wireless.h>
15
#include <linux/export.h>
16
#include <net/iw_handler.h>
S
Samuel Ortiz 已提交
17 18 19
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include "nl80211.h"
20
#include "reg.h"
21
#include "rdev-ops.h"
S
Samuel Ortiz 已提交
22

23 24 25 26 27 28
/*
 * Software SME in cfg80211, using auth/assoc/deauth calls to the
 * driver. This is is for implementing nl80211's connect/disconnect
 * and wireless extensions (if configured.)
 */

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_SCANNING,
		CFG80211_CONN_SCAN_AGAIN,
		CFG80211_CONN_AUTHENTICATE_NEXT,
		CFG80211_CONN_AUTHENTICATING,
		CFG80211_CONN_ASSOCIATE_NEXT,
		CFG80211_CONN_ASSOCIATING,
39 40
		CFG80211_CONN_DEAUTH,
		CFG80211_CONN_CONNECTED,
41
	} state;
42
	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
43 44
	u8 *ie;
	size_t ie_len;
45
	bool auto_auth, prev_bssid_valid;
46 47
};

48
static void cfg80211_sme_free(struct wireless_dev *wdev)
49
{
50 51
	if (!wdev->conn)
		return;
52

53 54 55
	kfree(wdev->conn->ie);
	kfree(wdev->conn);
	wdev->conn = NULL;
56 57
}

58 59
static int cfg80211_conn_scan(struct wireless_dev *wdev)
{
60
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
61 62 63 64
	struct cfg80211_scan_request *request;
	int n_channels, err;

	ASSERT_RTNL();
65
	ASSERT_RDEV_LOCK(rdev);
J
Johannes Berg 已提交
66
	ASSERT_WDEV_LOCK(wdev);
67

68
	if (rdev->scan_req)
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
		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;

	if (wdev->conn->params.channel)
		request->channels[0] = wdev->conn->params.channel;
	else {
		int i = 0, j;
		enum ieee80211_band band;
94 95
		struct ieee80211_supported_band *bands;
		struct ieee80211_channel *channel;
96 97

		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
98 99
			bands = wdev->wiphy->bands[band];
			if (!bands)
100
				continue;
101 102 103 104 105 106 107
			for (j = 0; j < bands->n_channels; j++) {
				channel = &bands->channels[j];
				if (channel->flags & IEEE80211_CHAN_DISABLED)
					continue;
				request->channels[i++] = channel;
			}
			request->rates[band] = (1 << bands->n_bitrates) - 1;
108
		}
109
		n_channels = i;
110 111
	}
	request->n_channels = n_channels;
112
	request->ssids = (void *)&request->channels[n_channels];
113 114 115 116 117 118
	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;

J
Johannes Berg 已提交
119
	request->wdev = wdev;
120
	request->wiphy = &rdev->wiphy;
121
	request->scan_start = jiffies;
122

123
	rdev->scan_req = request;
124

125
	err = rdev_scan(rdev, request);
126 127
	if (!err) {
		wdev->conn->state = CFG80211_CONN_SCANNING;
J
Johannes Berg 已提交
128
		nl80211_send_scan_start(rdev, wdev);
129
		dev_hold(wdev->netdev);
130
	} else {
131
		rdev->scan_req = NULL;
132 133 134 135 136 137 138
		kfree(request);
	}
	return err;
}

static int cfg80211_conn_do_work(struct wireless_dev *wdev)
{
139
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
J
Johannes Berg 已提交
140
	struct cfg80211_connect_params *params;
141
	struct cfg80211_assoc_request req = {};
J
Johannes Berg 已提交
142
	int err;
143

J
Johannes Berg 已提交
144 145
	ASSERT_WDEV_LOCK(wdev);

146 147 148
	if (!wdev->conn)
		return 0;

J
Johannes Berg 已提交
149 150
	params = &wdev->conn->params;

151
	switch (wdev->conn->state) {
152 153 154
	case CFG80211_CONN_SCANNING:
		/* didn't find it during scan ... */
		return -ENOENT;
155 156 157
	case CFG80211_CONN_SCAN_AGAIN:
		return cfg80211_conn_scan(wdev);
	case CFG80211_CONN_AUTHENTICATE_NEXT:
158
		BUG_ON(!rdev->ops->auth);
J
Johannes Berg 已提交
159
		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
160 161 162 163 164 165 166
		return cfg80211_mlme_auth(rdev, wdev->netdev,
					  params->channel, params->auth_type,
					  params->bssid,
					  params->ssid, params->ssid_len,
					  NULL, 0,
					  params->key, params->key_len,
					  params->key_idx, NULL, 0);
167
	case CFG80211_CONN_ASSOCIATE_NEXT:
168
		BUG_ON(!rdev->ops->assoc);
J
Johannes Berg 已提交
169
		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
170
		if (wdev->conn->prev_bssid_valid)
171 172 173 174 175 176 177 178 179 180 181
			req.prev_bssid = wdev->conn->prev_bssid;
		req.ie = params->ie;
		req.ie_len = params->ie_len;
		req.use_mfp = params->mfp != NL80211_MFP_NO;
		req.crypto = params->crypto;
		req.flags = params->flags;
		req.ht_capa = params->ht_capa;
		req.ht_capa_mask = params->ht_capa_mask;
		req.vht_capa = params->vht_capa;
		req.vht_capa_mask = params->vht_capa_mask;

182 183 184
		err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
					  params->bssid, params->ssid,
					  params->ssid_len, &req);
J
Johannes Berg 已提交
185
		if (err)
186 187 188 189
			cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
					     NULL, 0,
					     WLAN_REASON_DEAUTH_LEAVING,
					     false);
J
Johannes Berg 已提交
190
		return err;
191
	case CFG80211_CONN_DEAUTH:
192 193 194
		cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
				     NULL, 0,
				     WLAN_REASON_DEAUTH_LEAVING, false);
195
		return 0;
196 197 198 199 200 201 202
	default:
		return 0;
	}
}

void cfg80211_conn_work(struct work_struct *work)
{
203
	struct cfg80211_registered_device *rdev =
204 205
		container_of(work, struct cfg80211_registered_device, conn_work);
	struct wireless_dev *wdev;
J
Johannes Berg 已提交
206
	u8 bssid_buf[ETH_ALEN], *bssid = NULL;
207 208 209

	rtnl_lock();

210
	list_for_each_entry(wdev, &rdev->wdev_list, list) {
211 212 213
		if (!wdev->netdev)
			continue;

J
Johannes Berg 已提交
214 215 216
		wdev_lock(wdev);
		if (!netif_running(wdev->netdev)) {
			wdev_unlock(wdev);
217
			continue;
J
Johannes Berg 已提交
218
		}
219 220
		if (!wdev->conn ||
		    wdev->conn->state == CFG80211_CONN_CONNECTED) {
J
Johannes Berg 已提交
221
			wdev_unlock(wdev);
222
			continue;
J
Johannes Berg 已提交
223
		}
J
Johannes Berg 已提交
224 225 226 227
		if (wdev->conn->params.bssid) {
			memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
			bssid = bssid_buf;
		}
228
		if (cfg80211_conn_do_work(wdev)) {
J
Johannes Berg 已提交
229
			__cfg80211_connect_result(
230
					wdev->netdev, bssid,
J
Johannes Berg 已提交
231 232
					NULL, 0, NULL, 0,
					WLAN_STATUS_UNSPECIFIED_FAILURE,
233
					false, NULL);
234 235
			cfg80211_sme_free(wdev);
		}
J
Johannes Berg 已提交
236
		wdev_unlock(wdev);
237 238 239 240 241
	}

	rtnl_unlock();
}

J
Johannes Berg 已提交
242
static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
243
{
244
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
245 246 247
	struct cfg80211_bss *bss;
	u16 capa = WLAN_CAPABILITY_ESS;

J
Johannes Berg 已提交
248 249
	ASSERT_WDEV_LOCK(wdev);

250 251 252
	if (wdev->conn->params.privacy)
		capa |= WLAN_CAPABILITY_PRIVACY;

253 254
	bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
			       wdev->conn->params.bssid,
255 256 257 258 259
			       wdev->conn->params.ssid,
			       wdev->conn->params.ssid_len,
			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
			       capa);
	if (!bss)
J
Johannes Berg 已提交
260
		return NULL;
261 262 263 264 265

	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;
266
	schedule_work(&rdev->conn_work);
267

J
Johannes Berg 已提交
268
	return bss;
269 270
}

J
Johannes Berg 已提交
271
static void __cfg80211_sme_scan_done(struct net_device *dev)
272 273
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
274
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
J
Johannes Berg 已提交
275
	struct cfg80211_bss *bss;
276

J
Johannes Berg 已提交
277 278
	ASSERT_WDEV_LOCK(wdev);

279
	if (!wdev->conn)
280 281 282 283 284 285
		return;

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

J
Johannes Berg 已提交
286
	bss = cfg80211_get_conn_bss(wdev);
287
	if (bss)
288
		cfg80211_put_bss(&rdev->wiphy, bss);
289 290
	else
		schedule_work(&rdev->conn_work);
291 292
}

J
Johannes Berg 已提交
293 294 295 296 297 298 299 300 301
void cfg80211_sme_scan_done(struct net_device *dev)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;

	wdev_lock(wdev);
	__cfg80211_sme_scan_done(dev);
	wdev_unlock(wdev);
}

302
void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
303 304 305 306 307 308
{
	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);

J
Johannes Berg 已提交
309 310
	ASSERT_WDEV_LOCK(wdev);

311
	if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
312 313 314 315 316 317 318 319
		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:
J
Johannes Berg 已提交
320 321 322 323 324 325
			if (wdev->connect_keys)
				wdev->conn->params.auth_type =
					NL80211_AUTHTYPE_SHARED_KEY;
			else
				wdev->conn->params.auth_type =
					NL80211_AUTHTYPE_NETWORK_EAP;
326 327 328 329 330 331 332 333 334 335 336 337 338
			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);
J
Johannes Berg 已提交
339
	} else if (status_code != WLAN_STATUS_SUCCESS) {
340 341
		__cfg80211_connect_result(wdev->netdev, mgmt->bssid,
					  NULL, 0, NULL, 0,
342
					  status_code, false, NULL);
343
	} else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
344 345 346 347
		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
		schedule_work(&rdev->conn_work);
	}
}
S
Samuel Ortiz 已提交
348

349
bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
350
{
351
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
352

353
	if (!wdev->conn)
354 355
		return false;

356 357
	if (status == WLAN_STATUS_SUCCESS) {
		wdev->conn->state = CFG80211_CONN_CONNECTED;
358
		return false;
359
	}
360

361 362 363 364 365 366 367 368 369 370 371 372 373
	if (wdev->conn->prev_bssid_valid) {
		/*
		 * Some stupid APs don't accept reassoc, so we
		 * need to fall back to trying regular assoc;
		 * return true so no event is sent to userspace.
		 */
		wdev->conn->prev_bssid_valid = false;
		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
		schedule_work(&rdev->conn_work);
		return true;
	}

	wdev->conn->state = CFG80211_CONN_DEAUTH;
374
	schedule_work(&rdev->conn_work);
375 376
	return false;
}
377

378 379 380
void cfg80211_sme_deauth(struct wireless_dev *wdev)
{
	cfg80211_sme_free(wdev);
381 382
}

383
void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
384
{
385 386
	cfg80211_sme_free(wdev);
}
387

388 389 390 391 392 393 394 395
void cfg80211_sme_disassoc(struct wireless_dev *wdev)
{
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);

	if (!wdev->conn)
		return;

	wdev->conn->state = CFG80211_CONN_DEAUTH;
396 397 398
	schedule_work(&rdev->conn_work);
}

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 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 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
{
	cfg80211_sme_disassoc(wdev);
}

static int cfg80211_sme_connect(struct wireless_dev *wdev,
				struct cfg80211_connect_params *connect,
				const u8 *prev_bssid)
{
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	struct cfg80211_bss *bss;
	int err;

	if (!rdev->ops->auth || !rdev->ops->assoc)
		return -EOPNOTSUPP;

	if (wdev->current_bss)
		return -EALREADY;

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

	wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
	if (!wdev->conn)
		return -ENOMEM;

	/*
	 * 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;
		if (!wdev->conn->ie) {
			kfree(wdev->conn);
			wdev->conn = NULL;
			return -ENOMEM;
		}
	}

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

	wdev->conn->params.ssid = wdev->ssid;
	wdev->conn->params.ssid_len = connect->ssid_len;

	/* see if we have the bss already */
	bss = cfg80211_get_conn_bss(wdev);

	if (prev_bssid) {
		memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
		wdev->conn->prev_bssid_valid = true;
	}

	/* we're good if we have a matching bss struct */
	if (bss) {
		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
		err = cfg80211_conn_do_work(wdev);
		cfg80211_put_bss(wdev->wiphy, bss);
	} 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;
		}
	}

	if (err)
		cfg80211_sme_free(wdev);

	return err;
}

static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
{
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	int err;

	if (!wdev->conn)
		return 0;

	if (!rdev->ops->deauth)
		return -EOPNOTSUPP;

	if (wdev->conn->state == CFG80211_CONN_SCANNING ||
	    wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
		err = 0;
		goto out;
	}

	/* wdev->conn->params.bssid must be set if > SCANNING */
	err = cfg80211_mlme_deauth(rdev, wdev->netdev,
				   wdev->conn->params.bssid,
				   NULL, 0, reason, false);
 out:
	cfg80211_sme_free(wdev);
	return err;
}

/*
 * code shared for in-device and software SME
 */

static bool cfg80211_is_all_idle(void)
{
	struct cfg80211_registered_device *rdev;
	struct wireless_dev *wdev;
	bool is_all_idle = true;

	/*
	 * All devices must be idle as otherwise if you are actively
	 * scanning some new beacon hints could be learned and would
	 * count as new regulatory hints.
	 */
	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
		list_for_each_entry(wdev, &rdev->wdev_list, list) {
			wdev_lock(wdev);
			if (wdev->conn || wdev->current_bss)
				is_all_idle = false;
			wdev_unlock(wdev);
		}
	}

	return is_all_idle;
}

static void disconnect_work(struct work_struct *work)
{
	rtnl_lock();
	if (cfg80211_is_all_idle())
		regulatory_hint_disconnect();
	rtnl_unlock();
}

static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);


/*
 * API calls for drivers implementing connect/disconnect and
 * SME event handling
 */

J
Johannes Berg 已提交
560 561 562
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,
563 564
			       u16 status, bool wextev,
			       struct cfg80211_bss *bss)
S
Samuel Ortiz 已提交
565 566
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
567
	const u8 *country_ie;
J
Johannes Berg 已提交
568
#ifdef CONFIG_CFG80211_WEXT
S
Samuel Ortiz 已提交
569 570 571
	union iwreq_data wrqu;
#endif

J
Johannes Berg 已提交
572 573
	ASSERT_WDEV_LOCK(wdev);

574 575
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
S
Samuel Ortiz 已提交
576 577
		return;

578
	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
579
				    bssid, req_ie, req_ie_len,
580 581
				    resp_ie, resp_ie_len,
				    status, GFP_KERNEL);
582

J
Johannes Berg 已提交
583
#ifdef CONFIG_CFG80211_WEXT
584 585 586 587
	if (wextev) {
		if (req_ie && status == WLAN_STATUS_SUCCESS) {
			memset(&wrqu, 0, sizeof(wrqu));
			wrqu.data.length = req_ie_len;
Z
Zhu Yi 已提交
588
			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
589 590 591 592 593 594 595 596 597 598
		}

		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;
599
		if (bssid && status == WLAN_STATUS_SUCCESS) {
600
			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
601 602 603
			memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
			wdev->wext.prev_bssid_valid = true;
		}
604 605 606 607
		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
	}
#endif

608 609
	if (wdev->current_bss) {
		cfg80211_unhold_bss(wdev->current_bss);
610
		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
611 612 613
		wdev->current_bss = NULL;
	}

J
Johannes Berg 已提交
614 615 616
	if (status != WLAN_STATUS_SUCCESS) {
		kfree(wdev->connect_keys);
		wdev->connect_keys = NULL;
617
		wdev->ssid_len = 0;
618 619 620 621
		if (bss) {
			cfg80211_unhold_bss(bss_from_pub(bss));
			cfg80211_put_bss(wdev->wiphy, bss);
		}
J
Johannes Berg 已提交
622
		return;
S
Samuel Ortiz 已提交
623
	}
J
Johannes Berg 已提交
624

625 626
	if (!bss) {
		WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect);
627
		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
628 629 630
				       wdev->ssid, wdev->ssid_len,
				       WLAN_CAPABILITY_ESS,
				       WLAN_CAPABILITY_ESS);
631 632 633 634
		if (WARN_ON(!bss))
			return;
		cfg80211_hold_bss(bss_from_pub(bss));
	}
J
Johannes Berg 已提交
635 636 637 638

	wdev->current_bss = bss_from_pub(bss);

	cfg80211_upload_connect_keys(wdev);
639

640 641 642 643 644 645 646 647 648
	rcu_read_lock();
	country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
	if (!country_ie) {
		rcu_read_unlock();
		return;
	}

	country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC);
	rcu_read_unlock();
649 650 651 652 653 654 655 656 657

	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
	 */
J
Johannes Berg 已提交
658 659
	regulatory_hint_11d(wdev->wiphy, bss->channel->band,
			    country_ie + 2, country_ie[1]);
660
	kfree(country_ie);
S
Samuel Ortiz 已提交
661
}
662 663 664 665 666 667

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)
{
J
Johannes Berg 已提交
668 669 670 671 672 673 674 675 676 677
	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;
678 679
	if (bssid)
		memcpy(ev->cr.bssid, bssid, ETH_ALEN);
680 681 682 683 684 685 686 687 688 689
	if (req_ie_len) {
		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);
	}
	if (resp_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);
	}
J
Johannes Berg 已提交
690 691 692 693 694
	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);
695
	queue_work(cfg80211_wq, &rdev->event_work);
696
}
S
Samuel Ortiz 已提交
697 698
EXPORT_SYMBOL(cfg80211_connect_result);

699
void __cfg80211_roamed(struct wireless_dev *wdev,
700
		       struct cfg80211_bss *bss,
J
Johannes Berg 已提交
701 702
		       const u8 *req_ie, size_t req_ie_len,
		       const u8 *resp_ie, size_t resp_ie_len)
S
Samuel Ortiz 已提交
703
{
J
Johannes Berg 已提交
704
#ifdef CONFIG_CFG80211_WEXT
S
Samuel Ortiz 已提交
705 706
	union iwreq_data wrqu;
#endif
J
Johannes Berg 已提交
707 708
	ASSERT_WDEV_LOCK(wdev);

709 710
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
711
		goto out;
S
Samuel Ortiz 已提交
712

713
	if (WARN_ON(!wdev->current_bss))
714
		goto out;
S
Samuel Ortiz 已提交
715 716

	cfg80211_unhold_bss(wdev->current_bss);
717
	cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
S
Samuel Ortiz 已提交
718 719
	wdev->current_bss = NULL;

J
Johannes Berg 已提交
720 721
	cfg80211_hold_bss(bss_from_pub(bss));
	wdev->current_bss = bss_from_pub(bss);
S
Samuel Ortiz 已提交
722

723
	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid,
J
Johannes Berg 已提交
724 725
			    req_ie, req_ie_len, resp_ie, resp_ie_len,
			    GFP_KERNEL);
S
Samuel Ortiz 已提交
726

J
Johannes Berg 已提交
727
#ifdef CONFIG_CFG80211_WEXT
S
Samuel Ortiz 已提交
728 729 730
	if (req_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = req_ie_len;
Z
Zhu Yi 已提交
731
		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
J
Johannes Berg 已提交
732
				    &wrqu, req_ie);
S
Samuel Ortiz 已提交
733 734 735 736 737
	}

	if (resp_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = resp_ie_len;
J
Johannes Berg 已提交
738 739
		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
				    &wrqu, resp_ie);
S
Samuel Ortiz 已提交
740 741 742 743
	}

	memset(&wrqu, 0, sizeof(wrqu));
	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
744 745
	memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
	memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN);
746
	wdev->wext.prev_bssid_valid = true;
J
Johannes Berg 已提交
747
	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
S
Samuel Ortiz 已提交
748
#endif
749 750 751

	return;
out:
752
	cfg80211_put_bss(wdev->wiphy, bss);
S
Samuel Ortiz 已提交
753
}
J
Johannes Berg 已提交
754

755 756 757
void cfg80211_roamed(struct net_device *dev,
		     struct ieee80211_channel *channel,
		     const u8 *bssid,
J
Johannes Berg 已提交
758 759
		     const u8 *req_ie, size_t req_ie_len,
		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_bss *bss;

	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
			       wdev->ssid_len, WLAN_CAPABILITY_ESS,
			       WLAN_CAPABILITY_ESS);
	if (WARN_ON(!bss))
		return;

	cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie,
			    resp_ie_len, gfp);
}
EXPORT_SYMBOL(cfg80211_roamed);

void cfg80211_roamed_bss(struct net_device *dev,
			 struct cfg80211_bss *bss, const u8 *req_ie,
			 size_t req_ie_len, const u8 *resp_ie,
			 size_t resp_ie_len, gfp_t gfp)
J
Johannes Berg 已提交
779 780 781 782 783 784
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	struct cfg80211_event *ev;
	unsigned long flags;

785 786 787
	if (WARN_ON(!bss))
		return;

J
Johannes Berg 已提交
788
	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
789
	if (!ev) {
790
		cfg80211_put_bss(wdev->wiphy, bss);
J
Johannes Berg 已提交
791
		return;
792
	}
J
Johannes Berg 已提交
793 794 795 796 797 798 799 800

	ev->type = EVENT_ROAMED;
	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);
801
	ev->rm.bss = bss;
J
Johannes Berg 已提交
802 803 804 805

	spin_lock_irqsave(&wdev->event_lock, flags);
	list_add_tail(&ev->list, &wdev->event_list);
	spin_unlock_irqrestore(&wdev->event_lock, flags);
806
	queue_work(cfg80211_wq, &rdev->event_work);
J
Johannes Berg 已提交
807
}
808
EXPORT_SYMBOL(cfg80211_roamed_bss);
S
Samuel Ortiz 已提交
809

J
Johannes Berg 已提交
810
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
811
			     size_t ie_len, u16 reason, bool from_ap)
S
Samuel Ortiz 已提交
812 813
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
J
Johannes Berg 已提交
814 815
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	int i;
J
Johannes Berg 已提交
816
#ifdef CONFIG_CFG80211_WEXT
S
Samuel Ortiz 已提交
817 818 819
	union iwreq_data wrqu;
#endif

J
Johannes Berg 已提交
820 821
	ASSERT_WDEV_LOCK(wdev);

822 823
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
S
Samuel Ortiz 已提交
824 825 826 827
		return;

	if (wdev->current_bss) {
		cfg80211_unhold_bss(wdev->current_bss);
828
		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
S
Samuel Ortiz 已提交
829 830 831
	}

	wdev->current_bss = NULL;
832
	wdev->ssid_len = 0;
S
Samuel Ortiz 已提交
833

J
Johannes Berg 已提交
834 835 836 837 838 839 840 841
	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++)
842
			rdev_del_key(rdev, dev, i, false, NULL);
S
Samuel Ortiz 已提交
843

J
Johannes Berg 已提交
844
#ifdef CONFIG_CFG80211_WEXT
S
Samuel Ortiz 已提交
845 846 847
	memset(&wrqu, 0, sizeof(wrqu));
	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
848
	wdev->wext.connect.ssid_len = 0;
S
Samuel Ortiz 已提交
849
#endif
850 851

	schedule_work(&cfg80211_disconnect_work);
S
Samuel Ortiz 已提交
852 853 854 855 856
}

void cfg80211_disconnected(struct net_device *dev, u16 reason,
			   u8 *ie, size_t ie_len, gfp_t gfp)
{
J
Johannes Berg 已提交
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
	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);
875
	queue_work(cfg80211_wq, &rdev->event_work);
S
Samuel Ortiz 已提交
876 877 878
}
EXPORT_SYMBOL(cfg80211_disconnected);

879 880 881
/*
 * API calls for nl80211/wext compatibility code
 */
882 883 884 885 886
int cfg80211_connect(struct cfg80211_registered_device *rdev,
		     struct net_device *dev,
		     struct cfg80211_connect_params *connect,
		     struct cfg80211_cached_keys *connkeys,
		     const u8 *prev_bssid)
S
Samuel Ortiz 已提交
887 888
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
J
Johannes Berg 已提交
889 890 891
	int err;

	ASSERT_WDEV_LOCK(wdev);
S
Samuel Ortiz 已提交
892

J
Johannes Berg 已提交
893 894 895 896 897
	if (WARN_ON(wdev->connect_keys)) {
		kfree(wdev->connect_keys);
		wdev->connect_keys = NULL;
	}

898 899 900
	cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
				  rdev->wiphy.ht_capa_mod_mask);

J
Johannes Berg 已提交
901 902
	if (connkeys && connkeys->def >= 0) {
		int idx;
S
Samuel Ortiz 已提交
903
		u32 cipher;
J
Johannes Berg 已提交
904 905

		idx = connkeys->def;
S
Samuel Ortiz 已提交
906
		cipher = connkeys->params[idx].cipher;
J
Johannes Berg 已提交
907
		/* If given a WEP key we may need it for shared key auth */
S
Samuel Ortiz 已提交
908 909
		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
		    cipher == WLAN_CIPHER_SUITE_WEP104) {
J
Johannes Berg 已提交
910 911 912
			connect->key_idx = idx;
			connect->key = connkeys->params[idx].key;
			connect->key_len = connkeys->params[idx].key_len;
S
Samuel Ortiz 已提交
913 914 915 916 917 918 919 920 921 922 923 924

			/*
			 * 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;
			}
J
Johannes Berg 已提交
925 926 927
		}
	}

928 929 930
	wdev->connect_keys = connkeys;
	memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
	wdev->ssid_len = connect->ssid_len;
931

932 933 934
	if (!rdev->ops->connect)
		err = cfg80211_sme_connect(wdev, connect, prev_bssid);
	else
935
		err = rdev_connect(rdev, dev, connect);
S
Samuel Ortiz 已提交
936

937 938 939 940
	if (err) {
		wdev->connect_keys = NULL;
		wdev->ssid_len = 0;
		return err;
941
	}
942 943

	return 0;
S
Samuel Ortiz 已提交
944 945
}

946 947
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
			struct net_device *dev, u16 reason, bool wextev)
S
Samuel Ortiz 已提交
948
{
949
	struct wireless_dev *wdev = dev->ieee80211_ptr;
S
Samuel Ortiz 已提交
950 951
	int err;

J
Johannes Berg 已提交
952 953
	ASSERT_WDEV_LOCK(wdev);

J
Johannes Berg 已提交
954 955 956
	kfree(wdev->connect_keys);
	wdev->connect_keys = NULL;

957 958 959 960 961
	if (wdev->conn) {
		err = cfg80211_sme_disconnect(wdev, reason);
	} else if (!rdev->ops->disconnect) {
		cfg80211_mlme_down(rdev, dev);
		err = 0;
S
Samuel Ortiz 已提交
962
	} else {
963
		err = rdev_disconnect(rdev, dev, reason);
S
Samuel Ortiz 已提交
964 965
	}

966
	return err;
J
Johannes Berg 已提交
967
}