blob: bda9114ded77f86dbf66d393a47511b2a9d55763 [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
1459static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
1460 struct cfg80211_inform_bss *data,
1461 enum cfg80211_bss_frame_type ftype,
1462 const u8 *bssid, u64 tsf,
1463 u16 beacon_interval, const u8 *ie,
1464 size_t ielen,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001465 struct cfg80211_non_tx_bss *non_tx_data,
Peng Xu0b8fb822019-01-21 12:14:57 +02001466 gfp_t gfp)
1467{
Johannes Berg1c8745f2019-02-07 22:36:33 +01001468 const u8 *mbssid_index_ie;
1469 const struct element *elem, *sub;
1470 size_t new_ie_len;
Peng Xu0b8fb822019-01-21 12:14:57 +02001471 u8 new_bssid[ETH_ALEN];
1472 u8 *new_ie;
1473 u16 capability;
1474 struct cfg80211_bss *bss;
1475
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001476 if (!non_tx_data)
Peng Xu0b8fb822019-01-21 12:14:57 +02001477 return;
1478 if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1479 return;
Sara Sharon213ed572019-01-16 23:02:03 +02001480 if (!wiphy->support_mbssid)
1481 return;
1482 if (wiphy->support_only_he_mbssid &&
1483 !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
1484 return;
Peng Xu0b8fb822019-01-21 12:14:57 +02001485
Peng Xu0b8fb822019-01-21 12:14:57 +02001486 new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
1487 if (!new_ie)
1488 return;
1489
Johannes Berg1c8745f2019-02-07 22:36:33 +01001490 for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
1491 if (elem->datalen < 4)
1492 continue;
1493 for_each_element(sub, elem->data + 1, elem->datalen - 1) {
1494 if (sub->id != 0 || sub->datalen < 4) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001495 /* not a valid BSS profile */
1496 continue;
1497 }
1498
Johannes Berg1c8745f2019-02-07 22:36:33 +01001499 if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
1500 sub->data[1] != 2) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001501 /* The first element within the Nontransmitted
1502 * BSSID Profile is not the Nontransmitted
1503 * BSSID Capability element.
1504 */
1505 continue;
1506 }
1507
1508 /* found a Nontransmitted BSSID Profile */
1509 mbssid_index_ie = cfg80211_find_ie
1510 (WLAN_EID_MULTI_BSSID_IDX,
Johannes Berg1c8745f2019-02-07 22:36:33 +01001511 sub->data, sub->datalen);
Peng Xu0b8fb822019-01-21 12:14:57 +02001512 if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
1513 mbssid_index_ie[2] == 0) {
1514 /* No valid Multiple BSSID-Index element */
1515 continue;
1516 }
1517
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001518 non_tx_data->bssid_index = mbssid_index_ie[2];
1519 non_tx_data->max_bssid_indicator = elem->data[0];
1520
1521 cfg80211_gen_new_bssid(bssid,
1522 non_tx_data->max_bssid_indicator,
1523 non_tx_data->bssid_index,
Peng Xu0b8fb822019-01-21 12:14:57 +02001524 new_bssid);
1525 memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
Johannes Berg1c8745f2019-02-07 22:36:33 +01001526 new_ie_len = cfg80211_gen_new_ie(ie, ielen, sub->data,
1527 sub->datalen, new_ie,
Peng Xu0b8fb822019-01-21 12:14:57 +02001528 gfp);
1529 if (!new_ie_len)
1530 continue;
1531
Johannes Berg1c8745f2019-02-07 22:36:33 +01001532 capability = get_unaligned_le16(sub->data + 2);
Peng Xu0b8fb822019-01-21 12:14:57 +02001533 bss = cfg80211_inform_single_bss_data(wiphy, data,
1534 ftype,
1535 new_bssid, tsf,
1536 capability,
1537 beacon_interval,
1538 new_ie,
1539 new_ie_len,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001540 non_tx_data,
1541 gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001542 if (!bss)
1543 break;
1544 cfg80211_put_bss(wiphy, bss);
1545 }
Peng Xu0b8fb822019-01-21 12:14:57 +02001546 }
1547
1548 kfree(new_ie);
1549}
1550
1551struct cfg80211_bss *
1552cfg80211_inform_bss_data(struct wiphy *wiphy,
1553 struct cfg80211_inform_bss *data,
1554 enum cfg80211_bss_frame_type ftype,
1555 const u8 *bssid, u64 tsf, u16 capability,
1556 u16 beacon_interval, const u8 *ie, size_t ielen,
1557 gfp_t gfp)
1558{
1559 struct cfg80211_bss *res;
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001560 struct cfg80211_non_tx_bss non_tx_data;
Peng Xu0b8fb822019-01-21 12:14:57 +02001561
1562 res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
1563 capability, beacon_interval, ie,
1564 ielen, NULL, gfp);
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001565 non_tx_data.tx_bss = res;
Peng Xu0b8fb822019-01-21 12:14:57 +02001566 cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001567 beacon_interval, ie, ielen, &non_tx_data,
1568 gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001569 return res;
1570}
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001571EXPORT_SYMBOL(cfg80211_inform_bss_data);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001572
Peng Xu0b8fb822019-01-21 12:14:57 +02001573static void
1574cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy,
1575 struct cfg80211_inform_bss *data,
1576 struct ieee80211_mgmt *mgmt, size_t len,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001577 struct cfg80211_non_tx_bss *non_tx_data,
Peng Xu0b8fb822019-01-21 12:14:57 +02001578 gfp_t gfp)
1579{
1580 enum cfg80211_bss_frame_type ftype;
1581 const u8 *ie = mgmt->u.probe_resp.variable;
1582 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1583 u.probe_resp.variable);
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001584
Peng Xu0b8fb822019-01-21 12:14:57 +02001585 ftype = ieee80211_is_beacon(mgmt->frame_control) ?
1586 CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
1587
1588 cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid,
1589 le64_to_cpu(mgmt->u.probe_resp.timestamp),
1590 le16_to_cpu(mgmt->u.probe_resp.beacon_int),
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001591 ie, ielen, non_tx_data, gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001592}
1593
1594static void
1595cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
Sara Sharon7011ba52019-01-21 12:25:59 +02001596 struct cfg80211_bss *nontrans_bss,
Peng Xu0b8fb822019-01-21 12:14:57 +02001597 struct ieee80211_mgmt *mgmt, size_t len,
1598 gfp_t gfp)
1599{
1600 u8 *ie, *new_ie, *pos;
1601 const u8 *nontrans_ssid, *trans_ssid, *mbssid;
1602 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1603 u.probe_resp.variable);
1604 size_t new_ie_len;
1605 struct cfg80211_bss_ies *new_ies;
1606 const struct cfg80211_bss_ies *old;
1607 u8 cpy_len;
1608
1609 ie = mgmt->u.probe_resp.variable;
1610
1611 new_ie_len = ielen;
1612 trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
1613 if (!trans_ssid)
1614 return;
1615 new_ie_len -= trans_ssid[1];
1616 mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
1617 if (!mbssid)
1618 return;
1619 new_ie_len -= mbssid[1];
1620 rcu_read_lock();
Sara Sharon7011ba52019-01-21 12:25:59 +02001621 nontrans_ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID);
Peng Xu0b8fb822019-01-21 12:14:57 +02001622 if (!nontrans_ssid) {
1623 rcu_read_unlock();
1624 return;
1625 }
1626 new_ie_len += nontrans_ssid[1];
1627 rcu_read_unlock();
1628
1629 /* generate new ie for nontrans BSS
1630 * 1. replace SSID with nontrans BSS' SSID
1631 * 2. skip MBSSID IE
1632 */
1633 new_ie = kzalloc(new_ie_len, gfp);
1634 if (!new_ie)
1635 return;
1636 new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, gfp);
Sara Sharonbede8d22019-01-30 08:48:21 +02001637 if (!new_ies)
1638 goto out_free;
Peng Xu0b8fb822019-01-21 12:14:57 +02001639
1640 pos = new_ie;
1641
1642 /* copy the nontransmitted SSID */
1643 cpy_len = nontrans_ssid[1] + 2;
1644 memcpy(pos, nontrans_ssid, cpy_len);
1645 pos += cpy_len;
1646 /* copy the IEs between SSID and MBSSID */
1647 cpy_len = trans_ssid[1] + 2;
1648 memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
1649 pos += (mbssid - (trans_ssid + cpy_len));
1650 /* copy the IEs after MBSSID */
1651 cpy_len = mbssid[1] + 2;
1652 memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
1653
1654 /* update ie */
1655 new_ies->len = new_ie_len;
1656 new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
1657 new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
1658 memcpy(new_ies->data, new_ie, new_ie_len);
1659 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
Sara Sharon7011ba52019-01-21 12:25:59 +02001660 old = rcu_access_pointer(nontrans_bss->proberesp_ies);
1661 rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies);
1662 rcu_assign_pointer(nontrans_bss->ies, new_ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001663 if (old)
1664 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1665 } else {
Sara Sharon7011ba52019-01-21 12:25:59 +02001666 old = rcu_access_pointer(nontrans_bss->beacon_ies);
1667 rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies);
1668 rcu_assign_pointer(nontrans_bss->ies, new_ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001669 if (old)
1670 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1671 }
Sara Sharonbede8d22019-01-30 08:48:21 +02001672
1673out_free:
1674 kfree(new_ie);
Peng Xu0b8fb822019-01-21 12:14:57 +02001675}
1676
1677/* cfg80211_inform_bss_width_frame helper */
1678static struct cfg80211_bss *
1679cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
1680 struct cfg80211_inform_bss *data,
1681 struct ieee80211_mgmt *mgmt, size_t len,
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001682 struct cfg80211_non_tx_bss *non_tx_data,
Peng Xu0b8fb822019-01-21 12:14:57 +02001683 gfp_t gfp)
Johannes Berg2a519312009-02-10 21:25:55 +01001684{
Johannes Berg9caf0362012-11-29 01:25:20 +01001685 struct cfg80211_internal_bss tmp = {}, *res;
1686 struct cfg80211_bss_ies *ies;
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001687 struct ieee80211_channel *channel;
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001688 bool signal_valid;
Johannes Berg2a519312009-02-10 21:25:55 +01001689 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1690 u.probe_resp.variable);
Dedy Lansky6eb18132015-02-08 15:52:03 +02001691 int bss_type;
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001692
Johannes Berg0172bb72012-11-23 14:23:30 +01001693 BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
1694 offsetof(struct ieee80211_mgmt, u.beacon.variable));
1695
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001696 trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
Beni Lev4ee3e062012-08-27 12:49:39 +03001697
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001698 if (WARN_ON(!mgmt))
1699 return NULL;
1700
1701 if (WARN_ON(!wiphy))
1702 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001703
Sujith22fe88d2010-05-13 10:34:08 +05301704 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001705 (data->signal < 0 || data->signal > 100)))
Johannes Berg2a519312009-02-10 21:25:55 +01001706 return NULL;
1707
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001708 if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
Johannes Berg2a519312009-02-10 21:25:55 +01001709 return NULL;
1710
Johannes Berg0172bb72012-11-23 14:23:30 +01001711 channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
Jouni Malinen119f94a2018-09-05 18:52:22 +03001712 ielen, data->chan, data->scan_width);
Johannes Berg0172bb72012-11-23 14:23:30 +01001713 if (!channel)
1714 return NULL;
1715
Johannes Berg0e227082014-08-12 20:34:30 +02001716 ies = kzalloc(sizeof(*ies) + ielen, gfp);
Johannes Berg9caf0362012-11-29 01:25:20 +01001717 if (!ies)
Johannes Berg2a519312009-02-10 21:25:55 +01001718 return NULL;
Johannes Berg9caf0362012-11-29 01:25:20 +01001719 ies->len = ielen;
Johannes Berg8cef2c92013-02-05 16:54:31 +01001720 ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
Johannes Berg0e227082014-08-12 20:34:30 +02001721 ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
Johannes Berg9caf0362012-11-29 01:25:20 +01001722 memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
Johannes Berg2a519312009-02-10 21:25:55 +01001723
Johannes Berg9caf0362012-11-29 01:25:20 +01001724 if (ieee80211_is_probe_resp(mgmt->frame_control))
1725 rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
1726 else
1727 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
1728 rcu_assign_pointer(tmp.pub.ies, ies);
Arend Van Spriel505a2e82016-12-16 11:21:54 +00001729
Johannes Berg9caf0362012-11-29 01:25:20 +01001730 memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
1731 tmp.pub.channel = channel;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001732 tmp.pub.scan_width = data->scan_width;
1733 tmp.pub.signal = data->signal;
Johannes Berg9caf0362012-11-29 01:25:20 +01001734 tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
1735 tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001736 tmp.ts_boottime = data->boottime_ns;
Avraham Stern1d762502016-07-05 17:10:13 +03001737 tmp.parent_tsf = data->parent_tsf;
Sunil Dutt983dafa2017-12-13 19:51:36 +02001738 tmp.pub.chains = data->chains;
1739 memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
Avraham Stern1d762502016-07-05 17:10:13 +03001740 ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001741 if (non_tx_data) {
1742 tmp.pub.transmitted_bss = non_tx_data->tx_bss;
1743 tmp.pub.bssid_index = non_tx_data->bssid_index;
1744 tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
1745 }
Johannes Berg2a519312009-02-10 21:25:55 +01001746
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001747 signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001748 wiphy->max_adj_channel_rssi_comp;
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001749 res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
Johannes Berg2a519312009-02-10 21:25:55 +01001750 if (!res)
1751 return NULL;
1752
Johannes Berg57fbcce2016-04-12 15:56:15 +02001753 if (channel->band == NL80211_BAND_60GHZ) {
Dedy Lansky6eb18132015-02-08 15:52:03 +02001754 bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
1755 if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
1756 bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
1757 regulatory_hint_found_beacon(wiphy, channel, gfp);
1758 } else {
1759 if (res->pub.capability & WLAN_CAPABILITY_ESS)
1760 regulatory_hint_found_beacon(wiphy, channel, gfp);
1761 }
Luis R. Rodrigueze38f8a72009-02-21 00:20:39 -05001762
Beni Lev4ee3e062012-08-27 12:49:39 +03001763 trace_cfg80211_return_bss(&res->pub);
Johannes Berg2a519312009-02-10 21:25:55 +01001764 /* cfg80211_bss_update gives us a referenced result */
1765 return &res->pub;
1766}
Peng Xu0b8fb822019-01-21 12:14:57 +02001767
1768struct cfg80211_bss *
1769cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
1770 struct cfg80211_inform_bss *data,
1771 struct ieee80211_mgmt *mgmt, size_t len,
1772 gfp_t gfp)
1773{
Sara Sharon7011ba52019-01-21 12:25:59 +02001774 struct cfg80211_bss *res, *tmp_bss;
Peng Xu0b8fb822019-01-21 12:14:57 +02001775 const u8 *ie = mgmt->u.probe_resp.variable;
1776 const struct cfg80211_bss_ies *ies1, *ies2;
1777 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1778 u.probe_resp.variable);
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001779 struct cfg80211_non_tx_bss non_tx_data;
Peng Xu0b8fb822019-01-21 12:14:57 +02001780
1781 res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
1782 len, NULL, gfp);
Sara Sharon213ed572019-01-16 23:02:03 +02001783 if (!res || !wiphy->support_mbssid ||
1784 !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1785 return res;
1786 if (wiphy->support_only_he_mbssid &&
1787 !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
Peng Xu0b8fb822019-01-21 12:14:57 +02001788 return res;
1789
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001790 non_tx_data.tx_bss = res;
Peng Xu0b8fb822019-01-21 12:14:57 +02001791 /* process each non-transmitting bss */
Sara Sharon0cd01ef2019-01-22 09:50:50 +02001792 cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len,
1793 &non_tx_data, gfp);
Peng Xu0b8fb822019-01-21 12:14:57 +02001794
1795 /* check if the res has other nontransmitting bss which is not
1796 * in MBSSID IE
1797 */
1798 ies1 = rcu_access_pointer(res->ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001799
1800 /* go through nontrans_list, if the timestamp of the BSS is
1801 * earlier than the timestamp of the transmitting BSS then
1802 * update it
1803 */
Sara Sharon7011ba52019-01-21 12:25:59 +02001804 list_for_each_entry(tmp_bss, &res->nontrans_list,
Peng Xu0b8fb822019-01-21 12:14:57 +02001805 nontrans_list) {
Sara Sharon7011ba52019-01-21 12:25:59 +02001806 ies2 = rcu_access_pointer(tmp_bss->ies);
Peng Xu0b8fb822019-01-21 12:14:57 +02001807 if (ies2->tsf < ies1->tsf)
1808 cfg80211_update_notlisted_nontrans(wiphy, tmp_bss,
1809 mgmt, len, gfp);
1810 }
1811
1812 return res;
1813}
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001814EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
Johannes Berg2a519312009-02-10 21:25:55 +01001815
Johannes Berg5b112d32013-02-01 01:49:58 +01001816void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001817{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001818 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001819 struct cfg80211_internal_bss *bss;
1820
1821 if (!pub)
1822 return;
1823
1824 bss = container_of(pub, struct cfg80211_internal_bss, pub);
Johannes Berg776b3582013-02-01 02:06:18 +01001825
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001826 spin_lock_bh(&rdev->bss_lock);
1827 bss_ref_get(rdev, bss);
1828 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001829}
1830EXPORT_SYMBOL(cfg80211_ref_bss);
1831
Johannes Berg5b112d32013-02-01 01:49:58 +01001832void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
Johannes Berg2a519312009-02-10 21:25:55 +01001833{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001834 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +01001835 struct cfg80211_internal_bss *bss;
1836
1837 if (!pub)
1838 return;
1839
1840 bss = container_of(pub, struct cfg80211_internal_bss, pub);
Johannes Berg776b3582013-02-01 02:06:18 +01001841
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001842 spin_lock_bh(&rdev->bss_lock);
1843 bss_ref_put(rdev, bss);
1844 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +01001845}
1846EXPORT_SYMBOL(cfg80211_put_bss);
1847
Johannes Bergd491af12009-02-10 21:25:58 +01001848void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1849{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001850 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Sara Sharon7011ba52019-01-21 12:25:59 +02001851 struct cfg80211_internal_bss *bss, *tmp1;
1852 struct cfg80211_bss *nontrans_bss, *tmp;
Johannes Bergd491af12009-02-10 21:25:58 +01001853
1854 if (WARN_ON(!pub))
1855 return;
1856
1857 bss = container_of(pub, struct cfg80211_internal_bss, pub);
1858
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001859 spin_lock_bh(&rdev->bss_lock);
Sara Sharon7011ba52019-01-21 12:25:59 +02001860 if (list_empty(&bss->list))
1861 goto out;
Peng Xu0b8fb822019-01-21 12:14:57 +02001862
Sara Sharon7011ba52019-01-21 12:25:59 +02001863 list_for_each_entry_safe(nontrans_bss, tmp,
1864 &pub->nontrans_list,
1865 nontrans_list) {
1866 tmp1 = container_of(nontrans_bss,
1867 struct cfg80211_internal_bss, pub);
1868 if (__cfg80211_unlink_bss(rdev, tmp1))
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001869 rdev->bss_generation++;
Johannes Berg32073902010-10-06 21:18:04 +02001870 }
Sara Sharon7011ba52019-01-21 12:25:59 +02001871
1872 if (__cfg80211_unlink_bss(rdev, bss))
1873 rdev->bss_generation++;
1874out:
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001875 spin_unlock_bh(&rdev->bss_lock);
Johannes Bergd491af12009-02-10 21:25:58 +01001876}
1877EXPORT_SYMBOL(cfg80211_unlink_bss);
1878
Johannes Berg3d23e342009-09-29 23:27:28 +02001879#ifdef CONFIG_CFG80211_WEXT
Johannes Berg9f419f32013-05-08 21:34:22 +02001880static struct cfg80211_registered_device *
1881cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
1882{
Johannes Berg5fe231e2013-05-08 21:45:15 +02001883 struct cfg80211_registered_device *rdev;
Johannes Berg9f419f32013-05-08 21:34:22 +02001884 struct net_device *dev;
1885
Johannes Berg5fe231e2013-05-08 21:45:15 +02001886 ASSERT_RTNL();
1887
Johannes Berg9f419f32013-05-08 21:34:22 +02001888 dev = dev_get_by_index(net, ifindex);
1889 if (!dev)
Johannes Berg5fe231e2013-05-08 21:45:15 +02001890 return ERR_PTR(-ENODEV);
1891 if (dev->ieee80211_ptr)
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001892 rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
Johannes Berg5fe231e2013-05-08 21:45:15 +02001893 else
Johannes Berg9f419f32013-05-08 21:34:22 +02001894 rdev = ERR_PTR(-ENODEV);
1895 dev_put(dev);
Johannes Berg9f419f32013-05-08 21:34:22 +02001896 return rdev;
1897}
1898
Johannes Berg2a519312009-02-10 21:25:55 +01001899int cfg80211_wext_siwscan(struct net_device *dev,
1900 struct iw_request_info *info,
1901 union iwreq_data *wrqu, char *extra)
1902{
1903 struct cfg80211_registered_device *rdev;
1904 struct wiphy *wiphy;
1905 struct iw_scan_req *wreq = NULL;
Johannes Berg65486c8b2009-12-23 15:33:35 +01001906 struct cfg80211_scan_request *creq = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001907 int i, err, n_channels = 0;
Johannes Berg57fbcce2016-04-12 15:56:15 +02001908 enum nl80211_band band;
Johannes Berg2a519312009-02-10 21:25:55 +01001909
1910 if (!netif_running(dev))
1911 return -ENETDOWN;
1912
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001913 if (wrqu->data.length == sizeof(struct iw_scan_req))
1914 wreq = (struct iw_scan_req *)extra;
1915
Johannes Berg463d0182009-07-14 00:33:35 +02001916 rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01001917
1918 if (IS_ERR(rdev))
1919 return PTR_ERR(rdev);
1920
Johannes Bergf9d15d12014-01-22 11:14:19 +02001921 if (rdev->scan_req || rdev->scan_msg) {
Johannes Berg2a519312009-02-10 21:25:55 +01001922 err = -EBUSY;
1923 goto out;
1924 }
1925
1926 wiphy = &rdev->wiphy;
1927
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001928 /* Determine number of channels, needed to allocate creq */
1929 if (wreq && wreq->num_channels)
1930 n_channels = wreq->num_channels;
Ilan Peerbdfbec22014-01-09 11:37:23 +02001931 else
1932 n_channels = ieee80211_get_num_supported_channels(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +01001933
1934 creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
1935 n_channels * sizeof(void *),
1936 GFP_ATOMIC);
1937 if (!creq) {
1938 err = -ENOMEM;
1939 goto out;
1940 }
1941
1942 creq->wiphy = wiphy;
Johannes Bergfd014282012-06-18 19:17:03 +02001943 creq->wdev = dev->ieee80211_ptr;
Johannes Berg5ba63532009-08-07 17:54:07 +02001944 /* SSIDs come after channels */
1945 creq->ssids = (void *)&creq->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01001946 creq->n_channels = n_channels;
1947 creq->n_ssids = 1;
Sam Leffler15d60302012-10-11 21:03:34 -07001948 creq->scan_start = jiffies;
Johannes Berg2a519312009-02-10 21:25:55 +01001949
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001950 /* translate "Scan on frequencies" request */
Johannes Berg2a519312009-02-10 21:25:55 +01001951 i = 0;
Johannes Berg57fbcce2016-04-12 15:56:15 +02001952 for (band = 0; band < NUM_NL80211_BANDS; band++) {
Johannes Berg2a519312009-02-10 21:25:55 +01001953 int j;
Johannes Berg584991d2009-11-02 13:32:03 +01001954
Johannes Berg2a519312009-02-10 21:25:55 +01001955 if (!wiphy->bands[band])
1956 continue;
Johannes Berg584991d2009-11-02 13:32:03 +01001957
Johannes Berg2a519312009-02-10 21:25:55 +01001958 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01001959 /* ignore disabled channels */
1960 if (wiphy->bands[band]->channels[j].flags &
1961 IEEE80211_CHAN_DISABLED)
1962 continue;
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001963
1964 /* If we have a wireless request structure and the
1965 * wireless request specifies frequencies, then search
1966 * for the matching hardware channel.
1967 */
1968 if (wreq && wreq->num_channels) {
1969 int k;
1970 int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
1971 for (k = 0; k < wreq->num_channels; k++) {
Zhao, Gang96998e32014-04-09 09:28:06 +08001972 struct iw_freq *freq =
1973 &wreq->channel_list[k];
1974 int wext_freq =
1975 cfg80211_wext_freq(freq);
1976
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001977 if (wext_freq == wiphy_freq)
1978 goto wext_freq_found;
1979 }
1980 goto wext_freq_not_found;
1981 }
1982
1983 wext_freq_found:
Johannes Berg2a519312009-02-10 21:25:55 +01001984 creq->channels[i] = &wiphy->bands[band]->channels[j];
1985 i++;
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001986 wext_freq_not_found: ;
Johannes Berg2a519312009-02-10 21:25:55 +01001987 }
1988 }
Holger Schurig8862dc52009-09-11 10:13:55 +02001989 /* No channels found? */
1990 if (!i) {
1991 err = -EINVAL;
1992 goto out;
1993 }
Johannes Berg2a519312009-02-10 21:25:55 +01001994
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001995 /* Set real number of channels specified in creq->channels[] */
1996 creq->n_channels = i;
Johannes Berg2a519312009-02-10 21:25:55 +01001997
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001998 /* translate "Scan for SSID" request */
1999 if (wreq) {
Johannes Berg2a519312009-02-10 21:25:55 +01002000 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
Johannes Berg65486c8b2009-12-23 15:33:35 +01002001 if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
2002 err = -EINVAL;
2003 goto out;
2004 }
Johannes Berg2a519312009-02-10 21:25:55 +01002005 memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
2006 creq->ssids[0].ssid_len = wreq->essid_len;
2007 }
2008 if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
2009 creq->n_ssids = 0;
2010 }
2011
Johannes Berg57fbcce2016-04-12 15:56:15 +02002012 for (i = 0; i < NUM_NL80211_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02002013 if (wiphy->bands[i])
2014 creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02002015
Jouni Malinen818965d2016-02-26 22:12:47 +02002016 eth_broadcast_addr(creq->bssid);
2017
Johannes Berg2a519312009-02-10 21:25:55 +01002018 rdev->scan_req = creq;
Hila Gonene35e4d22012-06-27 17:19:42 +03002019 err = rdev_scan(rdev, creq);
Johannes Berg2a519312009-02-10 21:25:55 +01002020 if (err) {
2021 rdev->scan_req = NULL;
Johannes Berg65486c8b2009-12-23 15:33:35 +01002022 /* creq will be freed below */
Johannes Berg463d0182009-07-14 00:33:35 +02002023 } else {
Johannes Bergfd014282012-06-18 19:17:03 +02002024 nl80211_send_scan_start(rdev, dev->ieee80211_ptr);
Johannes Berg65486c8b2009-12-23 15:33:35 +01002025 /* creq now owned by driver */
2026 creq = NULL;
Johannes Berg463d0182009-07-14 00:33:35 +02002027 dev_hold(dev);
2028 }
Johannes Berg2a519312009-02-10 21:25:55 +01002029 out:
Johannes Berg65486c8b2009-12-23 15:33:35 +01002030 kfree(creq);
Johannes Berg2a519312009-02-10 21:25:55 +01002031 return err;
2032}
Johannes Berg2afe38d2015-01-06 14:00:53 +01002033EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
Johannes Berg2a519312009-02-10 21:25:55 +01002034
James Minor76a70e92015-02-24 12:58:20 -06002035static char *ieee80211_scan_add_ies(struct iw_request_info *info,
2036 const struct cfg80211_bss_ies *ies,
2037 char *current_ev, char *end_buf)
Johannes Berg2a519312009-02-10 21:25:55 +01002038{
Johannes Berg9caf0362012-11-29 01:25:20 +01002039 const u8 *pos, *end, *next;
Johannes Berg2a519312009-02-10 21:25:55 +01002040 struct iw_event iwe;
2041
Johannes Berg9caf0362012-11-29 01:25:20 +01002042 if (!ies)
James Minor76a70e92015-02-24 12:58:20 -06002043 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002044
2045 /*
2046 * If needed, fragment the IEs buffer (at IE boundaries) into short
2047 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
2048 */
Johannes Berg9caf0362012-11-29 01:25:20 +01002049 pos = ies->data;
2050 end = pos + ies->len;
Johannes Berg2a519312009-02-10 21:25:55 +01002051
2052 while (end - pos > IW_GENERIC_IE_MAX) {
2053 next = pos + 2 + pos[1];
2054 while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
2055 next = next + 2 + next[1];
2056
2057 memset(&iwe, 0, sizeof(iwe));
2058 iwe.cmd = IWEVGENIE;
2059 iwe.u.data.length = next - pos;
James Minor76a70e92015-02-24 12:58:20 -06002060 current_ev = iwe_stream_add_point_check(info, current_ev,
2061 end_buf, &iwe,
2062 (void *)pos);
2063 if (IS_ERR(current_ev))
2064 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002065 pos = next;
2066 }
2067
2068 if (end > pos) {
2069 memset(&iwe, 0, sizeof(iwe));
2070 iwe.cmd = IWEVGENIE;
2071 iwe.u.data.length = end - pos;
James Minor76a70e92015-02-24 12:58:20 -06002072 current_ev = iwe_stream_add_point_check(info, current_ev,
2073 end_buf, &iwe,
2074 (void *)pos);
2075 if (IS_ERR(current_ev))
2076 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002077 }
James Minor76a70e92015-02-24 12:58:20 -06002078
2079 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002080}
2081
Johannes Berg2a519312009-02-10 21:25:55 +01002082static char *
Johannes Berg77965c972009-02-18 18:45:06 +01002083ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
2084 struct cfg80211_internal_bss *bss, char *current_ev,
2085 char *end_buf)
Johannes Berg2a519312009-02-10 21:25:55 +01002086{
Johannes Berg9caf0362012-11-29 01:25:20 +01002087 const struct cfg80211_bss_ies *ies;
Johannes Berg2a519312009-02-10 21:25:55 +01002088 struct iw_event iwe;
Johannes Berg9caf0362012-11-29 01:25:20 +01002089 const u8 *ie;
James Minor76a70e92015-02-24 12:58:20 -06002090 u8 buf[50];
2091 u8 *cfg, *p, *tmp;
Johannes Berg9caf0362012-11-29 01:25:20 +01002092 int rem, i, sig;
Johannes Berg2a519312009-02-10 21:25:55 +01002093 bool ismesh = false;
2094
2095 memset(&iwe, 0, sizeof(iwe));
2096 iwe.cmd = SIOCGIWAP;
2097 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
2098 memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
James Minor76a70e92015-02-24 12:58:20 -06002099 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2100 IW_EV_ADDR_LEN);
2101 if (IS_ERR(current_ev))
2102 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002103
2104 memset(&iwe, 0, sizeof(iwe));
2105 iwe.cmd = SIOCGIWFREQ;
2106 iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
2107 iwe.u.freq.e = 0;
James Minor76a70e92015-02-24 12:58:20 -06002108 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2109 IW_EV_FREQ_LEN);
2110 if (IS_ERR(current_ev))
2111 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002112
2113 memset(&iwe, 0, sizeof(iwe));
2114 iwe.cmd = SIOCGIWFREQ;
2115 iwe.u.freq.m = bss->pub.channel->center_freq;
2116 iwe.u.freq.e = 6;
James Minor76a70e92015-02-24 12:58:20 -06002117 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2118 IW_EV_FREQ_LEN);
2119 if (IS_ERR(current_ev))
2120 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002121
Johannes Berg77965c972009-02-18 18:45:06 +01002122 if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
Johannes Berg2a519312009-02-10 21:25:55 +01002123 memset(&iwe, 0, sizeof(iwe));
2124 iwe.cmd = IWEVQUAL;
2125 iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
2126 IW_QUAL_NOISE_INVALID |
Johannes Berga77b8552009-02-18 18:27:22 +01002127 IW_QUAL_QUAL_UPDATED;
Johannes Berg77965c972009-02-18 18:45:06 +01002128 switch (wiphy->signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002129 case CFG80211_SIGNAL_TYPE_MBM:
Johannes Berga77b8552009-02-18 18:27:22 +01002130 sig = bss->pub.signal / 100;
2131 iwe.u.qual.level = sig;
Johannes Berg2a519312009-02-10 21:25:55 +01002132 iwe.u.qual.updated |= IW_QUAL_DBM;
Johannes Berga77b8552009-02-18 18:27:22 +01002133 if (sig < -110) /* rather bad */
2134 sig = -110;
2135 else if (sig > -40) /* perfect */
2136 sig = -40;
2137 /* will give a range of 0 .. 70 */
2138 iwe.u.qual.qual = sig + 110;
Johannes Berg2a519312009-02-10 21:25:55 +01002139 break;
2140 case CFG80211_SIGNAL_TYPE_UNSPEC:
2141 iwe.u.qual.level = bss->pub.signal;
Johannes Berga77b8552009-02-18 18:27:22 +01002142 /* will give range 0 .. 100 */
2143 iwe.u.qual.qual = bss->pub.signal;
Johannes Berg2a519312009-02-10 21:25:55 +01002144 break;
2145 default:
2146 /* not reached */
2147 break;
2148 }
James Minor76a70e92015-02-24 12:58:20 -06002149 current_ev = iwe_stream_add_event_check(info, current_ev,
2150 end_buf, &iwe,
2151 IW_EV_QUAL_LEN);
2152 if (IS_ERR(current_ev))
2153 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002154 }
2155
2156 memset(&iwe, 0, sizeof(iwe));
2157 iwe.cmd = SIOCGIWENCODE;
2158 if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
2159 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
2160 else
2161 iwe.u.data.flags = IW_ENCODE_DISABLED;
2162 iwe.u.data.length = 0;
James Minor76a70e92015-02-24 12:58:20 -06002163 current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2164 &iwe, "");
2165 if (IS_ERR(current_ev))
2166 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002167
Johannes Berg9caf0362012-11-29 01:25:20 +01002168 rcu_read_lock();
2169 ies = rcu_dereference(bss->pub.ies);
Johannes Berg83c7aa12013-02-05 16:51:29 +01002170 rem = ies->len;
2171 ie = ies->data;
Johannes Berg9caf0362012-11-29 01:25:20 +01002172
Johannes Berg83c7aa12013-02-05 16:51:29 +01002173 while (rem >= 2) {
Johannes Berg2a519312009-02-10 21:25:55 +01002174 /* invalid data */
2175 if (ie[1] > rem - 2)
2176 break;
2177
2178 switch (ie[0]) {
2179 case WLAN_EID_SSID:
2180 memset(&iwe, 0, sizeof(iwe));
2181 iwe.cmd = SIOCGIWESSID;
2182 iwe.u.data.length = ie[1];
2183 iwe.u.data.flags = 1;
James Minor76a70e92015-02-24 12:58:20 -06002184 current_ev = iwe_stream_add_point_check(info,
2185 current_ev,
2186 end_buf, &iwe,
2187 (u8 *)ie + 2);
2188 if (IS_ERR(current_ev))
2189 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002190 break;
2191 case WLAN_EID_MESH_ID:
2192 memset(&iwe, 0, sizeof(iwe));
2193 iwe.cmd = SIOCGIWESSID;
2194 iwe.u.data.length = ie[1];
2195 iwe.u.data.flags = 1;
James Minor76a70e92015-02-24 12:58:20 -06002196 current_ev = iwe_stream_add_point_check(info,
2197 current_ev,
2198 end_buf, &iwe,
2199 (u8 *)ie + 2);
2200 if (IS_ERR(current_ev))
2201 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002202 break;
2203 case WLAN_EID_MESH_CONFIG:
2204 ismesh = true;
Rui Paulo136cfa22009-11-18 18:40:00 +00002205 if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
Johannes Berg2a519312009-02-10 21:25:55 +01002206 break;
Johannes Berg9caf0362012-11-29 01:25:20 +01002207 cfg = (u8 *)ie + 2;
Johannes Berg2a519312009-02-10 21:25:55 +01002208 memset(&iwe, 0, sizeof(iwe));
2209 iwe.cmd = IWEVCUSTOM;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002210 sprintf(buf, "Mesh Network Path Selection Protocol ID: "
2211 "0x%02X", cfg[0]);
Johannes Berg2a519312009-02-10 21:25:55 +01002212 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002213 current_ev = iwe_stream_add_point_check(info,
2214 current_ev,
2215 end_buf,
2216 &iwe, buf);
2217 if (IS_ERR(current_ev))
2218 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002219 sprintf(buf, "Path Selection Metric ID: 0x%02X",
2220 cfg[1]);
Johannes Berg2a519312009-02-10 21:25:55 +01002221 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002222 current_ev = iwe_stream_add_point_check(info,
2223 current_ev,
2224 end_buf,
2225 &iwe, buf);
2226 if (IS_ERR(current_ev))
2227 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002228 sprintf(buf, "Congestion Control Mode ID: 0x%02X",
2229 cfg[2]);
Johannes Berg2a519312009-02-10 21:25:55 +01002230 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002231 current_ev = iwe_stream_add_point_check(info,
2232 current_ev,
2233 end_buf,
2234 &iwe, buf);
2235 if (IS_ERR(current_ev))
2236 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002237 sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
Johannes Berg2a519312009-02-10 21:25:55 +01002238 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002239 current_ev = iwe_stream_add_point_check(info,
2240 current_ev,
2241 end_buf,
2242 &iwe, buf);
2243 if (IS_ERR(current_ev))
2244 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002245 sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
2246 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002247 current_ev = iwe_stream_add_point_check(info,
2248 current_ev,
2249 end_buf,
2250 &iwe, buf);
2251 if (IS_ERR(current_ev))
2252 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002253 sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
2254 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002255 current_ev = iwe_stream_add_point_check(info,
2256 current_ev,
2257 end_buf,
2258 &iwe, buf);
2259 if (IS_ERR(current_ev))
2260 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002261 sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
Johannes Berg2a519312009-02-10 21:25:55 +01002262 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002263 current_ev = iwe_stream_add_point_check(info,
2264 current_ev,
2265 end_buf,
2266 &iwe, buf);
2267 if (IS_ERR(current_ev))
2268 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002269 break;
2270 case WLAN_EID_SUPP_RATES:
2271 case WLAN_EID_EXT_SUPP_RATES:
2272 /* display all supported rates in readable format */
2273 p = current_ev + iwe_stream_lcp_len(info);
2274
2275 memset(&iwe, 0, sizeof(iwe));
2276 iwe.cmd = SIOCGIWRATE;
2277 /* Those two flags are ignored... */
2278 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
2279
2280 for (i = 0; i < ie[1]; i++) {
2281 iwe.u.bitrate.value =
2282 ((ie[i + 2] & 0x7f) * 500000);
James Minor76a70e92015-02-24 12:58:20 -06002283 tmp = p;
Johannes Berg2a519312009-02-10 21:25:55 +01002284 p = iwe_stream_add_value(info, current_ev, p,
James Minor76a70e92015-02-24 12:58:20 -06002285 end_buf, &iwe,
2286 IW_EV_PARAM_LEN);
2287 if (p == tmp) {
2288 current_ev = ERR_PTR(-E2BIG);
2289 goto unlock;
2290 }
Johannes Berg2a519312009-02-10 21:25:55 +01002291 }
2292 current_ev = p;
2293 break;
2294 }
2295 rem -= ie[1] + 2;
2296 ie += ie[1] + 2;
2297 }
2298
Joe Perchesf64f9e72009-11-29 16:55:45 -08002299 if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
2300 ismesh) {
Johannes Berg2a519312009-02-10 21:25:55 +01002301 memset(&iwe, 0, sizeof(iwe));
2302 iwe.cmd = SIOCGIWMODE;
2303 if (ismesh)
2304 iwe.u.mode = IW_MODE_MESH;
2305 else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
2306 iwe.u.mode = IW_MODE_MASTER;
2307 else
2308 iwe.u.mode = IW_MODE_ADHOC;
James Minor76a70e92015-02-24 12:58:20 -06002309 current_ev = iwe_stream_add_event_check(info, current_ev,
2310 end_buf, &iwe,
2311 IW_EV_UINT_LEN);
2312 if (IS_ERR(current_ev))
2313 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002314 }
2315
James Minor76a70e92015-02-24 12:58:20 -06002316 memset(&iwe, 0, sizeof(iwe));
2317 iwe.cmd = IWEVCUSTOM;
2318 sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
2319 iwe.u.data.length = strlen(buf);
2320 current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2321 &iwe, buf);
2322 if (IS_ERR(current_ev))
2323 goto unlock;
2324 memset(&iwe, 0, sizeof(iwe));
2325 iwe.cmd = IWEVCUSTOM;
2326 sprintf(buf, " Last beacon: %ums ago",
2327 elapsed_jiffies_msecs(bss->ts));
2328 iwe.u.data.length = strlen(buf);
2329 current_ev = iwe_stream_add_point_check(info, current_ev,
2330 end_buf, &iwe, buf);
2331 if (IS_ERR(current_ev))
2332 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002333
James Minor76a70e92015-02-24 12:58:20 -06002334 current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
2335
2336 unlock:
Johannes Berg9caf0362012-11-29 01:25:20 +01002337 rcu_read_unlock();
Johannes Berg2a519312009-02-10 21:25:55 +01002338 return current_ev;
2339}
2340
2341
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002342static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
Johannes Berg2a519312009-02-10 21:25:55 +01002343 struct iw_request_info *info,
2344 char *buf, size_t len)
2345{
2346 char *current_ev = buf;
2347 char *end_buf = buf + len;
2348 struct cfg80211_internal_bss *bss;
James Minor76a70e92015-02-24 12:58:20 -06002349 int err = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01002350
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002351 spin_lock_bh(&rdev->bss_lock);
2352 cfg80211_bss_expire(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01002353
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002354 list_for_each_entry(bss, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01002355 if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
James Minor76a70e92015-02-24 12:58:20 -06002356 err = -E2BIG;
2357 break;
Johannes Berg2a519312009-02-10 21:25:55 +01002358 }
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002359 current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
Johannes Berg77965c972009-02-18 18:45:06 +01002360 current_ev, end_buf);
James Minor76a70e92015-02-24 12:58:20 -06002361 if (IS_ERR(current_ev)) {
2362 err = PTR_ERR(current_ev);
2363 break;
2364 }
Johannes Berg2a519312009-02-10 21:25:55 +01002365 }
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002366 spin_unlock_bh(&rdev->bss_lock);
James Minor76a70e92015-02-24 12:58:20 -06002367
2368 if (err)
2369 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01002370 return current_ev - buf;
2371}
2372
2373
2374int cfg80211_wext_giwscan(struct net_device *dev,
2375 struct iw_request_info *info,
2376 struct iw_point *data, char *extra)
2377{
2378 struct cfg80211_registered_device *rdev;
2379 int res;
2380
2381 if (!netif_running(dev))
2382 return -ENETDOWN;
2383
Johannes Berg463d0182009-07-14 00:33:35 +02002384 rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01002385
2386 if (IS_ERR(rdev))
2387 return PTR_ERR(rdev);
2388
Johannes Bergf9d15d12014-01-22 11:14:19 +02002389 if (rdev->scan_req || rdev->scan_msg)
Johannes Berg5fe231e2013-05-08 21:45:15 +02002390 return -EAGAIN;
Johannes Berg2a519312009-02-10 21:25:55 +01002391
2392 res = ieee80211_scan_results(rdev, info, extra, data->length);
2393 data->length = 0;
2394 if (res >= 0) {
2395 data->length = res;
2396 res = 0;
2397 }
2398
Johannes Berg2a519312009-02-10 21:25:55 +01002399 return res;
2400}
Johannes Berg2afe38d2015-01-06 14:00:53 +01002401EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
Johannes Berg2a519312009-02-10 21:25:55 +01002402#endif