sme.c 24.2 KB
Newer Older
S
Samuel Ortiz 已提交
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>
S
Samuel Ortiz 已提交
13 14 15
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include "nl80211.h"
16
#include "reg.h"
S
Samuel Ortiz 已提交
17

18 19 20 21 22 23 24 25 26 27 28 29
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;
30
	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
31 32
	u8 *ie;
	size_t ie_len;
33
	bool auto_auth, prev_bssid_valid;
34 35 36 37 38
};


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);
J
Johannes Berg 已提交
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);
J
Johannes Berg 已提交
112
	struct cfg80211_connect_params *params;
113
	const u8 *prev_bssid = NULL;
J
Johannes Berg 已提交
114
	int err;
115

J
Johannes Berg 已提交
116 117
	ASSERT_WDEV_LOCK(wdev);

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

J
Johannes Berg 已提交
121 122
	params = &wdev->conn->params;

123 124 125 126
	switch (wdev->conn->state) {
	case CFG80211_CONN_SCAN_AGAIN:
		return cfg80211_conn_scan(wdev);
	case CFG80211_CONN_AUTHENTICATE_NEXT:
127
		BUG_ON(!rdev->ops->auth);
J
Johannes Berg 已提交
128
		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
129
		return __cfg80211_mlme_auth(rdev, wdev->netdev,
J
Johannes Berg 已提交
130 131 132
					    params->channel, params->auth_type,
					    params->bssid,
					    params->ssid, params->ssid_len,
J
Johannes Berg 已提交
133 134 135
					    NULL, 0,
					    params->key, params->key_len,
					    params->key_idx);
136
	case CFG80211_CONN_ASSOCIATE_NEXT:
137
		BUG_ON(!rdev->ops->assoc);
J
Johannes Berg 已提交
138
		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
139 140
		if (wdev->conn->prev_bssid_valid)
			prev_bssid = wdev->conn->prev_bssid;
141
		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
J
Johannes Berg 已提交
142
					    params->channel, params->bssid,
143
					    prev_bssid,
J
Johannes Berg 已提交
144 145 146
					    params->ssid, params->ssid_len,
					    params->ie, params->ie_len,
					    false, &params->crypto);
J
Johannes Berg 已提交
147
		if (err)
148
			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
J
Johannes Berg 已提交
149 150
					       NULL, 0,
					       WLAN_REASON_DEAUTH_LEAVING);
J
Johannes Berg 已提交
151
		return err;
152 153 154 155 156 157 158
	default:
		return 0;
	}
}

void cfg80211_conn_work(struct work_struct *work)
{
159
	struct cfg80211_registered_device *rdev =
160 161 162 163
		container_of(work, struct cfg80211_registered_device, conn_work);
	struct wireless_dev *wdev;

	rtnl_lock();
164 165
	cfg80211_lock_rdev(rdev);
	mutex_lock(&rdev->devlist_mtx);
166

167
	list_for_each_entry(wdev, &rdev->netdev_list, list) {
J
Johannes Berg 已提交
168 169 170
		wdev_lock(wdev);
		if (!netif_running(wdev->netdev)) {
			wdev_unlock(wdev);
171
			continue;
J
Johannes Berg 已提交
172 173 174
		}
		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
			wdev_unlock(wdev);
175
			continue;
J
Johannes Berg 已提交
176
		}
177
		if (cfg80211_conn_do_work(wdev))
J
Johannes Berg 已提交
178 179 180 181 182
			__cfg80211_connect_result(
					wdev->netdev,
					wdev->conn->params.bssid,
					NULL, 0, NULL, 0,
					WLAN_STATUS_UNSPECIFIED_FAILURE,
183
					false, NULL);
J
Johannes Berg 已提交
184
		wdev_unlock(wdev);
185 186
	}

187 188
	mutex_unlock(&rdev->devlist_mtx);
	cfg80211_unlock_rdev(rdev);
189 190 191 192 193
	rtnl_unlock();
}

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

J
Johannes Berg 已提交
198 199
	ASSERT_WDEV_LOCK(wdev);

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
	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;
215
	schedule_work(&rdev->conn_work);
216 217 218 219 220

	cfg80211_put_bss(bss);
	return true;
}

J
Johannes Berg 已提交
221
static void __cfg80211_sme_scan_done(struct net_device *dev)
222 223
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
224
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
225

J
Johannes Berg 已提交
226 227
	ASSERT_WDEV_LOCK(wdev);

228 229 230
	if (wdev->sme_state != CFG80211_SME_CONNECTING)
		return;

231
	if (!wdev->conn)
232 233 234 235 236 237 238 239 240
		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)
241
			schedule_work(&rdev->conn_work);
242
		else
J
Johannes Berg 已提交
243 244 245 246 247
			__cfg80211_connect_result(
					wdev->netdev,
					wdev->conn->params.bssid,
					NULL, 0, NULL, 0,
					WLAN_STATUS_UNSPECIFIED_FAILURE,
248
					false, NULL);
249 250 251
	}
}

J
Johannes Berg 已提交
252 253 254 255
void cfg80211_sme_scan_done(struct net_device *dev)
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;

256
	mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
J
Johannes Berg 已提交
257 258 259
	wdev_lock(wdev);
	__cfg80211_sme_scan_done(dev);
	wdev_unlock(wdev);
260
	mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
J
Johannes Berg 已提交
261 262 263 264
}

void cfg80211_sme_rx_auth(struct net_device *dev,
			  const u8 *buf, size_t len)
265 266 267 268 269 270 271
{
	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);

J
Johannes Berg 已提交
272 273
	ASSERT_WDEV_LOCK(wdev);

274 275 276 277 278 279 280 281 282 283 284 285 286
	/* 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:
J
Johannes Berg 已提交
287 288 289 290 291 292
			if (wdev->connect_keys)
				wdev->conn->params.auth_type =
					NL80211_AUTHTYPE_SHARED_KEY;
			else
				wdev->conn->params.auth_type =
					NL80211_AUTHTYPE_NETWORK_EAP;
293 294 295 296 297 298 299 300 301 302 303 304 305
			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 已提交
306
	} else if (status_code != WLAN_STATUS_SUCCESS) {
J
Johannes Berg 已提交
307
		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
308
					  status_code, false, NULL);
J
Johannes Berg 已提交
309
	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
310 311 312 313 314
		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
		schedule_work(&rdev->conn_work);
	}
}
S
Samuel Ortiz 已提交
315

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev)
{
	struct wiphy *wiphy = wdev->wiphy;
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);

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

	if (!wdev->conn->prev_bssid_valid)
		return false;

	/*
	 * Some stupid APs don't accept reassoc, so we
	 * need to fall back to trying regular assoc.
	 */
	wdev->conn->prev_bssid_valid = false;
	wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
	schedule_work(&rdev->conn_work);

	return true;
}

J
Johannes Berg 已提交
338 339 340
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,
341 342
			       u16 status, bool wextev,
			       struct cfg80211_bss *bss)
S
Samuel Ortiz 已提交
343 344
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
345
	u8 *country_ie;
S
Samuel Ortiz 已提交
346 347 348 349
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

J
Johannes Berg 已提交
350 351
	ASSERT_WDEV_LOCK(wdev);

S
Samuel Ortiz 已提交
352 353 354
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
		return;

355 356 357
	if (wdev->sme_state == CFG80211_SME_CONNECTED)
		nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev,
				    bssid, req_ie, req_ie_len,
J
Johannes Berg 已提交
358
				    resp_ie, resp_ie_len, GFP_KERNEL);
359 360 361 362
	else
		nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
					    bssid, req_ie, req_ie_len,
					    resp_ie, resp_ie_len,
J
Johannes Berg 已提交
363
					    status, GFP_KERNEL);
364 365 366 367 368 369

#ifdef CONFIG_WIRELESS_EXT
	if (wextev) {
		if (req_ie && status == WLAN_STATUS_SUCCESS) {
			memset(&wrqu, 0, sizeof(wrqu));
			wrqu.data.length = req_ie_len;
Z
Zhu Yi 已提交
370
			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
371 372 373 374 375 376 377 378 379 380
		}

		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;
381
		if (bssid && status == WLAN_STATUS_SUCCESS) {
382
			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
383 384 385
			memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
			wdev->wext.prev_bssid_valid = true;
		}
386 387 388 389
		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
	}
#endif

390 391 392 393 394 395
	if (wdev->current_bss) {
		cfg80211_unhold_bss(wdev->current_bss);
		cfg80211_put_bss(&wdev->current_bss->pub);
		wdev->current_bss = NULL;
	}

396
	if (status == WLAN_STATUS_SUCCESS &&
J
Johannes Berg 已提交
397 398
	    wdev->sme_state == CFG80211_SME_IDLE)
		goto success;
399

400
	if (wdev->sme_state != CFG80211_SME_CONNECTING)
S
Samuel Ortiz 已提交
401 402
		return;

J
Johannes Berg 已提交
403 404 405
	if (wdev->conn)
		wdev->conn->state = CFG80211_CONN_IDLE;

J
Johannes Berg 已提交
406
	if (status != WLAN_STATUS_SUCCESS) {
S
Samuel Ortiz 已提交
407
		wdev->sme_state = CFG80211_SME_IDLE;
J
Johannes Berg 已提交
408 409
		kfree(wdev->conn);
		wdev->conn = NULL;
J
Johannes Berg 已提交
410 411
		kfree(wdev->connect_keys);
		wdev->connect_keys = NULL;
412
		wdev->ssid_len = 0;
J
Johannes Berg 已提交
413
		return;
S
Samuel Ortiz 已提交
414
	}
J
Johannes Berg 已提交
415

416 417 418 419 420 421
 success:
	if (!bss)
		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
				       wdev->ssid, wdev->ssid_len,
				       WLAN_CAPABILITY_ESS,
				       WLAN_CAPABILITY_ESS);
J
Johannes Berg 已提交
422 423 424 425 426 427 428 429 430

	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);
431 432 433 434 435 436 437 438 439 440 441 442 443 444

	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]);
S
Samuel Ortiz 已提交
445
}
446 447 448 449 450 451

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 已提交
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
	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);
475
}
S
Samuel Ortiz 已提交
476 477
EXPORT_SYMBOL(cfg80211_connect_result);

J
Johannes Berg 已提交
478 479 480
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)
S
Samuel Ortiz 已提交
481 482 483 484 485 486
{
	struct cfg80211_bss *bss;
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

J
Johannes Berg 已提交
487 488
	ASSERT_WDEV_LOCK(wdev);

S
Samuel Ortiz 已提交
489 490 491 492 493 494 495 496 497 498 499 500
	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);
J
Johannes Berg 已提交
501
	cfg80211_put_bss(&wdev->current_bss->pub);
S
Samuel Ortiz 已提交
502 503 504 505 506 507 508 509 510
	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;

J
Johannes Berg 已提交
511 512
	cfg80211_hold_bss(bss_from_pub(bss));
	wdev->current_bss = bss_from_pub(bss);
S
Samuel Ortiz 已提交
513

J
Johannes Berg 已提交
514 515 516
	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
			    req_ie, req_ie_len, resp_ie, resp_ie_len,
			    GFP_KERNEL);
S
Samuel Ortiz 已提交
517 518 519 520 521

#ifdef CONFIG_WIRELESS_EXT
	if (req_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = req_ie_len;
Z
Zhu Yi 已提交
522
		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
J
Johannes Berg 已提交
523
				    &wrqu, req_ie);
S
Samuel Ortiz 已提交
524 525 526 527 528
	}

	if (resp_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = resp_ie_len;
J
Johannes Berg 已提交
529 530
		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
				    &wrqu, resp_ie);
S
Samuel Ortiz 已提交
531 532 533 534 535
	}

	memset(&wrqu, 0, sizeof(wrqu));
	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
536 537
	memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
	wdev->wext.prev_bssid_valid = true;
J
Johannes Berg 已提交
538
	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
S
Samuel Ortiz 已提交
539 540
#endif
}
J
Johannes Berg 已提交
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568

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);
}
S
Samuel Ortiz 已提交
569 570
EXPORT_SYMBOL(cfg80211_roamed);

J
Johannes Berg 已提交
571
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
572
			     size_t ie_len, u16 reason, bool from_ap)
S
Samuel Ortiz 已提交
573 574
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
J
Johannes Berg 已提交
575 576
	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
	int i;
S
Samuel Ortiz 已提交
577 578 579 580
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

J
Johannes Berg 已提交
581 582
	ASSERT_WDEV_LOCK(wdev);

S
Samuel Ortiz 已提交
583 584 585 586 587 588 589 590
	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);
J
Johannes Berg 已提交
591
		cfg80211_put_bss(&wdev->current_bss->pub);
S
Samuel Ortiz 已提交
592 593 594 595
	}

	wdev->current_bss = NULL;
	wdev->sme_state = CFG80211_SME_IDLE;
596
	wdev->ssid_len = 0;
S
Samuel Ortiz 已提交
597

598
	if (wdev->conn) {
599 600 601
		const u8 *bssid;
		int ret;

602 603
		kfree(wdev->conn->ie);
		wdev->conn->ie = NULL;
J
Johannes Berg 已提交
604 605
		kfree(wdev->conn);
		wdev->conn = NULL;
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622

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

J
Johannes Berg 已提交
625 626 627 628 629 630 631 632 633
	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);
S
Samuel Ortiz 已提交
634 635 636 637 638 639 640 641 642 643 644

#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)
{
J
Johannes Berg 已提交
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
	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);
S
Samuel Ortiz 已提交
664 665 666
}
EXPORT_SYMBOL(cfg80211_disconnected);

J
Johannes Berg 已提交
667 668
int __cfg80211_connect(struct cfg80211_registered_device *rdev,
		       struct net_device *dev,
J
Johannes Berg 已提交
669
		       struct cfg80211_connect_params *connect,
670 671
		       struct cfg80211_cached_keys *connkeys,
		       const u8 *prev_bssid)
S
Samuel Ortiz 已提交
672 673
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
674
	struct ieee80211_channel *chan;
J
Johannes Berg 已提交
675 676 677
	int err;

	ASSERT_WDEV_LOCK(wdev);
S
Samuel Ortiz 已提交
678 679 680 681

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

682 683 684 685
	chan = rdev_fixed_channel(rdev, wdev);
	if (chan && chan != connect->channel)
		return -EBUSY;

J
Johannes Berg 已提交
686 687 688 689 690 691 692
	if (WARN_ON(wdev->connect_keys)) {
		kfree(wdev->connect_keys);
		wdev->connect_keys = NULL;
	}

	if (connkeys && connkeys->def >= 0) {
		int idx;
S
Samuel Ortiz 已提交
693
		u32 cipher;
J
Johannes Berg 已提交
694 695

		idx = connkeys->def;
S
Samuel Ortiz 已提交
696
		cipher = connkeys->params[idx].cipher;
J
Johannes Berg 已提交
697
		/* If given a WEP key we may need it for shared key auth */
S
Samuel Ortiz 已提交
698 699
		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
		    cipher == WLAN_CIPHER_SUITE_WEP104) {
J
Johannes Berg 已提交
700 701 702
			connect->key_idx = idx;
			connect->key = connkeys->params[idx].key;
			connect->key_len = connkeys->params[idx].key_len;
S
Samuel Ortiz 已提交
703 704 705 706 707 708 709 710 711 712 713 714

			/*
			 * 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 已提交
715 716 717
		}
	}

S
Samuel Ortiz 已提交
718
	if (!rdev->ops->connect) {
719 720 721
		if (!rdev->ops->auth || !rdev->ops->assoc)
			return -EOPNOTSUPP;

J
Johannes Berg 已提交
722 723 724 725 726 727
		if (WARN_ON(wdev->conn))
			return -EINPROGRESS;

		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
		if (!wdev->conn)
			return -ENOMEM;
728 729 730 731 732 733 734 735 736 737 738 739 740 741

		/*
		 * 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;
J
Johannes Berg 已提交
742 743 744
			if (!wdev->conn->ie) {
				kfree(wdev->conn);
				wdev->conn = NULL;
745
				return -ENOMEM;
J
Johannes Berg 已提交
746
			}
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
		}

		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;
J
Johannes Berg 已提交
768
		wdev->connect_keys = connkeys;
769

770 771 772 773 774
		if (prev_bssid) {
			memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
			wdev->conn->prev_bssid_valid = true;
		}

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
		/* 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;
			}
		}
J
Johannes Berg 已提交
792 793 794
		if (err) {
			kfree(wdev->conn);
			wdev->conn = NULL;
795
			wdev->sme_state = CFG80211_SME_IDLE;
J
Johannes Berg 已提交
796
			wdev->connect_keys = NULL;
797
			wdev->ssid_len = 0;
J
Johannes Berg 已提交
798
		}
799 800

		return err;
S
Samuel Ortiz 已提交
801 802
	} else {
		wdev->sme_state = CFG80211_SME_CONNECTING;
J
Johannes Berg 已提交
803
		wdev->connect_keys = connkeys;
S
Samuel Ortiz 已提交
804 805
		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
		if (err) {
J
Johannes Berg 已提交
806
			wdev->connect_keys = NULL;
S
Samuel Ortiz 已提交
807 808 809 810
			wdev->sme_state = CFG80211_SME_IDLE;
			return err;
		}

811 812
		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
		wdev->ssid_len = connect->ssid_len;
S
Samuel Ortiz 已提交
813

814 815
		return 0;
	}
S
Samuel Ortiz 已提交
816 817
}

J
Johannes Berg 已提交
818 819
int cfg80211_connect(struct cfg80211_registered_device *rdev,
		     struct net_device *dev,
J
Johannes Berg 已提交
820 821
		     struct cfg80211_connect_params *connect,
		     struct cfg80211_cached_keys *connkeys)
J
Johannes Berg 已提交
822 823 824
{
	int err;

825
	mutex_lock(&rdev->devlist_mtx);
J
Johannes Berg 已提交
826
	wdev_lock(dev->ieee80211_ptr);
827
	err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL);
J
Johannes Berg 已提交
828
	wdev_unlock(dev->ieee80211_ptr);
829
	mutex_unlock(&rdev->devlist_mtx);
J
Johannes Berg 已提交
830 831 832 833 834 835

	return err;
}

int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
			  struct net_device *dev, u16 reason, bool wextev)
S
Samuel Ortiz 已提交
836
{
837
	struct wireless_dev *wdev = dev->ieee80211_ptr;
S
Samuel Ortiz 已提交
838 839
	int err;

J
Johannes Berg 已提交
840 841
	ASSERT_WDEV_LOCK(wdev);

842 843 844
	if (wdev->sme_state == CFG80211_SME_IDLE)
		return -EINVAL;

J
Johannes Berg 已提交
845 846 847
	kfree(wdev->connect_keys);
	wdev->connect_keys = NULL;

S
Samuel Ortiz 已提交
848
	if (!rdev->ops->disconnect) {
J
Johannes Berg 已提交
849 850
		if (!rdev->ops->deauth)
			return -EOPNOTSUPP;
851

J
Johannes Berg 已提交
852 853 854 855 856
		/* was it connected by userspace SME? */
		if (!wdev->conn) {
			cfg80211_mlme_down(rdev, dev);
			return 0;
		}
857 858 859 860 861

		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;
J
Johannes Berg 已提交
862 863
			kfree(wdev->conn);
			wdev->conn = NULL;
864
			wdev->ssid_len = 0;
865 866 867 868
			return 0;
		}

		/* wdev->conn->params.bssid must be set if > SCANNING */
J
Johannes Berg 已提交
869 870 871
		err = __cfg80211_mlme_deauth(rdev, dev,
					     wdev->conn->params.bssid,
					     NULL, 0, reason);
872 873
		if (err)
			return err;
S
Samuel Ortiz 已提交
874 875 876 877 878 879
	} else {
		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
		if (err)
			return err;
	}

880
	if (wdev->sme_state == CFG80211_SME_CONNECTED)
J
Johannes Berg 已提交
881
		__cfg80211_disconnected(dev, NULL, 0, 0, false);
882
	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
883 884
		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
					  WLAN_STATUS_UNSPECIFIED_FAILURE,
885
					  wextev, NULL);
S
Samuel Ortiz 已提交
886 887 888

	return 0;
}
J
Johannes Berg 已提交
889

J
Johannes Berg 已提交
890 891 892 893 894 895 896 897 898 899 900 901 902
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;
}

J
Johannes Berg 已提交
903 904 905 906 907 908
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];

J
Johannes Berg 已提交
909 910
	ASSERT_WDEV_LOCK(wdev);

J
Johannes Berg 已提交
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
	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);
J
Johannes Berg 已提交
926 927
	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
J
Johannes Berg 已提交
928 929 930 931 932 933
		/* whatever -- assume gone anyway */
		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
		wdev->auth_bsses[idx] = NULL;
	}
}