xref: /linux/net/wireless/mlme.c (revision 273b281fa22c293963ee3e6eec418f5dda2dbc83)
1 /*
2  * cfg80211 MLME SAP interface
3  *
4  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
5  */
6 
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/netdevice.h>
10 #include <linux/nl80211.h>
11 #include <linux/wireless.h>
12 #include <net/cfg80211.h>
13 #include <net/iw_handler.h>
14 #include "core.h"
15 #include "nl80211.h"
16 
17 void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
18 {
19 	struct wireless_dev *wdev = dev->ieee80211_ptr;
20 	struct wiphy *wiphy = wdev->wiphy;
21 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
22 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
23 	u8 *bssid = mgmt->bssid;
24 	int i;
25 	u16 status = le16_to_cpu(mgmt->u.auth.status_code);
26 	bool done = false;
27 
28 	wdev_lock(wdev);
29 
30 	for (i = 0; i < MAX_AUTH_BSSES; i++) {
31 		if (wdev->authtry_bsses[i] &&
32 		    memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid,
33 							ETH_ALEN) == 0) {
34 			if (status == WLAN_STATUS_SUCCESS) {
35 				wdev->auth_bsses[i] = wdev->authtry_bsses[i];
36 			} else {
37 				cfg80211_unhold_bss(wdev->authtry_bsses[i]);
38 				cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
39 			}
40 			wdev->authtry_bsses[i] = NULL;
41 			done = true;
42 			break;
43 		}
44 	}
45 
46 	WARN_ON(!done);
47 
48 	nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
49 	cfg80211_sme_rx_auth(dev, buf, len);
50 
51 	wdev_unlock(wdev);
52 }
53 EXPORT_SYMBOL(cfg80211_send_rx_auth);
54 
55 void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
56 {
57 	u16 status_code;
58 	struct wireless_dev *wdev = dev->ieee80211_ptr;
59 	struct wiphy *wiphy = wdev->wiphy;
60 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
61 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
62 	u8 *ie = mgmt->u.assoc_resp.variable;
63 	int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
64 	struct cfg80211_internal_bss *bss = NULL;
65 
66 	wdev_lock(wdev);
67 
68 	status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
69 
70 	/*
71 	 * This is a bit of a hack, we don't notify userspace of
72 	 * a (re-)association reply if we tried to send a reassoc
73 	 * and got a reject -- we only try again with an assoc
74 	 * frame instead of reassoc.
75 	 */
76 	if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
77 	    cfg80211_sme_failed_reassoc(wdev))
78 		goto out;
79 
80 	nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
81 
82 	if (status_code == WLAN_STATUS_SUCCESS) {
83 		for (i = 0; i < MAX_AUTH_BSSES; i++) {
84 			if (!wdev->auth_bsses[i])
85 				continue;
86 			if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid,
87 				   ETH_ALEN) == 0) {
88 				bss = wdev->auth_bsses[i];
89 				wdev->auth_bsses[i] = NULL;
90 				/* additional reference to drop hold */
91 				cfg80211_ref_bss(bss);
92 				break;
93 			}
94 		}
95 
96 		WARN_ON(!bss);
97 	} else if (wdev->conn) {
98 		cfg80211_sme_failed_assoc(wdev);
99 		/*
100 		 * do not call connect_result() now because the
101 		 * sme will schedule work that does it later.
102 		 */
103 		goto out;
104 	}
105 
106 	if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
107 		/*
108 		 * This is for the userspace SME, the CONNECTING
109 		 * state will be changed to CONNECTED by
110 		 * __cfg80211_connect_result() below.
111 		 */
112 		wdev->sme_state = CFG80211_SME_CONNECTING;
113 	}
114 
115 	/* this consumes one bss reference (unless bss is NULL) */
116 	__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
117 				  status_code,
118 				  status_code == WLAN_STATUS_SUCCESS,
119 				  bss ? &bss->pub : NULL);
120 	/* drop hold now, and also reference acquired above */
121 	if (bss) {
122 		cfg80211_unhold_bss(bss);
123 		cfg80211_put_bss(&bss->pub);
124 	}
125 
126  out:
127 	wdev_unlock(wdev);
128 }
129 EXPORT_SYMBOL(cfg80211_send_rx_assoc);
130 
131 void __cfg80211_send_deauth(struct net_device *dev,
132 				   const u8 *buf, size_t len)
133 {
134 	struct wireless_dev *wdev = dev->ieee80211_ptr;
135 	struct wiphy *wiphy = wdev->wiphy;
136 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
137 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
138 	const u8 *bssid = mgmt->bssid;
139 	int i;
140 
141 	ASSERT_WDEV_LOCK(wdev);
142 
143 	nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
144 
145 	if (wdev->current_bss &&
146 	    memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
147 		cfg80211_unhold_bss(wdev->current_bss);
148 		cfg80211_put_bss(&wdev->current_bss->pub);
149 		wdev->current_bss = NULL;
150 	} else for (i = 0; i < MAX_AUTH_BSSES; i++) {
151 		if (wdev->auth_bsses[i] &&
152 		    memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
153 			cfg80211_unhold_bss(wdev->auth_bsses[i]);
154 			cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
155 			wdev->auth_bsses[i] = NULL;
156 			break;
157 		}
158 		if (wdev->authtry_bsses[i] &&
159 		    memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
160 			cfg80211_unhold_bss(wdev->authtry_bsses[i]);
161 			cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
162 			wdev->authtry_bsses[i] = NULL;
163 			break;
164 		}
165 	}
166 
167 	if (wdev->sme_state == CFG80211_SME_CONNECTED) {
168 		u16 reason_code;
169 		bool from_ap;
170 
171 		reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
172 
173 		from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
174 		__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
175 	} else if (wdev->sme_state == CFG80211_SME_CONNECTING) {
176 		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
177 					  WLAN_STATUS_UNSPECIFIED_FAILURE,
178 					  false, NULL);
179 	}
180 }
181 EXPORT_SYMBOL(__cfg80211_send_deauth);
182 
183 void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
184 {
185 	struct wireless_dev *wdev = dev->ieee80211_ptr;
186 
187 	wdev_lock(wdev);
188 	__cfg80211_send_deauth(dev, buf, len);
189 	wdev_unlock(wdev);
190 }
191 EXPORT_SYMBOL(cfg80211_send_deauth);
192 
193 void __cfg80211_send_disassoc(struct net_device *dev,
194 				     const u8 *buf, size_t len)
195 {
196 	struct wireless_dev *wdev = dev->ieee80211_ptr;
197 	struct wiphy *wiphy = wdev->wiphy;
198 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
199 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
200 	const u8 *bssid = mgmt->bssid;
201 	int i;
202 	u16 reason_code;
203 	bool from_ap;
204 	bool done = false;
205 
206 	ASSERT_WDEV_LOCK(wdev);
207 
208 	nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
209 
210 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
211 		return;
212 
213 	if (wdev->current_bss &&
214 	    memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
215 		for (i = 0; i < MAX_AUTH_BSSES; i++) {
216 			if (wdev->authtry_bsses[i] || wdev->auth_bsses[i])
217 				continue;
218 			wdev->auth_bsses[i] = wdev->current_bss;
219 			wdev->current_bss = NULL;
220 			done = true;
221 			cfg80211_sme_disassoc(dev, i);
222 			break;
223 		}
224 		WARN_ON(!done);
225 	} else
226 		WARN_ON(1);
227 
228 
229 	reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
230 
231 	from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0;
232 	__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
233 }
234 EXPORT_SYMBOL(__cfg80211_send_disassoc);
235 
236 void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
237 {
238 	struct wireless_dev *wdev = dev->ieee80211_ptr;
239 
240 	wdev_lock(wdev);
241 	__cfg80211_send_disassoc(dev, buf, len);
242 	wdev_unlock(wdev);
243 }
244 EXPORT_SYMBOL(cfg80211_send_disassoc);
245 
246 static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr)
247 {
248 	int i;
249 	bool done = false;
250 
251 	ASSERT_WDEV_LOCK(wdev);
252 
253 	for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
254 		if (wdev->authtry_bsses[i] &&
255 		    memcmp(wdev->authtry_bsses[i]->pub.bssid,
256 			   addr, ETH_ALEN) == 0) {
257 			cfg80211_unhold_bss(wdev->authtry_bsses[i]);
258 			cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
259 			wdev->authtry_bsses[i] = NULL;
260 			done = true;
261 			break;
262 		}
263 	}
264 
265 	WARN_ON(!done);
266 }
267 
268 void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr)
269 {
270 	__cfg80211_auth_remove(dev->ieee80211_ptr, addr);
271 }
272 EXPORT_SYMBOL(__cfg80211_auth_canceled);
273 
274 void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
275 {
276 	struct wireless_dev *wdev = dev->ieee80211_ptr;
277 	struct wiphy *wiphy = wdev->wiphy;
278 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
279 
280 	wdev_lock(wdev);
281 
282 	nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
283 	if (wdev->sme_state == CFG80211_SME_CONNECTING)
284 		__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
285 					  WLAN_STATUS_UNSPECIFIED_FAILURE,
286 					  false, NULL);
287 
288 	__cfg80211_auth_remove(wdev, addr);
289 
290 	wdev_unlock(wdev);
291 }
292 EXPORT_SYMBOL(cfg80211_send_auth_timeout);
293 
294 void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
295 {
296 	struct wireless_dev *wdev = dev->ieee80211_ptr;
297 	struct wiphy *wiphy = wdev->wiphy;
298 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
299 	int i;
300 	bool done = false;
301 
302 	wdev_lock(wdev);
303 
304 	nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
305 	if (wdev->sme_state == CFG80211_SME_CONNECTING)
306 		__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
307 					  WLAN_STATUS_UNSPECIFIED_FAILURE,
308 					  false, NULL);
309 
310 	for (i = 0; addr && i < MAX_AUTH_BSSES; i++) {
311 		if (wdev->auth_bsses[i] &&
312 		    memcmp(wdev->auth_bsses[i]->pub.bssid,
313 			   addr, ETH_ALEN) == 0) {
314 			cfg80211_unhold_bss(wdev->auth_bsses[i]);
315 			cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
316 			wdev->auth_bsses[i] = NULL;
317 			done = true;
318 			break;
319 		}
320 	}
321 
322 	WARN_ON(!done);
323 
324 	wdev_unlock(wdev);
325 }
326 EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
327 
328 void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
329 				  enum nl80211_key_type key_type, int key_id,
330 				  const u8 *tsc, gfp_t gfp)
331 {
332 	struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
333 	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
334 #ifdef CONFIG_CFG80211_WEXT
335 	union iwreq_data wrqu;
336 	char *buf = kmalloc(128, gfp);
337 
338 	if (buf) {
339 		sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
340 			"keyid=%d %scast addr=%pM)", key_id,
341 			key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
342 			addr);
343 		memset(&wrqu, 0, sizeof(wrqu));
344 		wrqu.data.length = strlen(buf);
345 		wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
346 		kfree(buf);
347 	}
348 #endif
349 
350 	nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
351 }
352 EXPORT_SYMBOL(cfg80211_michael_mic_failure);
353 
354 /* some MLME handling for userspace SME */
355 int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
356 			 struct net_device *dev,
357 			 struct ieee80211_channel *chan,
358 			 enum nl80211_auth_type auth_type,
359 			 const u8 *bssid,
360 			 const u8 *ssid, int ssid_len,
361 			 const u8 *ie, int ie_len,
362 			 const u8 *key, int key_len, int key_idx)
363 {
364 	struct wireless_dev *wdev = dev->ieee80211_ptr;
365 	struct cfg80211_auth_request req;
366 	struct cfg80211_internal_bss *bss;
367 	int i, err, slot = -1, nfree = 0;
368 
369 	ASSERT_WDEV_LOCK(wdev);
370 
371 	if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
372 		if (!key || !key_len || key_idx < 0 || key_idx > 4)
373 			return -EINVAL;
374 
375 	if (wdev->current_bss &&
376 	    memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0)
377 		return -EALREADY;
378 
379 	for (i = 0; i < MAX_AUTH_BSSES; i++) {
380 		if (wdev->authtry_bsses[i] &&
381 		    memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid,
382 						ETH_ALEN) == 0)
383 			return -EALREADY;
384 		if (wdev->auth_bsses[i] &&
385 		    memcmp(bssid, wdev->auth_bsses[i]->pub.bssid,
386 						ETH_ALEN) == 0)
387 			return -EALREADY;
388 	}
389 
390 	memset(&req, 0, sizeof(req));
391 
392 	req.ie = ie;
393 	req.ie_len = ie_len;
394 	req.auth_type = auth_type;
395 	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
396 				   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
397 	req.key = key;
398 	req.key_len = key_len;
399 	req.key_idx = key_idx;
400 	if (!req.bss)
401 		return -ENOENT;
402 
403 	bss = bss_from_pub(req.bss);
404 
405 	for (i = 0; i < MAX_AUTH_BSSES; i++) {
406 		if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) {
407 			slot = i;
408 			nfree++;
409 		}
410 	}
411 
412 	/* we need one free slot for disassoc and one for this auth */
413 	if (nfree < 2) {
414 		err = -ENOSPC;
415 		goto out;
416 	}
417 
418 	wdev->authtry_bsses[slot] = bss;
419 	cfg80211_hold_bss(bss);
420 
421 	err = rdev->ops->auth(&rdev->wiphy, dev, &req);
422 	if (err) {
423 		wdev->authtry_bsses[slot] = NULL;
424 		cfg80211_unhold_bss(bss);
425 	}
426 
427  out:
428 	if (err)
429 		cfg80211_put_bss(req.bss);
430 	return err;
431 }
432 
433 int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
434 		       struct net_device *dev, struct ieee80211_channel *chan,
435 		       enum nl80211_auth_type auth_type, const u8 *bssid,
436 		       const u8 *ssid, int ssid_len,
437 		       const u8 *ie, int ie_len,
438 		       const u8 *key, int key_len, int key_idx)
439 {
440 	int err;
441 
442 	wdev_lock(dev->ieee80211_ptr);
443 	err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
444 				   ssid, ssid_len, ie, ie_len,
445 				   key, key_len, key_idx);
446 	wdev_unlock(dev->ieee80211_ptr);
447 
448 	return err;
449 }
450 
451 int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
452 			  struct net_device *dev,
453 			  struct ieee80211_channel *chan,
454 			  const u8 *bssid, const u8 *prev_bssid,
455 			  const u8 *ssid, int ssid_len,
456 			  const u8 *ie, int ie_len, bool use_mfp,
457 			  struct cfg80211_crypto_settings *crypt)
458 {
459 	struct wireless_dev *wdev = dev->ieee80211_ptr;
460 	struct cfg80211_assoc_request req;
461 	struct cfg80211_internal_bss *bss;
462 	int i, err, slot = -1;
463 	bool was_connected = false;
464 
465 	ASSERT_WDEV_LOCK(wdev);
466 
467 	memset(&req, 0, sizeof(req));
468 
469 	if (wdev->current_bss && prev_bssid &&
470 	    memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) {
471 		/*
472 		 * Trying to reassociate: Allow this to proceed and let the old
473 		 * association to be dropped when the new one is completed.
474 		 */
475 		if (wdev->sme_state == CFG80211_SME_CONNECTED) {
476 			was_connected = true;
477 			wdev->sme_state = CFG80211_SME_CONNECTING;
478 		}
479 	} else if (wdev->current_bss)
480 		return -EALREADY;
481 
482 	req.ie = ie;
483 	req.ie_len = ie_len;
484 	memcpy(&req.crypto, crypt, sizeof(req.crypto));
485 	req.use_mfp = use_mfp;
486 	req.prev_bssid = prev_bssid;
487 	req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
488 				   WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
489 	if (!req.bss) {
490 		if (was_connected)
491 			wdev->sme_state = CFG80211_SME_CONNECTED;
492 		return -ENOENT;
493 	}
494 
495 	bss = bss_from_pub(req.bss);
496 
497 	for (i = 0; i < MAX_AUTH_BSSES; i++) {
498 		if (bss == wdev->auth_bsses[i]) {
499 			slot = i;
500 			break;
501 		}
502 	}
503 
504 	if (slot < 0) {
505 		err = -ENOTCONN;
506 		goto out;
507 	}
508 
509 	err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
510  out:
511 	if (err && was_connected)
512 		wdev->sme_state = CFG80211_SME_CONNECTED;
513 	/* still a reference in wdev->auth_bsses[slot] */
514 	cfg80211_put_bss(req.bss);
515 	return err;
516 }
517 
518 int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
519 			struct net_device *dev,
520 			struct ieee80211_channel *chan,
521 			const u8 *bssid, const u8 *prev_bssid,
522 			const u8 *ssid, int ssid_len,
523 			const u8 *ie, int ie_len, bool use_mfp,
524 			struct cfg80211_crypto_settings *crypt)
525 {
526 	struct wireless_dev *wdev = dev->ieee80211_ptr;
527 	int err;
528 
529 	wdev_lock(wdev);
530 	err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
531 				    ssid, ssid_len, ie, ie_len, use_mfp, crypt);
532 	wdev_unlock(wdev);
533 
534 	return err;
535 }
536 
537 int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
538 			   struct net_device *dev, const u8 *bssid,
539 			   const u8 *ie, int ie_len, u16 reason)
540 {
541 	struct wireless_dev *wdev = dev->ieee80211_ptr;
542 	struct cfg80211_deauth_request req;
543 	int i;
544 
545 	ASSERT_WDEV_LOCK(wdev);
546 
547 	memset(&req, 0, sizeof(req));
548 	req.reason_code = reason;
549 	req.ie = ie;
550 	req.ie_len = ie_len;
551 	if (wdev->current_bss &&
552 	    memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
553 		req.bss = &wdev->current_bss->pub;
554 	} else for (i = 0; i < MAX_AUTH_BSSES; i++) {
555 		if (wdev->auth_bsses[i] &&
556 		    memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
557 			req.bss = &wdev->auth_bsses[i]->pub;
558 			break;
559 		}
560 		if (wdev->authtry_bsses[i] &&
561 		    memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) {
562 			req.bss = &wdev->authtry_bsses[i]->pub;
563 			break;
564 		}
565 	}
566 
567 	if (!req.bss)
568 		return -ENOTCONN;
569 
570 	return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
571 }
572 
573 int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
574 			 struct net_device *dev, const u8 *bssid,
575 			 const u8 *ie, int ie_len, u16 reason)
576 {
577 	struct wireless_dev *wdev = dev->ieee80211_ptr;
578 	int err;
579 
580 	wdev_lock(wdev);
581 	err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason);
582 	wdev_unlock(wdev);
583 
584 	return err;
585 }
586 
587 static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
588 				    struct net_device *dev, const u8 *bssid,
589 				    const u8 *ie, int ie_len, u16 reason)
590 {
591 	struct wireless_dev *wdev = dev->ieee80211_ptr;
592 	struct cfg80211_disassoc_request req;
593 
594 	ASSERT_WDEV_LOCK(wdev);
595 
596 	if (wdev->sme_state != CFG80211_SME_CONNECTED)
597 		return -ENOTCONN;
598 
599 	if (WARN_ON(!wdev->current_bss))
600 		return -ENOTCONN;
601 
602 	memset(&req, 0, sizeof(req));
603 	req.reason_code = reason;
604 	req.ie = ie;
605 	req.ie_len = ie_len;
606 	if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0)
607 		req.bss = &wdev->current_bss->pub;
608 	else
609 		return -ENOTCONN;
610 
611 	return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev);
612 }
613 
614 int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
615 			   struct net_device *dev, const u8 *bssid,
616 			   const u8 *ie, int ie_len, u16 reason)
617 {
618 	struct wireless_dev *wdev = dev->ieee80211_ptr;
619 	int err;
620 
621 	wdev_lock(wdev);
622 	err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason);
623 	wdev_unlock(wdev);
624 
625 	return err;
626 }
627 
628 void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
629 			struct net_device *dev)
630 {
631 	struct wireless_dev *wdev = dev->ieee80211_ptr;
632 	struct cfg80211_deauth_request req;
633 	int i;
634 
635 	ASSERT_WDEV_LOCK(wdev);
636 
637 	if (!rdev->ops->deauth)
638 		return;
639 
640 	memset(&req, 0, sizeof(req));
641 	req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
642 	req.ie = NULL;
643 	req.ie_len = 0;
644 
645 	if (wdev->current_bss) {
646 		req.bss = &wdev->current_bss->pub;
647 		rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
648 		if (wdev->current_bss) {
649 			cfg80211_unhold_bss(wdev->current_bss);
650 			cfg80211_put_bss(&wdev->current_bss->pub);
651 			wdev->current_bss = NULL;
652 		}
653 	}
654 
655 	for (i = 0; i < MAX_AUTH_BSSES; i++) {
656 		if (wdev->auth_bsses[i]) {
657 			req.bss = &wdev->auth_bsses[i]->pub;
658 			rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
659 			if (wdev->auth_bsses[i]) {
660 				cfg80211_unhold_bss(wdev->auth_bsses[i]);
661 				cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
662 				wdev->auth_bsses[i] = NULL;
663 			}
664 		}
665 		if (wdev->authtry_bsses[i]) {
666 			req.bss = &wdev->authtry_bsses[i]->pub;
667 			rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev);
668 			if (wdev->authtry_bsses[i]) {
669 				cfg80211_unhold_bss(wdev->authtry_bsses[i]);
670 				cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
671 				wdev->authtry_bsses[i] = NULL;
672 			}
673 		}
674 	}
675 }
676