blob: 878c867f3f7d40f7cdafb406d9e09d450b10bfbf [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Johannes Berg2a519312009-02-10 21:25:55 +01002/*
3 * cfg80211 scan result handling
4 *
5 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg2740f0c2014-09-03 15:24:58 +03006 * Copyright 2013-2014 Intel Mobile Communications GmbH
Avraham Stern1d762502016-07-05 17:10:13 +03007 * Copyright 2016 Intel Deutschland GmbH
Peng Xu0b8fb822019-01-21 12:14:57 +02008 * Copyright (C) 2018-2019 Intel Corporation
Johannes Berg2a519312009-02-10 21:25:55 +01009 */
10#include <linux/kernel.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090011#include <linux/slab.h>
Johannes Berg2a519312009-02-10 21:25:55 +010012#include <linux/module.h>
13#include <linux/netdevice.h>
14#include <linux/wireless.h>
15#include <linux/nl80211.h>
16#include <linux/etherdevice.h>
17#include <net/arp.h>
18#include <net/cfg80211.h>
Johannes Berg262eb9b22011-07-13 10:39:09 +020019#include <net/cfg80211-wext.h>
Johannes Berg2a519312009-02-10 21:25:55 +010020#include <net/iw_handler.h>
21#include "core.h"
22#include "nl80211.h"
Johannes Berga9a11622009-07-27 12:01:53 +020023#include "wext-compat.h"
Hila Gonene35e4d22012-06-27 17:19:42 +030024#include "rdev-ops.h"
Johannes Berg2a519312009-02-10 21:25:55 +010025
Johannes Berg776b3582013-02-01 02:06:18 +010026/**
27 * DOC: BSS tree/list structure
28 *
29 * At the top level, the BSS list is kept in both a list in each
30 * registered device (@bss_list) as well as an RB-tree for faster
31 * lookup. In the RB-tree, entries can be looked up using their
32 * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID
33 * for other BSSes.
34 *
35 * Due to the possibility of hidden SSIDs, there's a second level
36 * structure, the "hidden_list" and "hidden_beacon_bss" pointer.
37 * The hidden_list connects all BSSes belonging to a single AP
38 * that has a hidden SSID, and connects beacon and probe response
39 * entries. For a probe response entry for a hidden SSID, the
40 * hidden_beacon_bss pointer points to the BSS struct holding the
41 * beacon's information.
42 *
43 * Reference counting is done for all these references except for
44 * the hidden_list, so that a beacon BSS struct that is otherwise
45 * not referenced has one reference for being on the bss_list and
46 * one for each probe response entry that points to it using the
47 * hidden_beacon_bss pointer. When a BSS struct that has such a
48 * pointer is get/put, the refcount update is also propagated to
49 * the referenced struct, this ensure that it cannot get removed
50 * while somebody is using the probe response version.
51 *
52 * Note that the hidden_beacon_bss pointer never changes, due to
53 * the reference counting. Therefore, no locking is needed for
54 * it.
55 *
56 * Also note that the hidden_beacon_bss pointer is only relevant
57 * if the driver uses something other than the IEs, e.g. private
58 * data stored stored in the BSS struct, since the beacon IEs are
59 * also linked into the probe response struct.
60 */
61
Johannes Berg9853a552016-11-15 12:05:11 +010062/*
63 * Limit the number of BSS entries stored in mac80211. Each one is
64 * a bit over 4k at most, so this limits to roughly 4-5M of memory.
65 * If somebody wants to really attack this though, they'd likely
66 * use small beacons, and only one type of frame, limiting each of
67 * the entries to a much smaller size (in order to generate more
68 * entries in total, so overhead is bigger.)
69 */
70static int bss_entries_limit = 1000;
71module_param(bss_entries_limit, int, 0644);
72MODULE_PARM_DESC(bss_entries_limit,
73 "limit to number of scan BSS entries (per wiphy, default 1000)");
74
Rajkumar Manoharanf9616e02012-04-13 16:38:40 +053075#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
Johannes Berg2a519312009-02-10 21:25:55 +010076
Johannes Berg776b3582013-02-01 02:06:18 +010077static void bss_free(struct cfg80211_internal_bss *bss)
Amitkumar Karware8e27c62012-10-11 21:03:33 -070078{
Johannes Berg9caf0362012-11-29 01:25:20 +010079 struct cfg80211_bss_ies *ies;
Johannes Bergb629ea32012-11-28 22:14:56 +010080
81 if (WARN_ON(atomic_read(&bss->hold)))
82 return;
83
Johannes Berg9caf0362012-11-29 01:25:20 +010084 ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
Johannes Berg776b3582013-02-01 02:06:18 +010085 if (ies && !bss->pub.hidden_beacon_bss)
Johannes Berg9caf0362012-11-29 01:25:20 +010086 kfree_rcu(ies, rcu_head);
87 ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
88 if (ies)
89 kfree_rcu(ies, rcu_head);
Amitkumar Karware8e27c62012-10-11 21:03:33 -070090
Johannes Berg776b3582013-02-01 02:06:18 +010091 /*
92 * This happens when the module is removed, it doesn't
93 * really matter any more save for completeness
94 */
95 if (!list_empty(&bss->hidden_list))
96 list_del(&bss->hidden_list);
97
Amitkumar Karware8e27c62012-10-11 21:03:33 -070098 kfree(bss);
99}
100
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800101static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
Johannes Berg776b3582013-02-01 02:06:18 +0100102 struct cfg80211_internal_bss *bss)
Johannes Berg0532d4f2013-02-01 01:34:36 +0100103{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800104 lockdep_assert_held(&rdev->bss_lock);
Johannes Berg776b3582013-02-01 02:06:18 +0100105
106 bss->refcount++;
107 if (bss->pub.hidden_beacon_bss) {
108 bss = container_of(bss->pub.hidden_beacon_bss,
109 struct cfg80211_internal_bss,
110 pub);
111 bss->refcount++;
112 }
Sara Sharon7011ba52019-01-21 12:25:59 +0200113 if (bss->pub.transmitted_bss) {
114 bss = container_of(bss->pub.transmitted_bss,
Sara Sharona3584f56d2019-01-21 12:22:21 +0200115 struct cfg80211_internal_bss,
116 pub);
117 bss->refcount++;
118 }
Johannes Berg0532d4f2013-02-01 01:34:36 +0100119}
120
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800121static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
Johannes Berg776b3582013-02-01 02:06:18 +0100122 struct cfg80211_internal_bss *bss)
Johannes Berg0532d4f2013-02-01 01:34:36 +0100123{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800124 lockdep_assert_held(&rdev->bss_lock);
Johannes Berg776b3582013-02-01 02:06:18 +0100125
126 if (bss->pub.hidden_beacon_bss) {
127 struct cfg80211_internal_bss *hbss;
128 hbss = container_of(bss->pub.hidden_beacon_bss,
129 struct cfg80211_internal_bss,
130 pub);
131 hbss->refcount--;
132 if (hbss->refcount == 0)
133 bss_free(hbss);
134 }
Sara Sharona3584f56d2019-01-21 12:22:21 +0200135
Sara Sharon7011ba52019-01-21 12:25:59 +0200136 if (bss->pub.transmitted_bss) {
Sara Sharona3584f56d2019-01-21 12:22:21 +0200137 struct cfg80211_internal_bss *tbss;
138
Sara Sharon7011ba52019-01-21 12:25:59 +0200139 tbss = container_of(bss->pub.transmitted_bss,
Sara Sharona3584f56d2019-01-21 12:22:21 +0200140 struct cfg80211_internal_bss,
141 pub);
142 tbss->refcount--;
143 if (tbss->refcount == 0)
144 bss_free(tbss);
145 }
146
Johannes Berg776b3582013-02-01 02:06:18 +0100147 bss->refcount--;
148 if (bss->refcount == 0)
149 bss_free(bss);
Johannes Berg0532d4f2013-02-01 01:34:36 +0100150}
151
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800152static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
Amitkumar Karware8e27c62012-10-11 21:03:33 -0700153 struct cfg80211_internal_bss *bss)
154{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800155 lockdep_assert_held(&rdev->bss_lock);
Johannes Berg4b1af472013-02-01 01:05:43 +0100156
Johannes Berg776b3582013-02-01 02:06:18 +0100157 if (!list_empty(&bss->hidden_list)) {
158 /*
159 * don't remove the beacon entry if it has
160 * probe responses associated with it
161 */
162 if (!bss->pub.hidden_beacon_bss)
163 return false;
164 /*
165 * if it's a probe response entry break its
166 * link to the other entries in the group
167 */
168 list_del_init(&bss->hidden_list);
169 }
170
Amitkumar Karware8e27c62012-10-11 21:03:33 -0700171 list_del_init(&bss->list);
Sara Sharon7011ba52019-01-21 12:25:59 +0200172 list_del_init(&bss->pub.nontrans_list);
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800173 rb_erase(&bss->rbn, &rdev->bss_tree);
Johannes Berg9853a552016-11-15 12:05:11 +0100174 rdev->bss_entries--;
175 WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
176 "rdev bss entries[%d]/list[empty:%d] corruption\n",
177 rdev->bss_entries, list_empty(&rdev->bss_list));
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800178 bss_ref_put(rdev, bss);
Johannes Berg776b3582013-02-01 02:06:18 +0100179 return true;
Amitkumar Karware8e27c62012-10-11 21:03:33 -0700180}
181
Sara Sharonf7dacfb2019-03-15 17:39:03 +0200182bool cfg80211_is_element_inherited(const struct element *elem,
183 const struct element *non_inherit_elem)
184{
185 u8 id_len, ext_id_len, i, loop_len, id;
186 const u8 *list;
187
188 if (elem->id == WLAN_EID_MULTIPLE_BSSID)
189 return false;
190
191 if (!non_inherit_elem || non_inherit_elem->datalen < 2)
192 return true;
193
194 /*
195 * non inheritance element format is:
196 * ext ID (56) | IDs list len | list | extension IDs list len | list
197 * Both lists are optional. Both lengths are mandatory.
198 * This means valid length is:
199 * elem_len = 1 (extension ID) + 2 (list len fields) + list lengths
200 */
201 id_len = non_inherit_elem->data[1];
202 if (non_inherit_elem->datalen < 3 + id_len)
203 return true;
204
205 ext_id_len = non_inherit_elem->data[2 + id_len];
206 if (non_inherit_elem->datalen < 3 + id_len + ext_id_len)
207 return true;
208
209 if (elem->id == WLAN_EID_EXTENSION) {
210 if (!ext_id_len)
211 return true;
212 loop_len = ext_id_len;
213 list = &non_inherit_elem->data[3 + id_len];
214 id = elem->data[0];
215 } else {
216 if (!id_len)
217 return true;
218 loop_len = id_len;
219 list = &non_inherit_elem->data[2];
220 id = elem->id;
221 }
222
223 for (i = 0; i < loop_len; i++) {
224 if (list[i] == id)
225 return false;
226 }
227
228 return true;
229}
230EXPORT_SYMBOL(cfg80211_is_element_inherited);
231
Peng Xu0b8fb822019-01-21 12:14:57 +0200232static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
233 const u8 *subelement, size_t subie_len,
234 u8 *new_ie, gfp_t gfp)
235{
236 u8 *pos, *tmp;
237 const u8 *tmp_old, *tmp_new;
Sara Sharonf7dacfb2019-03-15 17:39:03 +0200238 const struct element *non_inherit_elem;
Peng Xu0b8fb822019-01-21 12:14:57 +0200239 u8 *sub_copy;
240
241 /* copy subelement as we need to change its content to
242 * mark an ie after it is processed.
243 */
244 sub_copy = kmalloc(subie_len, gfp);
245 if (!sub_copy)
246 return 0;
247 memcpy(sub_copy, subelement, subie_len);
248
249 pos = &new_ie[0];
250
251 /* set new ssid */
252 tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len);
253 if (tmp_new) {
254 memcpy(pos, tmp_new, tmp_new[1] + 2);
255 pos += (tmp_new[1] + 2);
256 }
257
Sara Sharonf7dacfb2019-03-15 17:39:03 +0200258 /* get non inheritance list if exists */
259 non_inherit_elem =
260 cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
261 sub_copy, subie_len);
262
Peng Xu0b8fb822019-01-21 12:14:57 +0200263 /* go through IEs in ie (skip SSID) and subelement,
264 * merge them into new_ie
265 */
266 tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
267 tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie;
268
269 while (tmp_old + tmp_old[1] + 2 - ie <= ielen) {
270 if (tmp_old[0] == 0) {
271 tmp_old++;
272 continue;
273 }
274
Sara Sharonc17fe042019-01-29 14:00:58 +0200275 if (tmp_old[0] == WLAN_EID_EXTENSION)
276 tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy,
277 subie_len);
278 else
279 tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy,
280 subie_len);
281
Peng Xu0b8fb822019-01-21 12:14:57 +0200282 if (!tmp) {
Sara Sharonf7dacfb2019-03-15 17:39:03 +0200283 const struct element *old_elem = (void *)tmp_old;
284
Peng Xu0b8fb822019-01-21 12:14:57 +0200285 /* ie in old ie but not in subelement */
Sara Sharonf7dacfb2019-03-15 17:39:03 +0200286 if (cfg80211_is_element_inherited(old_elem,
287 non_inherit_elem)) {
Peng Xu0b8fb822019-01-21 12:14:57 +0200288 memcpy(pos, tmp_old, tmp_old[1] + 2);
289 pos += tmp_old[1] + 2;
290 }
291 } else {
292 /* ie in transmitting ie also in subelement,
293 * copy from subelement and flag the ie in subelement
Sara Sharonc17fe042019-01-29 14:00:58 +0200294 * as copied (by setting eid field to WLAN_EID_SSID,
295 * which is skipped anyway).
296 * For vendor ie, compare OUI + type + subType to
Peng Xu0b8fb822019-01-21 12:14:57 +0200297 * determine if they are the same ie.
298 */
299 if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) {
300 if (!memcmp(tmp_old + 2, tmp + 2, 5)) {
301 /* same vendor ie, copy from
302 * subelement
303 */
304 memcpy(pos, tmp, tmp[1] + 2);
305 pos += tmp[1] + 2;
Sara Sharonc17fe042019-01-29 14:00:58 +0200306 tmp[0] = WLAN_EID_SSID;
Peng Xu0b8fb822019-01-21 12:14:57 +0200307 } else {
308 memcpy(pos, tmp_old, tmp_old[1] + 2);
309 pos += tmp_old[1] + 2;
310 }
311 } else {
312 /* copy ie from subelement into new ie */
313 memcpy(pos, tmp, tmp[1] + 2);
314 pos += tmp[1] + 2;
Sara Sharonc17fe042019-01-29 14:00:58 +0200315 tmp[0] = WLAN_EID_SSID;
Peng Xu0b8fb822019-01-21 12:14:57 +0200316 }
317 }
318
319 if (tmp_old + tmp_old[1] + 2 - ie == ielen)
320 break;
321
322 tmp_old += tmp_old[1] + 2;
323 }
324
325 /* go through subelement again to check if there is any ie not
326 * copied to new ie, skip ssid, capability, bssid-index ie
327 */
328 tmp_new = sub_copy;
329 while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
330 if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP ||
Sara Sharon5bd9d102019-03-15 17:39:02 +0200331 tmp_new[0] == WLAN_EID_SSID)) {
Peng Xu0b8fb822019-01-21 12:14:57 +0200332 memcpy(pos, tmp_new, tmp_new[1] + 2);
333 pos += tmp_new[1] + 2;
334 }
335 if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len)
336 break;
337 tmp_new += tmp_new[1] + 2;
338 }
339
340 kfree(sub_copy);
341 return pos - new_ie;
342}
343
344static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
345 const u8 *ssid, size_t ssid_len)
346{
347 const struct cfg80211_bss_ies *ies;
348 const u8 *ssidie;
349
350 if (bssid && !ether_addr_equal(a->bssid, bssid))
351 return false;
352
353 if (!ssid)
354 return true;
355
356 ies = rcu_access_pointer(a->ies);
357 if (!ies)
358 return false;
359 ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
360 if (!ssidie)
361 return false;
362 if (ssidie[1] != ssid_len)
363 return false;
364 return memcmp(ssidie + 2, ssid, ssid_len) == 0;
365}
366
367static int
Sara Sharon7011ba52019-01-21 12:25:59 +0200368cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss,
369 struct cfg80211_bss *nontrans_bss)
Peng Xu0b8fb822019-01-21 12:14:57 +0200370{
371 const u8 *ssid;
372 size_t ssid_len;
Sara Sharon7011ba52019-01-21 12:25:59 +0200373 struct cfg80211_bss *bss = NULL;
Peng Xu0b8fb822019-01-21 12:14:57 +0200374
375 rcu_read_lock();
Sara Sharon7011ba52019-01-21 12:25:59 +0200376 ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID);
Peng Xu0b8fb822019-01-21 12:14:57 +0200377 if (!ssid) {
378 rcu_read_unlock();
379 return -EINVAL;
380 }
381 ssid_len = ssid[1];
382 ssid = ssid + 2;
383 rcu_read_unlock();
384
385 /* check if nontrans_bss is in the list */
386 list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) {
Sara Sharon7011ba52019-01-21 12:25:59 +0200387 if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len))
Peng Xu0b8fb822019-01-21 12:14:57 +0200388 return 0;
389 }
390
391 /* add to the list */
392 list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
393 return 0;
394}
395
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800396static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
Sam Leffler15d60302012-10-11 21:03:34 -0700397 unsigned long expire_time)
398{
399 struct cfg80211_internal_bss *bss, *tmp;
400 bool expired = false;
401
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800402 lockdep_assert_held(&rdev->bss_lock);
Johannes Berg4b1af472013-02-01 01:05:43 +0100403
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800404 list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {
Sam Leffler15d60302012-10-11 21:03:34 -0700405 if (atomic_read(&bss->hold))
406 continue;
407 if (!time_after(expire_time, bss->ts))
408 continue;
409
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800410 if (__cfg80211_unlink_bss(rdev, bss))
Johannes Berg776b3582013-02-01 02:06:18 +0100411 expired = true;
Sam Leffler15d60302012-10-11 21:03:34 -0700412 }
413
414 if (expired)
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800415 rdev->bss_generation++;
Sam Leffler15d60302012-10-11 21:03:34 -0700416}
417
Johannes Berg9853a552016-11-15 12:05:11 +0100418static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev)
419{
420 struct cfg80211_internal_bss *bss, *oldest = NULL;
421 bool ret;
422
423 lockdep_assert_held(&rdev->bss_lock);
424
425 list_for_each_entry(bss, &rdev->bss_list, list) {
426 if (atomic_read(&bss->hold))
427 continue;
428
429 if (!list_empty(&bss->hidden_list) &&
430 !bss->pub.hidden_beacon_bss)
431 continue;
432
433 if (oldest && time_before(oldest->ts, bss->ts))
434 continue;
435 oldest = bss;
436 }
437
438 if (WARN_ON(!oldest))
439 return false;
440
441 /*
442 * The callers make sure to increase rdev->bss_generation if anything
443 * gets removed (and a new entry added), so there's no need to also do
444 * it here.
445 */
446
447 ret = __cfg80211_unlink_bss(rdev, oldest);
448 WARN_ON(!ret);
449 return ret;
450}
451
Johannes Bergf9d15d12014-01-22 11:14:19 +0200452void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
453 bool send_message)
Johannes Berg2a519312009-02-10 21:25:55 +0100454{
Johannes Berg667503d2009-07-07 03:56:11 +0200455 struct cfg80211_scan_request *request;
Johannes Bergfd014282012-06-18 19:17:03 +0200456 struct wireless_dev *wdev;
Johannes Bergf9d15d12014-01-22 11:14:19 +0200457 struct sk_buff *msg;
Johannes Berg3d23e342009-09-29 23:27:28 +0200458#ifdef CONFIG_CFG80211_WEXT
Johannes Berg2a519312009-02-10 21:25:55 +0100459 union iwreq_data wrqu;
460#endif
461
Johannes Berg5fe231e2013-05-08 21:45:15 +0200462 ASSERT_RTNL();
Johannes Berg01a0ac42009-08-20 21:36:16 +0200463
Johannes Bergf9d15d12014-01-22 11:14:19 +0200464 if (rdev->scan_msg) {
Arend Van Spriel505a2e82016-12-16 11:21:54 +0000465 nl80211_send_scan_msg(rdev, rdev->scan_msg);
Johannes Bergf9d15d12014-01-22 11:14:19 +0200466 rdev->scan_msg = NULL;
467 return;
468 }
Johannes Berg667503d2009-07-07 03:56:11 +0200469
Johannes Bergf9d15d12014-01-22 11:14:19 +0200470 request = rdev->scan_req;
Johannes Berg01a0ac42009-08-20 21:36:16 +0200471 if (!request)
472 return;
473
Johannes Bergfd014282012-06-18 19:17:03 +0200474 wdev = request->wdev;
Johannes Berg2a519312009-02-10 21:25:55 +0100475
Johannes Berg6829c872009-07-02 09:13:27 +0200476 /*
477 * This must be before sending the other events!
478 * Otherwise, wpa_supplicant gets completely confused with
479 * wext events.
480 */
Johannes Bergfd014282012-06-18 19:17:03 +0200481 if (wdev->netdev)
482 cfg80211_sme_scan_done(wdev->netdev);
Johannes Berg6829c872009-07-02 09:13:27 +0200483
Avraham Stern1d762502016-07-05 17:10:13 +0300484 if (!request->info.aborted &&
Johannes Bergf9d15d12014-01-22 11:14:19 +0200485 request->flags & NL80211_SCAN_FLAG_FLUSH) {
486 /* flush entries from previous scans */
487 spin_lock_bh(&rdev->bss_lock);
488 __cfg80211_bss_expire(rdev, request->scan_start);
489 spin_unlock_bh(&rdev->bss_lock);
Sam Leffler15d60302012-10-11 21:03:34 -0700490 }
Johannes Berg2a519312009-02-10 21:25:55 +0100491
Avraham Stern1d762502016-07-05 17:10:13 +0300492 msg = nl80211_build_scan_msg(rdev, wdev, request->info.aborted);
Johannes Bergf9d15d12014-01-22 11:14:19 +0200493
Johannes Berg3d23e342009-09-29 23:27:28 +0200494#ifdef CONFIG_CFG80211_WEXT
Avraham Stern1d762502016-07-05 17:10:13 +0300495 if (wdev->netdev && !request->info.aborted) {
Johannes Berg2a519312009-02-10 21:25:55 +0100496 memset(&wrqu, 0, sizeof(wrqu));
497
Johannes Bergfd014282012-06-18 19:17:03 +0200498 wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL);
Johannes Berg2a519312009-02-10 21:25:55 +0100499 }
500#endif
501
Johannes Bergfd014282012-06-18 19:17:03 +0200502 if (wdev->netdev)
503 dev_put(wdev->netdev);
Johannes Berg2a519312009-02-10 21:25:55 +0100504
Johannes Berg36e6fea2009-08-12 22:21:21 +0200505 rdev->scan_req = NULL;
Eliad Peller4a58e7c2013-12-05 18:30:17 +0200506 kfree(request);
Johannes Bergf9d15d12014-01-22 11:14:19 +0200507
508 if (!send_message)
509 rdev->scan_msg = msg;
510 else
Arend Van Spriel505a2e82016-12-16 11:21:54 +0000511 nl80211_send_scan_msg(rdev, msg);
Johannes Berg2a519312009-02-10 21:25:55 +0100512}
Johannes Berg667503d2009-07-07 03:56:11 +0200513
Johannes Berg36e6fea2009-08-12 22:21:21 +0200514void __cfg80211_scan_done(struct work_struct *wk)
515{
516 struct cfg80211_registered_device *rdev;
517
518 rdev = container_of(wk, struct cfg80211_registered_device,
519 scan_done_wk);
520
Johannes Berg5fe231e2013-05-08 21:45:15 +0200521 rtnl_lock();
Johannes Bergf9d15d12014-01-22 11:14:19 +0200522 ___cfg80211_scan_done(rdev, true);
Johannes Berg5fe231e2013-05-08 21:45:15 +0200523 rtnl_unlock();
Johannes Berg36e6fea2009-08-12 22:21:21 +0200524}
525
Avraham Stern1d762502016-07-05 17:10:13 +0300526void cfg80211_scan_done(struct cfg80211_scan_request *request,
527 struct cfg80211_scan_info *info)
Johannes Berg667503d2009-07-07 03:56:11 +0200528{
Avraham Stern1d762502016-07-05 17:10:13 +0300529 trace_cfg80211_scan_done(request, info);
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800530 WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);
Johannes Berg667503d2009-07-07 03:56:11 +0200531
Avraham Stern1d762502016-07-05 17:10:13 +0300532 request->info = *info;
Johannes Berg5fe231e2013-05-08 21:45:15 +0200533 request->notified = true;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800534 queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
Johannes Berg667503d2009-07-07 03:56:11 +0200535}
Johannes Berg2a519312009-02-10 21:25:55 +0100536EXPORT_SYMBOL(cfg80211_scan_done);
537
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100538void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
539 struct cfg80211_sched_scan_request *req)
540{
541 ASSERT_RTNL();
542
543 list_add_rcu(&req->list, &rdev->sched_scan_req_list);
544}
545
546static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
547 struct cfg80211_sched_scan_request *req)
548{
549 ASSERT_RTNL();
550
551 list_del_rcu(&req->list);
552 kfree_rcu(req, rcu_head);
553}
554
555static struct cfg80211_sched_scan_request *
556cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
557{
558 struct cfg80211_sched_scan_request *pos;
559
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100560 WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held());
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100561
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100562 list_for_each_entry_rcu(pos, &rdev->sched_scan_req_list, list) {
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100563 if (pos->reqid == reqid)
564 return pos;
565 }
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100566 return NULL;
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100567}
568
569/*
570 * Determines if a scheduled scan request can be handled. When a legacy
571 * scheduled scan is running no other scheduled scan is allowed regardless
572 * whether the request is for legacy or multi-support scan. When a multi-support
573 * scheduled scan is running a request for legacy scan is not allowed. In this
574 * case a request for multi-support scan can be handled if resources are
575 * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
576 */
577int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
578 bool want_multi)
579{
580 struct cfg80211_sched_scan_request *pos;
581 int i = 0;
582
583 list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
584 /* request id zero means legacy in progress */
585 if (!i && !pos->reqid)
586 return -EINPROGRESS;
587 i++;
588 }
589
590 if (i) {
591 /* no legacy allowed when multi request(s) are active */
592 if (!want_multi)
593 return -EINPROGRESS;
594
595 /* resource limit reached */
596 if (i == rdev->wiphy.max_sched_scan_reqs)
597 return -ENOSPC;
598 }
599 return 0;
600}
601
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100602void cfg80211_sched_scan_results_wk(struct work_struct *work)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300603{
604 struct cfg80211_registered_device *rdev;
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100605 struct cfg80211_sched_scan_request *req, *tmp;
Luciano Coelho807f8a82011-05-11 17:09:35 +0300606
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100607 rdev = container_of(work, struct cfg80211_registered_device,
608 sched_scan_res_wk);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300609
Johannes Berg5fe231e2013-05-08 21:45:15 +0200610 rtnl_lock();
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100611 list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
612 if (req->report_results) {
613 req->report_results = false;
614 if (req->flags & NL80211_SCAN_FLAG_FLUSH) {
615 /* flush entries from previous scans */
616 spin_lock_bh(&rdev->bss_lock);
617 __cfg80211_bss_expire(rdev, req->scan_start);
618 spin_unlock_bh(&rdev->bss_lock);
619 req->scan_start = jiffies;
620 }
621 nl80211_send_sched_scan(req,
622 NL80211_CMD_SCHED_SCAN_RESULTS);
Sam Leffler15d60302012-10-11 21:03:34 -0700623 }
Sam Leffler15d60302012-10-11 21:03:34 -0700624 }
Johannes Berg5fe231e2013-05-08 21:45:15 +0200625 rtnl_unlock();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300626}
627
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100628void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300629{
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100630 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
631 struct cfg80211_sched_scan_request *request;
632
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100633 trace_cfg80211_sched_scan_results(wiphy, reqid);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300634 /* ignore if we're not scanning */
Jukka Rissanen31a60ed2014-12-15 13:25:38 +0200635
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100636 rcu_read_lock();
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100637 request = cfg80211_find_sched_scan_req(rdev, reqid);
638 if (request) {
639 request->report_results = true;
640 queue_work(cfg80211_wq, &rdev->sched_scan_res_wk);
641 }
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100642 rcu_read_unlock();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300643}
644EXPORT_SYMBOL(cfg80211_sched_scan_results);
645
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100646void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300647{
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800648 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300649
Eliad Peller792e6aa2014-04-30 16:14:23 +0300650 ASSERT_RTNL();
651
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100652 trace_cfg80211_sched_scan_stopped(wiphy, reqid);
Beni Lev4ee3e062012-08-27 12:49:39 +0300653
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100654 __cfg80211_stop_sched_scan(rdev, reqid, true);
Eliad Peller792e6aa2014-04-30 16:14:23 +0300655}
656EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl);
657
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100658void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid)
Eliad Peller792e6aa2014-04-30 16:14:23 +0300659{
660 rtnl_lock();
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100661 cfg80211_sched_scan_stopped_rtnl(wiphy, reqid);
Johannes Berg5fe231e2013-05-08 21:45:15 +0200662 rtnl_unlock();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300663}
Luciano Coelho807f8a82011-05-11 17:09:35 +0300664EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
665
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100666int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
667 struct cfg80211_sched_scan_request *req,
668 bool driver_initiated)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300669{
Johannes Berg5fe231e2013-05-08 21:45:15 +0200670 ASSERT_RTNL();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300671
Luciano Coelho85a99942011-05-12 16:28:29 +0300672 if (!driver_initiated) {
Arend Van Spriel3a3ecf12017-04-21 13:05:02 +0100673 int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid);
Luciano Coelho85a99942011-05-12 16:28:29 +0300674 if (err)
675 return err;
676 }
Luciano Coelho807f8a82011-05-11 17:09:35 +0300677
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100678 nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300679
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100680 cfg80211_del_sched_scan_req(rdev, req);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300681
Jesper Juhl3b4670f2011-06-29 22:49:33 +0200682 return 0;
Luciano Coelho807f8a82011-05-11 17:09:35 +0300683}
684
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100685int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
686 u64 reqid, bool driver_initiated)
687{
688 struct cfg80211_sched_scan_request *sched_scan_req;
689
690 ASSERT_RTNL();
691
692 sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100693 if (!sched_scan_req)
694 return -ENOENT;
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100695
696 return cfg80211_stop_sched_scan_req(rdev, sched_scan_req,
697 driver_initiated);
698}
699
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800700void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500701 unsigned long age_secs)
702{
703 struct cfg80211_internal_bss *bss;
704 unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
705
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800706 spin_lock_bh(&rdev->bss_lock);
707 list_for_each_entry(bss, &rdev->bss_list, list)
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500708 bss->ts -= age_jiffies;
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800709 spin_unlock_bh(&rdev->bss_lock);
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500710}
711
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800712void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
Johannes Berg2a519312009-02-10 21:25:55 +0100713{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800714 __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
Johannes Berg2a519312009-02-10 21:25:55 +0100715}
716
Johannes Berg49a68e02019-02-07 23:26:38 +0100717const struct element *
718cfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len,
719 const u8 *match, unsigned int match_len,
720 unsigned int match_offset)
Johannes Berg2a519312009-02-10 21:25:55 +0100721{
Johannes Berg0f3b07f2019-02-07 21:44:41 +0100722 const struct element *elem;
723
Johannes Berg0f3b07f2019-02-07 21:44:41 +0100724 for_each_element_id(elem, eid, ies, len) {
Johannes Berg49a68e02019-02-07 23:26:38 +0100725 if (elem->datalen >= match_offset + match_len &&
726 !memcmp(elem->data + match_offset, match, match_len))
727 return elem;
Johannes Berg2a519312009-02-10 21:25:55 +0100728 }
Luca Coelhofbd05e42016-09-15 18:15:09 +0300729
730 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +0100731}
Johannes Berg49a68e02019-02-07 23:26:38 +0100732EXPORT_SYMBOL(cfg80211_find_elem_match);
Johannes Berg2a519312009-02-10 21:25:55 +0100733
Johannes Berg49a68e02019-02-07 23:26:38 +0100734const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type,
735 const u8 *ies,
736 unsigned int len)
Eliad Peller0c28ec52011-09-15 11:53:01 +0300737{
Johannes Berg49a68e02019-02-07 23:26:38 +0100738 const struct element *elem;
Luca Coelhofbd05e42016-09-15 18:15:09 +0300739 u8 match[] = { oui >> 16, oui >> 8, oui, oui_type };
740 int match_len = (oui_type < 0) ? 3 : sizeof(match);
Eliad Peller0c28ec52011-09-15 11:53:01 +0300741
Emmanuel Grumbach9e9ea432016-05-03 16:08:07 +0300742 if (WARN_ON(oui_type > 0xff))
743 return NULL;
744
Johannes Berg49a68e02019-02-07 23:26:38 +0100745 elem = cfg80211_find_elem_match(WLAN_EID_VENDOR_SPECIFIC, ies, len,
746 match, match_len, 0);
Eliad Peller0c28ec52011-09-15 11:53:01 +0300747
Johannes Berg49a68e02019-02-07 23:26:38 +0100748 if (!elem || elem->datalen < 4)
Luca Coelhofbd05e42016-09-15 18:15:09 +0300749 return NULL;
Luciano Coelho67194292013-02-12 20:11:38 +0200750
Johannes Berg49a68e02019-02-07 23:26:38 +0100751 return elem;
Eliad Peller0c28ec52011-09-15 11:53:01 +0300752}
Johannes Berg49a68e02019-02-07 23:26:38 +0100753EXPORT_SYMBOL(cfg80211_find_vendor_elem);
Eliad Peller0c28ec52011-09-15 11:53:01 +0300754
Johannes Berg4593c4c2013-02-01 19:20:03 +0100755/**
756 * enum bss_compare_mode - BSS compare mode
757 * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
758 * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
759 * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
760 */
761enum bss_compare_mode {
762 BSS_CMP_REGULAR,
763 BSS_CMP_HIDE_ZLEN,
764 BSS_CMP_HIDE_NUL,
765};
766
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100767static int cmp_bss(struct cfg80211_bss *a,
Johannes Berg5622f5b2013-01-30 00:26:45 +0100768 struct cfg80211_bss *b,
Johannes Berg4593c4c2013-02-01 19:20:03 +0100769 enum bss_compare_mode mode)
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100770{
Johannes Berg9caf0362012-11-29 01:25:20 +0100771 const struct cfg80211_bss_ies *a_ies, *b_ies;
Johannes Berg3af63412013-01-30 00:40:20 +0100772 const u8 *ie1 = NULL;
773 const u8 *ie2 = NULL;
Johannes Berg5622f5b2013-01-30 00:26:45 +0100774 int i, r;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100775
Johannes Berg3af63412013-01-30 00:40:20 +0100776 if (a->channel != b->channel)
777 return b->channel->center_freq - a->channel->center_freq;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100778
Johannes Berg9caf0362012-11-29 01:25:20 +0100779 a_ies = rcu_access_pointer(a->ies);
780 if (!a_ies)
781 return -1;
782 b_ies = rcu_access_pointer(b->ies);
783 if (!b_ies)
784 return 1;
785
Johannes Berg3af63412013-01-30 00:40:20 +0100786 if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
787 ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
788 a_ies->data, a_ies->len);
789 if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
790 ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
791 b_ies->data, b_ies->len);
792 if (ie1 && ie2) {
793 int mesh_id_cmp;
794
795 if (ie1[1] == ie2[1])
796 mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
797 else
798 mesh_id_cmp = ie2[1] - ie1[1];
799
800 ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
801 a_ies->data, a_ies->len);
802 ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
803 b_ies->data, b_ies->len);
804 if (ie1 && ie2) {
805 if (mesh_id_cmp)
806 return mesh_id_cmp;
807 if (ie1[1] != ie2[1])
808 return ie2[1] - ie1[1];
809 return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
810 }
811 }
812
Johannes Berg3af63412013-01-30 00:40:20 +0100813 r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
814 if (r)
815 return r;
816
Johannes Berg9caf0362012-11-29 01:25:20 +0100817 ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
818 ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100819
Johannes Berg5622f5b2013-01-30 00:26:45 +0100820 if (!ie1 && !ie2)
821 return 0;
822
Johannes Bergf94f8b12012-11-28 22:42:34 +0100823 /*
Johannes Berg5622f5b2013-01-30 00:26:45 +0100824 * Note that with "hide_ssid", the function returns a match if
825 * the already-present BSS ("b") is a hidden SSID beacon for
826 * the new BSS ("a").
Johannes Bergf94f8b12012-11-28 22:42:34 +0100827 */
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100828
829 /* sort missing IE before (left of) present IE */
830 if (!ie1)
831 return -1;
832 if (!ie2)
833 return 1;
834
Johannes Berg4593c4c2013-02-01 19:20:03 +0100835 switch (mode) {
836 case BSS_CMP_HIDE_ZLEN:
837 /*
838 * In ZLEN mode we assume the BSS entry we're
839 * looking for has a zero-length SSID. So if
840 * the one we're looking at right now has that,
841 * return 0. Otherwise, return the difference
842 * in length, but since we're looking for the
843 * 0-length it's really equivalent to returning
844 * the length of the one we're looking at.
845 *
846 * No content comparison is needed as we assume
847 * the content length is zero.
848 */
849 return ie2[1];
850 case BSS_CMP_REGULAR:
851 default:
852 /* sort by length first, then by contents */
853 if (ie1[1] != ie2[1])
854 return ie2[1] - ie1[1];
Johannes Berg5622f5b2013-01-30 00:26:45 +0100855 return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
Johannes Berg4593c4c2013-02-01 19:20:03 +0100856 case BSS_CMP_HIDE_NUL:
857 if (ie1[1] != ie2[1])
858 return ie2[1] - ie1[1];
859 /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
860 for (i = 0; i < ie2[1]; i++)
861 if (ie2[i + 2])
862 return -1;
863 return 0;
864 }
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100865}
866
Dedy Lansky6eb18132015-02-08 15:52:03 +0200867static bool cfg80211_bss_type_match(u16 capability,
Johannes Berg57fbcce2016-04-12 15:56:15 +0200868 enum nl80211_band band,
Dedy Lansky6eb18132015-02-08 15:52:03 +0200869 enum ieee80211_bss_type bss_type)
870{
871 bool ret = true;
872 u16 mask, val;
873
874 if (bss_type == IEEE80211_BSS_TYPE_ANY)
875 return ret;
876
Johannes Berg57fbcce2016-04-12 15:56:15 +0200877 if (band == NL80211_BAND_60GHZ) {
Dedy Lansky6eb18132015-02-08 15:52:03 +0200878 mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
879 switch (bss_type) {
880 case IEEE80211_BSS_TYPE_ESS:
881 val = WLAN_CAPABILITY_DMG_TYPE_AP;
882 break;
883 case IEEE80211_BSS_TYPE_PBSS:
884 val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
885 break;
886 case IEEE80211_BSS_TYPE_IBSS:
887 val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
888 break;
889 default:
890 return false;
891 }
892 } else {
893 mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
894 switch (bss_type) {
895 case IEEE80211_BSS_TYPE_ESS:
896 val = WLAN_CAPABILITY_ESS;
897 break;
898 case IEEE80211_BSS_TYPE_IBSS:
899 val = WLAN_CAPABILITY_IBSS;
900 break;
901 case IEEE80211_BSS_TYPE_MBSS:
902 val = 0;
903 break;
904 default:
905 return false;
906 }
907 }
908
909 ret = ((capability & mask) == val);
910 return ret;
911}
912
Ben Greear0e3a39b2013-06-19 14:06:27 -0700913/* Returned bss is reference counted and must be cleaned up appropriately. */
Johannes Berg2a519312009-02-10 21:25:55 +0100914struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
915 struct ieee80211_channel *channel,
916 const u8 *bssid,
Johannes Berg79420f02009-02-10 21:25:59 +0100917 const u8 *ssid, size_t ssid_len,
Dedy Lansky6eb18132015-02-08 15:52:03 +0200918 enum ieee80211_bss_type bss_type,
919 enum ieee80211_privacy privacy)
Johannes Berg2a519312009-02-10 21:25:55 +0100920{
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800921 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +0100922 struct cfg80211_internal_bss *bss, *res = NULL;
Johannes Bergccb6c132010-07-13 10:55:38 +0200923 unsigned long now = jiffies;
Dedy Lansky6eb18132015-02-08 15:52:03 +0200924 int bss_privacy;
Johannes Berg2a519312009-02-10 21:25:55 +0100925
Dedy Lansky6eb18132015-02-08 15:52:03 +0200926 trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
927 privacy);
Beni Lev4ee3e062012-08-27 12:49:39 +0300928
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800929 spin_lock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +0100930
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800931 list_for_each_entry(bss, &rdev->bss_list, list) {
Dedy Lansky6eb18132015-02-08 15:52:03 +0200932 if (!cfg80211_bss_type_match(bss->pub.capability,
933 bss->pub.channel->band, bss_type))
934 continue;
935
936 bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
937 if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
938 (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
Johannes Berg79420f02009-02-10 21:25:59 +0100939 continue;
Johannes Berg2a519312009-02-10 21:25:55 +0100940 if (channel && bss->pub.channel != channel)
941 continue;
Johannes Bergc14a7402014-04-09 22:36:50 +0200942 if (!is_valid_ether_addr(bss->pub.bssid))
943 continue;
Johannes Bergccb6c132010-07-13 10:55:38 +0200944 /* Don't get expired BSS structs */
945 if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
946 !atomic_read(&bss->hold))
947 continue;
Johannes Berg2a519312009-02-10 21:25:55 +0100948 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
949 res = bss;
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800950 bss_ref_get(rdev, res);
Johannes Berg2a519312009-02-10 21:25:55 +0100951 break;
952 }
953 }
954
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800955 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +0100956 if (!res)
957 return NULL;
Beni Lev4ee3e062012-08-27 12:49:39 +0300958 trace_cfg80211_return_bss(&res->pub);
Johannes Berg2a519312009-02-10 21:25:55 +0100959 return &res->pub;
960}
961EXPORT_SYMBOL(cfg80211_get_bss);
962
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800963static void rb_insert_bss(struct cfg80211_registered_device *rdev,
Johannes Berg2a519312009-02-10 21:25:55 +0100964 struct cfg80211_internal_bss *bss)
965{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800966 struct rb_node **p = &rdev->bss_tree.rb_node;
Johannes Berg2a519312009-02-10 21:25:55 +0100967 struct rb_node *parent = NULL;
968 struct cfg80211_internal_bss *tbss;
969 int cmp;
970
971 while (*p) {
972 parent = *p;
973 tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
974
Johannes Berg4593c4c2013-02-01 19:20:03 +0100975 cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
Johannes Berg2a519312009-02-10 21:25:55 +0100976
977 if (WARN_ON(!cmp)) {
978 /* will sort of leak this BSS */
979 return;
980 }
981
982 if (cmp < 0)
983 p = &(*p)->rb_left;
984 else
985 p = &(*p)->rb_right;
986 }
987
988 rb_link_node(&bss->rbn, parent, p);
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800989 rb_insert_color(&bss->rbn, &rdev->bss_tree);
Johannes Berg2a519312009-02-10 21:25:55 +0100990}
991
992static struct cfg80211_internal_bss *
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800993rb_find_bss(struct cfg80211_registered_device *rdev,
Johannes Berg5622f5b2013-01-30 00:26:45 +0100994 struct cfg80211_internal_bss *res,
Johannes Berg4593c4c2013-02-01 19:20:03 +0100995 enum bss_compare_mode mode)
Johannes Berg2a519312009-02-10 21:25:55 +0100996{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800997 struct rb_node *n = rdev->bss_tree.rb_node;
Johannes Berg2a519312009-02-10 21:25:55 +0100998 struct cfg80211_internal_bss *bss;
999 int r;
1000
1001 while (n) {
1002 bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
Johannes Berg4593c4c2013-02-01 19:20:03 +01001003 r = cmp_bss(&res->pub, &bss->pub, mode);
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001004
1005 if (r == 0)
1006 return bss;
1007 else if (r < 0)
1008 n = n->rb_left;
1009 else
1010 n = n->rb_right;
1011 }
1012
1013 return NULL;
1014}
1015
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001016static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
Johannes Berg776b3582013-02-01 02:06:18 +01001017 struct cfg80211_internal_bss *new)
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001018{
Johannes Berg9caf0362012-11-29 01:25:20 +01001019 const struct cfg80211_bss_ies *ies;
Johannes Berg776b3582013-02-01 02:06:18 +01001020 struct cfg80211_internal_bss *bss;
1021 const u8 *ie;
1022 int i, ssidlen;
1023 u8 fold = 0;
Johannes Berg9853a552016-11-15 12:05:11 +01001024 u32 n_entries = 0;
Johannes Berg9caf0362012-11-29 01:25:20 +01001025
Johannes Berg776b3582013-02-01 02:06:18 +01001026 ies = rcu_access_pointer(new->pub.beacon_ies);
Johannes Berg9caf0362012-11-29 01:25:20 +01001027 if (WARN_ON(!ies))
Johannes Berg776b3582013-02-01 02:06:18 +01001028 return false;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001029
Johannes Berg776b3582013-02-01 02:06:18 +01001030 ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
1031 if (!ie) {
1032 /* nothing to do */
1033 return true;
1034 }
1035
1036 ssidlen = ie[1];
1037 for (i = 0; i < ssidlen; i++)
1038 fold |= ie[2 + i];
1039
1040 if (fold) {
1041 /* not a hidden SSID */
1042 return true;
1043 }
1044
1045 /* This is the bad part ... */
1046
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001047 list_for_each_entry(bss, &rdev->bss_list, list) {
Johannes Berg9853a552016-11-15 12:05:11 +01001048 /*
1049 * we're iterating all the entries anyway, so take the
1050 * opportunity to validate the list length accounting
1051 */
1052 n_entries++;
1053
Johannes Berg776b3582013-02-01 02:06:18 +01001054 if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
1055 continue;
1056 if (bss->pub.channel != new->pub.channel)
1057 continue;
Simon Wunderlichdcd6eac2013-07-08 16:55:49 +02001058 if (bss->pub.scan_width != new->pub.scan_width)
1059 continue;
Johannes Berg776b3582013-02-01 02:06:18 +01001060 if (rcu_access_pointer(bss->pub.beacon_ies))
1061 continue;
1062 ies = rcu_access_pointer(bss->pub.ies);
1063 if (!ies)
1064 continue;
1065 ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
1066 if (!ie)
1067 continue;
1068 if (ssidlen && ie[1] != ssidlen)
1069 continue;
Johannes Berg776b3582013-02-01 02:06:18 +01001070 if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
1071 continue;
1072 if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
1073 list_del(&bss->hidden_list);
1074 /* combine them */
1075 list_add(&bss->hidden_list, &new->hidden_list);
1076 bss->pub.hidden_beacon_bss = &new->pub;
1077 new->refcount += bss->refcount;
1078 rcu_assign_pointer(bss->pub.beacon_ies,
1079 new->pub.beacon_ies);
1080 }
1081
Johannes Berg9853a552016-11-15 12:05:11 +01001082 WARN_ONCE(n_entries != rdev->bss_entries,
1083 "rdev bss entries[%d]/list[len:%d] corruption\n",
1084 rdev->bss_entries, n_entries);
1085
Johannes Berg776b3582013-02-01 02:06:18 +01001086 return true;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001087}
1088
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001089struct cfg80211_non_tx_bss {
1090 struct cfg80211_bss *tx_bss;
1091 u8 max_bssid_indicator;
1092 u8 bssid_index;
1093};
1094
Ben Greear0e3a39b2013-06-19 14:06:27 -07001095/* Returned bss is reference counted and must be cleaned up appropriately. */
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001096static struct cfg80211_internal_bss *
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001097cfg80211_bss_update(struct cfg80211_registered_device *rdev,
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001098 struct cfg80211_internal_bss *tmp,
1099 bool signal_valid)
Johannes Berg2a519312009-02-10 21:25:55 +01001100{
1101 struct cfg80211_internal_bss *found = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001102
Johannes Berg9caf0362012-11-29 01:25:20 +01001103 if (WARN_ON(!tmp->pub.channel))
Johannes Berg2a519312009-02-10 21:25:55 +01001104 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001105
Johannes Berg9caf0362012-11-29 01:25:20 +01001106 tmp->ts = jiffies;
Johannes Berg2a519312009-02-10 21:25:55 +01001107
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001108 spin_lock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +01001109
Johannes Berg9caf0362012-11-29 01:25:20 +01001110 if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001111 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg9caf0362012-11-29 01:25:20 +01001112 return NULL;
1113 }
1114
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001115 found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
Johannes Berg2a519312009-02-10 21:25:55 +01001116
Johannes Bergcd1658f2009-04-16 15:00:58 +02001117 if (found) {
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001118 /* Update IEs */
Johannes Berg9caf0362012-11-29 01:25:20 +01001119 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
1120 const struct cfg80211_bss_ies *old;
Johannes Bergcd1658f2009-04-16 15:00:58 +02001121
Johannes Berg9caf0362012-11-29 01:25:20 +01001122 old = rcu_access_pointer(found->pub.proberesp_ies);
Johannes Bergcd1658f2009-04-16 15:00:58 +02001123
Johannes Berg9caf0362012-11-29 01:25:20 +01001124 rcu_assign_pointer(found->pub.proberesp_ies,
1125 tmp->pub.proberesp_ies);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001126 /* Override possible earlier Beacon frame IEs */
Johannes Berg9caf0362012-11-29 01:25:20 +01001127 rcu_assign_pointer(found->pub.ies,
1128 tmp->pub.proberesp_ies);
1129 if (old)
1130 kfree_rcu((struct cfg80211_bss_ies *)old,
1131 rcu_head);
1132 } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
Johannes Berg9537f222013-02-01 01:19:48 +01001133 const struct cfg80211_bss_ies *old;
Johannes Berg776b3582013-02-01 02:06:18 +01001134 struct cfg80211_internal_bss *bss;
1135
1136 if (found->pub.hidden_beacon_bss &&
1137 !list_empty(&found->hidden_list)) {
Johannes Berg1345ee62013-03-06 10:31:05 +01001138 const struct cfg80211_bss_ies *f;
1139
Johannes Berg776b3582013-02-01 02:06:18 +01001140 /*
1141 * The found BSS struct is one of the probe
1142 * response members of a group, but we're
1143 * receiving a beacon (beacon_ies in the tmp
1144 * bss is used). This can only mean that the
1145 * AP changed its beacon from not having an
1146 * SSID to showing it, which is confusing so
1147 * drop this information.
1148 */
Johannes Berg1345ee62013-03-06 10:31:05 +01001149
1150 f = rcu_access_pointer(tmp->pub.beacon_ies);
1151 kfree_rcu((struct cfg80211_bss_ies *)f,
1152 rcu_head);
Johannes Berg776b3582013-02-01 02:06:18 +01001153 goto drop;
1154 }
Johannes Berg915de2f2012-11-28 22:39:37 +01001155
Johannes Berg9caf0362012-11-29 01:25:20 +01001156 old = rcu_access_pointer(found->pub.beacon_ies);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001157
Johannes Berg9caf0362012-11-29 01:25:20 +01001158 rcu_assign_pointer(found->pub.beacon_ies,
1159 tmp->pub.beacon_ies);
Sven Neumann01123e22010-12-09 15:05:24 +01001160
1161 /* Override IEs if they were from a beacon before */
Johannes Berg9537f222013-02-01 01:19:48 +01001162 if (old == rcu_access_pointer(found->pub.ies))
Johannes Berg9caf0362012-11-29 01:25:20 +01001163 rcu_assign_pointer(found->pub.ies,
1164 tmp->pub.beacon_ies);
Johannes Bergcd1658f2009-04-16 15:00:58 +02001165
Johannes Berg776b3582013-02-01 02:06:18 +01001166 /* Assign beacon IEs to all sub entries */
1167 list_for_each_entry(bss, &found->hidden_list,
1168 hidden_list) {
1169 const struct cfg80211_bss_ies *ies;
1170
1171 ies = rcu_access_pointer(bss->pub.beacon_ies);
1172 WARN_ON(ies != old);
1173
1174 rcu_assign_pointer(bss->pub.beacon_ies,
1175 tmp->pub.beacon_ies);
1176 }
1177
Johannes Berg9caf0362012-11-29 01:25:20 +01001178 if (old)
1179 kfree_rcu((struct cfg80211_bss_ies *)old,
1180 rcu_head);
1181 }
Johannes Berg1345ee62013-03-06 10:31:05 +01001182
1183 found->pub.beacon_interval = tmp->pub.beacon_interval;
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001184 /*
1185 * don't update the signal if beacon was heard on
1186 * adjacent channel.
1187 */
1188 if (signal_valid)
1189 found->pub.signal = tmp->pub.signal;
Johannes Berg1345ee62013-03-06 10:31:05 +01001190 found->pub.capability = tmp->pub.capability;
1191 found->ts = tmp->ts;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001192 found->ts_boottime = tmp->ts_boottime;
Avraham Stern1d762502016-07-05 17:10:13 +03001193 found->parent_tsf = tmp->parent_tsf;
Sunil Dutt983dafa2017-12-13 19:51:36 +02001194 found->pub.chains = tmp->pub.chains;
1195 memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
1196 IEEE80211_MAX_CHAINS);
Avraham Stern1d762502016-07-05 17:10:13 +03001197 ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001198 found->pub.max_bssid_indicator = tmp->pub.max_bssid_indicator;
1199 found->pub.bssid_index = tmp->pub.bssid_index;
Johannes Berg2a519312009-02-10 21:25:55 +01001200 } else {
Johannes Berg9caf0362012-11-29 01:25:20 +01001201 struct cfg80211_internal_bss *new;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001202 struct cfg80211_internal_bss *hidden;
Johannes Berg9caf0362012-11-29 01:25:20 +01001203 struct cfg80211_bss_ies *ies;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001204
Johannes Berg9caf0362012-11-29 01:25:20 +01001205 /*
1206 * create a copy -- the "res" variable that is passed in
1207 * is allocated on the stack since it's not needed in the
1208 * more common case of an update
1209 */
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001210 new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
Johannes Berg9caf0362012-11-29 01:25:20 +01001211 GFP_ATOMIC);
1212 if (!new) {
1213 ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
1214 if (ies)
1215 kfree_rcu(ies, rcu_head);
1216 ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
1217 if (ies)
1218 kfree_rcu(ies, rcu_head);
Johannes Berg776b3582013-02-01 02:06:18 +01001219 goto drop;
Johannes Berg9caf0362012-11-29 01:25:20 +01001220 }
1221 memcpy(new, tmp, sizeof(*new));
Johannes Berg776b3582013-02-01 02:06:18 +01001222 new->refcount = 1;
1223 INIT_LIST_HEAD(&new->hidden_list);
Sara Sharon7011ba52019-01-21 12:25:59 +02001224 INIT_LIST_HEAD(&new->pub.nontrans_list);
Johannes Berg776b3582013-02-01 02:06:18 +01001225
1226 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001227 hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
Johannes Berg776b3582013-02-01 02:06:18 +01001228 if (!hidden)
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001229 hidden = rb_find_bss(rdev, tmp,
Johannes Berg776b3582013-02-01 02:06:18 +01001230 BSS_CMP_HIDE_NUL);
1231 if (hidden) {
1232 new->pub.hidden_beacon_bss = &hidden->pub;
1233 list_add(&new->hidden_list,
1234 &hidden->hidden_list);
1235 hidden->refcount++;
1236 rcu_assign_pointer(new->pub.beacon_ies,
1237 hidden->pub.beacon_ies);
1238 }
1239 } else {
1240 /*
1241 * Ok so we found a beacon, and don't have an entry. If
1242 * it's a beacon with hidden SSID, we might be in for an
1243 * expensive search for any probe responses that should
1244 * be grouped with this beacon for updates ...
1245 */
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001246 if (!cfg80211_combine_bsses(rdev, new)) {
Johannes Berg776b3582013-02-01 02:06:18 +01001247 kfree(new);
1248 goto drop;
1249 }
1250 }
1251
Johannes Berg9853a552016-11-15 12:05:11 +01001252 if (rdev->bss_entries >= bss_entries_limit &&
1253 !cfg80211_bss_expire_oldest(rdev)) {
1254 kfree(new);
1255 goto drop;
1256 }
1257
Sara Sharona3584f56d2019-01-21 12:22:21 +02001258 /* This must be before the call to bss_ref_get */
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001259 if (tmp->pub.transmitted_bss) {
Sara Sharona3584f56d2019-01-21 12:22:21 +02001260 struct cfg80211_internal_bss *pbss =
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001261 container_of(tmp->pub.transmitted_bss,
Sara Sharona3584f56d2019-01-21 12:22:21 +02001262 struct cfg80211_internal_bss,
1263 pub);
1264
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001265 new->pub.transmitted_bss = tmp->pub.transmitted_bss;
Sara Sharona3584f56d2019-01-21 12:22:21 +02001266 bss_ref_get(rdev, pbss);
1267 }
1268
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001269 list_add_tail(&new->list, &rdev->bss_list);
Johannes Berg9853a552016-11-15 12:05:11 +01001270 rdev->bss_entries++;
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001271 rb_insert_bss(rdev, new);
Johannes Berg9caf0362012-11-29 01:25:20 +01001272 found = new;
Johannes Berg2a519312009-02-10 21:25:55 +01001273 }
1274
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001275 rdev->bss_generation++;
1276 bss_ref_get(rdev, found);
1277 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +01001278
Johannes Berg2a519312009-02-10 21:25:55 +01001279 return found;
Johannes Berg776b3582013-02-01 02:06:18 +01001280 drop:
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001281 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg776b3582013-02-01 02:06:18 +01001282 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001283}
1284
Jouni Malinen119f94a2018-09-05 18:52:22 +03001285/*
1286 * Update RX channel information based on the available frame payload
1287 * information. This is mainly for the 2.4 GHz band where frames can be received
1288 * from neighboring channels and the Beacon frames use the DSSS Parameter Set
1289 * element to indicate the current (transmitting) channel, but this might also
1290 * be needed on other bands if RX frequency does not match with the actual
1291 * operating channel of a BSS.
1292 */
Johannes Berg0172bb72012-11-23 14:23:30 +01001293static struct ieee80211_channel *
1294cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
Jouni Malinen119f94a2018-09-05 18:52:22 +03001295 struct ieee80211_channel *channel,
1296 enum nl80211_bss_scan_width scan_width)
Johannes Berg0172bb72012-11-23 14:23:30 +01001297{
1298 const u8 *tmp;
1299 u32 freq;
1300 int channel_number = -1;
Jouni Malinen119f94a2018-09-05 18:52:22 +03001301 struct ieee80211_channel *alt_channel;
Johannes Berg0172bb72012-11-23 14:23:30 +01001302
1303 tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
1304 if (tmp && tmp[1] == 1) {
1305 channel_number = tmp[2];
1306 } else {
1307 tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
1308 if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
1309 struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
1310
1311 channel_number = htop->primary_chan;
1312 }
1313 }
1314
Jouni Malinen119f94a2018-09-05 18:52:22 +03001315 if (channel_number < 0) {
1316 /* No channel information in frame payload */
Johannes Berg0172bb72012-11-23 14:23:30 +01001317 return channel;
Jouni Malinen119f94a2018-09-05 18:52:22 +03001318 }
Johannes Berg0172bb72012-11-23 14:23:30 +01001319
1320 freq = ieee80211_channel_to_frequency(channel_number, channel->band);
Jouni Malinen119f94a2018-09-05 18:52:22 +03001321 alt_channel = ieee80211_get_channel(wiphy, freq);
1322 if (!alt_channel) {
1323 if (channel->band == NL80211_BAND_2GHZ) {
1324 /*
1325 * Better not allow unexpected channels when that could
1326 * be going beyond the 1-11 range (e.g., discovering
1327 * BSS on channel 12 when radio is configured for
1328 * channel 11.
1329 */
1330 return NULL;
1331 }
1332
1333 /* No match for the payload channel number - ignore it */
1334 return channel;
1335 }
1336
1337 if (scan_width == NL80211_BSS_CHAN_WIDTH_10 ||
1338 scan_width == NL80211_BSS_CHAN_WIDTH_5) {
1339 /*
1340 * Ignore channel number in 5 and 10 MHz channels where there
1341 * may not be an n:1 or 1:n mapping between frequencies and
1342 * channel numbers.
1343 */
1344 return channel;
1345 }
1346
1347 /*
1348 * Use the channel determined through the payload channel number
1349 * instead of the RX channel reported by the driver.
1350 */
1351 if (alt_channel->flags & IEEE80211_CHAN_DISABLED)
Johannes Berg0172bb72012-11-23 14:23:30 +01001352 return NULL;
Jouni Malinen119f94a2018-09-05 18:52:22 +03001353 return alt_channel;
Johannes Berg0172bb72012-11-23 14:23:30 +01001354}
1355
Ben Greear0e3a39b2013-06-19 14:06:27 -07001356/* Returned bss is reference counted and must be cleaned up appropriately. */
Peng Xu0b8fb822019-01-21 12:14:57 +02001357static struct cfg80211_bss *
1358cfg80211_inform_single_bss_data(struct wiphy *wiphy,
1359 struct cfg80211_inform_bss *data,
1360 enum cfg80211_bss_frame_type ftype,
1361 const u8 *bssid, u64 tsf, u16 capability,
1362 u16 beacon_interval, const u8 *ie, size_t ielen,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001363 struct cfg80211_non_tx_bss *non_tx_data,
Peng Xu0b8fb822019-01-21 12:14:57 +02001364 gfp_t gfp)
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001365{
Peng Xu0b8fb822019-01-21 12:14:57 +02001366 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg9caf0362012-11-29 01:25:20 +01001367 struct cfg80211_bss_ies *ies;
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001368 struct ieee80211_channel *channel;
Sara Sharon7011ba52019-01-21 12:25:59 +02001369 struct cfg80211_internal_bss tmp = {}, *res;
Dedy Lansky6eb18132015-02-08 15:52:03 +02001370 int bss_type;
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001371 bool signal_valid;
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001372
1373 if (WARN_ON(!wiphy))
1374 return NULL;
1375
Sujith22fe88d2010-05-13 10:34:08 +05301376 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001377 (data->signal < 0 || data->signal > 100)))
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001378 return NULL;
1379
Jouni Malinen119f94a2018-09-05 18:52:22 +03001380 channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan,
1381 data->scan_width);
Johannes Berg0172bb72012-11-23 14:23:30 +01001382 if (!channel)
1383 return NULL;
1384
Johannes Berg9caf0362012-11-29 01:25:20 +01001385 memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
1386 tmp.pub.channel = channel;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001387 tmp.pub.scan_width = data->scan_width;
1388 tmp.pub.signal = data->signal;
Johannes Berg9caf0362012-11-29 01:25:20 +01001389 tmp.pub.beacon_interval = beacon_interval;
1390 tmp.pub.capability = capability;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001391 tmp.ts_boottime = data->boottime_ns;
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001392 if (non_tx_data) {
1393 tmp.pub.transmitted_bss = non_tx_data->tx_bss;
1394 tmp.pub.bssid_index = non_tx_data->bssid_index;
1395 tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
1396 }
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001397
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001398 /*
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02001399 * If we do not know here whether the IEs are from a Beacon or Probe
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001400 * Response frame, we need to pick one of the options and only use it
1401 * with the driver that does not provide the full Beacon/Probe Response
1402 * frame. Use Beacon frame pointer to avoid indicating that this should
Johannes Berg50521aa2013-01-30 21:33:19 +01001403 * override the IEs pointer should we have received an earlier
Johannes Berg9caf0362012-11-29 01:25:20 +01001404 * indication of Probe Response data.
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001405 */
Johannes Berg0e227082014-08-12 20:34:30 +02001406 ies = kzalloc(sizeof(*ies) + ielen, gfp);
Johannes Berg9caf0362012-11-29 01:25:20 +01001407 if (!ies)
1408 return NULL;
1409 ies->len = ielen;
Johannes Berg8cef2c92013-02-05 16:54:31 +01001410 ies->tsf = tsf;
Johannes Berg0e227082014-08-12 20:34:30 +02001411 ies->from_beacon = false;
Johannes Berg9caf0362012-11-29 01:25:20 +01001412 memcpy(ies->data, ie, ielen);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001413
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02001414 switch (ftype) {
1415 case CFG80211_BSS_FTYPE_BEACON:
1416 ies->from_beacon = true;
Luca Coelho925b5972018-12-15 11:03:21 +02001417 /* fall through */
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02001418 case CFG80211_BSS_FTYPE_UNKNOWN:
1419 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
1420 break;
1421 case CFG80211_BSS_FTYPE_PRESP:
1422 rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
1423 break;
1424 }
Johannes Berg9caf0362012-11-29 01:25:20 +01001425 rcu_assign_pointer(tmp.pub.ies, ies);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001426
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001427 signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001428 wiphy->max_adj_channel_rssi_comp;
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001429 res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001430 if (!res)
1431 return NULL;
1432
Johannes Berg57fbcce2016-04-12 15:56:15 +02001433 if (channel->band == NL80211_BAND_60GHZ) {
Dedy Lansky6eb18132015-02-08 15:52:03 +02001434 bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
1435 if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
1436 bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
1437 regulatory_hint_found_beacon(wiphy, channel, gfp);
1438 } else {
1439 if (res->pub.capability & WLAN_CAPABILITY_ESS)
1440 regulatory_hint_found_beacon(wiphy, channel, gfp);
1441 }
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001442
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001443 if (non_tx_data && non_tx_data->tx_bss) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001444 /* this is a nontransmitting bss, we need to add it to
1445 * transmitting bss' list if it is not there
1446 */
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001447 if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
1448 &res->pub)) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001449 if (__cfg80211_unlink_bss(rdev, res))
1450 rdev->bss_generation++;
1451 }
1452 }
1453
Beni Lev4ee3e062012-08-27 12:49:39 +03001454 trace_cfg80211_return_bss(&res->pub);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001455 /* cfg80211_bss_update gives us a referenced result */
1456 return &res->pub;
1457}
Peng Xu0b8fb822019-01-21 12:14:57 +02001458
Sara Sharonfe806e42019-03-15 17:39:05 +02001459static const struct element
1460*cfg80211_get_profile_continuation(const u8 *ie, size_t ielen,
1461 const struct element *mbssid_elem,
1462 const struct element *sub_elem)
1463{
1464 const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen;
1465 const struct element *next_mbssid;
1466 const struct element *next_sub;
1467
1468 next_mbssid = cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
1469 mbssid_end,
1470 ielen - (mbssid_end - ie));
1471
1472 /*
1473 * If is is not the last subelement in current MBSSID IE or there isn't
1474 * a next MBSSID IE - profile is complete.
1475 */
1476 if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
1477 !next_mbssid)
1478 return NULL;
1479
1480 /* For any length error, just return NULL */
1481
1482 if (next_mbssid->datalen < 4)
1483 return NULL;
1484
1485 next_sub = (void *)&next_mbssid->data[1];
1486
1487 if (next_mbssid->data + next_mbssid->datalen <
1488 next_sub->data + next_sub->datalen)
1489 return NULL;
1490
1491 if (next_sub->id != 0 || next_sub->datalen < 2)
1492 return NULL;
1493
1494 /*
1495 * Check if the first element in the next sub element is a start
1496 * of a new profile
1497 */
1498 return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
1499 NULL : next_mbssid;
1500}
1501
1502size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
1503 const struct element *mbssid_elem,
1504 const struct element *sub_elem,
1505 u8 **merged_ie, size_t max_copy_len)
1506{
1507 size_t copied_len = sub_elem->datalen;
1508 const struct element *next_mbssid;
1509
1510 if (sub_elem->datalen > max_copy_len)
1511 return 0;
1512
1513 memcpy(*merged_ie, sub_elem->data, sub_elem->datalen);
1514
1515 while ((next_mbssid = cfg80211_get_profile_continuation(ie, ielen,
1516 mbssid_elem,
1517 sub_elem))) {
1518 const struct element *next_sub = (void *)&next_mbssid->data[1];
1519
1520 if (copied_len + next_sub->datalen > max_copy_len)
1521 break;
1522 memcpy(*merged_ie + copied_len, next_sub->data,
1523 next_sub->datalen);
1524 copied_len += next_sub->datalen;
1525 }
1526
1527 return copied_len;
1528}
1529EXPORT_SYMBOL(cfg80211_merge_profile);
1530
Peng Xu0b8fb822019-01-21 12:14:57 +02001531static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
1532 struct cfg80211_inform_bss *data,
1533 enum cfg80211_bss_frame_type ftype,
1534 const u8 *bssid, u64 tsf,
1535 u16 beacon_interval, const u8 *ie,
1536 size_t ielen,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001537 struct cfg80211_non_tx_bss *non_tx_data,
Peng Xu0b8fb822019-01-21 12:14:57 +02001538 gfp_t gfp)
1539{
Johannes Berg1c8745f2019-02-07 22:36:33 +01001540 const u8 *mbssid_index_ie;
1541 const struct element *elem, *sub;
1542 size_t new_ie_len;
Peng Xu0b8fb822019-01-21 12:14:57 +02001543 u8 new_bssid[ETH_ALEN];
Sara Sharonfe806e42019-03-15 17:39:05 +02001544 u8 *new_ie, *profile;
1545 u64 seen_indices = 0;
Peng Xu0b8fb822019-01-21 12:14:57 +02001546 u16 capability;
1547 struct cfg80211_bss *bss;
1548
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001549 if (!non_tx_data)
Peng Xu0b8fb822019-01-21 12:14:57 +02001550 return;
1551 if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1552 return;
Sara Sharon213ed572019-01-16 23:02:03 +02001553 if (!wiphy->support_mbssid)
1554 return;
1555 if (wiphy->support_only_he_mbssid &&
1556 !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
1557 return;
Peng Xu0b8fb822019-01-21 12:14:57 +02001558
Peng Xu0b8fb822019-01-21 12:14:57 +02001559 new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
1560 if (!new_ie)
1561 return;
1562
Sara Sharonfe806e42019-03-15 17:39:05 +02001563 profile = kmalloc(ielen, gfp);
1564 if (!profile)
1565 goto out;
1566
Johannes Berg1c8745f2019-02-07 22:36:33 +01001567 for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
1568 if (elem->datalen < 4)
1569 continue;
1570 for_each_element(sub, elem->data + 1, elem->datalen - 1) {
Sara Sharonfe806e42019-03-15 17:39:05 +02001571 u8 profile_len;
1572
Johannes Berg1c8745f2019-02-07 22:36:33 +01001573 if (sub->id != 0 || sub->datalen < 4) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001574 /* not a valid BSS profile */
1575 continue;
1576 }
1577
Johannes Berg1c8745f2019-02-07 22:36:33 +01001578 if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
1579 sub->data[1] != 2) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001580 /* The first element within the Nontransmitted
1581 * BSSID Profile is not the Nontransmitted
1582 * BSSID Capability element.
1583 */
1584 continue;
1585 }
1586
Sara Sharonfe806e42019-03-15 17:39:05 +02001587 memset(profile, 0, ielen);
1588 profile_len = cfg80211_merge_profile(ie, ielen,
1589 elem,
1590 sub,
1591 &profile,
1592 ielen);
1593
Peng Xu0b8fb822019-01-21 12:14:57 +02001594 /* found a Nontransmitted BSSID Profile */
1595 mbssid_index_ie = cfg80211_find_ie
1596 (WLAN_EID_MULTI_BSSID_IDX,
Sara Sharonfe806e42019-03-15 17:39:05 +02001597 profile, profile_len);
Peng Xu0b8fb822019-01-21 12:14:57 +02001598 if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
Sara Sharonfe806e42019-03-15 17:39:05 +02001599 mbssid_index_ie[2] == 0 ||
1600 mbssid_index_ie[2] > 46) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001601 /* No valid Multiple BSSID-Index element */
1602 continue;
1603 }
1604
Sara Sharonfe806e42019-03-15 17:39:05 +02001605 if (seen_indices & BIT(mbssid_index_ie[2]))
1606 /* We don't support legacy split of a profile */
1607 net_dbg_ratelimited("Partial info for BSSID index %d\n",
1608 mbssid_index_ie[2]);
1609
1610 seen_indices |= BIT(mbssid_index_ie[2]);
1611
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001612 non_tx_data->bssid_index = mbssid_index_ie[2];
1613 non_tx_data->max_bssid_indicator = elem->data[0];
1614
1615 cfg80211_gen_new_bssid(bssid,
1616 non_tx_data->max_bssid_indicator,
1617 non_tx_data->bssid_index,
Peng Xu0b8fb822019-01-21 12:14:57 +02001618 new_bssid);
1619 memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
Sara Sharonfe806e42019-03-15 17:39:05 +02001620 new_ie_len = cfg80211_gen_new_ie(ie, ielen,
1621 profile,
1622 profile_len, new_ie,
Peng Xu0b8fb822019-01-21 12:14:57 +02001623 gfp);
1624 if (!new_ie_len)
1625 continue;
1626
Sara Sharonfe806e42019-03-15 17:39:05 +02001627 capability = get_unaligned_le16(profile + 2);
Peng Xu0b8fb822019-01-21 12:14:57 +02001628 bss = cfg80211_inform_single_bss_data(wiphy, data,
1629 ftype,
1630 new_bssid, tsf,
1631 capability,
1632 beacon_interval,
1633 new_ie,
1634 new_ie_len,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001635 non_tx_data,
1636 gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001637 if (!bss)
1638 break;
1639 cfg80211_put_bss(wiphy, bss);
1640 }
Peng Xu0b8fb822019-01-21 12:14:57 +02001641 }
1642
Sara Sharonfe806e42019-03-15 17:39:05 +02001643out:
Peng Xu0b8fb822019-01-21 12:14:57 +02001644 kfree(new_ie);
Sara Sharonfe806e42019-03-15 17:39:05 +02001645 kfree(profile);
Peng Xu0b8fb822019-01-21 12:14:57 +02001646}
1647
1648struct cfg80211_bss *
1649cfg80211_inform_bss_data(struct wiphy *wiphy,
1650 struct cfg80211_inform_bss *data,
1651 enum cfg80211_bss_frame_type ftype,
1652 const u8 *bssid, u64 tsf, u16 capability,
1653 u16 beacon_interval, const u8 *ie, size_t ielen,
1654 gfp_t gfp)
1655{
1656 struct cfg80211_bss *res;
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001657 struct cfg80211_non_tx_bss non_tx_data;
Peng Xu0b8fb822019-01-21 12:14:57 +02001658
1659 res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
1660 capability, beacon_interval, ie,
1661 ielen, NULL, gfp);
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001662 non_tx_data.tx_bss = res;
Peng Xu0b8fb822019-01-21 12:14:57 +02001663 cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001664 beacon_interval, ie, ielen, &non_tx_data,
1665 gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001666 return res;
1667}
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001668EXPORT_SYMBOL(cfg80211_inform_bss_data);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001669
Peng Xu0b8fb822019-01-21 12:14:57 +02001670static void
1671cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy,
1672 struct cfg80211_inform_bss *data,
1673 struct ieee80211_mgmt *mgmt, size_t len,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001674 struct cfg80211_non_tx_bss *non_tx_data,
Peng Xu0b8fb822019-01-21 12:14:57 +02001675 gfp_t gfp)
1676{
1677 enum cfg80211_bss_frame_type ftype;
1678 const u8 *ie = mgmt->u.probe_resp.variable;
1679 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1680 u.probe_resp.variable);
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001681
Peng Xu0b8fb822019-01-21 12:14:57 +02001682 ftype = ieee80211_is_beacon(mgmt->frame_control) ?
1683 CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
1684
1685 cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid,
1686 le64_to_cpu(mgmt->u.probe_resp.timestamp),
1687 le16_to_cpu(mgmt->u.probe_resp.beacon_int),
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001688 ie, ielen, non_tx_data, gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001689}
1690
1691static void
1692cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
Sara Sharon7011ba52019-01-21 12:25:59 +02001693 struct cfg80211_bss *nontrans_bss,
Peng Xu0b8fb822019-01-21 12:14:57 +02001694 struct ieee80211_mgmt *mgmt, size_t len,
1695 gfp_t gfp)
1696{
1697 u8 *ie, *new_ie, *pos;
1698 const u8 *nontrans_ssid, *trans_ssid, *mbssid;
1699 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1700 u.probe_resp.variable);
1701 size_t new_ie_len;
1702 struct cfg80211_bss_ies *new_ies;
1703 const struct cfg80211_bss_ies *old;
1704 u8 cpy_len;
1705
1706 ie = mgmt->u.probe_resp.variable;
1707
1708 new_ie_len = ielen;
1709 trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
1710 if (!trans_ssid)
1711 return;
1712 new_ie_len -= trans_ssid[1];
1713 mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
1714 if (!mbssid)
1715 return;
1716 new_ie_len -= mbssid[1];
1717 rcu_read_lock();
Sara Sharon7011ba52019-01-21 12:25:59 +02001718 nontrans_ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID);
Peng Xu0b8fb822019-01-21 12:14:57 +02001719 if (!nontrans_ssid) {
1720 rcu_read_unlock();
1721 return;
1722 }
1723 new_ie_len += nontrans_ssid[1];
1724 rcu_read_unlock();
1725
1726 /* generate new ie for nontrans BSS
1727 * 1. replace SSID with nontrans BSS' SSID
1728 * 2. skip MBSSID IE
1729 */
1730 new_ie = kzalloc(new_ie_len, gfp);
1731 if (!new_ie)
1732 return;
1733 new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, gfp);
Sara Sharonbede8d22019-01-30 08:48:21 +02001734 if (!new_ies)
1735 goto out_free;
Peng Xu0b8fb822019-01-21 12:14:57 +02001736
1737 pos = new_ie;
1738
1739 /* copy the nontransmitted SSID */
1740 cpy_len = nontrans_ssid[1] + 2;
1741 memcpy(pos, nontrans_ssid, cpy_len);
1742 pos += cpy_len;
1743 /* copy the IEs between SSID and MBSSID */
1744 cpy_len = trans_ssid[1] + 2;
1745 memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
1746 pos += (mbssid - (trans_ssid + cpy_len));
1747 /* copy the IEs after MBSSID */
1748 cpy_len = mbssid[1] + 2;
1749 memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
1750
1751 /* update ie */
1752 new_ies->len = new_ie_len;
1753 new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
1754 new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
1755 memcpy(new_ies->data, new_ie, new_ie_len);
1756 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
Sara Sharon7011ba52019-01-21 12:25:59 +02001757 old = rcu_access_pointer(nontrans_bss->proberesp_ies);
1758 rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies);
1759 rcu_assign_pointer(nontrans_bss->ies, new_ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001760 if (old)
1761 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1762 } else {
Sara Sharon7011ba52019-01-21 12:25:59 +02001763 old = rcu_access_pointer(nontrans_bss->beacon_ies);
1764 rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies);
1765 rcu_assign_pointer(nontrans_bss->ies, new_ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001766 if (old)
1767 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1768 }
Sara Sharonbede8d22019-01-30 08:48:21 +02001769
1770out_free:
1771 kfree(new_ie);
Peng Xu0b8fb822019-01-21 12:14:57 +02001772}
1773
1774/* cfg80211_inform_bss_width_frame helper */
1775static struct cfg80211_bss *
1776cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
1777 struct cfg80211_inform_bss *data,
1778 struct ieee80211_mgmt *mgmt, size_t len,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001779 struct cfg80211_non_tx_bss *non_tx_data,
Peng Xu0b8fb822019-01-21 12:14:57 +02001780 gfp_t gfp)
Johannes Berg2a519312009-02-10 21:25:55 +01001781{
Johannes Berg9caf0362012-11-29 01:25:20 +01001782 struct cfg80211_internal_bss tmp = {}, *res;
1783 struct cfg80211_bss_ies *ies;
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001784 struct ieee80211_channel *channel;
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001785 bool signal_valid;
Johannes Berg2a519312009-02-10 21:25:55 +01001786 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1787 u.probe_resp.variable);
Dedy Lansky6eb18132015-02-08 15:52:03 +02001788 int bss_type;
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001789
Johannes Berg0172bb72012-11-23 14:23:30 +01001790 BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
1791 offsetof(struct ieee80211_mgmt, u.beacon.variable));
1792
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001793 trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
Beni Lev4ee3e062012-08-27 12:49:39 +03001794
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001795 if (WARN_ON(!mgmt))
1796 return NULL;
1797
1798 if (WARN_ON(!wiphy))
1799 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001800
Sujith22fe88d2010-05-13 10:34:08 +05301801 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001802 (data->signal < 0 || data->signal > 100)))
Johannes Berg2a519312009-02-10 21:25:55 +01001803 return NULL;
1804
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001805 if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
Johannes Berg2a519312009-02-10 21:25:55 +01001806 return NULL;
1807
Johannes Berg0172bb72012-11-23 14:23:30 +01001808 channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
Jouni Malinen119f94a2018-09-05 18:52:22 +03001809 ielen, data->chan, data->scan_width);
Johannes Berg0172bb72012-11-23 14:23:30 +01001810 if (!channel)
1811 return NULL;
1812
Johannes Berg0e227082014-08-12 20:34:30 +02001813 ies = kzalloc(sizeof(*ies) + ielen, gfp);
Johannes Berg9caf0362012-11-29 01:25:20 +01001814 if (!ies)
Johannes Berg2a519312009-02-10 21:25:55 +01001815 return NULL;
Johannes Berg9caf0362012-11-29 01:25:20 +01001816 ies->len = ielen;
Johannes Berg8cef2c92013-02-05 16:54:31 +01001817 ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
Johannes Berg0e227082014-08-12 20:34:30 +02001818 ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
Johannes Berg9caf0362012-11-29 01:25:20 +01001819 memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
Johannes Berg2a519312009-02-10 21:25:55 +01001820
Johannes Berg9caf0362012-11-29 01:25:20 +01001821 if (ieee80211_is_probe_resp(mgmt->frame_control))
1822 rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
1823 else
1824 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
1825 rcu_assign_pointer(tmp.pub.ies, ies);
Arend Van Spriel505a2e82016-12-16 11:21:54 +00001826
Johannes Berg9caf0362012-11-29 01:25:20 +01001827 memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
1828 tmp.pub.channel = channel;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001829 tmp.pub.scan_width = data->scan_width;
1830 tmp.pub.signal = data->signal;
Johannes Berg9caf0362012-11-29 01:25:20 +01001831 tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
1832 tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001833 tmp.ts_boottime = data->boottime_ns;
Avraham Stern1d762502016-07-05 17:10:13 +03001834 tmp.parent_tsf = data->parent_tsf;
Sunil Dutt983dafa2017-12-13 19:51:36 +02001835 tmp.pub.chains = data->chains;
1836 memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
Avraham Stern1d762502016-07-05 17:10:13 +03001837 ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001838 if (non_tx_data) {
1839 tmp.pub.transmitted_bss = non_tx_data->tx_bss;
1840 tmp.pub.bssid_index = non_tx_data->bssid_index;
1841 tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
1842 }
Johannes Berg2a519312009-02-10 21:25:55 +01001843
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001844 signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001845 wiphy->max_adj_channel_rssi_comp;
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001846 res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
Johannes Berg2a519312009-02-10 21:25:55 +01001847 if (!res)
1848 return NULL;
1849
Johannes Berg57fbcce2016-04-12 15:56:15 +02001850 if (channel->band == NL80211_BAND_60GHZ) {
Dedy Lansky6eb18132015-02-08 15:52:03 +02001851 bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
1852 if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
1853 bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
1854 regulatory_hint_found_beacon(wiphy, channel, gfp);
1855 } else {
1856 if (res->pub.capability & WLAN_CAPABILITY_ESS)
1857 regulatory_hint_found_beacon(wiphy, channel, gfp);
1858 }
Luis R. Rodrigueze38f8a72009-02-21 00:20:39 -05001859
Beni Lev4ee3e062012-08-27 12:49:39 +03001860 trace_cfg80211_return_bss(&res->pub);
Johannes Berg2a519312009-02-10 21:25:55 +01001861 /* cfg80211_bss_update gives us a referenced result */
1862 return &res->pub;
1863}
Peng Xu0b8fb822019-01-21 12:14:57 +02001864
1865struct cfg80211_bss *
1866cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
1867 struct cfg80211_inform_bss *data,
1868 struct ieee80211_mgmt *mgmt, size_t len,
1869 gfp_t gfp)
1870{
Sara Sharon7011ba52019-01-21 12:25:59 +02001871 struct cfg80211_bss *res, *tmp_bss;
Peng Xu0b8fb822019-01-21 12:14:57 +02001872 const u8 *ie = mgmt->u.probe_resp.variable;
1873 const struct cfg80211_bss_ies *ies1, *ies2;
1874 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1875 u.probe_resp.variable);
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001876 struct cfg80211_non_tx_bss non_tx_data;
Peng Xu0b8fb822019-01-21 12:14:57 +02001877
1878 res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
1879 len, NULL, gfp);
Sara Sharon213ed572019-01-16 23:02:03 +02001880 if (!res || !wiphy->support_mbssid ||
1881 !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1882 return res;
1883 if (wiphy->support_only_he_mbssid &&
1884 !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
Peng Xu0b8fb822019-01-21 12:14:57 +02001885 return res;
1886
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001887 non_tx_data.tx_bss = res;
Peng Xu0b8fb822019-01-21 12:14:57 +02001888 /* process each non-transmitting bss */
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001889 cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len,
1890 &non_tx_data, gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001891
1892 /* check if the res has other nontransmitting bss which is not
1893 * in MBSSID IE
1894 */
1895 ies1 = rcu_access_pointer(res->ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001896
1897 /* go through nontrans_list, if the timestamp of the BSS is
1898 * earlier than the timestamp of the transmitting BSS then
1899 * update it
1900 */
Sara Sharon7011ba52019-01-21 12:25:59 +02001901 list_for_each_entry(tmp_bss, &res->nontrans_list,
Peng Xu0b8fb822019-01-21 12:14:57 +02001902 nontrans_list) {
Sara Sharon7011ba52019-01-21 12:25:59 +02001903 ies2 = rcu_access_pointer(tmp_bss->ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001904 if (ies2->tsf < ies1->tsf)
1905 cfg80211_update_notlisted_nontrans(wiphy, tmp_bss,
1906 mgmt, len, gfp);
1907 }
1908
1909 return res;
1910}
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001911EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
Johannes Berg2a519312009-02-10 21:25:55 +01001912
Johannes Berg5b112d32013-02-01 01:49:58 +01001913void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001914{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001915 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001916 struct cfg80211_internal_bss *bss;
1917
1918 if (!pub)
1919 return;
1920
1921 bss = container_of(pub, struct cfg80211_internal_bss, pub);
Johannes Berg776b3582013-02-01 02:06:18 +01001922
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001923 spin_lock_bh(&rdev->bss_lock);
1924 bss_ref_get(rdev, bss);
1925 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001926}
1927EXPORT_SYMBOL(cfg80211_ref_bss);
1928
Johannes Berg5b112d32013-02-01 01:49:58 +01001929void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
Johannes Berg2a519312009-02-10 21:25:55 +01001930{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001931 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +01001932 struct cfg80211_internal_bss *bss;
1933
1934 if (!pub)
1935 return;
1936
1937 bss = container_of(pub, struct cfg80211_internal_bss, pub);
Johannes Berg776b3582013-02-01 02:06:18 +01001938
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001939 spin_lock_bh(&rdev->bss_lock);
1940 bss_ref_put(rdev, bss);
1941 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +01001942}
1943EXPORT_SYMBOL(cfg80211_put_bss);
1944
Johannes Bergd491af12009-02-10 21:25:58 +01001945void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1946{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001947 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Sara Sharon7011ba52019-01-21 12:25:59 +02001948 struct cfg80211_internal_bss *bss, *tmp1;
1949 struct cfg80211_bss *nontrans_bss, *tmp;
Johannes Bergd491af12009-02-10 21:25:58 +01001950
1951 if (WARN_ON(!pub))
1952 return;
1953
1954 bss = container_of(pub, struct cfg80211_internal_bss, pub);
1955
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001956 spin_lock_bh(&rdev->bss_lock);
Sara Sharon7011ba52019-01-21 12:25:59 +02001957 if (list_empty(&bss->list))
1958 goto out;
Peng Xu0b8fb822019-01-21 12:14:57 +02001959
Sara Sharon7011ba52019-01-21 12:25:59 +02001960 list_for_each_entry_safe(nontrans_bss, tmp,
1961 &pub->nontrans_list,
1962 nontrans_list) {
1963 tmp1 = container_of(nontrans_bss,
1964 struct cfg80211_internal_bss, pub);
1965 if (__cfg80211_unlink_bss(rdev, tmp1))
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001966 rdev->bss_generation++;
Johannes Berg32073902010-10-06 21:18:04 +02001967 }
Sara Sharon7011ba52019-01-21 12:25:59 +02001968
1969 if (__cfg80211_unlink_bss(rdev, bss))
1970 rdev->bss_generation++;
1971out:
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001972 spin_unlock_bh(&rdev->bss_lock);
Johannes Bergd491af12009-02-10 21:25:58 +01001973}
1974EXPORT_SYMBOL(cfg80211_unlink_bss);
1975
Johannes Berg3d23e342009-09-29 23:27:28 +02001976#ifdef CONFIG_CFG80211_WEXT
Johannes Berg9f419f32013-05-08 21:34:22 +02001977static struct cfg80211_registered_device *
1978cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
1979{
Johannes Berg5fe231e2013-05-08 21:45:15 +02001980 struct cfg80211_registered_device *rdev;
Johannes Berg9f419f32013-05-08 21:34:22 +02001981 struct net_device *dev;
1982
Johannes Berg5fe231e2013-05-08 21:45:15 +02001983 ASSERT_RTNL();
1984
Johannes Berg9f419f32013-05-08 21:34:22 +02001985 dev = dev_get_by_index(net, ifindex);
1986 if (!dev)
Johannes Berg5fe231e2013-05-08 21:45:15 +02001987 return ERR_PTR(-ENODEV);
1988 if (dev->ieee80211_ptr)
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001989 rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
Johannes Berg5fe231e2013-05-08 21:45:15 +02001990 else
Johannes Berg9f419f32013-05-08 21:34:22 +02001991 rdev = ERR_PTR(-ENODEV);
1992 dev_put(dev);
Johannes Berg9f419f32013-05-08 21:34:22 +02001993 return rdev;
1994}
1995
Johannes Berg2a519312009-02-10 21:25:55 +01001996int cfg80211_wext_siwscan(struct net_device *dev,
1997 struct iw_request_info *info,
1998 union iwreq_data *wrqu, char *extra)
1999{
2000 struct cfg80211_registered_device *rdev;
2001 struct wiphy *wiphy;
2002 struct iw_scan_req *wreq = NULL;
Johannes Berg65486c8b2009-12-23 15:33:35 +01002003 struct cfg80211_scan_request *creq = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01002004 int i, err, n_channels = 0;
Johannes Berg57fbcce2016-04-12 15:56:15 +02002005 enum nl80211_band band;
Johannes Berg2a519312009-02-10 21:25:55 +01002006
2007 if (!netif_running(dev))
2008 return -ENETDOWN;
2009
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002010 if (wrqu->data.length == sizeof(struct iw_scan_req))
2011 wreq = (struct iw_scan_req *)extra;
2012
Johannes Berg463d0182009-07-14 00:33:35 +02002013 rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01002014
2015 if (IS_ERR(rdev))
2016 return PTR_ERR(rdev);
2017
Johannes Bergf9d15d12014-01-22 11:14:19 +02002018 if (rdev->scan_req || rdev->scan_msg) {
Johannes Berg2a519312009-02-10 21:25:55 +01002019 err = -EBUSY;
2020 goto out;
2021 }
2022
2023 wiphy = &rdev->wiphy;
2024
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002025 /* Determine number of channels, needed to allocate creq */
2026 if (wreq && wreq->num_channels)
2027 n_channels = wreq->num_channels;
Ilan Peerbdfbec22014-01-09 11:37:23 +02002028 else
2029 n_channels = ieee80211_get_num_supported_channels(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +01002030
2031 creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
2032 n_channels * sizeof(void *),
2033 GFP_ATOMIC);
2034 if (!creq) {
2035 err = -ENOMEM;
2036 goto out;
2037 }
2038
2039 creq->wiphy = wiphy;
Johannes Bergfd014282012-06-18 19:17:03 +02002040 creq->wdev = dev->ieee80211_ptr;
Johannes Berg5ba63532009-08-07 17:54:07 +02002041 /* SSIDs come after channels */
2042 creq->ssids = (void *)&creq->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01002043 creq->n_channels = n_channels;
2044 creq->n_ssids = 1;
Sam Leffler15d60302012-10-11 21:03:34 -07002045 creq->scan_start = jiffies;
Johannes Berg2a519312009-02-10 21:25:55 +01002046
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002047 /* translate "Scan on frequencies" request */
Johannes Berg2a519312009-02-10 21:25:55 +01002048 i = 0;
Johannes Berg57fbcce2016-04-12 15:56:15 +02002049 for (band = 0; band < NUM_NL80211_BANDS; band++) {
Johannes Berg2a519312009-02-10 21:25:55 +01002050 int j;
Johannes Berg584991d2009-11-02 13:32:03 +01002051
Johannes Berg2a519312009-02-10 21:25:55 +01002052 if (!wiphy->bands[band])
2053 continue;
Johannes Berg584991d2009-11-02 13:32:03 +01002054
Johannes Berg2a519312009-02-10 21:25:55 +01002055 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01002056 /* ignore disabled channels */
2057 if (wiphy->bands[band]->channels[j].flags &
2058 IEEE80211_CHAN_DISABLED)
2059 continue;
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002060
2061 /* If we have a wireless request structure and the
2062 * wireless request specifies frequencies, then search
2063 * for the matching hardware channel.
2064 */
2065 if (wreq && wreq->num_channels) {
2066 int k;
2067 int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
2068 for (k = 0; k < wreq->num_channels; k++) {
Zhao, Gang96998e32014-04-09 09:28:06 +08002069 struct iw_freq *freq =
2070 &wreq->channel_list[k];
2071 int wext_freq =
2072 cfg80211_wext_freq(freq);
2073
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002074 if (wext_freq == wiphy_freq)
2075 goto wext_freq_found;
2076 }
2077 goto wext_freq_not_found;
2078 }
2079
2080 wext_freq_found:
Johannes Berg2a519312009-02-10 21:25:55 +01002081 creq->channels[i] = &wiphy->bands[band]->channels[j];
2082 i++;
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002083 wext_freq_not_found: ;
Johannes Berg2a519312009-02-10 21:25:55 +01002084 }
2085 }
Holger Schurig8862dc52009-09-11 10:13:55 +02002086 /* No channels found? */
2087 if (!i) {
2088 err = -EINVAL;
2089 goto out;
2090 }
Johannes Berg2a519312009-02-10 21:25:55 +01002091
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002092 /* Set real number of channels specified in creq->channels[] */
2093 creq->n_channels = i;
Johannes Berg2a519312009-02-10 21:25:55 +01002094
Holger Schurigb2e3abd2009-09-09 13:09:54 +02002095 /* translate "Scan for SSID" request */
2096 if (wreq) {
Johannes Berg2a519312009-02-10 21:25:55 +01002097 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
Johannes Berg65486c8b2009-12-23 15:33:35 +01002098 if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
2099 err = -EINVAL;
2100 goto out;
2101 }
Johannes Berg2a519312009-02-10 21:25:55 +01002102 memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
2103 creq->ssids[0].ssid_len = wreq->essid_len;
2104 }
2105 if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
2106 creq->n_ssids = 0;
2107 }
2108
Johannes Berg57fbcce2016-04-12 15:56:15 +02002109 for (i = 0; i < NUM_NL80211_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02002110 if (wiphy->bands[i])
2111 creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02002112
Jouni Malinen818965d2016-02-26 22:12:47 +02002113 eth_broadcast_addr(creq->bssid);
2114
Johannes Berg2a519312009-02-10 21:25:55 +01002115 rdev->scan_req = creq;
Hila Gonene35e4d22012-06-27 17:19:42 +03002116 err = rdev_scan(rdev, creq);
Johannes Berg2a519312009-02-10 21:25:55 +01002117 if (err) {
2118 rdev->scan_req = NULL;
Johannes Berg65486c8b2009-12-23 15:33:35 +01002119 /* creq will be freed below */
Johannes Berg463d0182009-07-14 00:33:35 +02002120 } else {
Johannes Bergfd014282012-06-18 19:17:03 +02002121 nl80211_send_scan_start(rdev, dev->ieee80211_ptr);
Johannes Berg65486c8b2009-12-23 15:33:35 +01002122 /* creq now owned by driver */
2123 creq = NULL;
Johannes Berg463d0182009-07-14 00:33:35 +02002124 dev_hold(dev);
2125 }
Johannes Berg2a519312009-02-10 21:25:55 +01002126 out:
Johannes Berg65486c8b2009-12-23 15:33:35 +01002127 kfree(creq);
Johannes Berg2a519312009-02-10 21:25:55 +01002128 return err;
2129}
Johannes Berg2afe38d2015-01-06 14:00:53 +01002130EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
Johannes Berg2a519312009-02-10 21:25:55 +01002131
James Minor76a70e92015-02-24 12:58:20 -06002132static char *ieee80211_scan_add_ies(struct iw_request_info *info,
2133 const struct cfg80211_bss_ies *ies,
2134 char *current_ev, char *end_buf)
Johannes Berg2a519312009-02-10 21:25:55 +01002135{
Johannes Berg9caf0362012-11-29 01:25:20 +01002136 const u8 *pos, *end, *next;
Johannes Berg2a519312009-02-10 21:25:55 +01002137 struct iw_event iwe;
2138
Johannes Berg9caf0362012-11-29 01:25:20 +01002139 if (!ies)
James Minor76a70e92015-02-24 12:58:20 -06002140 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002141
2142 /*
2143 * If needed, fragment the IEs buffer (at IE boundaries) into short
2144 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
2145 */
Johannes Berg9caf0362012-11-29 01:25:20 +01002146 pos = ies->data;
2147 end = pos + ies->len;
Johannes Berg2a519312009-02-10 21:25:55 +01002148
2149 while (end - pos > IW_GENERIC_IE_MAX) {
2150 next = pos + 2 + pos[1];
2151 while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
2152 next = next + 2 + next[1];
2153
2154 memset(&iwe, 0, sizeof(iwe));
2155 iwe.cmd = IWEVGENIE;
2156 iwe.u.data.length = next - pos;
James Minor76a70e92015-02-24 12:58:20 -06002157 current_ev = iwe_stream_add_point_check(info, current_ev,
2158 end_buf, &iwe,
2159 (void *)pos);
2160 if (IS_ERR(current_ev))
2161 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002162 pos = next;
2163 }
2164
2165 if (end > pos) {
2166 memset(&iwe, 0, sizeof(iwe));
2167 iwe.cmd = IWEVGENIE;
2168 iwe.u.data.length = end - pos;
James Minor76a70e92015-02-24 12:58:20 -06002169 current_ev = iwe_stream_add_point_check(info, current_ev,
2170 end_buf, &iwe,
2171 (void *)pos);
2172 if (IS_ERR(current_ev))
2173 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002174 }
James Minor76a70e92015-02-24 12:58:20 -06002175
2176 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002177}
2178
Johannes Berg2a519312009-02-10 21:25:55 +01002179static char *
Johannes Berg77965c972009-02-18 18:45:06 +01002180ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
2181 struct cfg80211_internal_bss *bss, char *current_ev,
2182 char *end_buf)
Johannes Berg2a519312009-02-10 21:25:55 +01002183{
Johannes Berg9caf0362012-11-29 01:25:20 +01002184 const struct cfg80211_bss_ies *ies;
Johannes Berg2a519312009-02-10 21:25:55 +01002185 struct iw_event iwe;
Johannes Berg9caf0362012-11-29 01:25:20 +01002186 const u8 *ie;
James Minor76a70e92015-02-24 12:58:20 -06002187 u8 buf[50];
2188 u8 *cfg, *p, *tmp;
Johannes Berg9caf0362012-11-29 01:25:20 +01002189 int rem, i, sig;
Johannes Berg2a519312009-02-10 21:25:55 +01002190 bool ismesh = false;
2191
2192 memset(&iwe, 0, sizeof(iwe));
2193 iwe.cmd = SIOCGIWAP;
2194 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
2195 memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
James Minor76a70e92015-02-24 12:58:20 -06002196 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2197 IW_EV_ADDR_LEN);
2198 if (IS_ERR(current_ev))
2199 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002200
2201 memset(&iwe, 0, sizeof(iwe));
2202 iwe.cmd = SIOCGIWFREQ;
2203 iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
2204 iwe.u.freq.e = 0;
James Minor76a70e92015-02-24 12:58:20 -06002205 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2206 IW_EV_FREQ_LEN);
2207 if (IS_ERR(current_ev))
2208 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002209
2210 memset(&iwe, 0, sizeof(iwe));
2211 iwe.cmd = SIOCGIWFREQ;
2212 iwe.u.freq.m = bss->pub.channel->center_freq;
2213 iwe.u.freq.e = 6;
James Minor76a70e92015-02-24 12:58:20 -06002214 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2215 IW_EV_FREQ_LEN);
2216 if (IS_ERR(current_ev))
2217 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002218
Johannes Berg77965c972009-02-18 18:45:06 +01002219 if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
Johannes Berg2a519312009-02-10 21:25:55 +01002220 memset(&iwe, 0, sizeof(iwe));
2221 iwe.cmd = IWEVQUAL;
2222 iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
2223 IW_QUAL_NOISE_INVALID |
Johannes Berga77b8552009-02-18 18:27:22 +01002224 IW_QUAL_QUAL_UPDATED;
Johannes Berg77965c972009-02-18 18:45:06 +01002225 switch (wiphy->signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002226 case CFG80211_SIGNAL_TYPE_MBM:
Johannes Berga77b8552009-02-18 18:27:22 +01002227 sig = bss->pub.signal / 100;
2228 iwe.u.qual.level = sig;
Johannes Berg2a519312009-02-10 21:25:55 +01002229 iwe.u.qual.updated |= IW_QUAL_DBM;
Johannes Berga77b8552009-02-18 18:27:22 +01002230 if (sig < -110) /* rather bad */
2231 sig = -110;
2232 else if (sig > -40) /* perfect */
2233 sig = -40;
2234 /* will give a range of 0 .. 70 */
2235 iwe.u.qual.qual = sig + 110;
Johannes Berg2a519312009-02-10 21:25:55 +01002236 break;
2237 case CFG80211_SIGNAL_TYPE_UNSPEC:
2238 iwe.u.qual.level = bss->pub.signal;
Johannes Berga77b8552009-02-18 18:27:22 +01002239 /* will give range 0 .. 100 */
2240 iwe.u.qual.qual = bss->pub.signal;
Johannes Berg2a519312009-02-10 21:25:55 +01002241 break;
2242 default:
2243 /* not reached */
2244 break;
2245 }
James Minor76a70e92015-02-24 12:58:20 -06002246 current_ev = iwe_stream_add_event_check(info, current_ev,
2247 end_buf, &iwe,
2248 IW_EV_QUAL_LEN);
2249 if (IS_ERR(current_ev))
2250 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002251 }
2252
2253 memset(&iwe, 0, sizeof(iwe));
2254 iwe.cmd = SIOCGIWENCODE;
2255 if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
2256 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
2257 else
2258 iwe.u.data.flags = IW_ENCODE_DISABLED;
2259 iwe.u.data.length = 0;
James Minor76a70e92015-02-24 12:58:20 -06002260 current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2261 &iwe, "");
2262 if (IS_ERR(current_ev))
2263 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002264
Johannes Berg9caf0362012-11-29 01:25:20 +01002265 rcu_read_lock();
2266 ies = rcu_dereference(bss->pub.ies);
Johannes Berg83c7aa12013-02-05 16:51:29 +01002267 rem = ies->len;
2268 ie = ies->data;
Johannes Berg9caf0362012-11-29 01:25:20 +01002269
Johannes Berg83c7aa12013-02-05 16:51:29 +01002270 while (rem >= 2) {
Johannes Berg2a519312009-02-10 21:25:55 +01002271 /* invalid data */
2272 if (ie[1] > rem - 2)
2273 break;
2274
2275 switch (ie[0]) {
2276 case WLAN_EID_SSID:
2277 memset(&iwe, 0, sizeof(iwe));
2278 iwe.cmd = SIOCGIWESSID;
2279 iwe.u.data.length = ie[1];
2280 iwe.u.data.flags = 1;
James Minor76a70e92015-02-24 12:58:20 -06002281 current_ev = iwe_stream_add_point_check(info,
2282 current_ev,
2283 end_buf, &iwe,
2284 (u8 *)ie + 2);
2285 if (IS_ERR(current_ev))
2286 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002287 break;
2288 case WLAN_EID_MESH_ID:
2289 memset(&iwe, 0, sizeof(iwe));
2290 iwe.cmd = SIOCGIWESSID;
2291 iwe.u.data.length = ie[1];
2292 iwe.u.data.flags = 1;
James Minor76a70e92015-02-24 12:58:20 -06002293 current_ev = iwe_stream_add_point_check(info,
2294 current_ev,
2295 end_buf, &iwe,
2296 (u8 *)ie + 2);
2297 if (IS_ERR(current_ev))
2298 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002299 break;
2300 case WLAN_EID_MESH_CONFIG:
2301 ismesh = true;
Rui Paulo136cfa22009-11-18 18:40:00 +00002302 if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
Johannes Berg2a519312009-02-10 21:25:55 +01002303 break;
Johannes Berg9caf0362012-11-29 01:25:20 +01002304 cfg = (u8 *)ie + 2;
Johannes Berg2a519312009-02-10 21:25:55 +01002305 memset(&iwe, 0, sizeof(iwe));
2306 iwe.cmd = IWEVCUSTOM;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002307 sprintf(buf, "Mesh Network Path Selection Protocol ID: "
2308 "0x%02X", cfg[0]);
Johannes Berg2a519312009-02-10 21:25:55 +01002309 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002310 current_ev = iwe_stream_add_point_check(info,
2311 current_ev,
2312 end_buf,
2313 &iwe, buf);
2314 if (IS_ERR(current_ev))
2315 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002316 sprintf(buf, "Path Selection Metric ID: 0x%02X",
2317 cfg[1]);
Johannes Berg2a519312009-02-10 21:25:55 +01002318 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002319 current_ev = iwe_stream_add_point_check(info,
2320 current_ev,
2321 end_buf,
2322 &iwe, buf);
2323 if (IS_ERR(current_ev))
2324 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002325 sprintf(buf, "Congestion Control Mode ID: 0x%02X",
2326 cfg[2]);
Johannes Berg2a519312009-02-10 21:25:55 +01002327 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002328 current_ev = iwe_stream_add_point_check(info,
2329 current_ev,
2330 end_buf,
2331 &iwe, buf);
2332 if (IS_ERR(current_ev))
2333 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002334 sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
Johannes Berg2a519312009-02-10 21:25:55 +01002335 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002336 current_ev = iwe_stream_add_point_check(info,
2337 current_ev,
2338 end_buf,
2339 &iwe, buf);
2340 if (IS_ERR(current_ev))
2341 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002342 sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
2343 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002344 current_ev = iwe_stream_add_point_check(info,
2345 current_ev,
2346 end_buf,
2347 &iwe, buf);
2348 if (IS_ERR(current_ev))
2349 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002350 sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
2351 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002352 current_ev = iwe_stream_add_point_check(info,
2353 current_ev,
2354 end_buf,
2355 &iwe, buf);
2356 if (IS_ERR(current_ev))
2357 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002358 sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
Johannes Berg2a519312009-02-10 21:25:55 +01002359 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002360 current_ev = iwe_stream_add_point_check(info,
2361 current_ev,
2362 end_buf,
2363 &iwe, buf);
2364 if (IS_ERR(current_ev))
2365 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002366 break;
2367 case WLAN_EID_SUPP_RATES:
2368 case WLAN_EID_EXT_SUPP_RATES:
2369 /* display all supported rates in readable format */
2370 p = current_ev + iwe_stream_lcp_len(info);
2371
2372 memset(&iwe, 0, sizeof(iwe));
2373 iwe.cmd = SIOCGIWRATE;
2374 /* Those two flags are ignored... */
2375 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
2376
2377 for (i = 0; i < ie[1]; i++) {
2378 iwe.u.bitrate.value =
2379 ((ie[i + 2] & 0x7f) * 500000);
James Minor76a70e92015-02-24 12:58:20 -06002380 tmp = p;
Johannes Berg2a519312009-02-10 21:25:55 +01002381 p = iwe_stream_add_value(info, current_ev, p,
James Minor76a70e92015-02-24 12:58:20 -06002382 end_buf, &iwe,
2383 IW_EV_PARAM_LEN);
2384 if (p == tmp) {
2385 current_ev = ERR_PTR(-E2BIG);
2386 goto unlock;
2387 }
Johannes Berg2a519312009-02-10 21:25:55 +01002388 }
2389 current_ev = p;
2390 break;
2391 }
2392 rem -= ie[1] + 2;
2393 ie += ie[1] + 2;
2394 }
2395
Joe Perchesf64f9e72009-11-29 16:55:45 -08002396 if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
2397 ismesh) {
Johannes Berg2a519312009-02-10 21:25:55 +01002398 memset(&iwe, 0, sizeof(iwe));
2399 iwe.cmd = SIOCGIWMODE;
2400 if (ismesh)
2401 iwe.u.mode = IW_MODE_MESH;
2402 else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
2403 iwe.u.mode = IW_MODE_MASTER;
2404 else
2405 iwe.u.mode = IW_MODE_ADHOC;
James Minor76a70e92015-02-24 12:58:20 -06002406 current_ev = iwe_stream_add_event_check(info, current_ev,
2407 end_buf, &iwe,
2408 IW_EV_UINT_LEN);
2409 if (IS_ERR(current_ev))
2410 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002411 }
2412
James Minor76a70e92015-02-24 12:58:20 -06002413 memset(&iwe, 0, sizeof(iwe));
2414 iwe.cmd = IWEVCUSTOM;
2415 sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
2416 iwe.u.data.length = strlen(buf);
2417 current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2418 &iwe, buf);
2419 if (IS_ERR(current_ev))
2420 goto unlock;
2421 memset(&iwe, 0, sizeof(iwe));
2422 iwe.cmd = IWEVCUSTOM;
2423 sprintf(buf, " Last beacon: %ums ago",
2424 elapsed_jiffies_msecs(bss->ts));
2425 iwe.u.data.length = strlen(buf);
2426 current_ev = iwe_stream_add_point_check(info, current_ev,
2427 end_buf, &iwe, buf);
2428 if (IS_ERR(current_ev))
2429 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002430
James Minor76a70e92015-02-24 12:58:20 -06002431 current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
2432
2433 unlock:
Johannes Berg9caf0362012-11-29 01:25:20 +01002434 rcu_read_unlock();
Johannes Berg2a519312009-02-10 21:25:55 +01002435 return current_ev;
2436}
2437
2438
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002439static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
Johannes Berg2a519312009-02-10 21:25:55 +01002440 struct iw_request_info *info,
2441 char *buf, size_t len)
2442{
2443 char *current_ev = buf;
2444 char *end_buf = buf + len;
2445 struct cfg80211_internal_bss *bss;
James Minor76a70e92015-02-24 12:58:20 -06002446 int err = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01002447
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002448 spin_lock_bh(&rdev->bss_lock);
2449 cfg80211_bss_expire(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01002450
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002451 list_for_each_entry(bss, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01002452 if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
James Minor76a70e92015-02-24 12:58:20 -06002453 err = -E2BIG;
2454 break;
Johannes Berg2a519312009-02-10 21:25:55 +01002455 }
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002456 current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
Johannes Berg77965c972009-02-18 18:45:06 +01002457 current_ev, end_buf);
James Minor76a70e92015-02-24 12:58:20 -06002458 if (IS_ERR(current_ev)) {
2459 err = PTR_ERR(current_ev);
2460 break;
2461 }
Johannes Berg2a519312009-02-10 21:25:55 +01002462 }
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002463 spin_unlock_bh(&rdev->bss_lock);
James Minor76a70e92015-02-24 12:58:20 -06002464
2465 if (err)
2466 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01002467 return current_ev - buf;
2468}
2469
2470
2471int cfg80211_wext_giwscan(struct net_device *dev,
2472 struct iw_request_info *info,
2473 struct iw_point *data, char *extra)
2474{
2475 struct cfg80211_registered_device *rdev;
2476 int res;
2477
2478 if (!netif_running(dev))
2479 return -ENETDOWN;
2480
Johannes Berg463d0182009-07-14 00:33:35 +02002481 rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01002482
2483 if (IS_ERR(rdev))
2484 return PTR_ERR(rdev);
2485
Johannes Bergf9d15d12014-01-22 11:14:19 +02002486 if (rdev->scan_req || rdev->scan_msg)
Johannes Berg5fe231e2013-05-08 21:45:15 +02002487 return -EAGAIN;
Johannes Berg2a519312009-02-10 21:25:55 +01002488
2489 res = ieee80211_scan_results(rdev, info, extra, data->length);
2490 data->length = 0;
2491 if (res >= 0) {
2492 data->length = res;
2493 res = 0;
2494 }
2495
Johannes Berg2a519312009-02-10 21:25:55 +01002496 return res;
2497}
Johannes Berg2afe38d2015-01-06 14:00:53 +01002498EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
Johannes Berg2a519312009-02-10 21:25:55 +01002499#endif