Blame view

net/wireless/sme.c 26.4 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
  #include <linux/wireless.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
13
  #include <linux/export.h>
a9a11622c   Johannes Berg   cfg80211: self-co...
14
  #include <net/iw_handler.h>
b23aa676a   Samuel Ortiz   cfg80211: connect...
15
16
17
  #include <net/cfg80211.h>
  #include <net/rtnetlink.h>
  #include "nl80211.h"
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
18
  #include "reg.h"
b23aa676a   Samuel Ortiz   cfg80211: connect...
19

6829c878e   Johannes Berg   cfg80211: emulate...
20
21
22
23
24
25
26
27
28
29
30
  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...
31
  		CFG80211_CONN_DEAUTH_ASSOC_FAIL,
6829c878e   Johannes Berg   cfg80211: emulate...
32
  	} state;
f401a6f7e   Johannes Berg   cfg80211: use rea...
33
  	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
6829c878e   Johannes Berg   cfg80211: emulate...
34
35
  	u8 *ie;
  	size_t ie_len;
f401a6f7e   Johannes Berg   cfg80211: use rea...
36
  	bool auto_auth, prev_bssid_valid;
6829c878e   Johannes Berg   cfg80211: emulate...
37
  };
20925feee   John W. Linville   wireless: mark cf...
38
  static bool cfg80211_is_all_idle(void)
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
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
75
  {
  	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...
76
77
78
  
  static int cfg80211_conn_scan(struct wireless_dev *wdev)
  {
79c97e97a   Johannes Berg   cfg80211: clean u...
79
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
6829c878e   Johannes Berg   cfg80211: emulate...
80
81
82
83
  	struct cfg80211_scan_request *request;
  	int n_channels, err;
  
  	ASSERT_RTNL();
79c97e97a   Johannes Berg   cfg80211: clean u...
84
  	ASSERT_RDEV_LOCK(rdev);
667503ddc   Johannes Berg   cfg80211: fix loc...
85
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
86

79c97e97a   Johannes Berg   cfg80211: clean u...
87
  	if (rdev->scan_req)
6829c878e   Johannes Berg   cfg80211: emulate...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  		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...
107
108
109
110
111
  	if (wdev->conn->params.channel)
  		request->channels[0] = wdev->conn->params.channel;
  	else {
  		int i = 0, j;
  		enum ieee80211_band band;
e30815016   Rajkumar Manoharan   wireless: Do not ...
112
113
  		struct ieee80211_supported_band *bands;
  		struct ieee80211_channel *channel;
6829c878e   Johannes Berg   cfg80211: emulate...
114
115
  
  		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
e30815016   Rajkumar Manoharan   wireless: Do not ...
116
117
  			bands = wdev->wiphy->bands[band];
  			if (!bands)
6829c878e   Johannes Berg   cfg80211: emulate...
118
  				continue;
e30815016   Rajkumar Manoharan   wireless: Do not ...
119
120
121
122
123
124
125
  			for (j = 0; j < bands->n_channels; j++) {
  				channel = &bands->channels[j];
  				if (channel->flags & IEEE80211_CHAN_DISABLED)
  					continue;
  				request->channels[i++] = channel;
  			}
  			request->rates[band] = (1 << bands->n_bitrates) - 1;
6829c878e   Johannes Berg   cfg80211: emulate...
126
  		}
e30815016   Rajkumar Manoharan   wireless: Do not ...
127
  		n_channels = i;
6829c878e   Johannes Berg   cfg80211: emulate...
128
129
  	}
  	request->n_channels = n_channels;
5ba63533b   Johannes Berg   cfg80211: fix ali...
130
  	request->ssids = (void *)&request->channels[n_channels];
6829c878e   Johannes Berg   cfg80211: emulate...
131
132
133
134
135
  	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...
136
  	request->dev = wdev->netdev;
79c97e97a   Johannes Berg   cfg80211: clean u...
137
  	request->wiphy = &rdev->wiphy;
6829c878e   Johannes Berg   cfg80211: emulate...
138

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

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

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

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

bbac31f4c   Johannes Berg   cfg80211: fix SME...
265
  	return bss;
6829c878e   Johannes Berg   cfg80211: emulate...
266
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
267
  static void __cfg80211_sme_scan_done(struct net_device *dev)
6829c878e   Johannes Berg   cfg80211: emulate...
268
269
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
79c97e97a   Johannes Berg   cfg80211: clean u...
270
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
bbac31f4c   Johannes Berg   cfg80211: fix SME...
271
  	struct cfg80211_bss *bss;
6829c878e   Johannes Berg   cfg80211: emulate...
272

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

f401a6f7e   Johannes Berg   cfg80211: use rea...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  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...
380
381
382
383
384
385
386
387
  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...
388
389
390
  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...
391
392
  			       u16 status, bool wextev,
  			       struct cfg80211_bss *bss)
b23aa676a   Samuel Ortiz   cfg80211: connect...
393
394
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
395
  	u8 *country_ie;
3d23e349d   Johannes Berg   wext: refactor
396
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
397
398
  	union iwreq_data wrqu;
  #endif
667503ddc   Johannes Berg   cfg80211: fix loc...
399
  	ASSERT_WDEV_LOCK(wdev);
074ac8df9   Johannes Berg   cfg80211/nl80211:...
400
401
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
  		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
b23aa676a   Samuel Ortiz   cfg80211: connect...
402
  		return;
f7969969f   Johannes Berg   cfg80211: make sp...
403
  	if (wdev->sme_state != CFG80211_SME_CONNECTING)
ea416a793   Johannes Berg   cfg80211: report ...
404
405
406
  		return;
  
  	nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
e45cd82ac   Johannes Berg   cfg80211: send ev...
407
  				    bssid, req_ie, req_ie_len,
ea416a793   Johannes Berg   cfg80211: report ...
408
409
  				    resp_ie, resp_ie_len,
  				    status, GFP_KERNEL);
e45cd82ac   Johannes Berg   cfg80211: send ev...
410

3d23e349d   Johannes Berg   wext: refactor
411
  #ifdef CONFIG_CFG80211_WEXT
e45cd82ac   Johannes Berg   cfg80211: send ev...
412
413
414
415
  	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...
416
  			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
e45cd82ac   Johannes Berg   cfg80211: send ev...
417
418
419
420
421
422
423
424
425
426
  		}
  
  		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...
427
  		if (bssid && status == WLAN_STATUS_SUCCESS) {
e45cd82ac   Johannes Berg   cfg80211: send ev...
428
  			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
f401a6f7e   Johannes Berg   cfg80211: use rea...
429
430
431
  			memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
  			wdev->wext.prev_bssid_valid = true;
  		}
e45cd82ac   Johannes Berg   cfg80211: send ev...
432
433
434
  		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
  	}
  #endif
df7fc0f97   Johannes Berg   cfg80211: keep tr...
435
436
437
438
439
  	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...
440
441
  	if (wdev->conn)
  		wdev->conn->state = CFG80211_CONN_IDLE;
fffd0934b   Johannes Berg   cfg80211: rework ...
442
  	if (status != WLAN_STATUS_SUCCESS) {
b23aa676a   Samuel Ortiz   cfg80211: connect...
443
  		wdev->sme_state = CFG80211_SME_IDLE;
415ad1efa   David Kilroy   cfg80211: fix lea...
444
445
  		if (wdev->conn)
  			kfree(wdev->conn->ie);
19957bb39   Johannes Berg   cfg80211: keep tr...
446
447
  		kfree(wdev->conn);
  		wdev->conn = NULL;
fffd0934b   Johannes Berg   cfg80211: rework ...
448
449
  		kfree(wdev->connect_keys);
  		wdev->connect_keys = NULL;
8dadadb7e   Johannes Berg   cfg80211: clear S...
450
  		wdev->ssid_len = 0;
fffd0934b   Johannes Berg   cfg80211: rework ...
451
  		return;
b23aa676a   Samuel Ortiz   cfg80211: connect...
452
  	}
fffd0934b   Johannes Berg   cfg80211: rework ...
453

df7fc0f97   Johannes Berg   cfg80211: keep tr...
454
  	if (!bss)
ed9d01026   Jouni Malinen   cfg80211: Use con...
455
456
457
458
  		bss = cfg80211_get_bss(wdev->wiphy,
  				       wdev->conn ? wdev->conn->params.channel :
  				       NULL,
  				       bssid,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
459
460
461
  				       wdev->ssid, wdev->ssid_len,
  				       WLAN_CAPABILITY_ESS,
  				       WLAN_CAPABILITY_ESS);
fffd0934b   Johannes Berg   cfg80211: rework ...
462
463
464
465
466
467
  
  	if (WARN_ON(!bss))
  		return;
  
  	cfg80211_hold_bss(bss_from_pub(bss));
  	wdev->current_bss = bss_from_pub(bss);
fffd0934b   Johannes Berg   cfg80211: rework ...
468
469
  	wdev->sme_state = CFG80211_SME_CONNECTED;
  	cfg80211_upload_connect_keys(wdev);
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
470
471
472
473
474
475
476
477
478
479
480
481
  
  	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...
482
  			    bss->channel->band,
8b19e6ca3   Luis R. Rodriguez   cfg80211: enable ...
483
484
  			    country_ie + 2,
  			    country_ie[1]);
b23aa676a   Samuel Ortiz   cfg80211: connect...
485
  }
f21293549   Johannes Berg   cfg80211: managed...
486
487
488
489
490
491
  
  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...
492
493
494
495
  	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...
496
  	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
667503ddc   Johannes Berg   cfg80211: fix loc...
497
498
499
500
501
  	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...
502
503
  	if (bssid)
  		memcpy(ev->cr.bssid, bssid, ETH_ALEN);
7834704be   Nishant Sarmukadam   cfg80211: Avoid s...
504
505
506
507
508
509
510
511
512
513
  	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...
514
515
516
517
518
  	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 ...
519
  	queue_work(cfg80211_wq, &rdev->event_work);
f21293549   Johannes Berg   cfg80211: managed...
520
  }
b23aa676a   Samuel Ortiz   cfg80211: connect...
521
  EXPORT_SYMBOL(cfg80211_connect_result);
ed9d01026   Jouni Malinen   cfg80211: Use con...
522
523
524
  void __cfg80211_roamed(struct wireless_dev *wdev,
  		       struct ieee80211_channel *channel,
  		       const u8 *bssid,
667503ddc   Johannes Berg   cfg80211: fix loc...
525
526
  		       const u8 *req_ie, size_t req_ie_len,
  		       const u8 *resp_ie, size_t resp_ie_len)
b23aa676a   Samuel Ortiz   cfg80211: connect...
527
  {
b23aa676a   Samuel Ortiz   cfg80211: connect...
528
  	struct cfg80211_bss *bss;
3d23e349d   Johannes Berg   wext: refactor
529
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
530
531
  	union iwreq_data wrqu;
  #endif
667503ddc   Johannes Berg   cfg80211: fix loc...
532
  	ASSERT_WDEV_LOCK(wdev);
074ac8df9   Johannes Berg   cfg80211/nl80211:...
533
534
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
  		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
b23aa676a   Samuel Ortiz   cfg80211: connect...
535
  		return;
f7969969f   Johannes Berg   cfg80211: make sp...
536
  	if (wdev->sme_state != CFG80211_SME_CONNECTED)
b23aa676a   Samuel Ortiz   cfg80211: connect...
537
538
539
540
541
542
543
544
  		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...
545
  	cfg80211_put_bss(&wdev->current_bss->pub);
b23aa676a   Samuel Ortiz   cfg80211: connect...
546
  	wdev->current_bss = NULL;
ed9d01026   Jouni Malinen   cfg80211: Use con...
547
  	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid,
b23aa676a   Samuel Ortiz   cfg80211: connect...
548
549
550
551
552
  			       wdev->ssid, wdev->ssid_len,
  			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
  
  	if (WARN_ON(!bss))
  		return;
19957bb39   Johannes Berg   cfg80211: keep tr...
553
554
  	cfg80211_hold_bss(bss_from_pub(bss));
  	wdev->current_bss = bss_from_pub(bss);
b23aa676a   Samuel Ortiz   cfg80211: connect...
555

667503ddc   Johannes Berg   cfg80211: fix loc...
556
557
558
  	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...
559

3d23e349d   Johannes Berg   wext: refactor
560
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
561
562
563
  	if (req_ie) {
  		memset(&wrqu, 0, sizeof(wrqu));
  		wrqu.data.length = req_ie_len;
3409ff771   Zhu Yi   cfg80211: fix typ...
564
  		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
667503ddc   Johannes Berg   cfg80211: fix loc...
565
  				    &wrqu, req_ie);
b23aa676a   Samuel Ortiz   cfg80211: connect...
566
567
568
569
570
  	}
  
  	if (resp_ie) {
  		memset(&wrqu, 0, sizeof(wrqu));
  		wrqu.data.length = resp_ie_len;
667503ddc   Johannes Berg   cfg80211: fix loc...
571
572
  		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
  				    &wrqu, resp_ie);
b23aa676a   Samuel Ortiz   cfg80211: connect...
573
574
575
576
577
  	}
  
  	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...
578
579
  	memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
  	wdev->wext.prev_bssid_valid = true;
667503ddc   Johannes Berg   cfg80211: fix loc...
580
  	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
b23aa676a   Samuel Ortiz   cfg80211: connect...
581
582
  #endif
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
583

ed9d01026   Jouni Malinen   cfg80211: Use con...
584
585
586
  void cfg80211_roamed(struct net_device *dev,
  		     struct ieee80211_channel *channel,
  		     const u8 *bssid,
667503ddc   Johannes Berg   cfg80211: fix loc...
587
588
589
590
591
592
593
  		     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...
594
  	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
667503ddc   Johannes Berg   cfg80211: fix loc...
595
596
597
598
599
  	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
  	if (!ev)
  		return;
  
  	ev->type = EVENT_ROAMED;
ed9d01026   Jouni Malinen   cfg80211: Use con...
600
  	ev->rm.channel = channel;
667503ddc   Johannes Berg   cfg80211: fix loc...
601
602
603
604
605
606
607
608
609
610
611
  	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 ...
612
  	queue_work(cfg80211_wq, &rdev->event_work);
667503ddc   Johannes Berg   cfg80211: fix loc...
613
  }
b23aa676a   Samuel Ortiz   cfg80211: connect...
614
  EXPORT_SYMBOL(cfg80211_roamed);
667503ddc   Johannes Berg   cfg80211: fix loc...
615
  void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
6829c878e   Johannes Berg   cfg80211: emulate...
616
  			     size_t ie_len, u16 reason, bool from_ap)
b23aa676a   Samuel Ortiz   cfg80211: connect...
617
618
  {
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
fffd0934b   Johannes Berg   cfg80211: rework ...
619
620
  	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
  	int i;
3d23e349d   Johannes Berg   wext: refactor
621
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
622
623
  	union iwreq_data wrqu;
  #endif
667503ddc   Johannes Berg   cfg80211: fix loc...
624
  	ASSERT_WDEV_LOCK(wdev);
074ac8df9   Johannes Berg   cfg80211/nl80211:...
625
626
  	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
  		    wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
b23aa676a   Samuel Ortiz   cfg80211: connect...
627
  		return;
f7969969f   Johannes Berg   cfg80211: make sp...
628
  	if (wdev->sme_state != CFG80211_SME_CONNECTED)
b23aa676a   Samuel Ortiz   cfg80211: connect...
629
630
631
632
  		return;
  
  	if (wdev->current_bss) {
  		cfg80211_unhold_bss(wdev->current_bss);
19957bb39   Johannes Berg   cfg80211: keep tr...
633
  		cfg80211_put_bss(&wdev->current_bss->pub);
b23aa676a   Samuel Ortiz   cfg80211: connect...
634
635
636
637
  	}
  
  	wdev->current_bss = NULL;
  	wdev->sme_state = CFG80211_SME_IDLE;
8dadadb7e   Johannes Berg   cfg80211: clear S...
638
  	wdev->ssid_len = 0;
b23aa676a   Samuel Ortiz   cfg80211: connect...
639

6829c878e   Johannes Berg   cfg80211: emulate...
640
  	if (wdev->conn) {
b6f0b6390   Johannes Berg   cfg80211: fix SME...
641
642
  		const u8 *bssid;
  		int ret;
6829c878e   Johannes Berg   cfg80211: emulate...
643
644
  		kfree(wdev->conn->ie);
  		wdev->conn->ie = NULL;
19957bb39   Johannes Berg   cfg80211: keep tr...
645
646
  		kfree(wdev->conn);
  		wdev->conn = NULL;
b6f0b6390   Johannes Berg   cfg80211: fix SME...
647
648
649
650
651
652
653
654
655
656
657
658
659
660
  
  		/*
  		 * 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...
661
662
  						WLAN_REASON_DEAUTH_LEAVING,
  						false);
b6f0b6390   Johannes Berg   cfg80211: fix SME...
663
664
665
  			WARN(ret, "deauth failed: %d
  ", ret);
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
666
  	}
fffd0934b   Johannes Berg   cfg80211: rework ...
667
668
669
670
671
672
673
674
  	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++)
e31b82136   Johannes Berg   cfg80211/mac80211...
675
  			rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL);
b23aa676a   Samuel Ortiz   cfg80211: connect...
676

3d23e349d   Johannes Berg   wext: refactor
677
  #ifdef CONFIG_CFG80211_WEXT
b23aa676a   Samuel Ortiz   cfg80211: connect...
678
679
680
  	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...
681
  	wdev->wext.connect.ssid_len = 0;
b23aa676a   Samuel Ortiz   cfg80211: connect...
682
  #endif
09d989d17   Luis R. Rodriguez   cfg80211: add reg...
683
684
  
  	schedule_work(&cfg80211_disconnect_work);
b23aa676a   Samuel Ortiz   cfg80211: connect...
685
686
687
688
689
  }
  
  void cfg80211_disconnected(struct net_device *dev, u16 reason,
  			   u8 *ie, size_t ie_len, gfp_t gfp)
  {
667503ddc   Johannes Berg   cfg80211: fix loc...
690
691
692
693
  	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...
694
  	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED);
667503ddc   Johannes Berg   cfg80211: fix loc...
695
696
697
698
699
700
701
702
703
704
705
706
707
  	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 ...
708
  	queue_work(cfg80211_wq, &rdev->event_work);
b23aa676a   Samuel Ortiz   cfg80211: connect...
709
710
  }
  EXPORT_SYMBOL(cfg80211_disconnected);
667503ddc   Johannes Berg   cfg80211: fix loc...
711
712
  int __cfg80211_connect(struct cfg80211_registered_device *rdev,
  		       struct net_device *dev,
fffd0934b   Johannes Berg   cfg80211: rework ...
713
  		       struct cfg80211_connect_params *connect,
f401a6f7e   Johannes Berg   cfg80211: use rea...
714
715
  		       struct cfg80211_cached_keys *connkeys,
  		       const u8 *prev_bssid)
b23aa676a   Samuel Ortiz   cfg80211: connect...
716
  {
b23aa676a   Samuel Ortiz   cfg80211: connect...
717
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
bbac31f4c   Johannes Berg   cfg80211: fix SME...
718
  	struct cfg80211_bss *bss = NULL;
667503ddc   Johannes Berg   cfg80211: fix loc...
719
720
721
  	int err;
  
  	ASSERT_WDEV_LOCK(wdev);
b23aa676a   Samuel Ortiz   cfg80211: connect...
722
723
724
  
  	if (wdev->sme_state != CFG80211_SME_IDLE)
  		return -EALREADY;
fffd0934b   Johannes Berg   cfg80211: rework ...
725
726
727
728
729
730
731
  	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...
732
  		u32 cipher;
fffd0934b   Johannes Berg   cfg80211: rework ...
733
734
  
  		idx = connkeys->def;
bcba8eae1   Samuel Ortiz   cfg80211: Set WEP...
735
  		cipher = connkeys->params[idx].cipher;
fffd0934b   Johannes Berg   cfg80211: rework ...
736
  		/* If given a WEP key we may need it for shared key auth */
bcba8eae1   Samuel Ortiz   cfg80211: Set WEP...
737
738
  		if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
  		    cipher == WLAN_CIPHER_SUITE_WEP104) {
fffd0934b   Johannes Berg   cfg80211: rework ...
739
740
741
  			connect->key_idx = idx;
  			connect->key = connkeys->params[idx].key;
  			connect->key_len = connkeys->params[idx].key_len;
bcba8eae1   Samuel Ortiz   cfg80211: Set WEP...
742
743
744
745
746
747
748
749
750
751
752
753
  
  			/*
  			 * 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 ...
754
755
  		}
  	}
b23aa676a   Samuel Ortiz   cfg80211: connect...
756
  	if (!rdev->ops->connect) {
6829c878e   Johannes Berg   cfg80211: emulate...
757
758
  		if (!rdev->ops->auth || !rdev->ops->assoc)
  			return -EOPNOTSUPP;
19957bb39   Johannes Berg   cfg80211: keep tr...
759
760
761
762
763
764
  		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...
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  
  		/*
  		 * 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...
779
780
781
  			if (!wdev->conn->ie) {
  				kfree(wdev->conn);
  				wdev->conn = NULL;
6829c878e   Johannes Berg   cfg80211: emulate...
782
  				return -ENOMEM;
19957bb39   Johannes Berg   cfg80211: keep tr...
783
  			}
6829c878e   Johannes Berg   cfg80211: emulate...
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
  		}
  
  		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 ...
799
800
  		/* see if we have the bss already */
  		bss = cfg80211_get_conn_bss(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
801
802
  
  		wdev->sme_state = CFG80211_SME_CONNECTING;
fffd0934b   Johannes Berg   cfg80211: rework ...
803
  		wdev->connect_keys = connkeys;
6829c878e   Johannes Berg   cfg80211: emulate...
804

f401a6f7e   Johannes Berg   cfg80211: use rea...
805
806
807
808
  		if (prev_bssid) {
  			memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
  			wdev->conn->prev_bssid_valid = true;
  		}
bbac31f4c   Johannes Berg   cfg80211: fix SME...
809
810
  		/* we're good if we have a matching bss struct */
  		if (bss) {
6829c878e   Johannes Berg   cfg80211: emulate...
811
812
  			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
  			err = cfg80211_conn_do_work(wdev);
bbac31f4c   Johannes Berg   cfg80211: fix SME...
813
  			cfg80211_put_bss(bss);
6829c878e   Johannes Berg   cfg80211: emulate...
814
815
816
817
818
819
820
821
822
823
824
825
826
  		} 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...
827
  		if (err) {
415ad1efa   David Kilroy   cfg80211: fix lea...
828
  			kfree(wdev->conn->ie);
19957bb39   Johannes Berg   cfg80211: keep tr...
829
830
  			kfree(wdev->conn);
  			wdev->conn = NULL;
6829c878e   Johannes Berg   cfg80211: emulate...
831
  			wdev->sme_state = CFG80211_SME_IDLE;
fffd0934b   Johannes Berg   cfg80211: rework ...
832
  			wdev->connect_keys = NULL;
8dadadb7e   Johannes Berg   cfg80211: clear S...
833
  			wdev->ssid_len = 0;
19957bb39   Johannes Berg   cfg80211: keep tr...
834
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
835
836
  
  		return err;
b23aa676a   Samuel Ortiz   cfg80211: connect...
837
838
  	} else {
  		wdev->sme_state = CFG80211_SME_CONNECTING;
fffd0934b   Johannes Berg   cfg80211: rework ...
839
  		wdev->connect_keys = connkeys;
b23aa676a   Samuel Ortiz   cfg80211: connect...
840
841
  		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
  		if (err) {
fffd0934b   Johannes Berg   cfg80211: rework ...
842
  			wdev->connect_keys = NULL;
b23aa676a   Samuel Ortiz   cfg80211: connect...
843
844
845
  			wdev->sme_state = CFG80211_SME_IDLE;
  			return err;
  		}
b23aa676a   Samuel Ortiz   cfg80211: connect...
846

6829c878e   Johannes Berg   cfg80211: emulate...
847
848
  		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
  		wdev->ssid_len = connect->ssid_len;
b23aa676a   Samuel Ortiz   cfg80211: connect...
849

6829c878e   Johannes Berg   cfg80211: emulate...
850
851
  		return 0;
  	}
b23aa676a   Samuel Ortiz   cfg80211: connect...
852
  }
667503ddc   Johannes Berg   cfg80211: fix loc...
853
854
  int cfg80211_connect(struct cfg80211_registered_device *rdev,
  		     struct net_device *dev,
fffd0934b   Johannes Berg   cfg80211: rework ...
855
856
  		     struct cfg80211_connect_params *connect,
  		     struct cfg80211_cached_keys *connkeys)
667503ddc   Johannes Berg   cfg80211: fix loc...
857
858
  {
  	int err;
59bbb6f75   Johannes Berg   cfg80211: validat...
859
  	mutex_lock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
860
  	wdev_lock(dev->ieee80211_ptr);
f401a6f7e   Johannes Berg   cfg80211: use rea...
861
  	err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL);
667503ddc   Johannes Berg   cfg80211: fix loc...
862
  	wdev_unlock(dev->ieee80211_ptr);
59bbb6f75   Johannes Berg   cfg80211: validat...
863
  	mutex_unlock(&rdev->devlist_mtx);
667503ddc   Johannes Berg   cfg80211: fix loc...
864
865
866
867
868
869
  
  	return err;
  }
  
  int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
  			  struct net_device *dev, u16 reason, bool wextev)
b23aa676a   Samuel Ortiz   cfg80211: connect...
870
  {
6829c878e   Johannes Berg   cfg80211: emulate...
871
  	struct wireless_dev *wdev = dev->ieee80211_ptr;
b23aa676a   Samuel Ortiz   cfg80211: connect...
872
  	int err;
667503ddc   Johannes Berg   cfg80211: fix loc...
873
  	ASSERT_WDEV_LOCK(wdev);
6829c878e   Johannes Berg   cfg80211: emulate...
874
875
  	if (wdev->sme_state == CFG80211_SME_IDLE)
  		return -EINVAL;
fffd0934b   Johannes Berg   cfg80211: rework ...
876
877
  	kfree(wdev->connect_keys);
  	wdev->connect_keys = NULL;
b23aa676a   Samuel Ortiz   cfg80211: connect...
878
  	if (!rdev->ops->disconnect) {
19957bb39   Johannes Berg   cfg80211: keep tr...
879
880
  		if (!rdev->ops->deauth)
  			return -EOPNOTSUPP;
6829c878e   Johannes Berg   cfg80211: emulate...
881

19957bb39   Johannes Berg   cfg80211: keep tr...
882
883
884
885
886
  		/* was it connected by userspace SME? */
  		if (!wdev->conn) {
  			cfg80211_mlme_down(rdev, dev);
  			return 0;
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
887
888
889
890
891
  
  		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...
892
  			kfree(wdev->conn->ie);
19957bb39   Johannes Berg   cfg80211: keep tr...
893
894
  			kfree(wdev->conn);
  			wdev->conn = NULL;
8dadadb7e   Johannes Berg   cfg80211: clear S...
895
  			wdev->ssid_len = 0;
6829c878e   Johannes Berg   cfg80211: emulate...
896
897
  			return 0;
  		}
6829c878e   Johannes Berg   cfg80211: emulate...
898
  		/* wdev->conn->params.bssid must be set if > SCANNING */
667503ddc   Johannes Berg   cfg80211: fix loc...
899
900
  		err = __cfg80211_mlme_deauth(rdev, dev,
  					     wdev->conn->params.bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
901
  					     NULL, 0, reason, false);
6829c878e   Johannes Berg   cfg80211: emulate...
902
903
  		if (err)
  			return err;
b23aa676a   Samuel Ortiz   cfg80211: connect...
904
905
906
907
908
  	} else {
  		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
  		if (err)
  			return err;
  	}
6829c878e   Johannes Berg   cfg80211: emulate...
909
  	if (wdev->sme_state == CFG80211_SME_CONNECTED)
667503ddc   Johannes Berg   cfg80211: fix loc...
910
  		__cfg80211_disconnected(dev, NULL, 0, 0, false);
6829c878e   Johannes Berg   cfg80211: emulate...
911
  	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
f21293549   Johannes Berg   cfg80211: managed...
912
913
  		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
  					  WLAN_STATUS_UNSPECIFIED_FAILURE,
df7fc0f97   Johannes Berg   cfg80211: keep tr...
914
  					  wextev, NULL);
b23aa676a   Samuel Ortiz   cfg80211: connect...
915
916
917
  
  	return 0;
  }
19957bb39   Johannes Berg   cfg80211: keep tr...
918

667503ddc   Johannes Berg   cfg80211: fix loc...
919
920
921
922
923
924
925
926
927
928
929
930
  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...
931
932
933
934
935
  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...
936
  	ASSERT_WDEV_LOCK(wdev);
19957bb39   Johannes Berg   cfg80211: keep tr...
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
  	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...
952
  	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
d5cdfacb3   Jouni Malinen   cfg80211: Add loc...
953
954
  				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING,
  				   false)) {
19957bb39   Johannes Berg   cfg80211: keep tr...
955
956
957
958
959
960
  		/* whatever -- assume gone anyway */
  		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
  		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
  		wdev->auth_bsses[idx] = NULL;
  	}
  }