xref: /openbmc/linux/net/wireless/sme.c (revision 7834704b)
1b23aa676SSamuel Ortiz /*
2b23aa676SSamuel Ortiz  * SME code for cfg80211's connect emulation.
3b23aa676SSamuel Ortiz  *
4b23aa676SSamuel Ortiz  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
5b23aa676SSamuel Ortiz  * Copyright (C) 2009   Intel Corporation. All rights reserved.
6b23aa676SSamuel Ortiz  */
7b23aa676SSamuel Ortiz 
8b23aa676SSamuel Ortiz #include <linux/etherdevice.h>
9b23aa676SSamuel Ortiz #include <linux/if_arp.h>
10b23aa676SSamuel Ortiz #include <linux/workqueue.h>
11a9a11622SJohannes Berg #include <linux/wireless.h>
12a9a11622SJohannes Berg #include <net/iw_handler.h>
13b23aa676SSamuel Ortiz #include <net/cfg80211.h>
14b23aa676SSamuel Ortiz #include <net/rtnetlink.h>
15b23aa676SSamuel Ortiz #include "nl80211.h"
168b19e6caSLuis R. Rodriguez #include "reg.h"
17b23aa676SSamuel Ortiz 
186829c878SJohannes Berg struct cfg80211_conn {
196829c878SJohannes Berg 	struct cfg80211_connect_params params;
206829c878SJohannes Berg 	/* these are sub-states of the _CONNECTING sme_state */
216829c878SJohannes Berg 	enum {
226829c878SJohannes Berg 		CFG80211_CONN_IDLE,
236829c878SJohannes Berg 		CFG80211_CONN_SCANNING,
246829c878SJohannes Berg 		CFG80211_CONN_SCAN_AGAIN,
256829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATE_NEXT,
266829c878SJohannes Berg 		CFG80211_CONN_AUTHENTICATING,
276829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATE_NEXT,
286829c878SJohannes Berg 		CFG80211_CONN_ASSOCIATING,
297d930bc3SJohannes Berg 		CFG80211_CONN_DEAUTH_ASSOC_FAIL,
306829c878SJohannes Berg 	} state;
31f401a6f7SJohannes Berg 	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
326829c878SJohannes Berg 	u8 *ie;
336829c878SJohannes Berg 	size_t ie_len;
34f401a6f7SJohannes Berg 	bool auto_auth, prev_bssid_valid;
356829c878SJohannes Berg };
366829c878SJohannes Berg 
3709d989d1SLuis R. Rodriguez bool cfg80211_is_all_idle(void)
3809d989d1SLuis R. Rodriguez {
3909d989d1SLuis R. Rodriguez 	struct cfg80211_registered_device *rdev;
4009d989d1SLuis R. Rodriguez 	struct wireless_dev *wdev;
4109d989d1SLuis R. Rodriguez 	bool is_all_idle = true;
4209d989d1SLuis R. Rodriguez 
4309d989d1SLuis R. Rodriguez 	mutex_lock(&cfg80211_mutex);
4409d989d1SLuis R. Rodriguez 
4509d989d1SLuis R. Rodriguez 	/*
4609d989d1SLuis R. Rodriguez 	 * All devices must be idle as otherwise if you are actively
4709d989d1SLuis R. Rodriguez 	 * scanning some new beacon hints could be learned and would
4809d989d1SLuis R. Rodriguez 	 * count as new regulatory hints.
4909d989d1SLuis R. Rodriguez 	 */
5009d989d1SLuis R. Rodriguez 	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
5109d989d1SLuis R. Rodriguez 		cfg80211_lock_rdev(rdev);
5209d989d1SLuis R. Rodriguez 		list_for_each_entry(wdev, &rdev->netdev_list, list) {
5309d989d1SLuis R. Rodriguez 			wdev_lock(wdev);
5409d989d1SLuis R. Rodriguez 			if (wdev->sme_state != CFG80211_SME_IDLE)
5509d989d1SLuis R. Rodriguez 				is_all_idle = false;
5609d989d1SLuis R. Rodriguez 			wdev_unlock(wdev);
5709d989d1SLuis R. Rodriguez 		}
5809d989d1SLuis R. Rodriguez 		cfg80211_unlock_rdev(rdev);
5909d989d1SLuis R. Rodriguez 	}
6009d989d1SLuis R. Rodriguez 
6109d989d1SLuis R. Rodriguez 	mutex_unlock(&cfg80211_mutex);
6209d989d1SLuis R. Rodriguez 
6309d989d1SLuis R. Rodriguez 	return is_all_idle;
6409d989d1SLuis R. Rodriguez }
6509d989d1SLuis R. Rodriguez 
6609d989d1SLuis R. Rodriguez static void disconnect_work(struct work_struct *work)
6709d989d1SLuis R. Rodriguez {
6809d989d1SLuis R. Rodriguez 	if (!cfg80211_is_all_idle())
6909d989d1SLuis R. Rodriguez 		return;
7009d989d1SLuis R. Rodriguez 
7109d989d1SLuis R. Rodriguez 	regulatory_hint_disconnect();
7209d989d1SLuis R. Rodriguez }
7309d989d1SLuis R. Rodriguez 
7409d989d1SLuis R. Rodriguez static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
756829c878SJohannes Berg 
766829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev)
776829c878SJohannes Berg {
7879c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
796829c878SJohannes Berg 	struct cfg80211_scan_request *request;
806829c878SJohannes Berg 	int n_channels, err;
816829c878SJohannes Berg 
826829c878SJohannes Berg 	ASSERT_RTNL();
8379c97e97SJohannes Berg 	ASSERT_RDEV_LOCK(rdev);
84667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
856829c878SJohannes Berg 
8679c97e97SJohannes Berg 	if (rdev->scan_req)
876829c878SJohannes Berg 		return -EBUSY;
886829c878SJohannes Berg 
896829c878SJohannes Berg 	if (wdev->conn->params.channel) {
906829c878SJohannes Berg 		n_channels = 1;
916829c878SJohannes Berg 	} else {
926829c878SJohannes Berg 		enum ieee80211_band band;
936829c878SJohannes Berg 		n_channels = 0;
946829c878SJohannes Berg 
956829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
966829c878SJohannes Berg 			if (!wdev->wiphy->bands[band])
976829c878SJohannes Berg 				continue;
986829c878SJohannes Berg 			n_channels += wdev->wiphy->bands[band]->n_channels;
996829c878SJohannes Berg 		}
1006829c878SJohannes Berg 	}
1016829c878SJohannes Berg 	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
1026829c878SJohannes Berg 			  sizeof(request->channels[0]) * n_channels,
1036829c878SJohannes Berg 			  GFP_KERNEL);
1046829c878SJohannes Berg 	if (!request)
1056829c878SJohannes Berg 		return -ENOMEM;
1066829c878SJohannes Berg 
1076829c878SJohannes Berg 	if (wdev->conn->params.channel)
1086829c878SJohannes Berg 		request->channels[0] = wdev->conn->params.channel;
1096829c878SJohannes Berg 	else {
1106829c878SJohannes Berg 		int i = 0, j;
1116829c878SJohannes Berg 		enum ieee80211_band band;
1126829c878SJohannes Berg 
1136829c878SJohannes Berg 		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
1146829c878SJohannes Berg 			if (!wdev->wiphy->bands[band])
1156829c878SJohannes Berg 				continue;
1166829c878SJohannes Berg 			for (j = 0; j < wdev->wiphy->bands[band]->n_channels;
1176829c878SJohannes Berg 			     i++, j++)
1186829c878SJohannes Berg 				request->channels[i] =
1196829c878SJohannes Berg 					&wdev->wiphy->bands[band]->channels[j];
1206829c878SJohannes Berg 		}
1216829c878SJohannes Berg 	}
1226829c878SJohannes Berg 	request->n_channels = n_channels;
1235ba63533SJohannes Berg 	request->ssids = (void *)&request->channels[n_channels];
1246829c878SJohannes Berg 	request->n_ssids = 1;
1256829c878SJohannes Berg 
1266829c878SJohannes Berg 	memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
1276829c878SJohannes Berg 		wdev->conn->params.ssid_len);
1286829c878SJohannes Berg 	request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
1296829c878SJohannes Berg 
130463d0183SJohannes Berg 	request->dev = wdev->netdev;
13179c97e97SJohannes Berg 	request->wiphy = &rdev->wiphy;
1326829c878SJohannes Berg 
13379c97e97SJohannes Berg 	rdev->scan_req = request;
1346829c878SJohannes Berg 
13579c97e97SJohannes Berg 	err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
1366829c878SJohannes Berg 	if (!err) {
1376829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_SCANNING;
13879c97e97SJohannes Berg 		nl80211_send_scan_start(rdev, wdev->netdev);
139463d0183SJohannes Berg 		dev_hold(wdev->netdev);
1406829c878SJohannes Berg 	} else {
14179c97e97SJohannes Berg 		rdev->scan_req = NULL;
1426829c878SJohannes Berg 		kfree(request);
1436829c878SJohannes Berg 	}
1446829c878SJohannes Berg 	return err;
1456829c878SJohannes Berg }
1466829c878SJohannes Berg 
1476829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev)
1486829c878SJohannes Berg {
14979c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
15019957bb3SJohannes Berg 	struct cfg80211_connect_params *params;
151f401a6f7SJohannes Berg 	const u8 *prev_bssid = NULL;
15219957bb3SJohannes Berg 	int err;
1536829c878SJohannes Berg 
154667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
155667503ddSJohannes Berg 
1566829c878SJohannes Berg 	if (!wdev->conn)
1576829c878SJohannes Berg 		return 0;
1586829c878SJohannes Berg 
15919957bb3SJohannes Berg 	params = &wdev->conn->params;
16019957bb3SJohannes Berg 
1616829c878SJohannes Berg 	switch (wdev->conn->state) {
1626829c878SJohannes Berg 	case CFG80211_CONN_SCAN_AGAIN:
1636829c878SJohannes Berg 		return cfg80211_conn_scan(wdev);
1646829c878SJohannes Berg 	case CFG80211_CONN_AUTHENTICATE_NEXT:
16579c97e97SJohannes Berg 		BUG_ON(!rdev->ops->auth);
16619957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
16779c97e97SJohannes Berg 		return __cfg80211_mlme_auth(rdev, wdev->netdev,
16819957bb3SJohannes Berg 					    params->channel, params->auth_type,
16919957bb3SJohannes Berg 					    params->bssid,
17019957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
171fffd0934SJohannes Berg 					    NULL, 0,
172fffd0934SJohannes Berg 					    params->key, params->key_len,
173d5cdfacbSJouni Malinen 					    params->key_idx, false);
1746829c878SJohannes Berg 	case CFG80211_CONN_ASSOCIATE_NEXT:
17579c97e97SJohannes Berg 		BUG_ON(!rdev->ops->assoc);
17619957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
177f401a6f7SJohannes Berg 		if (wdev->conn->prev_bssid_valid)
178f401a6f7SJohannes Berg 			prev_bssid = wdev->conn->prev_bssid;
17979c97e97SJohannes Berg 		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
180667503ddSJohannes Berg 					    params->channel, params->bssid,
181f401a6f7SJohannes Berg 					    prev_bssid,
18219957bb3SJohannes Berg 					    params->ssid, params->ssid_len,
18319957bb3SJohannes Berg 					    params->ie, params->ie_len,
18419957bb3SJohannes Berg 					    false, &params->crypto);
18519957bb3SJohannes Berg 		if (err)
18679c97e97SJohannes Berg 			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
187667503ddSJohannes Berg 					       NULL, 0,
188d5cdfacbSJouni Malinen 					       WLAN_REASON_DEAUTH_LEAVING,
189d5cdfacbSJouni Malinen 					       false);
19019957bb3SJohannes Berg 		return err;
1917d930bc3SJohannes Berg 	case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
1927d930bc3SJohannes Berg 		__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
1937d930bc3SJohannes Berg 				       NULL, 0,
194d5cdfacbSJouni Malinen 				       WLAN_REASON_DEAUTH_LEAVING, false);
1957d930bc3SJohannes Berg 		/* return an error so that we call __cfg80211_connect_result() */
1967d930bc3SJohannes Berg 		return -EINVAL;
1976829c878SJohannes Berg 	default:
1986829c878SJohannes Berg 		return 0;
1996829c878SJohannes Berg 	}
2006829c878SJohannes Berg }
2016829c878SJohannes Berg 
2026829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work)
2036829c878SJohannes Berg {
20479c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev =
2056829c878SJohannes Berg 		container_of(work, struct cfg80211_registered_device, conn_work);
2066829c878SJohannes Berg 	struct wireless_dev *wdev;
2077400f42eSJohannes Berg 	u8 bssid_buf[ETH_ALEN], *bssid = NULL;
2086829c878SJohannes Berg 
2096829c878SJohannes Berg 	rtnl_lock();
21079c97e97SJohannes Berg 	cfg80211_lock_rdev(rdev);
21179c97e97SJohannes Berg 	mutex_lock(&rdev->devlist_mtx);
2126829c878SJohannes Berg 
21379c97e97SJohannes Berg 	list_for_each_entry(wdev, &rdev->netdev_list, list) {
214667503ddSJohannes Berg 		wdev_lock(wdev);
215667503ddSJohannes Berg 		if (!netif_running(wdev->netdev)) {
216667503ddSJohannes Berg 			wdev_unlock(wdev);
2176829c878SJohannes Berg 			continue;
218667503ddSJohannes Berg 		}
219667503ddSJohannes Berg 		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
220667503ddSJohannes Berg 			wdev_unlock(wdev);
2216829c878SJohannes Berg 			continue;
222667503ddSJohannes Berg 		}
2237400f42eSJohannes Berg 		if (wdev->conn->params.bssid) {
2247400f42eSJohannes Berg 			memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
2257400f42eSJohannes Berg 			bssid = bssid_buf;
2267400f42eSJohannes Berg 		}
2276829c878SJohannes Berg 		if (cfg80211_conn_do_work(wdev))
228667503ddSJohannes Berg 			__cfg80211_connect_result(
2297d930bc3SJohannes Berg 					wdev->netdev, bssid,
2306829c878SJohannes Berg 					NULL, 0, NULL, 0,
2316829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
232df7fc0f9SJohannes Berg 					false, NULL);
233667503ddSJohannes Berg 		wdev_unlock(wdev);
2346829c878SJohannes Berg 	}
2356829c878SJohannes Berg 
23679c97e97SJohannes Berg 	mutex_unlock(&rdev->devlist_mtx);
23779c97e97SJohannes Berg 	cfg80211_unlock_rdev(rdev);
2386829c878SJohannes Berg 	rtnl_unlock();
2396829c878SJohannes Berg }
2406829c878SJohannes Berg 
241bbac31f4SJohannes Berg static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
2426829c878SJohannes Berg {
24379c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
2446829c878SJohannes Berg 	struct cfg80211_bss *bss;
2456829c878SJohannes Berg 	u16 capa = WLAN_CAPABILITY_ESS;
2466829c878SJohannes Berg 
247667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
248667503ddSJohannes Berg 
2496829c878SJohannes Berg 	if (wdev->conn->params.privacy)
2506829c878SJohannes Berg 		capa |= WLAN_CAPABILITY_PRIVACY;
2516829c878SJohannes Berg 
2526829c878SJohannes Berg 	bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid,
2536829c878SJohannes Berg 			       wdev->conn->params.ssid,
2546829c878SJohannes Berg 			       wdev->conn->params.ssid_len,
2556829c878SJohannes Berg 			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
2566829c878SJohannes Berg 			       capa);
2576829c878SJohannes Berg 	if (!bss)
258bbac31f4SJohannes Berg 		return NULL;
2596829c878SJohannes Berg 
2606829c878SJohannes Berg 	memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
2616829c878SJohannes Berg 	wdev->conn->params.bssid = wdev->conn->bssid;
2626829c878SJohannes Berg 	wdev->conn->params.channel = bss->channel;
2636829c878SJohannes Berg 	wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
26479c97e97SJohannes Berg 	schedule_work(&rdev->conn_work);
2656829c878SJohannes Berg 
266bbac31f4SJohannes Berg 	return bss;
2676829c878SJohannes Berg }
2686829c878SJohannes Berg 
269667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev)
2706829c878SJohannes Berg {
2716829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
27279c97e97SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
273bbac31f4SJohannes Berg 	struct cfg80211_bss *bss;
2746829c878SJohannes Berg 
275667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
276667503ddSJohannes Berg 
2776829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
2786829c878SJohannes Berg 		return;
2796829c878SJohannes Berg 
280d4b1a687SZhu Yi 	if (!wdev->conn)
2816829c878SJohannes Berg 		return;
2826829c878SJohannes Berg 
2836829c878SJohannes Berg 	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
2846829c878SJohannes Berg 	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
2856829c878SJohannes Berg 		return;
2866829c878SJohannes Berg 
287bbac31f4SJohannes Berg 	bss = cfg80211_get_conn_bss(wdev);
288bbac31f4SJohannes Berg 	if (bss) {
289bbac31f4SJohannes Berg 		cfg80211_put_bss(bss);
290bbac31f4SJohannes Berg 	} else {
2916829c878SJohannes Berg 		/* not found */
2926829c878SJohannes Berg 		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
29379c97e97SJohannes Berg 			schedule_work(&rdev->conn_work);
2946829c878SJohannes Berg 		else
295667503ddSJohannes Berg 			__cfg80211_connect_result(
296667503ddSJohannes Berg 					wdev->netdev,
297667503ddSJohannes Berg 					wdev->conn->params.bssid,
2986829c878SJohannes Berg 					NULL, 0, NULL, 0,
2996829c878SJohannes Berg 					WLAN_STATUS_UNSPECIFIED_FAILURE,
300df7fc0f9SJohannes Berg 					false, NULL);
3016829c878SJohannes Berg 	}
3026829c878SJohannes Berg }
3036829c878SJohannes Berg 
304667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev)
305667503ddSJohannes Berg {
306667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
307667503ddSJohannes Berg 
30859bbb6f7SJohannes Berg 	mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
309667503ddSJohannes Berg 	wdev_lock(wdev);
310667503ddSJohannes Berg 	__cfg80211_sme_scan_done(dev);
311667503ddSJohannes Berg 	wdev_unlock(wdev);
31259bbb6f7SJohannes Berg 	mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
313667503ddSJohannes Berg }
314667503ddSJohannes Berg 
315667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev,
316667503ddSJohannes Berg 			  const u8 *buf, size_t len)
3176829c878SJohannes Berg {
3186829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
3196829c878SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
3206829c878SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3216829c878SJohannes Berg 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
3226829c878SJohannes Berg 	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
3236829c878SJohannes Berg 
324667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
325667503ddSJohannes Berg 
3266829c878SJohannes Berg 	/* should only RX auth frames when connecting */
3276829c878SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
3286829c878SJohannes Berg 		return;
3296829c878SJohannes Berg 
3306829c878SJohannes Berg 	if (WARN_ON(!wdev->conn))
3316829c878SJohannes Berg 		return;
3326829c878SJohannes Berg 
3336829c878SJohannes Berg 	if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
3346829c878SJohannes Berg 	    wdev->conn->auto_auth &&
3356829c878SJohannes Berg 	    wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
3366829c878SJohannes Berg 		/* select automatically between only open, shared, leap */
3376829c878SJohannes Berg 		switch (wdev->conn->params.auth_type) {
3386829c878SJohannes Berg 		case NL80211_AUTHTYPE_OPEN_SYSTEM:
339fffd0934SJohannes Berg 			if (wdev->connect_keys)
3406829c878SJohannes Berg 				wdev->conn->params.auth_type =
3416829c878SJohannes Berg 					NL80211_AUTHTYPE_SHARED_KEY;
342fffd0934SJohannes Berg 			else
343fffd0934SJohannes Berg 				wdev->conn->params.auth_type =
344fffd0934SJohannes Berg 					NL80211_AUTHTYPE_NETWORK_EAP;
3456829c878SJohannes Berg 			break;
3466829c878SJohannes Berg 		case NL80211_AUTHTYPE_SHARED_KEY:
3476829c878SJohannes Berg 			wdev->conn->params.auth_type =
3486829c878SJohannes Berg 				NL80211_AUTHTYPE_NETWORK_EAP;
3496829c878SJohannes Berg 			break;
3506829c878SJohannes Berg 		default:
3516829c878SJohannes Berg 			/* huh? */
3526829c878SJohannes Berg 			wdev->conn->params.auth_type =
3536829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
3546829c878SJohannes Berg 			break;
3556829c878SJohannes Berg 		}
3566829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
3576829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
35819957bb3SJohannes Berg 	} else if (status_code != WLAN_STATUS_SUCCESS) {
3594bde0f7dSJohannes Berg 		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
360df7fc0f9SJohannes Berg 					  status_code, false, NULL);
36119957bb3SJohannes Berg 	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
3626829c878SJohannes Berg 		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
3636829c878SJohannes Berg 		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
3646829c878SJohannes Berg 		schedule_work(&rdev->conn_work);
3656829c878SJohannes Berg 	}
3666829c878SJohannes Berg }
367b23aa676SSamuel Ortiz 
368f401a6f7SJohannes Berg bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev)
369f401a6f7SJohannes Berg {
370f401a6f7SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
371f401a6f7SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
372f401a6f7SJohannes Berg 
373f401a6f7SJohannes Berg 	if (WARN_ON(!wdev->conn))
374f401a6f7SJohannes Berg 		return false;
375f401a6f7SJohannes Berg 
376f401a6f7SJohannes Berg 	if (!wdev->conn->prev_bssid_valid)
377f401a6f7SJohannes Berg 		return false;
378f401a6f7SJohannes Berg 
379f401a6f7SJohannes Berg 	/*
380f401a6f7SJohannes Berg 	 * Some stupid APs don't accept reassoc, so we
381f401a6f7SJohannes Berg 	 * need to fall back to trying regular assoc.
382f401a6f7SJohannes Berg 	 */
383f401a6f7SJohannes Berg 	wdev->conn->prev_bssid_valid = false;
384f401a6f7SJohannes Berg 	wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
385f401a6f7SJohannes Berg 	schedule_work(&rdev->conn_work);
386f401a6f7SJohannes Berg 
387f401a6f7SJohannes Berg 	return true;
388f401a6f7SJohannes Berg }
389f401a6f7SJohannes Berg 
3907d930bc3SJohannes Berg void cfg80211_sme_failed_assoc(struct wireless_dev *wdev)
3917d930bc3SJohannes Berg {
3927d930bc3SJohannes Berg 	struct wiphy *wiphy = wdev->wiphy;
3937d930bc3SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
3947d930bc3SJohannes Berg 
3957d930bc3SJohannes Berg 	wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL;
3967d930bc3SJohannes Berg 	schedule_work(&rdev->conn_work);
3977d930bc3SJohannes Berg }
3987d930bc3SJohannes Berg 
399667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
400b23aa676SSamuel Ortiz 			       const u8 *req_ie, size_t req_ie_len,
401b23aa676SSamuel Ortiz 			       const u8 *resp_ie, size_t resp_ie_len,
402df7fc0f9SJohannes Berg 			       u16 status, bool wextev,
403df7fc0f9SJohannes Berg 			       struct cfg80211_bss *bss)
404b23aa676SSamuel Ortiz {
405b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
4068b19e6caSLuis R. Rodriguez 	u8 *country_ie;
4073d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
408b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
409b23aa676SSamuel Ortiz #endif
410b23aa676SSamuel Ortiz 
411667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
412667503ddSJohannes Berg 
413b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
414b23aa676SSamuel Ortiz 		return;
415b23aa676SSamuel Ortiz 
416f7969969SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTING)
417ea416a79SJohannes Berg 		return;
418ea416a79SJohannes Berg 
419e45cd82aSJohannes Berg 	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
420e45cd82aSJohannes Berg 				    bssid, req_ie, req_ie_len,
421e45cd82aSJohannes Berg 				    resp_ie, resp_ie_len,
422667503ddSJohannes Berg 				    status, GFP_KERNEL);
423e45cd82aSJohannes Berg 
4243d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
425e45cd82aSJohannes Berg 	if (wextev) {
426e45cd82aSJohannes Berg 		if (req_ie && status == WLAN_STATUS_SUCCESS) {
427e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
428e45cd82aSJohannes Berg 			wrqu.data.length = req_ie_len;
4293409ff77SZhu Yi 			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
430e45cd82aSJohannes Berg 		}
431e45cd82aSJohannes Berg 
432e45cd82aSJohannes Berg 		if (resp_ie && status == WLAN_STATUS_SUCCESS) {
433e45cd82aSJohannes Berg 			memset(&wrqu, 0, sizeof(wrqu));
434e45cd82aSJohannes Berg 			wrqu.data.length = resp_ie_len;
435e45cd82aSJohannes Berg 			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
436e45cd82aSJohannes Berg 		}
437e45cd82aSJohannes Berg 
438e45cd82aSJohannes Berg 		memset(&wrqu, 0, sizeof(wrqu));
439e45cd82aSJohannes Berg 		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
440f401a6f7SJohannes Berg 		if (bssid && status == WLAN_STATUS_SUCCESS) {
441e45cd82aSJohannes Berg 			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
442f401a6f7SJohannes Berg 			memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
443f401a6f7SJohannes Berg 			wdev->wext.prev_bssid_valid = true;
444f401a6f7SJohannes Berg 		}
445e45cd82aSJohannes Berg 		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
446e45cd82aSJohannes Berg 	}
447e45cd82aSJohannes Berg #endif
448e45cd82aSJohannes Berg 
449df7fc0f9SJohannes Berg 	if (wdev->current_bss) {
450df7fc0f9SJohannes Berg 		cfg80211_unhold_bss(wdev->current_bss);
451df7fc0f9SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
452df7fc0f9SJohannes Berg 		wdev->current_bss = NULL;
453df7fc0f9SJohannes Berg 	}
454df7fc0f9SJohannes Berg 
45519957bb3SJohannes Berg 	if (wdev->conn)
45619957bb3SJohannes Berg 		wdev->conn->state = CFG80211_CONN_IDLE;
45719957bb3SJohannes Berg 
458fffd0934SJohannes Berg 	if (status != WLAN_STATUS_SUCCESS) {
459fffd0934SJohannes Berg 		wdev->sme_state = CFG80211_SME_IDLE;
460415ad1efSDavid Kilroy 		if (wdev->conn)
461415ad1efSDavid Kilroy 			kfree(wdev->conn->ie);
462fffd0934SJohannes Berg 		kfree(wdev->conn);
463fffd0934SJohannes Berg 		wdev->conn = NULL;
464fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
465fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
4668dadadb7SJohannes Berg 		wdev->ssid_len = 0;
467fffd0934SJohannes Berg 		return;
468fffd0934SJohannes Berg 	}
469fffd0934SJohannes Berg 
470df7fc0f9SJohannes Berg 	if (!bss)
471b23aa676SSamuel Ortiz 		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
472b23aa676SSamuel Ortiz 				       wdev->ssid, wdev->ssid_len,
473b23aa676SSamuel Ortiz 				       WLAN_CAPABILITY_ESS,
474b23aa676SSamuel Ortiz 				       WLAN_CAPABILITY_ESS);
475b23aa676SSamuel Ortiz 
476b23aa676SSamuel Ortiz 	if (WARN_ON(!bss))
477b23aa676SSamuel Ortiz 		return;
478b23aa676SSamuel Ortiz 
47919957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
48019957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
481b23aa676SSamuel Ortiz 
482b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_CONNECTED;
483fffd0934SJohannes Berg 	cfg80211_upload_connect_keys(wdev);
4848b19e6caSLuis R. Rodriguez 
4858b19e6caSLuis R. Rodriguez 	country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
4868b19e6caSLuis R. Rodriguez 
4878b19e6caSLuis R. Rodriguez 	if (!country_ie)
4888b19e6caSLuis R. Rodriguez 		return;
4898b19e6caSLuis R. Rodriguez 
4908b19e6caSLuis R. Rodriguez 	/*
4918b19e6caSLuis R. Rodriguez 	 * ieee80211_bss_get_ie() ensures we can access:
4928b19e6caSLuis R. Rodriguez 	 * - country_ie + 2, the start of the country ie data, and
4938b19e6caSLuis R. Rodriguez 	 * - and country_ie[1] which is the IE length
4948b19e6caSLuis R. Rodriguez 	 */
4958b19e6caSLuis R. Rodriguez 	regulatory_hint_11d(wdev->wiphy,
49684920e3eSLuis R. Rodriguez 			    bss->channel->band,
4978b19e6caSLuis R. Rodriguez 			    country_ie + 2,
4988b19e6caSLuis R. Rodriguez 			    country_ie[1]);
499b23aa676SSamuel Ortiz }
500f2129354SJohannes Berg 
501f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
502f2129354SJohannes Berg 			     const u8 *req_ie, size_t req_ie_len,
503f2129354SJohannes Berg 			     const u8 *resp_ie, size_t resp_ie_len,
504f2129354SJohannes Berg 			     u16 status, gfp_t gfp)
505f2129354SJohannes Berg {
506667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
507667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
508667503ddSJohannes Berg 	struct cfg80211_event *ev;
509667503ddSJohannes Berg 	unsigned long flags;
510667503ddSJohannes Berg 
511f7969969SJohannes Berg 	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
512f7969969SJohannes Berg 
513667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
514667503ddSJohannes Berg 	if (!ev)
515667503ddSJohannes Berg 		return;
516667503ddSJohannes Berg 
517667503ddSJohannes Berg 	ev->type = EVENT_CONNECT_RESULT;
51816a832e7SZhu Yi 	if (bssid)
519667503ddSJohannes Berg 		memcpy(ev->cr.bssid, bssid, ETH_ALEN);
520*7834704bSNishant Sarmukadam 	if (req_ie_len) {
521667503ddSJohannes Berg 		ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
522667503ddSJohannes Berg 		ev->cr.req_ie_len = req_ie_len;
523667503ddSJohannes Berg 		memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
524*7834704bSNishant Sarmukadam 	}
525*7834704bSNishant Sarmukadam 	if (resp_ie_len) {
526667503ddSJohannes Berg 		ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
527667503ddSJohannes Berg 		ev->cr.resp_ie_len = resp_ie_len;
528667503ddSJohannes Berg 		memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
529*7834704bSNishant Sarmukadam 	}
530667503ddSJohannes Berg 	ev->cr.status = status;
531667503ddSJohannes Berg 
532667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
533667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
534667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
535e60d7443SAlban Browaeys 	queue_work(cfg80211_wq, &rdev->event_work);
536f2129354SJohannes Berg }
537b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result);
538b23aa676SSamuel Ortiz 
539667503ddSJohannes Berg void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
540b23aa676SSamuel Ortiz 		       const u8 *req_ie, size_t req_ie_len,
541667503ddSJohannes Berg 		       const u8 *resp_ie, size_t resp_ie_len)
542b23aa676SSamuel Ortiz {
543b23aa676SSamuel Ortiz 	struct cfg80211_bss *bss;
5443d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
545b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
546b23aa676SSamuel Ortiz #endif
547b23aa676SSamuel Ortiz 
548667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
549667503ddSJohannes Berg 
550b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
551b23aa676SSamuel Ortiz 		return;
552b23aa676SSamuel Ortiz 
553f7969969SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
554b23aa676SSamuel Ortiz 		return;
555b23aa676SSamuel Ortiz 
556b23aa676SSamuel Ortiz 	/* internal error -- how did we get to CONNECTED w/o BSS? */
557b23aa676SSamuel Ortiz 	if (WARN_ON(!wdev->current_bss)) {
558b23aa676SSamuel Ortiz 		return;
559b23aa676SSamuel Ortiz 	}
560b23aa676SSamuel Ortiz 
561b23aa676SSamuel Ortiz 	cfg80211_unhold_bss(wdev->current_bss);
56219957bb3SJohannes Berg 	cfg80211_put_bss(&wdev->current_bss->pub);
563b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
564b23aa676SSamuel Ortiz 
565b23aa676SSamuel Ortiz 	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
566b23aa676SSamuel Ortiz 			       wdev->ssid, wdev->ssid_len,
567b23aa676SSamuel Ortiz 			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
568b23aa676SSamuel Ortiz 
569b23aa676SSamuel Ortiz 	if (WARN_ON(!bss))
570b23aa676SSamuel Ortiz 		return;
571b23aa676SSamuel Ortiz 
57219957bb3SJohannes Berg 	cfg80211_hold_bss(bss_from_pub(bss));
57319957bb3SJohannes Berg 	wdev->current_bss = bss_from_pub(bss);
574b23aa676SSamuel Ortiz 
575667503ddSJohannes Berg 	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
576667503ddSJohannes Berg 			    req_ie, req_ie_len, resp_ie, resp_ie_len,
577667503ddSJohannes Berg 			    GFP_KERNEL);
578b23aa676SSamuel Ortiz 
5793d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
580b23aa676SSamuel Ortiz 	if (req_ie) {
581b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
582b23aa676SSamuel Ortiz 		wrqu.data.length = req_ie_len;
5833409ff77SZhu Yi 		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
584667503ddSJohannes Berg 				    &wrqu, req_ie);
585b23aa676SSamuel Ortiz 	}
586b23aa676SSamuel Ortiz 
587b23aa676SSamuel Ortiz 	if (resp_ie) {
588b23aa676SSamuel Ortiz 		memset(&wrqu, 0, sizeof(wrqu));
589b23aa676SSamuel Ortiz 		wrqu.data.length = resp_ie_len;
590667503ddSJohannes Berg 		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
591667503ddSJohannes Berg 				    &wrqu, resp_ie);
592b23aa676SSamuel Ortiz 	}
593b23aa676SSamuel Ortiz 
594b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
595b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
596b23aa676SSamuel Ortiz 	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
597f401a6f7SJohannes Berg 	memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
598f401a6f7SJohannes Berg 	wdev->wext.prev_bssid_valid = true;
599667503ddSJohannes Berg 	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
600b23aa676SSamuel Ortiz #endif
601b23aa676SSamuel Ortiz }
602667503ddSJohannes Berg 
603667503ddSJohannes Berg void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
604667503ddSJohannes Berg 		     const u8 *req_ie, size_t req_ie_len,
605667503ddSJohannes Berg 		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
606667503ddSJohannes Berg {
607667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
608667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
609667503ddSJohannes Berg 	struct cfg80211_event *ev;
610667503ddSJohannes Berg 	unsigned long flags;
611667503ddSJohannes Berg 
612f7969969SJohannes Berg 	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
613f7969969SJohannes Berg 
614667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
615667503ddSJohannes Berg 	if (!ev)
616667503ddSJohannes Berg 		return;
617667503ddSJohannes Berg 
618667503ddSJohannes Berg 	ev->type = EVENT_ROAMED;
619667503ddSJohannes Berg 	memcpy(ev->rm.bssid, bssid, ETH_ALEN);
620667503ddSJohannes Berg 	ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
621667503ddSJohannes Berg 	ev->rm.req_ie_len = req_ie_len;
622667503ddSJohannes Berg 	memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
623667503ddSJohannes Berg 	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
624667503ddSJohannes Berg 	ev->rm.resp_ie_len = resp_ie_len;
625667503ddSJohannes Berg 	memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
626667503ddSJohannes Berg 
627667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
628667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
629667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
630e60d7443SAlban Browaeys 	queue_work(cfg80211_wq, &rdev->event_work);
631667503ddSJohannes Berg }
632b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_roamed);
633b23aa676SSamuel Ortiz 
634667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
6356829c878SJohannes Berg 			     size_t ie_len, u16 reason, bool from_ap)
636b23aa676SSamuel Ortiz {
637b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
638fffd0934SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
639fffd0934SJohannes Berg 	int i;
6403d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
641b23aa676SSamuel Ortiz 	union iwreq_data wrqu;
642b23aa676SSamuel Ortiz #endif
643b23aa676SSamuel Ortiz 
644667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
645667503ddSJohannes Berg 
646b23aa676SSamuel Ortiz 	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
647b23aa676SSamuel Ortiz 		return;
648b23aa676SSamuel Ortiz 
649f7969969SJohannes Berg 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
650b23aa676SSamuel Ortiz 		return;
651b23aa676SSamuel Ortiz 
652b23aa676SSamuel Ortiz 	if (wdev->current_bss) {
653b23aa676SSamuel Ortiz 		cfg80211_unhold_bss(wdev->current_bss);
65419957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->current_bss->pub);
655b23aa676SSamuel Ortiz 	}
656b23aa676SSamuel Ortiz 
657b23aa676SSamuel Ortiz 	wdev->current_bss = NULL;
658b23aa676SSamuel Ortiz 	wdev->sme_state = CFG80211_SME_IDLE;
6598dadadb7SJohannes Berg 	wdev->ssid_len = 0;
660b23aa676SSamuel Ortiz 
6616829c878SJohannes Berg 	if (wdev->conn) {
662b6f0b639SJohannes Berg 		const u8 *bssid;
663b6f0b639SJohannes Berg 		int ret;
664b6f0b639SJohannes Berg 
6656829c878SJohannes Berg 		kfree(wdev->conn->ie);
6666829c878SJohannes Berg 		wdev->conn->ie = NULL;
66719957bb3SJohannes Berg 		kfree(wdev->conn);
66819957bb3SJohannes Berg 		wdev->conn = NULL;
669b6f0b639SJohannes Berg 
670b6f0b639SJohannes Berg 		/*
671b6f0b639SJohannes Berg 		 * If this disconnect was due to a disassoc, we
672b6f0b639SJohannes Berg 		 * we might still have an auth BSS around. For
673b6f0b639SJohannes Berg 		 * the userspace SME that's currently expected,
674b6f0b639SJohannes Berg 		 * but for the kernel SME (nl80211 CONNECT or
675b6f0b639SJohannes Berg 		 * wireless extensions) we want to clear up all
676b6f0b639SJohannes Berg 		 * state.
677b6f0b639SJohannes Berg 		 */
678b6f0b639SJohannes Berg 		for (i = 0; i < MAX_AUTH_BSSES; i++) {
679b6f0b639SJohannes Berg 			if (!wdev->auth_bsses[i])
680b6f0b639SJohannes Berg 				continue;
681b6f0b639SJohannes Berg 			bssid = wdev->auth_bsses[i]->pub.bssid;
682b6f0b639SJohannes Berg 			ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
683d5cdfacbSJouni Malinen 						WLAN_REASON_DEAUTH_LEAVING,
684d5cdfacbSJouni Malinen 						false);
685b6f0b639SJohannes Berg 			WARN(ret, "deauth failed: %d\n", ret);
686b6f0b639SJohannes Berg 		}
6876829c878SJohannes Berg 	}
6886829c878SJohannes Berg 
689fffd0934SJohannes Berg 	nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
690fffd0934SJohannes Berg 
691fffd0934SJohannes Berg 	/*
692fffd0934SJohannes Berg 	 * Delete all the keys ... pairwise keys can't really
693fffd0934SJohannes Berg 	 * exist any more anyway, but default keys might.
694fffd0934SJohannes Berg 	 */
695fffd0934SJohannes Berg 	if (rdev->ops->del_key)
696fffd0934SJohannes Berg 		for (i = 0; i < 6; i++)
697fffd0934SJohannes Berg 			rdev->ops->del_key(wdev->wiphy, dev, i, NULL);
698b23aa676SSamuel Ortiz 
6993d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
700b23aa676SSamuel Ortiz 	memset(&wrqu, 0, sizeof(wrqu));
701b23aa676SSamuel Ortiz 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
702b23aa676SSamuel Ortiz 	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
7035f612033SAbhijeet Kolekar 	wdev->wext.connect.ssid_len = 0;
704b23aa676SSamuel Ortiz #endif
70509d989d1SLuis R. Rodriguez 
70609d989d1SLuis R. Rodriguez 	schedule_work(&cfg80211_disconnect_work);
707b23aa676SSamuel Ortiz }
708b23aa676SSamuel Ortiz 
709b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason,
710b23aa676SSamuel Ortiz 			   u8 *ie, size_t ie_len, gfp_t gfp)
711b23aa676SSamuel Ortiz {
712667503ddSJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
713667503ddSJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
714667503ddSJohannes Berg 	struct cfg80211_event *ev;
715667503ddSJohannes Berg 	unsigned long flags;
716667503ddSJohannes Berg 
717f7969969SJohannes Berg 	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
718f7969969SJohannes Berg 
719667503ddSJohannes Berg 	ev = kzalloc(sizeof(*ev) + ie_len, gfp);
720667503ddSJohannes Berg 	if (!ev)
721667503ddSJohannes Berg 		return;
722667503ddSJohannes Berg 
723667503ddSJohannes Berg 	ev->type = EVENT_DISCONNECTED;
724667503ddSJohannes Berg 	ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
725667503ddSJohannes Berg 	ev->dc.ie_len = ie_len;
726667503ddSJohannes Berg 	memcpy((void *)ev->dc.ie, ie, ie_len);
727667503ddSJohannes Berg 	ev->dc.reason = reason;
728667503ddSJohannes Berg 
729667503ddSJohannes Berg 	spin_lock_irqsave(&wdev->event_lock, flags);
730667503ddSJohannes Berg 	list_add_tail(&ev->list, &wdev->event_list);
731667503ddSJohannes Berg 	spin_unlock_irqrestore(&wdev->event_lock, flags);
732e60d7443SAlban Browaeys 	queue_work(cfg80211_wq, &rdev->event_work);
733b23aa676SSamuel Ortiz }
734b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected);
735b23aa676SSamuel Ortiz 
736667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev,
737b23aa676SSamuel Ortiz 		       struct net_device *dev,
738fffd0934SJohannes Berg 		       struct cfg80211_connect_params *connect,
739f401a6f7SJohannes Berg 		       struct cfg80211_cached_keys *connkeys,
740f401a6f7SJohannes Berg 		       const u8 *prev_bssid)
741b23aa676SSamuel Ortiz {
742b23aa676SSamuel Ortiz 	struct wireless_dev *wdev = dev->ieee80211_ptr;
74359bbb6f7SJohannes Berg 	struct ieee80211_channel *chan;
744bbac31f4SJohannes Berg 	struct cfg80211_bss *bss = NULL;
745667503ddSJohannes Berg 	int err;
746667503ddSJohannes Berg 
747667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
748b23aa676SSamuel Ortiz 
749b23aa676SSamuel Ortiz 	if (wdev->sme_state != CFG80211_SME_IDLE)
750b23aa676SSamuel Ortiz 		return -EALREADY;
751b23aa676SSamuel Ortiz 
75259bbb6f7SJohannes Berg 	chan = rdev_fixed_channel(rdev, wdev);
75359bbb6f7SJohannes Berg 	if (chan && chan != connect->channel)
75459bbb6f7SJohannes Berg 		return -EBUSY;
75559bbb6f7SJohannes Berg 
756fffd0934SJohannes Berg 	if (WARN_ON(wdev->connect_keys)) {
757fffd0934SJohannes Berg 		kfree(wdev->connect_keys);
758fffd0934SJohannes Berg 		wdev->connect_keys = NULL;
759fffd0934SJohannes Berg 	}
760fffd0934SJohannes Berg 
761fffd0934SJohannes Berg 	if (connkeys && connkeys->def >= 0) {
762fffd0934SJohannes Berg 		int idx;
763bcba8eaeSSamuel Ortiz 		u32 cipher;
764fffd0934SJohannes Berg 
765fffd0934SJohannes Berg 		idx = connkeys->def;
766bcba8eaeSSamuel Ortiz 		cipher = connkeys->params[idx].cipher;
767fffd0934SJohannes Berg 		/* If given a WEP key we may need it for shared key auth */
768bcba8eaeSSamuel Ortiz 		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
769bcba8eaeSSamuel Ortiz 		    cipher == WLAN_CIPHER_SUITE_WEP104) {
770fffd0934SJohannes Berg 			connect->key_idx = idx;
771fffd0934SJohannes Berg 			connect->key = connkeys->params[idx].key;
772fffd0934SJohannes Berg 			connect->key_len = connkeys->params[idx].key_len;
773bcba8eaeSSamuel Ortiz 
774bcba8eaeSSamuel Ortiz 			/*
775bcba8eaeSSamuel Ortiz 			 * If ciphers are not set (e.g. when going through
776bcba8eaeSSamuel Ortiz 			 * iwconfig), we have to set them appropriately here.
777bcba8eaeSSamuel Ortiz 			 */
778bcba8eaeSSamuel Ortiz 			if (connect->crypto.cipher_group == 0)
779bcba8eaeSSamuel Ortiz 				connect->crypto.cipher_group = cipher;
780bcba8eaeSSamuel Ortiz 
781bcba8eaeSSamuel Ortiz 			if (connect->crypto.n_ciphers_pairwise == 0) {
782bcba8eaeSSamuel Ortiz 				connect->crypto.n_ciphers_pairwise = 1;
783bcba8eaeSSamuel Ortiz 				connect->crypto.ciphers_pairwise[0] = cipher;
784bcba8eaeSSamuel Ortiz 			}
785fffd0934SJohannes Berg 		}
786fffd0934SJohannes Berg 	}
787fffd0934SJohannes Berg 
788b23aa676SSamuel Ortiz 	if (!rdev->ops->connect) {
7896829c878SJohannes Berg 		if (!rdev->ops->auth || !rdev->ops->assoc)
790b23aa676SSamuel Ortiz 			return -EOPNOTSUPP;
7916829c878SJohannes Berg 
79219957bb3SJohannes Berg 		if (WARN_ON(wdev->conn))
79319957bb3SJohannes Berg 			return -EINPROGRESS;
79419957bb3SJohannes Berg 
7956829c878SJohannes Berg 		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
7966829c878SJohannes Berg 		if (!wdev->conn)
7976829c878SJohannes Berg 			return -ENOMEM;
7986829c878SJohannes Berg 
7996829c878SJohannes Berg 		/*
8006829c878SJohannes Berg 		 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
8016829c878SJohannes Berg 		 */
8026829c878SJohannes Berg 		memcpy(&wdev->conn->params, connect, sizeof(*connect));
8036829c878SJohannes Berg 		if (connect->bssid) {
8046829c878SJohannes Berg 			wdev->conn->params.bssid = wdev->conn->bssid;
8056829c878SJohannes Berg 			memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
8066829c878SJohannes Berg 		}
8076829c878SJohannes Berg 
8086829c878SJohannes Berg 		if (connect->ie) {
8096829c878SJohannes Berg 			wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
8106829c878SJohannes Berg 						GFP_KERNEL);
8116829c878SJohannes Berg 			wdev->conn->params.ie = wdev->conn->ie;
81219957bb3SJohannes Berg 			if (!wdev->conn->ie) {
81319957bb3SJohannes Berg 				kfree(wdev->conn);
81419957bb3SJohannes Berg 				wdev->conn = NULL;
8156829c878SJohannes Berg 				return -ENOMEM;
8166829c878SJohannes Berg 			}
81719957bb3SJohannes Berg 		}
8186829c878SJohannes Berg 
8196829c878SJohannes Berg 		if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
8206829c878SJohannes Berg 			wdev->conn->auto_auth = true;
8216829c878SJohannes Berg 			/* start with open system ... should mostly work */
8226829c878SJohannes Berg 			wdev->conn->params.auth_type =
8236829c878SJohannes Berg 				NL80211_AUTHTYPE_OPEN_SYSTEM;
8246829c878SJohannes Berg 		} else {
8256829c878SJohannes Berg 			wdev->conn->auto_auth = false;
8266829c878SJohannes Berg 		}
8276829c878SJohannes Berg 
8286829c878SJohannes Berg 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
8296829c878SJohannes Berg 		wdev->ssid_len = connect->ssid_len;
8306829c878SJohannes Berg 		wdev->conn->params.ssid = wdev->ssid;
8316829c878SJohannes Berg 		wdev->conn->params.ssid_len = connect->ssid_len;
8326829c878SJohannes Berg 
8338bb89485SJohannes Berg 		/* see if we have the bss already */
834bbac31f4SJohannes Berg 		bss = cfg80211_get_conn_bss(wdev);
8356829c878SJohannes Berg 
8366829c878SJohannes Berg 		wdev->sme_state = CFG80211_SME_CONNECTING;
837fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
8386829c878SJohannes Berg 
839f401a6f7SJohannes Berg 		if (prev_bssid) {
840f401a6f7SJohannes Berg 			memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
841f401a6f7SJohannes Berg 			wdev->conn->prev_bssid_valid = true;
842f401a6f7SJohannes Berg 		}
843f401a6f7SJohannes Berg 
844bbac31f4SJohannes Berg 		/* we're good if we have a matching bss struct */
845bbac31f4SJohannes Berg 		if (bss) {
8466829c878SJohannes Berg 			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
8476829c878SJohannes Berg 			err = cfg80211_conn_do_work(wdev);
848bbac31f4SJohannes Berg 			cfg80211_put_bss(bss);
8496829c878SJohannes Berg 		} else {
8506829c878SJohannes Berg 			/* otherwise we'll need to scan for the AP first */
8516829c878SJohannes Berg 			err = cfg80211_conn_scan(wdev);
8526829c878SJohannes Berg 			/*
8536829c878SJohannes Berg 			 * If we can't scan right now, then we need to scan again
8546829c878SJohannes Berg 			 * after the current scan finished, since the parameters
8556829c878SJohannes Berg 			 * changed (unless we find a good AP anyway).
8566829c878SJohannes Berg 			 */
8576829c878SJohannes Berg 			if (err == -EBUSY) {
8586829c878SJohannes Berg 				err = 0;
8596829c878SJohannes Berg 				wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
8606829c878SJohannes Berg 			}
8616829c878SJohannes Berg 		}
86219957bb3SJohannes Berg 		if (err) {
863415ad1efSDavid Kilroy 			kfree(wdev->conn->ie);
86419957bb3SJohannes Berg 			kfree(wdev->conn);
86519957bb3SJohannes Berg 			wdev->conn = NULL;
8666829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
867fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
8688dadadb7SJohannes Berg 			wdev->ssid_len = 0;
86919957bb3SJohannes Berg 		}
8706829c878SJohannes Berg 
8716829c878SJohannes Berg 		return err;
872b23aa676SSamuel Ortiz 	} else {
873b23aa676SSamuel Ortiz 		wdev->sme_state = CFG80211_SME_CONNECTING;
874fffd0934SJohannes Berg 		wdev->connect_keys = connkeys;
875b23aa676SSamuel Ortiz 		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
876b23aa676SSamuel Ortiz 		if (err) {
877fffd0934SJohannes Berg 			wdev->connect_keys = NULL;
878b23aa676SSamuel Ortiz 			wdev->sme_state = CFG80211_SME_IDLE;
879b23aa676SSamuel Ortiz 			return err;
880b23aa676SSamuel Ortiz 		}
881b23aa676SSamuel Ortiz 
882b23aa676SSamuel Ortiz 		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
883b23aa676SSamuel Ortiz 		wdev->ssid_len = connect->ssid_len;
884b23aa676SSamuel Ortiz 
885b23aa676SSamuel Ortiz 		return 0;
886b23aa676SSamuel Ortiz 	}
8876829c878SJohannes Berg }
888b23aa676SSamuel Ortiz 
889667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev,
890667503ddSJohannes Berg 		     struct net_device *dev,
891fffd0934SJohannes Berg 		     struct cfg80211_connect_params *connect,
892fffd0934SJohannes Berg 		     struct cfg80211_cached_keys *connkeys)
893667503ddSJohannes Berg {
894667503ddSJohannes Berg 	int err;
895667503ddSJohannes Berg 
89659bbb6f7SJohannes Berg 	mutex_lock(&rdev->devlist_mtx);
897667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
898f401a6f7SJohannes Berg 	err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL);
899667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
90059bbb6f7SJohannes Berg 	mutex_unlock(&rdev->devlist_mtx);
901667503ddSJohannes Berg 
902667503ddSJohannes Berg 	return err;
903667503ddSJohannes Berg }
904667503ddSJohannes Berg 
905667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
906f2129354SJohannes Berg 			  struct net_device *dev, u16 reason, bool wextev)
907b23aa676SSamuel Ortiz {
9086829c878SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
909b23aa676SSamuel Ortiz 	int err;
910b23aa676SSamuel Ortiz 
911667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
912667503ddSJohannes Berg 
9136829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_IDLE)
9146829c878SJohannes Berg 		return -EINVAL;
9156829c878SJohannes Berg 
916fffd0934SJohannes Berg 	kfree(wdev->connect_keys);
917fffd0934SJohannes Berg 	wdev->connect_keys = NULL;
918fffd0934SJohannes Berg 
919b23aa676SSamuel Ortiz 	if (!rdev->ops->disconnect) {
92019957bb3SJohannes Berg 		if (!rdev->ops->deauth)
92119957bb3SJohannes Berg 			return -EOPNOTSUPP;
9226829c878SJohannes Berg 
92319957bb3SJohannes Berg 		/* was it connected by userspace SME? */
92419957bb3SJohannes Berg 		if (!wdev->conn) {
92519957bb3SJohannes Berg 			cfg80211_mlme_down(rdev, dev);
92619957bb3SJohannes Berg 			return 0;
92719957bb3SJohannes Berg 		}
9286829c878SJohannes Berg 
9296829c878SJohannes Berg 		if (wdev->sme_state == CFG80211_SME_CONNECTING &&
9306829c878SJohannes Berg 		    (wdev->conn->state == CFG80211_CONN_SCANNING ||
9316829c878SJohannes Berg 		     wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
9326829c878SJohannes Berg 			wdev->sme_state = CFG80211_SME_IDLE;
933415ad1efSDavid Kilroy 			kfree(wdev->conn->ie);
93419957bb3SJohannes Berg 			kfree(wdev->conn);
93519957bb3SJohannes Berg 			wdev->conn = NULL;
9368dadadb7SJohannes Berg 			wdev->ssid_len = 0;
9376829c878SJohannes Berg 			return 0;
9386829c878SJohannes Berg 		}
9396829c878SJohannes Berg 
9406829c878SJohannes Berg 		/* wdev->conn->params.bssid must be set if > SCANNING */
941667503ddSJohannes Berg 		err = __cfg80211_mlme_deauth(rdev, dev,
942667503ddSJohannes Berg 					     wdev->conn->params.bssid,
943d5cdfacbSJouni Malinen 					     NULL, 0, reason, false);
9446829c878SJohannes Berg 		if (err)
9456829c878SJohannes Berg 			return err;
946b23aa676SSamuel Ortiz 	} else {
947b23aa676SSamuel Ortiz 		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
948b23aa676SSamuel Ortiz 		if (err)
949b23aa676SSamuel Ortiz 			return err;
950b23aa676SSamuel Ortiz 	}
951b23aa676SSamuel Ortiz 
9526829c878SJohannes Berg 	if (wdev->sme_state == CFG80211_SME_CONNECTED)
953667503ddSJohannes Berg 		__cfg80211_disconnected(dev, NULL, 0, 0, false);
9546829c878SJohannes Berg 	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
955f2129354SJohannes Berg 		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
9566829c878SJohannes Berg 					  WLAN_STATUS_UNSPECIFIED_FAILURE,
957df7fc0f9SJohannes Berg 					  wextev, NULL);
958b23aa676SSamuel Ortiz 
959b23aa676SSamuel Ortiz 	return 0;
960b23aa676SSamuel Ortiz }
96119957bb3SJohannes Berg 
962667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
963667503ddSJohannes Berg 			struct net_device *dev,
964667503ddSJohannes Berg 			u16 reason, bool wextev)
965667503ddSJohannes Berg {
966667503ddSJohannes Berg 	int err;
967667503ddSJohannes Berg 
968667503ddSJohannes Berg 	wdev_lock(dev->ieee80211_ptr);
969667503ddSJohannes Berg 	err = __cfg80211_disconnect(rdev, dev, reason, wextev);
970667503ddSJohannes Berg 	wdev_unlock(dev->ieee80211_ptr);
971667503ddSJohannes Berg 
972667503ddSJohannes Berg 	return err;
973667503ddSJohannes Berg }
974667503ddSJohannes Berg 
97519957bb3SJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, int idx)
97619957bb3SJohannes Berg {
97719957bb3SJohannes Berg 	struct wireless_dev *wdev = dev->ieee80211_ptr;
97819957bb3SJohannes Berg 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
97919957bb3SJohannes Berg 	u8 bssid[ETH_ALEN];
98019957bb3SJohannes Berg 
981667503ddSJohannes Berg 	ASSERT_WDEV_LOCK(wdev);
982667503ddSJohannes Berg 
98319957bb3SJohannes Berg 	if (!wdev->conn)
98419957bb3SJohannes Berg 		return;
98519957bb3SJohannes Berg 
98619957bb3SJohannes Berg 	if (wdev->conn->state == CFG80211_CONN_IDLE)
98719957bb3SJohannes Berg 		return;
98819957bb3SJohannes Berg 
98919957bb3SJohannes Berg 	/*
99019957bb3SJohannes Berg 	 * Ok, so the association was made by this SME -- we don't
99119957bb3SJohannes Berg 	 * want it any more so deauthenticate too.
99219957bb3SJohannes Berg 	 */
99319957bb3SJohannes Berg 
99419957bb3SJohannes Berg 	if (!wdev->auth_bsses[idx])
99519957bb3SJohannes Berg 		return;
99619957bb3SJohannes Berg 
99719957bb3SJohannes Berg 	memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
998ec3f1490SJohannes Berg 	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
999d5cdfacbSJouni Malinen 				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING,
1000d5cdfacbSJouni Malinen 				   false)) {
100119957bb3SJohannes Berg 		/* whatever -- assume gone anyway */
100219957bb3SJohannes Berg 		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
100319957bb3SJohannes Berg 		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
100419957bb3SJohannes Berg 		wdev->auth_bsses[idx] = NULL;
100519957bb3SJohannes Berg 	}
100619957bb3SJohannes Berg }
1007