Blame view

net/wireless/sme.c 25.8 KB
b23aa676a   Samuel Ortiz   cfg80211: connect...
1
2
3
4
5
6
7
8
9
  /*
   * 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>
5a0e3ad6a   Tejun Heo   include cleanup: ...
10
  #include <linux/slab.h>
b23aa676a   Samuel Ortiz   cfg80211: connect...
11
  #include <linux/workqueue.h>
a9a11622c   Johannes Berg   cfg80211: self-co...
12
13
  #include <linux/wireless.h>
  #include <net/iw_handler.h>
b23aa676a   Samuel Ortiz   cfg80211: connect...
14
15
16
  #include <net/cfg80211.h>
  #include <net/rtnetlink.h>
  #include "nl80211.h"
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
17
  #include "reg.h"
b23aa676a   Samuel Ortiz   cfg80211: connect...
18

6829c878e   Johannes Berg   cfg80211: emulate...
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,
7d930bc33   Johannes Berg   cfg80211: sme: de...
30
  		CFG80211_CONN_DEAUTH_ASSOC_FAIL,
6829c878e   Johannes Berg   cfg80211: emulate...
31
  	} state;
f401a6f7e   Johannes Berg   cfg80211: use rea...
32
  	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
6829c878e   Johannes Berg   cfg80211: emulate...
33
34
  	u8 *ie;
  	size_t ie_len;
f401a6f7e   Johannes Berg   cfg80211: use rea...
35
  	bool auto_auth, prev_bssid_valid;
6829c878e   Johannes Berg   cfg80211: emulate...
36
  };
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
37
38
39
40
41
42
43
44
45
46
47
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
  bool cfg80211_is_all_idle(void)
  {
  	struct cfg80211_registered_device *rdev;
  	struct wireless_dev *wdev;
  	bool is_all_idle = true;
  
  	mutex_lock(&cfg80211_mutex);
  
  	/*
  	 * All devices must be idle as otherwise if you are actively
  	 * scanning some new beacon hints could be learned and would
  	 * count as new regulatory hints.
  	 */
  	list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
  		cfg80211_lock_rdev(rdev);
  		list_for_each_entry(wdev, &rdev->netdev_list, list) {
  			wdev_lock(wdev);
  			if (wdev->sme_state != CFG80211_SME_IDLE)
  				is_all_idle = false;
  			wdev_unlock(wdev);
  		}
  		cfg80211_unlock_rdev(rdev);
  	}
  
  	mutex_unlock(&cfg80211_mutex);
  
  	return is_all_idle;
  }
  
  static void disconnect_work(struct work_struct *work)
  {
  	if (!cfg80211_is_all_idle())
  		return;
  
  	regulatory_hint_disconnect();
  }
  
  static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
6829c878e   Johannes Berg   cfg80211: emulate...
75
76
77
  
  static int cfg80211_conn_scan(struct wireless_dev *wdev)
  {
79c97e97a   Johannes Berg   cfg80211: clean u...
78
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
6829c878e   Johannes Berg   cfg80211: emulate...
79
80
81
82
  	struct cfg80211_scan_request *request;
  	int n_channels, err;
  
  	ASSERT_RTNL();
79c97e97a   Johannes Berg   cfg80211: clean u...
83
  	ASSERT_RDEV_LOCK(rdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
84
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
85

79c97e97a   Johannes Berg   cfg80211: clean u...
86
  	if (rdev->scan_req)
6829c878e   Johannes Berg   cfg80211: emulate...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  		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;
6829c878e   Johannes Berg   cfg80211: emulate...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  	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;
5ba63533b   Johannes Berg   cfg80211: fix ali...
122
  	request->ssids = (void *)&request->channels[n_channels];
6829c878e   Johannes Berg   cfg80211: emulate...
123
124
125
126
127
  	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;
463d01832   Johannes Berg   cfg80211: make aw...
128
  	request->dev = wdev->netdev;
79c97e97a   Johannes Berg   cfg80211: clean u...
129
  	request->wiphy = &rdev->wiphy;
6829c878e   Johannes Berg   cfg80211: emulate...
130

79c97e97a   Johannes Berg   cfg80211: clean u...
131
  	rdev->scan_req = request;
6829c878e   Johannes Berg   cfg80211: emulate...
132

79c97e97a   Johannes Berg   cfg80211: clean u...
133
  	err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
6829c878e   Johannes Berg   cfg80211: emulate...
134
135
  	if (!err) {
  		wdev->conn->state = CFG80211_CONN_SCANNING;
79c97e97a   Johannes Berg   cfg80211: clean u...
136
  		nl80211_send_scan_start(rdev, wdev->netdev);
463d01832   Johannes Berg   cfg80211: make aw...
137
  		dev_hold(wdev->netdev);
6829c878e   Johannes Berg   cfg80211: emulate...
138
  	} else {
79c97e97a   Johannes Berg   cfg80211: clean u...
139
  		rdev->scan_req = NULL;
6829c878e   Johannes Berg   cfg80211: emulate...
140
141
142
143
144
145
146
  		kfree(request);
  	}
  	return err;
  }
  
  static int cfg80211_conn_do_work(struct wireless_dev *wdev)
  {
79c97e97a   Johannes Berg   cfg80211: clean u...
147
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
19957bb39   Johannes Berg   cfg80211: keep tr...
148
  	struct cfg80211_connect_params *params;
f401a6f7e   Johannes Berg   cfg80211: use rea...
149
  	const u8 *prev_bssid = NULL;
19957bb39   Johannes Berg   cfg80211: keep tr...
150
  	int err;
6829c878e   Johannes Berg   cfg80211: emulate...
151

667503ddc   Johannes Berg   cfg80211: fix loc...
152
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
153
154
  	if (!wdev->conn)
  		return 0;
19957bb39   Johannes Berg   cfg80211: keep tr...
155
  	params = &wdev->conn->params;
6829c878e   Johannes Berg   cfg80211: emulate...
156
157
158
159
  	switch (wdev->conn->state) {
  	case CFG80211_CONN_SCAN_AGAIN:
  		return cfg80211_conn_scan(wdev);
  	case CFG80211_CONN_AUTHENTICATE_NEXT:
79c97e97a   Johannes Berg   cfg80211: clean u...
160
  		BUG_ON(!rdev->ops->auth);
19957bb39   Johannes Berg   cfg80211: keep tr...
161
  		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
79c97e97a   Johannes Berg   cfg80211: clean u...
162
  		return __cfg80211_mlme_auth(rdev, wdev->netdev,
667503ddc   Johannes Berg   cfg80211: fix loc...
163
164
165
  					    params->channel, params->auth_type,
  					    params->bssid,
  					    params->ssid, params->ssid_len,
fffd0934b   Johannes Berg   cfg80211: rework ...
166
167
  					    NULL, 0,
  					    params->key, params->key_len,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
168
  					    params->key_idx, false);
6829c878e   Johannes Berg   cfg80211: emulate...
169
  	case CFG80211_CONN_ASSOCIATE_NEXT:
79c97e97a   Johannes Berg   cfg80211: clean u...
170
  		BUG_ON(!rdev->ops->assoc);
19957bb39   Johannes Berg   cfg80211: keep tr...
171
  		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
f401a6f7e   Johannes Berg   cfg80211: use rea...
172
173
  		if (wdev->conn->prev_bssid_valid)
  			prev_bssid = wdev->conn->prev_bssid;
79c97e97a   Johannes Berg   cfg80211: clean u...
174
  		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
667503ddc   Johannes Berg   cfg80211: fix loc...
175
  					    params->channel, params->bssid,
f401a6f7e   Johannes Berg   cfg80211: use rea...
176
  					    prev_bssid,
667503ddc   Johannes Berg   cfg80211: fix loc...
177
178
179
  					    params->ssid, params->ssid_len,
  					    params->ie, params->ie_len,
  					    false, &params->crypto);
19957bb39   Johannes Berg   cfg80211: keep tr...
180
  		if (err)
79c97e97a   Johannes Berg   cfg80211: clean u...
181
  			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
667503ddc   Johannes Berg   cfg80211: fix loc...
182
  					       NULL, 0,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
183
184
  					       WLAN_REASON_DEAUTH_LEAVING,
  					       false);
19957bb39   Johannes Berg   cfg80211: keep tr...
185
  		return err;
7d930bc33   Johannes Berg   cfg80211: sme: de...
186
187
188
  	case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
  		__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
  				       NULL, 0,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
189
  				       WLAN_REASON_DEAUTH_LEAVING, false);
7d930bc33   Johannes Berg   cfg80211: sme: de...
190
191
  		/* return an error so that we call __cfg80211_connect_result() */
  		return -EINVAL;
6829c878e   Johannes Berg   cfg80211: emulate...
192
193
194
195
196
197
198
  	default:
  		return 0;
  	}
  }
  
  void cfg80211_conn_work(struct work_struct *work)
  {
79c97e97a   Johannes Berg   cfg80211: clean u...
199
  	struct cfg80211_registered_device *rdev =
6829c878e   Johannes Berg   cfg80211: emulate...
200
201
  		container_of(work, struct cfg80211_registered_device, conn_work);
  	struct wireless_dev *wdev;
7400f42e9   Johannes Berg   cfg80211: fix NUL...
202
  	u8 bssid_buf[ETH_ALEN], *bssid = NULL;
6829c878e   Johannes Berg   cfg80211: emulate...
203
204
  
  	rtnl_lock();
79c97e97a   Johannes Berg   cfg80211: clean u...
205
206
  	cfg80211_lock_rdev(rdev);
  	mutex_lock(&rdev->devlist_mtx);
6829c878e   Johannes Berg   cfg80211: emulate...
207

79c97e97a   Johannes Berg   cfg80211: clean u...
208
  	list_for_each_entry(wdev, &rdev->netdev_list, list) {
667503ddc   Johannes Berg   cfg80211: fix loc...
209
210
211
  		wdev_lock(wdev);
  		if (!netif_running(wdev->netdev)) {
  			wdev_unlock(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
212
  			continue;
667503ddc   Johannes Berg   cfg80211: fix loc...
213
214
215
  		}
  		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
  			wdev_unlock(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
216
  			continue;
667503ddc   Johannes Berg   cfg80211: fix loc...
217
  		}
7400f42e9   Johannes Berg   cfg80211: fix NUL...
218
219
220
221
  		if (wdev->conn->params.bssid) {
  			memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
  			bssid = bssid_buf;
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
222
  		if (cfg80211_conn_do_work(wdev))
667503ddc   Johannes Berg   cfg80211: fix loc...
223
  			__cfg80211_connect_result(
7d930bc33   Johannes Berg   cfg80211: sme: de...
224
  					wdev->netdev, bssid,
667503ddc   Johannes Berg   cfg80211: fix loc...
225
226
  					NULL, 0, NULL, 0,
  					WLAN_STATUS_UNSPECIFIED_FAILURE,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
227
  					false, NULL);
667503ddc   Johannes Berg   cfg80211: fix loc...
228
  		wdev_unlock(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
229
  	}
79c97e97a   Johannes Berg   cfg80211: clean u...
230
231
  	mutex_unlock(&rdev->devlist_mtx);
  	cfg80211_unlock_rdev(rdev);
6829c878e   Johannes Berg   cfg80211: emulate...
232
233
  	rtnl_unlock();
  }
bbac31f4c   Johannes Berg   cfg80211: fix SME...
234
  static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
6829c878e   Johannes Berg   cfg80211: emulate...
235
  {
79c97e97a   Johannes Berg   cfg80211: clean u...
236
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
6829c878e   Johannes Berg   cfg80211: emulate...
237
238
  	struct cfg80211_bss *bss;
  	u16 capa = WLAN_CAPABILITY_ESS;
667503ddc   Johannes Berg   cfg80211: fix loc...
239
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
240
241
242
243
244
245
246
247
  	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);
6829c878e   Johannes Berg   cfg80211: emulate...
248
  	if (!bss)
bbac31f4c   Johannes Berg   cfg80211: fix SME...
249
  		return NULL;
6829c878e   Johannes Berg   cfg80211: emulate...
250
251
252
253
254
  
  	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;
79c97e97a   Johannes Berg   cfg80211: clean u...
255
  	schedule_work(&rdev->conn_work);
6829c878e   Johannes Berg   cfg80211: emulate...
256

bbac31f4c   Johannes Berg   cfg80211: fix SME...
257
  	return bss;
6829c878e   Johannes Berg   cfg80211: emulate...
258
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
259
  static void __cfg80211_sme_scan_done(struct net_device *dev)
6829c878e   Johannes Berg   cfg80211: emulate...
260
261
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
79c97e97a   Johannes Berg   cfg80211: clean u...
262
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
bbac31f4c   Johannes Berg   cfg80211: fix SME...
263
  	struct cfg80211_bss *bss;
6829c878e   Johannes Berg   cfg80211: emulate...
264

667503ddc   Johannes Berg   cfg80211: fix loc...
265
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
266
267
  	if (wdev->sme_state != CFG80211_SME_CONNECTING)
  		return;
d4b1a6876   Zhu Yi   cfg80211: remove ...
268
  	if (!wdev->conn)
6829c878e   Johannes Berg   cfg80211: emulate...
269
270
271
272
273
  		return;
  
  	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
  	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
  		return;
bbac31f4c   Johannes Berg   cfg80211: fix SME...
274
275
276
277
  	bss = cfg80211_get_conn_bss(wdev);
  	if (bss) {
  		cfg80211_put_bss(bss);
  	} else {
6829c878e   Johannes Berg   cfg80211: emulate...
278
279
  		/* not found */
  		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
79c97e97a   Johannes Berg   cfg80211: clean u...
280
  			schedule_work(&rdev->conn_work);
6829c878e   Johannes Berg   cfg80211: emulate...
281
  		else
667503ddc   Johannes Berg   cfg80211: fix loc...
282
283
284
285
286
  			__cfg80211_connect_result(
  					wdev->netdev,
  					wdev->conn->params.bssid,
  					NULL, 0, NULL, 0,
  					WLAN_STATUS_UNSPECIFIED_FAILURE,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
287
  					false, NULL);
6829c878e   Johannes Berg   cfg80211: emulate...
288
289
  	}
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
290
291
292
  void cfg80211_sme_scan_done(struct net_device *dev)
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
59bbb6f75   Johannes Berg   cfg80211: validat...
293
  	mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
294
295
296
  	wdev_lock(wdev);
  	__cfg80211_sme_scan_done(dev);
  	wdev_unlock(wdev);
59bbb6f75   Johannes Berg   cfg80211: validat...
297
  	mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
298
299
300
301
  }
  
  void cfg80211_sme_rx_auth(struct net_device *dev,
  			  const u8 *buf, size_t len)
6829c878e   Johannes Berg   cfg80211: emulate...
302
303
304
305
306
307
  {
  	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);
667503ddc   Johannes Berg   cfg80211: fix loc...
308
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
309
310
311
312
313
314
315
316
317
318
319
320
321
  	/* 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:
fffd0934b   Johannes Berg   cfg80211: rework ...
322
323
324
325
326
327
  			if (wdev->connect_keys)
  				wdev->conn->params.auth_type =
  					NL80211_AUTHTYPE_SHARED_KEY;
  			else
  				wdev->conn->params.auth_type =
  					NL80211_AUTHTYPE_NETWORK_EAP;
6829c878e   Johannes Berg   cfg80211: emulate...
328
329
330
331
332
333
334
335
336
337
338
339
340
  			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);
19957bb39   Johannes Berg   cfg80211: keep tr...
341
  	} else if (status_code != WLAN_STATUS_SUCCESS) {
4bde0f7d1   Johannes Berg   cfg80211: fix two...
342
  		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
343
  					  status_code, false, NULL);
19957bb39   Johannes Berg   cfg80211: keep tr...
344
  	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
6829c878e   Johannes Berg   cfg80211: emulate...
345
346
347
348
349
  		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
  		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
  		schedule_work(&rdev->conn_work);
  	}
  }
b23aa676a   Samuel Ortiz   cfg80211: connect...
350

f401a6f7e   Johannes Berg   cfg80211: use rea...
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
  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;
  }
7d930bc33   Johannes Berg   cfg80211: sme: de...
372
373
374
375
376
377
378
379
  void cfg80211_sme_failed_assoc(struct wireless_dev *wdev)
  {
  	struct wiphy *wiphy = wdev->wiphy;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
  
  	wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL;
  	schedule_work(&rdev->conn_work);
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
380
381
382
  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,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
383
384
  			       u16 status, bool wextev,
  			       struct cfg80211_bss *bss)
b23aa676a   Samuel Ortiz   cfg80211: connect...
385
386
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
387
  	u8 *country_ie;
3d23e349d   Johannes Berg   wext: refactor
388
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
389
390
  	union iwreq_data wrqu;
  #endif
667503ddc   Johannes Berg   cfg80211: fix loc...
391
  	ASSERT_WDEV_LOCK(wdev);
b23aa676a   Samuel Ortiz   cfg80211: connect...
392
393
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return;
f7969969f   Johannes Berg   cfg80211: make sp...
394
  	if (wdev->sme_state != CFG80211_SME_CONNECTING)
ea416a793   Johannes Berg   cfg80211: report ...
395
396
397
  		return;
  
  	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
e45cd82ac   Johannes Berg   cfg80211: send ev...
398
  				    bssid, req_ie, req_ie_len,
ea416a793   Johannes Berg   cfg80211: report ...
399
400
  				    resp_ie, resp_ie_len,
  				    status, GFP_KERNEL);
e45cd82ac   Johannes Berg   cfg80211: send ev...
401

3d23e349d   Johannes Berg   wext: refactor
402
  #ifdef CONFIG_CFG80211_WEXT
e45cd82ac   Johannes Berg   cfg80211: send ev...
403
404
405
406
  	if (wextev) {
  		if (req_ie && status == WLAN_STATUS_SUCCESS) {
  			memset(&wrqu, 0, sizeof(wrqu));
  			wrqu.data.length = req_ie_len;
3409ff771   Zhu Yi   cfg80211: fix typ...
407
  			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
e45cd82ac   Johannes Berg   cfg80211: send ev...
408
409
410
411
412
413
414
415
416
417
  		}
  
  		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;
f401a6f7e   Johannes Berg   cfg80211: use rea...
418
  		if (bssid && status == WLAN_STATUS_SUCCESS) {
e45cd82ac   Johannes Berg   cfg80211: send ev...
419
  			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
f401a6f7e   Johannes Berg   cfg80211: use rea...
420
421
422
  			memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
  			wdev->wext.prev_bssid_valid = true;
  		}
e45cd82ac   Johannes Berg   cfg80211: send ev...
423
424
425
  		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
  	}
  #endif
df7fc0f97   Johannes Berg   cfg80211: keep tr...
426
427
428
429
430
  	if (wdev->current_bss) {
  		cfg80211_unhold_bss(wdev->current_bss);
  		cfg80211_put_bss(&wdev->current_bss->pub);
  		wdev->current_bss = NULL;
  	}
19957bb39   Johannes Berg   cfg80211: keep tr...
431
432
  	if (wdev->conn)
  		wdev->conn->state = CFG80211_CONN_IDLE;
fffd0934b   Johannes Berg   cfg80211: rework ...
433
  	if (status != WLAN_STATUS_SUCCESS) {
b23aa676a   Samuel Ortiz   cfg80211: connect...
434
  		wdev->sme_state = CFG80211_SME_IDLE;
415ad1efa   David Kilroy   cfg80211: fix lea...
435
436
  		if (wdev->conn)
  			kfree(wdev->conn->ie);
19957bb39   Johannes Berg   cfg80211: keep tr...
437
438
  		kfree(wdev->conn);
  		wdev->conn = NULL;
fffd0934b   Johannes Berg   cfg80211: rework ...
439
440
  		kfree(wdev->connect_keys);
  		wdev->connect_keys = NULL;
8dadadb7e   Johannes Berg   cfg80211: clear S...
441
  		wdev->ssid_len = 0;
fffd0934b   Johannes Berg   cfg80211: rework ...
442
  		return;
b23aa676a   Samuel Ortiz   cfg80211: connect...
443
  	}
fffd0934b   Johannes Berg   cfg80211: rework ...
444

df7fc0f97   Johannes Berg   cfg80211: keep tr...
445
446
447
448
449
  	if (!bss)
  		bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
  				       wdev->ssid, wdev->ssid_len,
  				       WLAN_CAPABILITY_ESS,
  				       WLAN_CAPABILITY_ESS);
fffd0934b   Johannes Berg   cfg80211: rework ...
450
451
452
453
454
455
  
  	if (WARN_ON(!bss))
  		return;
  
  	cfg80211_hold_bss(bss_from_pub(bss));
  	wdev->current_bss = bss_from_pub(bss);
fffd0934b   Johannes Berg   cfg80211: rework ...
456
457
  	wdev->sme_state = CFG80211_SME_CONNECTED;
  	cfg80211_upload_connect_keys(wdev);
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
458
459
460
461
462
463
464
465
466
467
468
469
  
  	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,
84920e3e4   Luis R. Rodriguez   cfg80211: make re...
470
  			    bss->channel->band,
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
471
472
  			    country_ie + 2,
  			    country_ie[1]);
b23aa676a   Samuel Ortiz   cfg80211: connect...
473
  }
f21293549   Johannes Berg   cfg80211: managed...
474
475
476
477
478
479
  
  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)
  {
667503ddc   Johannes Berg   cfg80211: fix loc...
480
481
482
483
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
  	struct cfg80211_event *ev;
  	unsigned long flags;
f7969969f   Johannes Berg   cfg80211: make sp...
484
  	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
667503ddc   Johannes Berg   cfg80211: fix loc...
485
486
487
488
489
  	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
  	if (!ev)
  		return;
  
  	ev->type = EVENT_CONNECT_RESULT;
16a832e78   Zhu Yi   cfg80211: allow c...
490
491
  	if (bssid)
  		memcpy(ev->cr.bssid, bssid, ETH_ALEN);
7834704be   Nishant Sarmukadam   cfg80211: Avoid s...
492
493
494
495
496
497
498
499
500
501
  	if (req_ie_len) {
  		ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
  		ev->cr.req_ie_len = req_ie_len;
  		memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
  	}
  	if (resp_ie_len) {
  		ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
  		ev->cr.resp_ie_len = resp_ie_len;
  		memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
  	}
667503ddc   Johannes Berg   cfg80211: fix loc...
502
503
504
505
506
  	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);
e60d7443e   Alban Browaeys   wireless : use a ...
507
  	queue_work(cfg80211_wq, &rdev->event_work);
f21293549   Johannes Berg   cfg80211: managed...
508
  }
b23aa676a   Samuel Ortiz   cfg80211: connect...
509
  EXPORT_SYMBOL(cfg80211_connect_result);
667503ddc   Johannes Berg   cfg80211: fix loc...
510
511
512
  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)
b23aa676a   Samuel Ortiz   cfg80211: connect...
513
  {
b23aa676a   Samuel Ortiz   cfg80211: connect...
514
  	struct cfg80211_bss *bss;
3d23e349d   Johannes Berg   wext: refactor
515
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
516
517
  	union iwreq_data wrqu;
  #endif
667503ddc   Johannes Berg   cfg80211: fix loc...
518
  	ASSERT_WDEV_LOCK(wdev);
b23aa676a   Samuel Ortiz   cfg80211: connect...
519
520
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return;
f7969969f   Johannes Berg   cfg80211: make sp...
521
  	if (wdev->sme_state != CFG80211_SME_CONNECTED)
b23aa676a   Samuel Ortiz   cfg80211: connect...
522
523
524
525
526
527
528
529
  		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);
19957bb39   Johannes Berg   cfg80211: keep tr...
530
  	cfg80211_put_bss(&wdev->current_bss->pub);
b23aa676a   Samuel Ortiz   cfg80211: connect...
531
532
533
534
535
536
537
538
  	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;
19957bb39   Johannes Berg   cfg80211: keep tr...
539
540
  	cfg80211_hold_bss(bss_from_pub(bss));
  	wdev->current_bss = bss_from_pub(bss);
b23aa676a   Samuel Ortiz   cfg80211: connect...
541

667503ddc   Johannes Berg   cfg80211: fix loc...
542
543
544
  	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
  			    req_ie, req_ie_len, resp_ie, resp_ie_len,
  			    GFP_KERNEL);
b23aa676a   Samuel Ortiz   cfg80211: connect...
545

3d23e349d   Johannes Berg   wext: refactor
546
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
547
548
549
  	if (req_ie) {
  		memset(&wrqu, 0, sizeof(wrqu));
  		wrqu.data.length = req_ie_len;
3409ff771   Zhu Yi   cfg80211: fix typ...
550
  		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
667503ddc   Johannes Berg   cfg80211: fix loc...
551
  				    &wrqu, req_ie);
b23aa676a   Samuel Ortiz   cfg80211: connect...
552
553
554
555
556
  	}
  
  	if (resp_ie) {
  		memset(&wrqu, 0, sizeof(wrqu));
  		wrqu.data.length = resp_ie_len;
667503ddc   Johannes Berg   cfg80211: fix loc...
557
558
  		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
  				    &wrqu, resp_ie);
b23aa676a   Samuel Ortiz   cfg80211: connect...
559
560
561
562
563
  	}
  
  	memset(&wrqu, 0, sizeof(wrqu));
  	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
  	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
f401a6f7e   Johannes Berg   cfg80211: use rea...
564
565
  	memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
  	wdev->wext.prev_bssid_valid = true;
667503ddc   Johannes Berg   cfg80211: fix loc...
566
  	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
b23aa676a   Samuel Ortiz   cfg80211: connect...
567
568
  #endif
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
569
570
571
572
573
574
575
576
577
  
  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;
f7969969f   Johannes Berg   cfg80211: make sp...
578
  	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
667503ddc   Johannes Berg   cfg80211: fix loc...
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
  	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);
e60d7443e   Alban Browaeys   wireless : use a ...
595
  	queue_work(cfg80211_wq, &rdev->event_work);
667503ddc   Johannes Berg   cfg80211: fix loc...
596
  }
b23aa676a   Samuel Ortiz   cfg80211: connect...
597
  EXPORT_SYMBOL(cfg80211_roamed);
667503ddc   Johannes Berg   cfg80211: fix loc...
598
  void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
6829c878e   Johannes Berg   cfg80211: emulate...
599
  			     size_t ie_len, u16 reason, bool from_ap)
b23aa676a   Samuel Ortiz   cfg80211: connect...
600
601
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
fffd0934b   Johannes Berg   cfg80211: rework ...
602
603
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
  	int i;
3d23e349d   Johannes Berg   wext: refactor
604
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
605
606
  	union iwreq_data wrqu;
  #endif
667503ddc   Johannes Berg   cfg80211: fix loc...
607
  	ASSERT_WDEV_LOCK(wdev);
b23aa676a   Samuel Ortiz   cfg80211: connect...
608
609
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
  		return;
f7969969f   Johannes Berg   cfg80211: make sp...
610
  	if (wdev->sme_state != CFG80211_SME_CONNECTED)
b23aa676a   Samuel Ortiz   cfg80211: connect...
611
612
613
614
  		return;
  
  	if (wdev->current_bss) {
  		cfg80211_unhold_bss(wdev->current_bss);
19957bb39   Johannes Berg   cfg80211: keep tr...
615
  		cfg80211_put_bss(&wdev->current_bss->pub);
b23aa676a   Samuel Ortiz   cfg80211: connect...
616
617
618
619
  	}
  
  	wdev->current_bss = NULL;
  	wdev->sme_state = CFG80211_SME_IDLE;
8dadadb7e   Johannes Berg   cfg80211: clear S...
620
  	wdev->ssid_len = 0;
b23aa676a   Samuel Ortiz   cfg80211: connect...
621

6829c878e   Johannes Berg   cfg80211: emulate...
622
  	if (wdev->conn) {
b6f0b6390   Johannes Berg   cfg80211: fix SME...
623
624
  		const u8 *bssid;
  		int ret;
6829c878e   Johannes Berg   cfg80211: emulate...
625
626
  		kfree(wdev->conn->ie);
  		wdev->conn->ie = NULL;
19957bb39   Johannes Berg   cfg80211: keep tr...
627
628
  		kfree(wdev->conn);
  		wdev->conn = NULL;
b6f0b6390   Johannes Berg   cfg80211: fix SME...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
  
  		/*
  		 * 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,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
643
644
  						WLAN_REASON_DEAUTH_LEAVING,
  						false);
b6f0b6390   Johannes Berg   cfg80211: fix SME...
645
646
647
  			WARN(ret, "deauth failed: %d
  ", ret);
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
648
  	}
fffd0934b   Johannes Berg   cfg80211: rework ...
649
650
651
652
653
654
655
656
657
  	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);
b23aa676a   Samuel Ortiz   cfg80211: connect...
658

3d23e349d   Johannes Berg   wext: refactor
659
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
660
661
662
  	memset(&wrqu, 0, sizeof(wrqu));
  	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
  	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
5f6120335   Abhijeet Kolekar   cfg80211: fix cha...
663
  	wdev->wext.connect.ssid_len = 0;
b23aa676a   Samuel Ortiz   cfg80211: connect...
664
  #endif
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
665
666
  
  	schedule_work(&cfg80211_disconnect_work);
b23aa676a   Samuel Ortiz   cfg80211: connect...
667
668
669
670
671
  }
  
  void cfg80211_disconnected(struct net_device *dev, u16 reason,
  			   u8 *ie, size_t ie_len, gfp_t gfp)
  {
667503ddc   Johannes Berg   cfg80211: fix loc...
672
673
674
675
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
  	struct cfg80211_event *ev;
  	unsigned long flags;
f7969969f   Johannes Berg   cfg80211: make sp...
676
  	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
667503ddc   Johannes Berg   cfg80211: fix loc...
677
678
679
680
681
682
683
684
685
686
687
688
689
  	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);
e60d7443e   Alban Browaeys   wireless : use a ...
690
  	queue_work(cfg80211_wq, &rdev->event_work);
b23aa676a   Samuel Ortiz   cfg80211: connect...
691
692
  }
  EXPORT_SYMBOL(cfg80211_disconnected);
667503ddc   Johannes Berg   cfg80211: fix loc...
693
694
  int __cfg80211_connect(struct cfg80211_registered_device *rdev,
  		       struct net_device *dev,
fffd0934b   Johannes Berg   cfg80211: rework ...
695
  		       struct cfg80211_connect_params *connect,
f401a6f7e   Johannes Berg   cfg80211: use rea...
696
697
  		       struct cfg80211_cached_keys *connkeys,
  		       const u8 *prev_bssid)
b23aa676a   Samuel Ortiz   cfg80211: connect...
698
  {
b23aa676a   Samuel Ortiz   cfg80211: connect...
699
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
bbac31f4c   Johannes Berg   cfg80211: fix SME...
700
  	struct cfg80211_bss *bss = NULL;
667503ddc   Johannes Berg   cfg80211: fix loc...
701
702
703
  	int err;
  
  	ASSERT_WDEV_LOCK(wdev);
b23aa676a   Samuel Ortiz   cfg80211: connect...
704
705
706
  
  	if (wdev->sme_state != CFG80211_SME_IDLE)
  		return -EALREADY;
fffd0934b   Johannes Berg   cfg80211: rework ...
707
708
709
710
711
712
713
  	if (WARN_ON(wdev->connect_keys)) {
  		kfree(wdev->connect_keys);
  		wdev->connect_keys = NULL;
  	}
  
  	if (connkeys && connkeys->def >= 0) {
  		int idx;
bcba8eae1   Samuel Ortiz   cfg80211: Set WEP...
714
  		u32 cipher;
fffd0934b   Johannes Berg   cfg80211: rework ...
715
716
  
  		idx = connkeys->def;
bcba8eae1   Samuel Ortiz   cfg80211: Set WEP...
717
  		cipher = connkeys->params[idx].cipher;
fffd0934b   Johannes Berg   cfg80211: rework ...
718
  		/* If given a WEP key we may need it for shared key auth */
bcba8eae1   Samuel Ortiz   cfg80211: Set WEP...
719
720
  		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
  		    cipher == WLAN_CIPHER_SUITE_WEP104) {
fffd0934b   Johannes Berg   cfg80211: rework ...
721
722
723
  			connect->key_idx = idx;
  			connect->key = connkeys->params[idx].key;
  			connect->key_len = connkeys->params[idx].key_len;
bcba8eae1   Samuel Ortiz   cfg80211: Set WEP...
724
725
726
727
728
729
730
731
732
733
734
735
  
  			/*
  			 * 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;
  			}
fffd0934b   Johannes Berg   cfg80211: rework ...
736
737
  		}
  	}
b23aa676a   Samuel Ortiz   cfg80211: connect...
738
  	if (!rdev->ops->connect) {
6829c878e   Johannes Berg   cfg80211: emulate...
739
740
  		if (!rdev->ops->auth || !rdev->ops->assoc)
  			return -EOPNOTSUPP;
19957bb39   Johannes Berg   cfg80211: keep tr...
741
742
743
744
745
746
  		if (WARN_ON(wdev->conn))
  			return -EINPROGRESS;
  
  		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
  		if (!wdev->conn)
  			return -ENOMEM;
6829c878e   Johannes Berg   cfg80211: emulate...
747
748
749
750
751
752
753
754
755
756
757
758
759
760
  
  		/*
  		 * 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;
19957bb39   Johannes Berg   cfg80211: keep tr...
761
762
763
  			if (!wdev->conn->ie) {
  				kfree(wdev->conn);
  				wdev->conn = NULL;
6829c878e   Johannes Berg   cfg80211: emulate...
764
  				return -ENOMEM;
19957bb39   Johannes Berg   cfg80211: keep tr...
765
  			}
6829c878e   Johannes Berg   cfg80211: emulate...
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
  		}
  
  		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;
8bb894859   Johannes Berg   cfg80211: always ...
781
782
  		/* see if we have the bss already */
  		bss = cfg80211_get_conn_bss(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
783
784
  
  		wdev->sme_state = CFG80211_SME_CONNECTING;
fffd0934b   Johannes Berg   cfg80211: rework ...
785
  		wdev->connect_keys = connkeys;
6829c878e   Johannes Berg   cfg80211: emulate...
786

f401a6f7e   Johannes Berg   cfg80211: use rea...
787
788
789
790
  		if (prev_bssid) {
  			memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
  			wdev->conn->prev_bssid_valid = true;
  		}
bbac31f4c   Johannes Berg   cfg80211: fix SME...
791
792
  		/* we're good if we have a matching bss struct */
  		if (bss) {
6829c878e   Johannes Berg   cfg80211: emulate...
793
794
  			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
  			err = cfg80211_conn_do_work(wdev);
bbac31f4c   Johannes Berg   cfg80211: fix SME...
795
  			cfg80211_put_bss(bss);
6829c878e   Johannes Berg   cfg80211: emulate...
796
797
798
799
800
801
802
803
804
805
806
807
808
  		} 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;
  			}
  		}
19957bb39   Johannes Berg   cfg80211: keep tr...
809
  		if (err) {
415ad1efa   David Kilroy   cfg80211: fix lea...
810
  			kfree(wdev->conn->ie);
19957bb39   Johannes Berg   cfg80211: keep tr...
811
812
  			kfree(wdev->conn);
  			wdev->conn = NULL;
6829c878e   Johannes Berg   cfg80211: emulate...
813
  			wdev->sme_state = CFG80211_SME_IDLE;
fffd0934b   Johannes Berg   cfg80211: rework ...
814
  			wdev->connect_keys = NULL;
8dadadb7e   Johannes Berg   cfg80211: clear S...
815
  			wdev->ssid_len = 0;
19957bb39   Johannes Berg   cfg80211: keep tr...
816
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
817
818
  
  		return err;
b23aa676a   Samuel Ortiz   cfg80211: connect...
819
820
  	} else {
  		wdev->sme_state = CFG80211_SME_CONNECTING;
fffd0934b   Johannes Berg   cfg80211: rework ...
821
  		wdev->connect_keys = connkeys;
b23aa676a   Samuel Ortiz   cfg80211: connect...
822
823
  		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
  		if (err) {
fffd0934b   Johannes Berg   cfg80211: rework ...
824
  			wdev->connect_keys = NULL;
b23aa676a   Samuel Ortiz   cfg80211: connect...
825
826
827
  			wdev->sme_state = CFG80211_SME_IDLE;
  			return err;
  		}
b23aa676a   Samuel Ortiz   cfg80211: connect...
828

6829c878e   Johannes Berg   cfg80211: emulate...
829
830
  		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
  		wdev->ssid_len = connect->ssid_len;
b23aa676a   Samuel Ortiz   cfg80211: connect...
831

6829c878e   Johannes Berg   cfg80211: emulate...
832
833
  		return 0;
  	}
b23aa676a   Samuel Ortiz   cfg80211: connect...
834
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
835
836
  int cfg80211_connect(struct cfg80211_registered_device *rdev,
  		     struct net_device *dev,
fffd0934b   Johannes Berg   cfg80211: rework ...
837
838
  		     struct cfg80211_connect_params *connect,
  		     struct cfg80211_cached_keys *connkeys)
667503ddc   Johannes Berg   cfg80211: fix loc...
839
840
  {
  	int err;
59bbb6f75   Johannes Berg   cfg80211: validat...
841
  	mutex_lock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
842
  	wdev_lock(dev->ieee80211_ptr);
f401a6f7e   Johannes Berg   cfg80211: use rea...
843
  	err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL);
667503ddc   Johannes Berg   cfg80211: fix loc...
844
  	wdev_unlock(dev->ieee80211_ptr);
59bbb6f75   Johannes Berg   cfg80211: validat...
845
  	mutex_unlock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
846
847
848
849
850
851
  
  	return err;
  }
  
  int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
  			  struct net_device *dev, u16 reason, bool wextev)
b23aa676a   Samuel Ortiz   cfg80211: connect...
852
  {
6829c878e   Johannes Berg   cfg80211: emulate...
853
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
b23aa676a   Samuel Ortiz   cfg80211: connect...
854
  	int err;
667503ddc   Johannes Berg   cfg80211: fix loc...
855
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
856
857
  	if (wdev->sme_state == CFG80211_SME_IDLE)
  		return -EINVAL;
fffd0934b   Johannes Berg   cfg80211: rework ...
858
859
  	kfree(wdev->connect_keys);
  	wdev->connect_keys = NULL;
b23aa676a   Samuel Ortiz   cfg80211: connect...
860
  	if (!rdev->ops->disconnect) {
19957bb39   Johannes Berg   cfg80211: keep tr...
861
862
  		if (!rdev->ops->deauth)
  			return -EOPNOTSUPP;
6829c878e   Johannes Berg   cfg80211: emulate...
863

19957bb39   Johannes Berg   cfg80211: keep tr...
864
865
866
867
868
  		/* was it connected by userspace SME? */
  		if (!wdev->conn) {
  			cfg80211_mlme_down(rdev, dev);
  			return 0;
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
869
870
871
872
873
  
  		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;
415ad1efa   David Kilroy   cfg80211: fix lea...
874
  			kfree(wdev->conn->ie);
19957bb39   Johannes Berg   cfg80211: keep tr...
875
876
  			kfree(wdev->conn);
  			wdev->conn = NULL;
8dadadb7e   Johannes Berg   cfg80211: clear S...
877
  			wdev->ssid_len = 0;
6829c878e   Johannes Berg   cfg80211: emulate...
878
879
  			return 0;
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
880
  		/* wdev->conn->params.bssid must be set if > SCANNING */
667503ddc   Johannes Berg   cfg80211: fix loc...
881
882
  		err = __cfg80211_mlme_deauth(rdev, dev,
  					     wdev->conn->params.bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
883
  					     NULL, 0, reason, false);
6829c878e   Johannes Berg   cfg80211: emulate...
884
885
  		if (err)
  			return err;
b23aa676a   Samuel Ortiz   cfg80211: connect...
886
887
888
889
890
  	} else {
  		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
  		if (err)
  			return err;
  	}
6829c878e   Johannes Berg   cfg80211: emulate...
891
  	if (wdev->sme_state == CFG80211_SME_CONNECTED)
667503ddc   Johannes Berg   cfg80211: fix loc...
892
  		__cfg80211_disconnected(dev, NULL, 0, 0, false);
6829c878e   Johannes Berg   cfg80211: emulate...
893
  	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
f21293549   Johannes Berg   cfg80211: managed...
894
895
  		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
  					  WLAN_STATUS_UNSPECIFIED_FAILURE,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
896
  					  wextev, NULL);
b23aa676a   Samuel Ortiz   cfg80211: connect...
897
898
899
  
  	return 0;
  }
19957bb39   Johannes Berg   cfg80211: keep tr...
900

667503ddc   Johannes Berg   cfg80211: fix loc...
901
902
903
904
905
906
907
908
909
910
911
912
  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;
  }
19957bb39   Johannes Berg   cfg80211: keep tr...
913
914
915
916
917
  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];
667503ddc   Johannes Berg   cfg80211: fix loc...
918
  	ASSERT_WDEV_LOCK(wdev);
19957bb39   Johannes Berg   cfg80211: keep tr...
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
  	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);
ec3f14901   Johannes Berg   cfg80211: fix a l...
934
  	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
935
936
  				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING,
  				   false)) {
19957bb39   Johannes Berg   cfg80211: keep tr...
937
938
939
940
941
942
  		/* whatever -- assume gone anyway */
  		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
  		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
  		wdev->auth_bsses[idx] = NULL;
  	}
  }