sme.c 24.2 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
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);
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);
112
	struct cfg80211_connect_params *params;
113
	const u8 *prev_bssid = NULL;
114
	int err;
115

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

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

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);
128
		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
129
		return __cfg80211_mlme_auth(rdev, wdev->netdev,
Johannes Berg's avatar
Johannes Berg committed
130 131 132
					    params->channel, params->auth_type,
					    params->bssid,
					    params->ssid, params->ssid_len,
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);
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,
Johannes Berg's avatar
Johannes Berg committed
142
					    params->channel, params->bssid,
143
					    prev_bssid,
Johannes Berg's avatar
Johannes Berg committed
144 145 146
					    params->ssid, params->ssid_len,
					    params->ie, params->ie_len,
					    false, &params->crypto);
147
		if (err)
148
			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
Johannes Berg's avatar
Johannes Berg committed
149 150
					       NULL, 0,
					       WLAN_REASON_DEAUTH_LEAVING);
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) {
Johannes Berg's avatar
Johannes Berg committed
168 169 170
		wdev_lock(wdev);
		if (!netif_running(wdev->netdev)) {
			wdev_unlock(wdev);
171
			continue;
Johannes Berg's avatar
Johannes Berg committed
172 173 174
		}
		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
			wdev_unlock(wdev);
175
			continue;
Johannes Berg's avatar
Johannes Berg committed
176
		}
177
		if (cfg80211_conn_do_work(wdev))
Johannes Berg's avatar
Johannes Berg committed
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);
Johannes Berg's avatar
Johannes Berg committed
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;

Johannes Berg's avatar
Johannes Berg committed
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;
}

Johannes Berg's avatar
Johannes Berg committed
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

Johannes Berg's avatar
Johannes Berg committed
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
Johannes Berg's avatar
Johannes Berg committed
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
	}
}

Johannes Berg's avatar
Johannes Berg committed
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);
Johannes Berg's avatar
Johannes Berg committed
257 258 259
	wdev_lock(wdev);
	__cfg80211_sme_scan_done(dev);
	wdev_unlock(wdev);
260
	mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
Johannes Berg's avatar
Johannes Berg committed
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);

Johannes Berg's avatar
Johannes Berg committed
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:
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);
306
	} else if (status_code != WLAN_STATUS_SUCCESS) {
Johannes Berg's avatar
Johannes Berg committed
307
		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
308
					  status_code, false, NULL);
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);
	}
}
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;
}

Johannes Berg's avatar
Johannes Berg committed
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)
343 344
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
345
	u8 *country_ie;
346 347 348 349
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

Johannes Berg's avatar
Johannes Berg committed
350 351
	ASSERT_WDEV_LOCK(wdev);

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,
Johannes Berg's avatar
Johannes Berg committed
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,
Johannes Berg's avatar
Johannes Berg committed
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;
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 &&
397 398
	    wdev->sme_state == CFG80211_SME_IDLE)
		goto success;
399

400
	if (wdev->sme_state != CFG80211_SME_CONNECTING)
401 402
		return;

403 404 405
	if (wdev->conn)
		wdev->conn->state = CFG80211_CONN_IDLE;

406
	if (status != WLAN_STATUS_SUCCESS) {
407
		wdev->sme_state = CFG80211_SME_IDLE;
408 409
		kfree(wdev->conn);
		wdev->conn = NULL;
410 411
		kfree(wdev->connect_keys);
		wdev->connect_keys = NULL;
412
		wdev->ssid_len = 0;
413
		return;
414
	}
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);
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]);
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)
{
Johannes Berg's avatar
Johannes Berg committed
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
}
476 477
EXPORT_SYMBOL(cfg80211_connect_result);

Johannes Berg's avatar
Johannes Berg committed
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)
481 482 483 484 485 486
{
	struct cfg80211_bss *bss;
#ifdef CONFIG_WIRELESS_EXT
	union iwreq_data wrqu;
#endif

Johannes Berg's avatar
Johannes Berg committed
487 488
	ASSERT_WDEV_LOCK(wdev);

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);
501
	cfg80211_put_bss(&wdev->current_bss->pub);
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;

511 512
	cfg80211_hold_bss(bss_from_pub(bss));
	wdev->current_bss = bss_from_pub(bss);
513

Johannes Berg's avatar
Johannes Berg committed
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);
517 518 519 520 521

#ifdef CONFIG_WIRELESS_EXT
	if (req_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = req_ie_len;
522
		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
Johannes Berg's avatar
Johannes Berg committed
523
				    &wrqu, req_ie);
524 525 526 527 528
	}

	if (resp_ie) {
		memset(&wrqu, 0, sizeof(wrqu));
		wrqu.data.length = resp_ie_len;
Johannes Berg's avatar
Johannes Berg committed
529 530
		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
				    &wrqu, resp_ie);
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;
Johannes Berg's avatar
Johannes Berg committed
538
	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
539 540
#endif
}
Johannes Berg's avatar
Johannes Berg committed
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);
}
569 570
EXPORT_SYMBOL(cfg80211_roamed);

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

Johannes Berg's avatar
Johannes Berg committed
581 582
	ASSERT_WDEV_LOCK(wdev);

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);
591
		cfg80211_put_bss(&wdev->current_bss->pub);
592 593 594 595
	}

	wdev->current_bss = NULL;
	wdev->sme_state = CFG80211_SME_IDLE;
596
	wdev->ssid_len = 0;
597

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

602 603
		kfree(wdev->conn->ie);
		wdev->conn->ie = NULL;
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
	}

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);
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)
{
Johannes Berg's avatar
Johannes Berg committed
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);
664 665 666
}
EXPORT_SYMBOL(cfg80211_disconnected);

Johannes Berg's avatar
Johannes Berg committed
667 668
int __cfg80211_connect(struct cfg80211_registered_device *rdev,
		       struct net_device *dev,
669
		       struct cfg80211_connect_params *connect,
670 671
		       struct cfg80211_cached_keys *connkeys,
		       const u8 *prev_bssid)
672 673
{
	struct wireless_dev *wdev = dev->ieee80211_ptr;
674
	struct ieee80211_channel *chan;
Johannes Berg's avatar
Johannes Berg committed
675 676 677
	int err;

	ASSERT_WDEV_LOCK(wdev);
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;

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;
Samuel Ortiz's avatar
Samuel Ortiz committed
693
		u32 cipher;
694 695

		idx = connkeys->def;
Samuel Ortiz's avatar
Samuel Ortiz committed
696
		cipher = connkeys->params[idx].cipher;
697
		/* If given a WEP key we may need it for shared key auth */
Samuel Ortiz's avatar
Samuel Ortiz committed
698 699
		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
		    cipher == WLAN_CIPHER_SUITE_WEP104) {
700 701 702
			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
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;
			}
715 716 717
		}
	}

718
	if (!rdev->ops->connect) {
719 720 721
		if (!rdev->ops->auth || !rdev->ops->assoc)
			return -EOPNOTSUPP;

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;
742 743 744
			if (!wdev->conn->ie) {
				kfree(wdev->conn);
				wdev->conn = NULL;
745
				return -ENOMEM;
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;
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;
			}
		}
792 793 794
		if (err) {
			kfree(wdev->conn);
			wdev->conn = NULL;
795
			wdev->sme_state = CFG80211_SME_IDLE;
796
			wdev->connect_keys = NULL;
797
			wdev->ssid_len = 0;
798
		}
799 800

		return err;
801 802
	} else {
		wdev->sme_state = CFG80211_SME_CONNECTING;
803
		wdev->connect_keys = connkeys;
804 805
		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
		if (err) {
806
			wdev->connect_keys = NULL;
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;
813

814 815
		return 0;
	}
816 817
}

Johannes Berg's avatar
Johannes Berg committed
818 819
int cfg80211_connect(struct cfg80211_registered_device *rdev,
		     struct net_device *dev,
820 821
		     struct cfg80211_connect_params *connect,
		     struct cfg80211_cached_keys *connkeys)
Johannes Berg's avatar
Johannes Berg committed
822 823 824
{
	int err;

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

	return err;
}

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

Johannes Berg's avatar
Johannes Berg committed
840 841
	ASSERT_WDEV_LOCK(wdev);

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

845 846 847
	kfree(wdev->connect_keys);
	wdev->connect_keys = NULL;

848
	if (!rdev->ops->disconnect) {
849 850
		if (!rdev->ops->deauth)
			return -EOPNOTSUPP;
851

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;
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 */
Johannes Berg's avatar
Johannes Berg committed
869 870 871
		err = __cfg80211_mlme_deauth(rdev, dev,
					     wdev->conn->params.bssid,
					     NULL, 0, reason);
872 873
		if (err)
			return err;
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)
Johannes Berg's avatar
Johannes Berg committed
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);
886 887 888

	return 0;
}
889

Johannes Berg's avatar
Johannes Berg committed
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;
}

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

Johannes Berg's avatar
Johannes Berg committed
909 910
	ASSERT_WDEV_LOCK(wdev);

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);
Johannes Berg's avatar
Johannes Berg committed
926 927
	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
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;
	}
}