blob: 531c2e56413fd686868a814b44e1c3e9fefe61ae [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 }
Johannes Berg0532d4f2013-02-01 01:34:36 +0100113}
114
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800115static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
Johannes Berg776b3582013-02-01 02:06:18 +0100116 struct cfg80211_internal_bss *bss)
Johannes Berg0532d4f2013-02-01 01:34:36 +0100117{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800118 lockdep_assert_held(&rdev->bss_lock);
Johannes Berg776b3582013-02-01 02:06:18 +0100119
120 if (bss->pub.hidden_beacon_bss) {
121 struct cfg80211_internal_bss *hbss;
122 hbss = container_of(bss->pub.hidden_beacon_bss,
123 struct cfg80211_internal_bss,
124 pub);
125 hbss->refcount--;
126 if (hbss->refcount == 0)
127 bss_free(hbss);
128 }
129 bss->refcount--;
130 if (bss->refcount == 0)
131 bss_free(bss);
Johannes Berg0532d4f2013-02-01 01:34:36 +0100132}
133
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800134static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
Amitkumar Karware8e27c62012-10-11 21:03:33 -0700135 struct cfg80211_internal_bss *bss)
136{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800137 lockdep_assert_held(&rdev->bss_lock);
Johannes Berg4b1af472013-02-01 01:05:43 +0100138
Johannes Berg776b3582013-02-01 02:06:18 +0100139 if (!list_empty(&bss->hidden_list)) {
140 /*
141 * don't remove the beacon entry if it has
142 * probe responses associated with it
143 */
144 if (!bss->pub.hidden_beacon_bss)
145 return false;
146 /*
147 * if it's a probe response entry break its
148 * link to the other entries in the group
149 */
150 list_del_init(&bss->hidden_list);
151 }
152
Amitkumar Karware8e27c62012-10-11 21:03:33 -0700153 list_del_init(&bss->list);
Peng Xu0b8fb822019-01-21 12:14:57 +0200154 list_del_init(&bss->nontrans_list);
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800155 rb_erase(&bss->rbn, &rdev->bss_tree);
Johannes Berg9853a552016-11-15 12:05:11 +0100156 rdev->bss_entries--;
157 WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
158 "rdev bss entries[%d]/list[empty:%d] corruption\n",
159 rdev->bss_entries, list_empty(&rdev->bss_list));
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800160 bss_ref_put(rdev, bss);
Johannes Berg776b3582013-02-01 02:06:18 +0100161 return true;
Amitkumar Karware8e27c62012-10-11 21:03:33 -0700162}
163
Peng Xu0b8fb822019-01-21 12:14:57 +0200164static void cfg80211_gen_new_bssid(const u8 *bssid, u8 max_bssid,
165 u8 mbssid_index, u8 *new_bssid_addr)
166{
167 u64 bssid_tmp, new_bssid = 0;
168 u64 lsb_n;
169
170 bssid_tmp = ether_addr_to_u64(bssid);
171
172 lsb_n = bssid_tmp & ((1 << max_bssid) - 1);
173 new_bssid = bssid_tmp;
174 new_bssid &= ~((1 << max_bssid) - 1);
175 new_bssid |= (lsb_n + mbssid_index) % (1 << max_bssid);
176
177 u64_to_ether_addr(new_bssid, new_bssid_addr);
178}
179
180static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
181 const u8 *subelement, size_t subie_len,
182 u8 *new_ie, gfp_t gfp)
183{
184 u8 *pos, *tmp;
185 const u8 *tmp_old, *tmp_new;
186 u8 *sub_copy;
187
188 /* copy subelement as we need to change its content to
189 * mark an ie after it is processed.
190 */
191 sub_copy = kmalloc(subie_len, gfp);
192 if (!sub_copy)
193 return 0;
194 memcpy(sub_copy, subelement, subie_len);
195
196 pos = &new_ie[0];
197
198 /* set new ssid */
199 tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len);
200 if (tmp_new) {
201 memcpy(pos, tmp_new, tmp_new[1] + 2);
202 pos += (tmp_new[1] + 2);
203 }
204
205 /* go through IEs in ie (skip SSID) and subelement,
206 * merge them into new_ie
207 */
208 tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
209 tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie;
210
211 while (tmp_old + tmp_old[1] + 2 - ie <= ielen) {
212 if (tmp_old[0] == 0) {
213 tmp_old++;
214 continue;
215 }
216
217 tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy, subie_len);
218 if (!tmp) {
219 /* ie in old ie but not in subelement */
220 if (tmp_old[0] != WLAN_EID_MULTIPLE_BSSID) {
221 memcpy(pos, tmp_old, tmp_old[1] + 2);
222 pos += tmp_old[1] + 2;
223 }
224 } else {
225 /* ie in transmitting ie also in subelement,
226 * copy from subelement and flag the ie in subelement
227 * as copied (by setting eid field to 0xff). For
228 * vendor ie, compare OUI + type + subType to
229 * determine if they are the same ie.
230 */
231 if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) {
232 if (!memcmp(tmp_old + 2, tmp + 2, 5)) {
233 /* same vendor ie, copy from
234 * subelement
235 */
236 memcpy(pos, tmp, tmp[1] + 2);
237 pos += tmp[1] + 2;
238 tmp[0] = 0xff;
239 } else {
240 memcpy(pos, tmp_old, tmp_old[1] + 2);
241 pos += tmp_old[1] + 2;
242 }
243 } else {
244 /* copy ie from subelement into new ie */
245 memcpy(pos, tmp, tmp[1] + 2);
246 pos += tmp[1] + 2;
247 tmp[0] = 0xff;
248 }
249 }
250
251 if (tmp_old + tmp_old[1] + 2 - ie == ielen)
252 break;
253
254 tmp_old += tmp_old[1] + 2;
255 }
256
257 /* go through subelement again to check if there is any ie not
258 * copied to new ie, skip ssid, capability, bssid-index ie
259 */
260 tmp_new = sub_copy;
261 while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
262 if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP ||
263 tmp_new[0] == WLAN_EID_SSID ||
264 tmp_new[0] == WLAN_EID_MULTI_BSSID_IDX ||
265 tmp_new[0] == 0xff)) {
266 memcpy(pos, tmp_new, tmp_new[1] + 2);
267 pos += tmp_new[1] + 2;
268 }
269 if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len)
270 break;
271 tmp_new += tmp_new[1] + 2;
272 }
273
274 kfree(sub_copy);
275 return pos - new_ie;
276}
277
278static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
279 const u8 *ssid, size_t ssid_len)
280{
281 const struct cfg80211_bss_ies *ies;
282 const u8 *ssidie;
283
284 if (bssid && !ether_addr_equal(a->bssid, bssid))
285 return false;
286
287 if (!ssid)
288 return true;
289
290 ies = rcu_access_pointer(a->ies);
291 if (!ies)
292 return false;
293 ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
294 if (!ssidie)
295 return false;
296 if (ssidie[1] != ssid_len)
297 return false;
298 return memcmp(ssidie + 2, ssid, ssid_len) == 0;
299}
300
301static int
302cfg80211_add_nontrans_list(struct cfg80211_internal_bss *trans_bss,
303 struct cfg80211_internal_bss *nontrans_bss)
304{
305 const u8 *ssid;
306 size_t ssid_len;
307 struct cfg80211_internal_bss *bss = NULL;
308
309 rcu_read_lock();
310 ssid = ieee80211_bss_get_ie(&nontrans_bss->pub, WLAN_EID_SSID);
311 if (!ssid) {
312 rcu_read_unlock();
313 return -EINVAL;
314 }
315 ssid_len = ssid[1];
316 ssid = ssid + 2;
317 rcu_read_unlock();
318
319 /* check if nontrans_bss is in the list */
320 list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) {
321 if (is_bss(&bss->pub, nontrans_bss->pub.bssid, ssid, ssid_len))
322 return 0;
323 }
324
325 /* add to the list */
326 list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
327 return 0;
328}
329
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800330static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
Sam Leffler15d60302012-10-11 21:03:34 -0700331 unsigned long expire_time)
332{
333 struct cfg80211_internal_bss *bss, *tmp;
334 bool expired = false;
335
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800336 lockdep_assert_held(&rdev->bss_lock);
Johannes Berg4b1af472013-02-01 01:05:43 +0100337
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800338 list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {
Sam Leffler15d60302012-10-11 21:03:34 -0700339 if (atomic_read(&bss->hold))
340 continue;
341 if (!time_after(expire_time, bss->ts))
342 continue;
343
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800344 if (__cfg80211_unlink_bss(rdev, bss))
Johannes Berg776b3582013-02-01 02:06:18 +0100345 expired = true;
Sam Leffler15d60302012-10-11 21:03:34 -0700346 }
347
348 if (expired)
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800349 rdev->bss_generation++;
Sam Leffler15d60302012-10-11 21:03:34 -0700350}
351
Johannes Berg9853a552016-11-15 12:05:11 +0100352static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev)
353{
354 struct cfg80211_internal_bss *bss, *oldest = NULL;
355 bool ret;
356
357 lockdep_assert_held(&rdev->bss_lock);
358
359 list_for_each_entry(bss, &rdev->bss_list, list) {
360 if (atomic_read(&bss->hold))
361 continue;
362
363 if (!list_empty(&bss->hidden_list) &&
364 !bss->pub.hidden_beacon_bss)
365 continue;
366
367 if (oldest && time_before(oldest->ts, bss->ts))
368 continue;
369 oldest = bss;
370 }
371
372 if (WARN_ON(!oldest))
373 return false;
374
375 /*
376 * The callers make sure to increase rdev->bss_generation if anything
377 * gets removed (and a new entry added), so there's no need to also do
378 * it here.
379 */
380
381 ret = __cfg80211_unlink_bss(rdev, oldest);
382 WARN_ON(!ret);
383 return ret;
384}
385
Johannes Bergf9d15d12014-01-22 11:14:19 +0200386void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
387 bool send_message)
Johannes Berg2a519312009-02-10 21:25:55 +0100388{
Johannes Berg667503d2009-07-07 03:56:11 +0200389 struct cfg80211_scan_request *request;
Johannes Bergfd014282012-06-18 19:17:03 +0200390 struct wireless_dev *wdev;
Johannes Bergf9d15d12014-01-22 11:14:19 +0200391 struct sk_buff *msg;
Johannes Berg3d23e342009-09-29 23:27:28 +0200392#ifdef CONFIG_CFG80211_WEXT
Johannes Berg2a519312009-02-10 21:25:55 +0100393 union iwreq_data wrqu;
394#endif
395
Johannes Berg5fe231e2013-05-08 21:45:15 +0200396 ASSERT_RTNL();
Johannes Berg01a0ac42009-08-20 21:36:16 +0200397
Johannes Bergf9d15d12014-01-22 11:14:19 +0200398 if (rdev->scan_msg) {
Arend Van Spriel505a2e82016-12-16 11:21:54 +0000399 nl80211_send_scan_msg(rdev, rdev->scan_msg);
Johannes Bergf9d15d12014-01-22 11:14:19 +0200400 rdev->scan_msg = NULL;
401 return;
402 }
Johannes Berg667503d2009-07-07 03:56:11 +0200403
Johannes Bergf9d15d12014-01-22 11:14:19 +0200404 request = rdev->scan_req;
Johannes Berg01a0ac42009-08-20 21:36:16 +0200405 if (!request)
406 return;
407
Johannes Bergfd014282012-06-18 19:17:03 +0200408 wdev = request->wdev;
Johannes Berg2a519312009-02-10 21:25:55 +0100409
Johannes Berg6829c872009-07-02 09:13:27 +0200410 /*
411 * This must be before sending the other events!
412 * Otherwise, wpa_supplicant gets completely confused with
413 * wext events.
414 */
Johannes Bergfd014282012-06-18 19:17:03 +0200415 if (wdev->netdev)
416 cfg80211_sme_scan_done(wdev->netdev);
Johannes Berg6829c872009-07-02 09:13:27 +0200417
Avraham Stern1d762502016-07-05 17:10:13 +0300418 if (!request->info.aborted &&
Johannes Bergf9d15d12014-01-22 11:14:19 +0200419 request->flags & NL80211_SCAN_FLAG_FLUSH) {
420 /* flush entries from previous scans */
421 spin_lock_bh(&rdev->bss_lock);
422 __cfg80211_bss_expire(rdev, request->scan_start);
423 spin_unlock_bh(&rdev->bss_lock);
Sam Leffler15d60302012-10-11 21:03:34 -0700424 }
Johannes Berg2a519312009-02-10 21:25:55 +0100425
Avraham Stern1d762502016-07-05 17:10:13 +0300426 msg = nl80211_build_scan_msg(rdev, wdev, request->info.aborted);
Johannes Bergf9d15d12014-01-22 11:14:19 +0200427
Johannes Berg3d23e342009-09-29 23:27:28 +0200428#ifdef CONFIG_CFG80211_WEXT
Avraham Stern1d762502016-07-05 17:10:13 +0300429 if (wdev->netdev && !request->info.aborted) {
Johannes Berg2a519312009-02-10 21:25:55 +0100430 memset(&wrqu, 0, sizeof(wrqu));
431
Johannes Bergfd014282012-06-18 19:17:03 +0200432 wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL);
Johannes Berg2a519312009-02-10 21:25:55 +0100433 }
434#endif
435
Johannes Bergfd014282012-06-18 19:17:03 +0200436 if (wdev->netdev)
437 dev_put(wdev->netdev);
Johannes Berg2a519312009-02-10 21:25:55 +0100438
Johannes Berg36e6fea2009-08-12 22:21:21 +0200439 rdev->scan_req = NULL;
Eliad Peller4a58e7c2013-12-05 18:30:17 +0200440 kfree(request);
Johannes Bergf9d15d12014-01-22 11:14:19 +0200441
442 if (!send_message)
443 rdev->scan_msg = msg;
444 else
Arend Van Spriel505a2e82016-12-16 11:21:54 +0000445 nl80211_send_scan_msg(rdev, msg);
Johannes Berg2a519312009-02-10 21:25:55 +0100446}
Johannes Berg667503d2009-07-07 03:56:11 +0200447
Johannes Berg36e6fea2009-08-12 22:21:21 +0200448void __cfg80211_scan_done(struct work_struct *wk)
449{
450 struct cfg80211_registered_device *rdev;
451
452 rdev = container_of(wk, struct cfg80211_registered_device,
453 scan_done_wk);
454
Johannes Berg5fe231e2013-05-08 21:45:15 +0200455 rtnl_lock();
Johannes Bergf9d15d12014-01-22 11:14:19 +0200456 ___cfg80211_scan_done(rdev, true);
Johannes Berg5fe231e2013-05-08 21:45:15 +0200457 rtnl_unlock();
Johannes Berg36e6fea2009-08-12 22:21:21 +0200458}
459
Avraham Stern1d762502016-07-05 17:10:13 +0300460void cfg80211_scan_done(struct cfg80211_scan_request *request,
461 struct cfg80211_scan_info *info)
Johannes Berg667503d2009-07-07 03:56:11 +0200462{
Avraham Stern1d762502016-07-05 17:10:13 +0300463 trace_cfg80211_scan_done(request, info);
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800464 WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);
Johannes Berg667503d2009-07-07 03:56:11 +0200465
Avraham Stern1d762502016-07-05 17:10:13 +0300466 request->info = *info;
Johannes Berg5fe231e2013-05-08 21:45:15 +0200467 request->notified = true;
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800468 queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
Johannes Berg667503d2009-07-07 03:56:11 +0200469}
Johannes Berg2a519312009-02-10 21:25:55 +0100470EXPORT_SYMBOL(cfg80211_scan_done);
471
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100472void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
473 struct cfg80211_sched_scan_request *req)
474{
475 ASSERT_RTNL();
476
477 list_add_rcu(&req->list, &rdev->sched_scan_req_list);
478}
479
480static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
481 struct cfg80211_sched_scan_request *req)
482{
483 ASSERT_RTNL();
484
485 list_del_rcu(&req->list);
486 kfree_rcu(req, rcu_head);
487}
488
489static struct cfg80211_sched_scan_request *
490cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
491{
492 struct cfg80211_sched_scan_request *pos;
493
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100494 WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held());
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100495
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100496 list_for_each_entry_rcu(pos, &rdev->sched_scan_req_list, list) {
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100497 if (pos->reqid == reqid)
498 return pos;
499 }
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100500 return NULL;
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100501}
502
503/*
504 * Determines if a scheduled scan request can be handled. When a legacy
505 * scheduled scan is running no other scheduled scan is allowed regardless
506 * whether the request is for legacy or multi-support scan. When a multi-support
507 * scheduled scan is running a request for legacy scan is not allowed. In this
508 * case a request for multi-support scan can be handled if resources are
509 * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
510 */
511int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
512 bool want_multi)
513{
514 struct cfg80211_sched_scan_request *pos;
515 int i = 0;
516
517 list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
518 /* request id zero means legacy in progress */
519 if (!i && !pos->reqid)
520 return -EINPROGRESS;
521 i++;
522 }
523
524 if (i) {
525 /* no legacy allowed when multi request(s) are active */
526 if (!want_multi)
527 return -EINPROGRESS;
528
529 /* resource limit reached */
530 if (i == rdev->wiphy.max_sched_scan_reqs)
531 return -ENOSPC;
532 }
533 return 0;
534}
535
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100536void cfg80211_sched_scan_results_wk(struct work_struct *work)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300537{
538 struct cfg80211_registered_device *rdev;
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100539 struct cfg80211_sched_scan_request *req, *tmp;
Luciano Coelho807f8a82011-05-11 17:09:35 +0300540
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100541 rdev = container_of(work, struct cfg80211_registered_device,
542 sched_scan_res_wk);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300543
Johannes Berg5fe231e2013-05-08 21:45:15 +0200544 rtnl_lock();
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100545 list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
546 if (req->report_results) {
547 req->report_results = false;
548 if (req->flags & NL80211_SCAN_FLAG_FLUSH) {
549 /* flush entries from previous scans */
550 spin_lock_bh(&rdev->bss_lock);
551 __cfg80211_bss_expire(rdev, req->scan_start);
552 spin_unlock_bh(&rdev->bss_lock);
553 req->scan_start = jiffies;
554 }
555 nl80211_send_sched_scan(req,
556 NL80211_CMD_SCHED_SCAN_RESULTS);
Sam Leffler15d60302012-10-11 21:03:34 -0700557 }
Sam Leffler15d60302012-10-11 21:03:34 -0700558 }
Johannes Berg5fe231e2013-05-08 21:45:15 +0200559 rtnl_unlock();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300560}
561
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100562void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300563{
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100564 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
565 struct cfg80211_sched_scan_request *request;
566
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100567 trace_cfg80211_sched_scan_results(wiphy, reqid);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300568 /* ignore if we're not scanning */
Jukka Rissanen31a60ed2014-12-15 13:25:38 +0200569
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100570 rcu_read_lock();
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100571 request = cfg80211_find_sched_scan_req(rdev, reqid);
572 if (request) {
573 request->report_results = true;
574 queue_work(cfg80211_wq, &rdev->sched_scan_res_wk);
575 }
Arend Van Spriel1b57b622017-05-23 09:58:07 +0100576 rcu_read_unlock();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300577}
578EXPORT_SYMBOL(cfg80211_sched_scan_results);
579
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100580void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300581{
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800582 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300583
Eliad Peller792e6aa2014-04-30 16:14:23 +0300584 ASSERT_RTNL();
585
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100586 trace_cfg80211_sched_scan_stopped(wiphy, reqid);
Beni Lev4ee3e062012-08-27 12:49:39 +0300587
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100588 __cfg80211_stop_sched_scan(rdev, reqid, true);
Eliad Peller792e6aa2014-04-30 16:14:23 +0300589}
590EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl);
591
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100592void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid)
Eliad Peller792e6aa2014-04-30 16:14:23 +0300593{
594 rtnl_lock();
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100595 cfg80211_sched_scan_stopped_rtnl(wiphy, reqid);
Johannes Berg5fe231e2013-05-08 21:45:15 +0200596 rtnl_unlock();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300597}
Luciano Coelho807f8a82011-05-11 17:09:35 +0300598EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
599
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100600int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
601 struct cfg80211_sched_scan_request *req,
602 bool driver_initiated)
Luciano Coelho807f8a82011-05-11 17:09:35 +0300603{
Johannes Berg5fe231e2013-05-08 21:45:15 +0200604 ASSERT_RTNL();
Luciano Coelho807f8a82011-05-11 17:09:35 +0300605
Luciano Coelho85a99942011-05-12 16:28:29 +0300606 if (!driver_initiated) {
Arend Van Spriel3a3ecf12017-04-21 13:05:02 +0100607 int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid);
Luciano Coelho85a99942011-05-12 16:28:29 +0300608 if (err)
609 return err;
610 }
Luciano Coelho807f8a82011-05-11 17:09:35 +0300611
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100612 nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300613
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100614 cfg80211_del_sched_scan_req(rdev, req);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300615
Jesper Juhl3b4670f2011-06-29 22:49:33 +0200616 return 0;
Luciano Coelho807f8a82011-05-11 17:09:35 +0300617}
618
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100619int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
620 u64 reqid, bool driver_initiated)
621{
622 struct cfg80211_sched_scan_request *sched_scan_req;
623
624 ASSERT_RTNL();
625
626 sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
Arend Van Sprielb34939b2017-04-28 13:40:28 +0100627 if (!sched_scan_req)
628 return -ENOENT;
Arend Van Sprielca986ad2017-04-21 13:05:00 +0100629
630 return cfg80211_stop_sched_scan_req(rdev, sched_scan_req,
631 driver_initiated);
632}
633
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800634void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500635 unsigned long age_secs)
636{
637 struct cfg80211_internal_bss *bss;
638 unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
639
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800640 spin_lock_bh(&rdev->bss_lock);
641 list_for_each_entry(bss, &rdev->bss_list, list)
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500642 bss->ts -= age_jiffies;
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800643 spin_unlock_bh(&rdev->bss_lock);
Dan Williamscb3a8ee2009-02-11 17:14:43 -0500644}
645
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800646void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
Johannes Berg2a519312009-02-10 21:25:55 +0100647{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800648 __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
Johannes Berg2a519312009-02-10 21:25:55 +0100649}
650
Johannes Berg49a68e02019-02-07 23:26:38 +0100651const struct element *
652cfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len,
653 const u8 *match, unsigned int match_len,
654 unsigned int match_offset)
Johannes Berg2a519312009-02-10 21:25:55 +0100655{
Johannes Berg0f3b07f2019-02-07 21:44:41 +0100656 const struct element *elem;
657
Johannes Berg0f3b07f2019-02-07 21:44:41 +0100658 for_each_element_id(elem, eid, ies, len) {
Johannes Berg49a68e02019-02-07 23:26:38 +0100659 if (elem->datalen >= match_offset + match_len &&
660 !memcmp(elem->data + match_offset, match, match_len))
661 return elem;
Johannes Berg2a519312009-02-10 21:25:55 +0100662 }
Luca Coelhofbd05e42016-09-15 18:15:09 +0300663
664 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +0100665}
Johannes Berg49a68e02019-02-07 23:26:38 +0100666EXPORT_SYMBOL(cfg80211_find_elem_match);
Johannes Berg2a519312009-02-10 21:25:55 +0100667
Johannes Berg49a68e02019-02-07 23:26:38 +0100668const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type,
669 const u8 *ies,
670 unsigned int len)
Eliad Peller0c28ec52011-09-15 11:53:01 +0300671{
Johannes Berg49a68e02019-02-07 23:26:38 +0100672 const struct element *elem;
Luca Coelhofbd05e42016-09-15 18:15:09 +0300673 u8 match[] = { oui >> 16, oui >> 8, oui, oui_type };
674 int match_len = (oui_type < 0) ? 3 : sizeof(match);
Eliad Peller0c28ec52011-09-15 11:53:01 +0300675
Emmanuel Grumbach9e9ea432016-05-03 16:08:07 +0300676 if (WARN_ON(oui_type > 0xff))
677 return NULL;
678
Johannes Berg49a68e02019-02-07 23:26:38 +0100679 elem = cfg80211_find_elem_match(WLAN_EID_VENDOR_SPECIFIC, ies, len,
680 match, match_len, 0);
Eliad Peller0c28ec52011-09-15 11:53:01 +0300681
Johannes Berg49a68e02019-02-07 23:26:38 +0100682 if (!elem || elem->datalen < 4)
Luca Coelhofbd05e42016-09-15 18:15:09 +0300683 return NULL;
Luciano Coelho67194292013-02-12 20:11:38 +0200684
Johannes Berg49a68e02019-02-07 23:26:38 +0100685 return elem;
Eliad Peller0c28ec52011-09-15 11:53:01 +0300686}
Johannes Berg49a68e02019-02-07 23:26:38 +0100687EXPORT_SYMBOL(cfg80211_find_vendor_elem);
Eliad Peller0c28ec52011-09-15 11:53:01 +0300688
Johannes Berg4593c4c2013-02-01 19:20:03 +0100689/**
690 * enum bss_compare_mode - BSS compare mode
691 * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
692 * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
693 * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
694 */
695enum bss_compare_mode {
696 BSS_CMP_REGULAR,
697 BSS_CMP_HIDE_ZLEN,
698 BSS_CMP_HIDE_NUL,
699};
700
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100701static int cmp_bss(struct cfg80211_bss *a,
Johannes Berg5622f5b2013-01-30 00:26:45 +0100702 struct cfg80211_bss *b,
Johannes Berg4593c4c2013-02-01 19:20:03 +0100703 enum bss_compare_mode mode)
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100704{
Johannes Berg9caf0362012-11-29 01:25:20 +0100705 const struct cfg80211_bss_ies *a_ies, *b_ies;
Johannes Berg3af63412013-01-30 00:40:20 +0100706 const u8 *ie1 = NULL;
707 const u8 *ie2 = NULL;
Johannes Berg5622f5b2013-01-30 00:26:45 +0100708 int i, r;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100709
Johannes Berg3af63412013-01-30 00:40:20 +0100710 if (a->channel != b->channel)
711 return b->channel->center_freq - a->channel->center_freq;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100712
Johannes Berg9caf0362012-11-29 01:25:20 +0100713 a_ies = rcu_access_pointer(a->ies);
714 if (!a_ies)
715 return -1;
716 b_ies = rcu_access_pointer(b->ies);
717 if (!b_ies)
718 return 1;
719
Johannes Berg3af63412013-01-30 00:40:20 +0100720 if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
721 ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
722 a_ies->data, a_ies->len);
723 if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
724 ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
725 b_ies->data, b_ies->len);
726 if (ie1 && ie2) {
727 int mesh_id_cmp;
728
729 if (ie1[1] == ie2[1])
730 mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
731 else
732 mesh_id_cmp = ie2[1] - ie1[1];
733
734 ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
735 a_ies->data, a_ies->len);
736 ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
737 b_ies->data, b_ies->len);
738 if (ie1 && ie2) {
739 if (mesh_id_cmp)
740 return mesh_id_cmp;
741 if (ie1[1] != ie2[1])
742 return ie2[1] - ie1[1];
743 return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
744 }
745 }
746
Johannes Berg3af63412013-01-30 00:40:20 +0100747 r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
748 if (r)
749 return r;
750
Johannes Berg9caf0362012-11-29 01:25:20 +0100751 ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
752 ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100753
Johannes Berg5622f5b2013-01-30 00:26:45 +0100754 if (!ie1 && !ie2)
755 return 0;
756
Johannes Bergf94f8b12012-11-28 22:42:34 +0100757 /*
Johannes Berg5622f5b2013-01-30 00:26:45 +0100758 * Note that with "hide_ssid", the function returns a match if
759 * the already-present BSS ("b") is a hidden SSID beacon for
760 * the new BSS ("a").
Johannes Bergf94f8b12012-11-28 22:42:34 +0100761 */
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100762
763 /* sort missing IE before (left of) present IE */
764 if (!ie1)
765 return -1;
766 if (!ie2)
767 return 1;
768
Johannes Berg4593c4c2013-02-01 19:20:03 +0100769 switch (mode) {
770 case BSS_CMP_HIDE_ZLEN:
771 /*
772 * In ZLEN mode we assume the BSS entry we're
773 * looking for has a zero-length SSID. So if
774 * the one we're looking at right now has that,
775 * return 0. Otherwise, return the difference
776 * in length, but since we're looking for the
777 * 0-length it's really equivalent to returning
778 * the length of the one we're looking at.
779 *
780 * No content comparison is needed as we assume
781 * the content length is zero.
782 */
783 return ie2[1];
784 case BSS_CMP_REGULAR:
785 default:
786 /* sort by length first, then by contents */
787 if (ie1[1] != ie2[1])
788 return ie2[1] - ie1[1];
Johannes Berg5622f5b2013-01-30 00:26:45 +0100789 return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
Johannes Berg4593c4c2013-02-01 19:20:03 +0100790 case BSS_CMP_HIDE_NUL:
791 if (ie1[1] != ie2[1])
792 return ie2[1] - ie1[1];
793 /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
794 for (i = 0; i < ie2[1]; i++)
795 if (ie2[i + 2])
796 return -1;
797 return 0;
798 }
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100799}
800
Dedy Lansky6eb18132015-02-08 15:52:03 +0200801static bool cfg80211_bss_type_match(u16 capability,
Johannes Berg57fbcce2016-04-12 15:56:15 +0200802 enum nl80211_band band,
Dedy Lansky6eb18132015-02-08 15:52:03 +0200803 enum ieee80211_bss_type bss_type)
804{
805 bool ret = true;
806 u16 mask, val;
807
808 if (bss_type == IEEE80211_BSS_TYPE_ANY)
809 return ret;
810
Johannes Berg57fbcce2016-04-12 15:56:15 +0200811 if (band == NL80211_BAND_60GHZ) {
Dedy Lansky6eb18132015-02-08 15:52:03 +0200812 mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
813 switch (bss_type) {
814 case IEEE80211_BSS_TYPE_ESS:
815 val = WLAN_CAPABILITY_DMG_TYPE_AP;
816 break;
817 case IEEE80211_BSS_TYPE_PBSS:
818 val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
819 break;
820 case IEEE80211_BSS_TYPE_IBSS:
821 val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
822 break;
823 default:
824 return false;
825 }
826 } else {
827 mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
828 switch (bss_type) {
829 case IEEE80211_BSS_TYPE_ESS:
830 val = WLAN_CAPABILITY_ESS;
831 break;
832 case IEEE80211_BSS_TYPE_IBSS:
833 val = WLAN_CAPABILITY_IBSS;
834 break;
835 case IEEE80211_BSS_TYPE_MBSS:
836 val = 0;
837 break;
838 default:
839 return false;
840 }
841 }
842
843 ret = ((capability & mask) == val);
844 return ret;
845}
846
Ben Greear0e3a39b2013-06-19 14:06:27 -0700847/* Returned bss is reference counted and must be cleaned up appropriately. */
Johannes Berg2a519312009-02-10 21:25:55 +0100848struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
849 struct ieee80211_channel *channel,
850 const u8 *bssid,
Johannes Berg79420f02009-02-10 21:25:59 +0100851 const u8 *ssid, size_t ssid_len,
Dedy Lansky6eb18132015-02-08 15:52:03 +0200852 enum ieee80211_bss_type bss_type,
853 enum ieee80211_privacy privacy)
Johannes Berg2a519312009-02-10 21:25:55 +0100854{
Zhao, Gangf26cbf42014-04-21 12:53:03 +0800855 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +0100856 struct cfg80211_internal_bss *bss, *res = NULL;
Johannes Bergccb6c132010-07-13 10:55:38 +0200857 unsigned long now = jiffies;
Dedy Lansky6eb18132015-02-08 15:52:03 +0200858 int bss_privacy;
Johannes Berg2a519312009-02-10 21:25:55 +0100859
Dedy Lansky6eb18132015-02-08 15:52:03 +0200860 trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
861 privacy);
Beni Lev4ee3e062012-08-27 12:49:39 +0300862
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800863 spin_lock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +0100864
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800865 list_for_each_entry(bss, &rdev->bss_list, list) {
Dedy Lansky6eb18132015-02-08 15:52:03 +0200866 if (!cfg80211_bss_type_match(bss->pub.capability,
867 bss->pub.channel->band, bss_type))
868 continue;
869
870 bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
871 if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
872 (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
Johannes Berg79420f02009-02-10 21:25:59 +0100873 continue;
Johannes Berg2a519312009-02-10 21:25:55 +0100874 if (channel && bss->pub.channel != channel)
875 continue;
Johannes Bergc14a7402014-04-09 22:36:50 +0200876 if (!is_valid_ether_addr(bss->pub.bssid))
877 continue;
Johannes Bergccb6c132010-07-13 10:55:38 +0200878 /* Don't get expired BSS structs */
879 if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
880 !atomic_read(&bss->hold))
881 continue;
Johannes Berg2a519312009-02-10 21:25:55 +0100882 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
883 res = bss;
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800884 bss_ref_get(rdev, res);
Johannes Berg2a519312009-02-10 21:25:55 +0100885 break;
886 }
887 }
888
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800889 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +0100890 if (!res)
891 return NULL;
Beni Lev4ee3e062012-08-27 12:49:39 +0300892 trace_cfg80211_return_bss(&res->pub);
Johannes Berg2a519312009-02-10 21:25:55 +0100893 return &res->pub;
894}
895EXPORT_SYMBOL(cfg80211_get_bss);
896
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800897static void rb_insert_bss(struct cfg80211_registered_device *rdev,
Johannes Berg2a519312009-02-10 21:25:55 +0100898 struct cfg80211_internal_bss *bss)
899{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800900 struct rb_node **p = &rdev->bss_tree.rb_node;
Johannes Berg2a519312009-02-10 21:25:55 +0100901 struct rb_node *parent = NULL;
902 struct cfg80211_internal_bss *tbss;
903 int cmp;
904
905 while (*p) {
906 parent = *p;
907 tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
908
Johannes Berg4593c4c2013-02-01 19:20:03 +0100909 cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
Johannes Berg2a519312009-02-10 21:25:55 +0100910
911 if (WARN_ON(!cmp)) {
912 /* will sort of leak this BSS */
913 return;
914 }
915
916 if (cmp < 0)
917 p = &(*p)->rb_left;
918 else
919 p = &(*p)->rb_right;
920 }
921
922 rb_link_node(&bss->rbn, parent, p);
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800923 rb_insert_color(&bss->rbn, &rdev->bss_tree);
Johannes Berg2a519312009-02-10 21:25:55 +0100924}
925
926static struct cfg80211_internal_bss *
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800927rb_find_bss(struct cfg80211_registered_device *rdev,
Johannes Berg5622f5b2013-01-30 00:26:45 +0100928 struct cfg80211_internal_bss *res,
Johannes Berg4593c4c2013-02-01 19:20:03 +0100929 enum bss_compare_mode mode)
Johannes Berg2a519312009-02-10 21:25:55 +0100930{
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800931 struct rb_node *n = rdev->bss_tree.rb_node;
Johannes Berg2a519312009-02-10 21:25:55 +0100932 struct cfg80211_internal_bss *bss;
933 int r;
934
935 while (n) {
936 bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
Johannes Berg4593c4c2013-02-01 19:20:03 +0100937 r = cmp_bss(&res->pub, &bss->pub, mode);
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100938
939 if (r == 0)
940 return bss;
941 else if (r < 0)
942 n = n->rb_left;
943 else
944 n = n->rb_right;
945 }
946
947 return NULL;
948}
949
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800950static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
Johannes Berg776b3582013-02-01 02:06:18 +0100951 struct cfg80211_internal_bss *new)
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100952{
Johannes Berg9caf0362012-11-29 01:25:20 +0100953 const struct cfg80211_bss_ies *ies;
Johannes Berg776b3582013-02-01 02:06:18 +0100954 struct cfg80211_internal_bss *bss;
955 const u8 *ie;
956 int i, ssidlen;
957 u8 fold = 0;
Johannes Berg9853a552016-11-15 12:05:11 +0100958 u32 n_entries = 0;
Johannes Berg9caf0362012-11-29 01:25:20 +0100959
Johannes Berg776b3582013-02-01 02:06:18 +0100960 ies = rcu_access_pointer(new->pub.beacon_ies);
Johannes Berg9caf0362012-11-29 01:25:20 +0100961 if (WARN_ON(!ies))
Johannes Berg776b3582013-02-01 02:06:18 +0100962 return false;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +0100963
Johannes Berg776b3582013-02-01 02:06:18 +0100964 ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
965 if (!ie) {
966 /* nothing to do */
967 return true;
968 }
969
970 ssidlen = ie[1];
971 for (i = 0; i < ssidlen; i++)
972 fold |= ie[2 + i];
973
974 if (fold) {
975 /* not a hidden SSID */
976 return true;
977 }
978
979 /* This is the bad part ... */
980
Zhao, Gang1b8ec872014-04-21 12:53:02 +0800981 list_for_each_entry(bss, &rdev->bss_list, list) {
Johannes Berg9853a552016-11-15 12:05:11 +0100982 /*
983 * we're iterating all the entries anyway, so take the
984 * opportunity to validate the list length accounting
985 */
986 n_entries++;
987
Johannes Berg776b3582013-02-01 02:06:18 +0100988 if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
989 continue;
990 if (bss->pub.channel != new->pub.channel)
991 continue;
Simon Wunderlichdcd6eac2013-07-08 16:55:49 +0200992 if (bss->pub.scan_width != new->pub.scan_width)
993 continue;
Johannes Berg776b3582013-02-01 02:06:18 +0100994 if (rcu_access_pointer(bss->pub.beacon_ies))
995 continue;
996 ies = rcu_access_pointer(bss->pub.ies);
997 if (!ies)
998 continue;
999 ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
1000 if (!ie)
1001 continue;
1002 if (ssidlen && ie[1] != ssidlen)
1003 continue;
Johannes Berg776b3582013-02-01 02:06:18 +01001004 if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
1005 continue;
1006 if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
1007 list_del(&bss->hidden_list);
1008 /* combine them */
1009 list_add(&bss->hidden_list, &new->hidden_list);
1010 bss->pub.hidden_beacon_bss = &new->pub;
1011 new->refcount += bss->refcount;
1012 rcu_assign_pointer(bss->pub.beacon_ies,
1013 new->pub.beacon_ies);
1014 }
1015
Johannes Berg9853a552016-11-15 12:05:11 +01001016 WARN_ONCE(n_entries != rdev->bss_entries,
1017 "rdev bss entries[%d]/list[len:%d] corruption\n",
1018 rdev->bss_entries, n_entries);
1019
Johannes Berg776b3582013-02-01 02:06:18 +01001020 return true;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001021}
1022
Ben Greear0e3a39b2013-06-19 14:06:27 -07001023/* Returned bss is reference counted and must be cleaned up appropriately. */
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001024static struct cfg80211_internal_bss *
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001025cfg80211_bss_update(struct cfg80211_registered_device *rdev,
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001026 struct cfg80211_internal_bss *tmp,
1027 bool signal_valid)
Johannes Berg2a519312009-02-10 21:25:55 +01001028{
1029 struct cfg80211_internal_bss *found = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001030
Johannes Berg9caf0362012-11-29 01:25:20 +01001031 if (WARN_ON(!tmp->pub.channel))
Johannes Berg2a519312009-02-10 21:25:55 +01001032 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001033
Johannes Berg9caf0362012-11-29 01:25:20 +01001034 tmp->ts = jiffies;
Johannes Berg2a519312009-02-10 21:25:55 +01001035
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001036 spin_lock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +01001037
Johannes Berg9caf0362012-11-29 01:25:20 +01001038 if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001039 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg9caf0362012-11-29 01:25:20 +01001040 return NULL;
1041 }
1042
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001043 found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
Johannes Berg2a519312009-02-10 21:25:55 +01001044
Johannes Bergcd1658f2009-04-16 15:00:58 +02001045 if (found) {
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001046 /* Update IEs */
Johannes Berg9caf0362012-11-29 01:25:20 +01001047 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
1048 const struct cfg80211_bss_ies *old;
Johannes Bergcd1658f2009-04-16 15:00:58 +02001049
Johannes Berg9caf0362012-11-29 01:25:20 +01001050 old = rcu_access_pointer(found->pub.proberesp_ies);
Johannes Bergcd1658f2009-04-16 15:00:58 +02001051
Johannes Berg9caf0362012-11-29 01:25:20 +01001052 rcu_assign_pointer(found->pub.proberesp_ies,
1053 tmp->pub.proberesp_ies);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001054 /* Override possible earlier Beacon frame IEs */
Johannes Berg9caf0362012-11-29 01:25:20 +01001055 rcu_assign_pointer(found->pub.ies,
1056 tmp->pub.proberesp_ies);
1057 if (old)
1058 kfree_rcu((struct cfg80211_bss_ies *)old,
1059 rcu_head);
1060 } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
Johannes Berg9537f222013-02-01 01:19:48 +01001061 const struct cfg80211_bss_ies *old;
Johannes Berg776b3582013-02-01 02:06:18 +01001062 struct cfg80211_internal_bss *bss;
1063
1064 if (found->pub.hidden_beacon_bss &&
1065 !list_empty(&found->hidden_list)) {
Johannes Berg1345ee62013-03-06 10:31:05 +01001066 const struct cfg80211_bss_ies *f;
1067
Johannes Berg776b3582013-02-01 02:06:18 +01001068 /*
1069 * The found BSS struct is one of the probe
1070 * response members of a group, but we're
1071 * receiving a beacon (beacon_ies in the tmp
1072 * bss is used). This can only mean that the
1073 * AP changed its beacon from not having an
1074 * SSID to showing it, which is confusing so
1075 * drop this information.
1076 */
Johannes Berg1345ee62013-03-06 10:31:05 +01001077
1078 f = rcu_access_pointer(tmp->pub.beacon_ies);
1079 kfree_rcu((struct cfg80211_bss_ies *)f,
1080 rcu_head);
Johannes Berg776b3582013-02-01 02:06:18 +01001081 goto drop;
1082 }
Johannes Berg915de2f2012-11-28 22:39:37 +01001083
Johannes Berg9caf0362012-11-29 01:25:20 +01001084 old = rcu_access_pointer(found->pub.beacon_ies);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001085
Johannes Berg9caf0362012-11-29 01:25:20 +01001086 rcu_assign_pointer(found->pub.beacon_ies,
1087 tmp->pub.beacon_ies);
Sven Neumann01123e22010-12-09 15:05:24 +01001088
1089 /* Override IEs if they were from a beacon before */
Johannes Berg9537f222013-02-01 01:19:48 +01001090 if (old == rcu_access_pointer(found->pub.ies))
Johannes Berg9caf0362012-11-29 01:25:20 +01001091 rcu_assign_pointer(found->pub.ies,
1092 tmp->pub.beacon_ies);
Johannes Bergcd1658f2009-04-16 15:00:58 +02001093
Johannes Berg776b3582013-02-01 02:06:18 +01001094 /* Assign beacon IEs to all sub entries */
1095 list_for_each_entry(bss, &found->hidden_list,
1096 hidden_list) {
1097 const struct cfg80211_bss_ies *ies;
1098
1099 ies = rcu_access_pointer(bss->pub.beacon_ies);
1100 WARN_ON(ies != old);
1101
1102 rcu_assign_pointer(bss->pub.beacon_ies,
1103 tmp->pub.beacon_ies);
1104 }
1105
Johannes Berg9caf0362012-11-29 01:25:20 +01001106 if (old)
1107 kfree_rcu((struct cfg80211_bss_ies *)old,
1108 rcu_head);
1109 }
Johannes Berg1345ee62013-03-06 10:31:05 +01001110
1111 found->pub.beacon_interval = tmp->pub.beacon_interval;
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001112 /*
1113 * don't update the signal if beacon was heard on
1114 * adjacent channel.
1115 */
1116 if (signal_valid)
1117 found->pub.signal = tmp->pub.signal;
Johannes Berg1345ee62013-03-06 10:31:05 +01001118 found->pub.capability = tmp->pub.capability;
1119 found->ts = tmp->ts;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001120 found->ts_boottime = tmp->ts_boottime;
Avraham Stern1d762502016-07-05 17:10:13 +03001121 found->parent_tsf = tmp->parent_tsf;
Sunil Dutt983dafa2017-12-13 19:51:36 +02001122 found->pub.chains = tmp->pub.chains;
1123 memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
1124 IEEE80211_MAX_CHAINS);
Avraham Stern1d762502016-07-05 17:10:13 +03001125 ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
Johannes Berg2a519312009-02-10 21:25:55 +01001126 } else {
Johannes Berg9caf0362012-11-29 01:25:20 +01001127 struct cfg80211_internal_bss *new;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001128 struct cfg80211_internal_bss *hidden;
Johannes Berg9caf0362012-11-29 01:25:20 +01001129 struct cfg80211_bss_ies *ies;
Dmitry Tarnyagindd9dfb92011-11-04 17:12:07 +01001130
Johannes Berg9caf0362012-11-29 01:25:20 +01001131 /*
1132 * create a copy -- the "res" variable that is passed in
1133 * is allocated on the stack since it's not needed in the
1134 * more common case of an update
1135 */
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001136 new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
Johannes Berg9caf0362012-11-29 01:25:20 +01001137 GFP_ATOMIC);
1138 if (!new) {
1139 ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
1140 if (ies)
1141 kfree_rcu(ies, rcu_head);
1142 ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
1143 if (ies)
1144 kfree_rcu(ies, rcu_head);
Johannes Berg776b3582013-02-01 02:06:18 +01001145 goto drop;
Johannes Berg9caf0362012-11-29 01:25:20 +01001146 }
1147 memcpy(new, tmp, sizeof(*new));
Johannes Berg776b3582013-02-01 02:06:18 +01001148 new->refcount = 1;
1149 INIT_LIST_HEAD(&new->hidden_list);
Peng Xu0b8fb822019-01-21 12:14:57 +02001150 INIT_LIST_HEAD(&new->nontrans_list);
Johannes Berg776b3582013-02-01 02:06:18 +01001151
1152 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001153 hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
Johannes Berg776b3582013-02-01 02:06:18 +01001154 if (!hidden)
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001155 hidden = rb_find_bss(rdev, tmp,
Johannes Berg776b3582013-02-01 02:06:18 +01001156 BSS_CMP_HIDE_NUL);
1157 if (hidden) {
1158 new->pub.hidden_beacon_bss = &hidden->pub;
1159 list_add(&new->hidden_list,
1160 &hidden->hidden_list);
1161 hidden->refcount++;
1162 rcu_assign_pointer(new->pub.beacon_ies,
1163 hidden->pub.beacon_ies);
1164 }
1165 } else {
1166 /*
1167 * Ok so we found a beacon, and don't have an entry. If
1168 * it's a beacon with hidden SSID, we might be in for an
1169 * expensive search for any probe responses that should
1170 * be grouped with this beacon for updates ...
1171 */
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001172 if (!cfg80211_combine_bsses(rdev, new)) {
Johannes Berg776b3582013-02-01 02:06:18 +01001173 kfree(new);
1174 goto drop;
1175 }
1176 }
1177
Johannes Berg9853a552016-11-15 12:05:11 +01001178 if (rdev->bss_entries >= bss_entries_limit &&
1179 !cfg80211_bss_expire_oldest(rdev)) {
1180 kfree(new);
1181 goto drop;
1182 }
1183
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001184 list_add_tail(&new->list, &rdev->bss_list);
Johannes Berg9853a552016-11-15 12:05:11 +01001185 rdev->bss_entries++;
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001186 rb_insert_bss(rdev, new);
Johannes Berg9caf0362012-11-29 01:25:20 +01001187 found = new;
Johannes Berg2a519312009-02-10 21:25:55 +01001188 }
1189
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001190 rdev->bss_generation++;
1191 bss_ref_get(rdev, found);
1192 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +01001193
Johannes Berg2a519312009-02-10 21:25:55 +01001194 return found;
Johannes Berg776b3582013-02-01 02:06:18 +01001195 drop:
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001196 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg776b3582013-02-01 02:06:18 +01001197 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001198}
1199
Jouni Malinen119f94a2018-09-05 18:52:22 +03001200/*
1201 * Update RX channel information based on the available frame payload
1202 * information. This is mainly for the 2.4 GHz band where frames can be received
1203 * from neighboring channels and the Beacon frames use the DSSS Parameter Set
1204 * element to indicate the current (transmitting) channel, but this might also
1205 * be needed on other bands if RX frequency does not match with the actual
1206 * operating channel of a BSS.
1207 */
Johannes Berg0172bb72012-11-23 14:23:30 +01001208static struct ieee80211_channel *
1209cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
Jouni Malinen119f94a2018-09-05 18:52:22 +03001210 struct ieee80211_channel *channel,
1211 enum nl80211_bss_scan_width scan_width)
Johannes Berg0172bb72012-11-23 14:23:30 +01001212{
1213 const u8 *tmp;
1214 u32 freq;
1215 int channel_number = -1;
Jouni Malinen119f94a2018-09-05 18:52:22 +03001216 struct ieee80211_channel *alt_channel;
Johannes Berg0172bb72012-11-23 14:23:30 +01001217
1218 tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
1219 if (tmp && tmp[1] == 1) {
1220 channel_number = tmp[2];
1221 } else {
1222 tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
1223 if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
1224 struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
1225
1226 channel_number = htop->primary_chan;
1227 }
1228 }
1229
Jouni Malinen119f94a2018-09-05 18:52:22 +03001230 if (channel_number < 0) {
1231 /* No channel information in frame payload */
Johannes Berg0172bb72012-11-23 14:23:30 +01001232 return channel;
Jouni Malinen119f94a2018-09-05 18:52:22 +03001233 }
Johannes Berg0172bb72012-11-23 14:23:30 +01001234
1235 freq = ieee80211_channel_to_frequency(channel_number, channel->band);
Jouni Malinen119f94a2018-09-05 18:52:22 +03001236 alt_channel = ieee80211_get_channel(wiphy, freq);
1237 if (!alt_channel) {
1238 if (channel->band == NL80211_BAND_2GHZ) {
1239 /*
1240 * Better not allow unexpected channels when that could
1241 * be going beyond the 1-11 range (e.g., discovering
1242 * BSS on channel 12 when radio is configured for
1243 * channel 11.
1244 */
1245 return NULL;
1246 }
1247
1248 /* No match for the payload channel number - ignore it */
1249 return channel;
1250 }
1251
1252 if (scan_width == NL80211_BSS_CHAN_WIDTH_10 ||
1253 scan_width == NL80211_BSS_CHAN_WIDTH_5) {
1254 /*
1255 * Ignore channel number in 5 and 10 MHz channels where there
1256 * may not be an n:1 or 1:n mapping between frequencies and
1257 * channel numbers.
1258 */
1259 return channel;
1260 }
1261
1262 /*
1263 * Use the channel determined through the payload channel number
1264 * instead of the RX channel reported by the driver.
1265 */
1266 if (alt_channel->flags & IEEE80211_CHAN_DISABLED)
Johannes Berg0172bb72012-11-23 14:23:30 +01001267 return NULL;
Jouni Malinen119f94a2018-09-05 18:52:22 +03001268 return alt_channel;
Johannes Berg0172bb72012-11-23 14:23:30 +01001269}
1270
Ben Greear0e3a39b2013-06-19 14:06:27 -07001271/* Returned bss is reference counted and must be cleaned up appropriately. */
Peng Xu0b8fb822019-01-21 12:14:57 +02001272static struct cfg80211_bss *
1273cfg80211_inform_single_bss_data(struct wiphy *wiphy,
1274 struct cfg80211_inform_bss *data,
1275 enum cfg80211_bss_frame_type ftype,
1276 const u8 *bssid, u64 tsf, u16 capability,
1277 u16 beacon_interval, const u8 *ie, size_t ielen,
1278 struct cfg80211_bss *trans_bss,
1279 gfp_t gfp)
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001280{
Peng Xu0b8fb822019-01-21 12:14:57 +02001281 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg9caf0362012-11-29 01:25:20 +01001282 struct cfg80211_bss_ies *ies;
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001283 struct ieee80211_channel *channel;
Peng Xu0b8fb822019-01-21 12:14:57 +02001284 struct cfg80211_internal_bss tmp = {}, *res, *trans_internal;
Dedy Lansky6eb18132015-02-08 15:52:03 +02001285 int bss_type;
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001286 bool signal_valid;
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001287
1288 if (WARN_ON(!wiphy))
1289 return NULL;
1290
Sujith22fe88d2010-05-13 10:34:08 +05301291 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001292 (data->signal < 0 || data->signal > 100)))
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001293 return NULL;
1294
Jouni Malinen119f94a2018-09-05 18:52:22 +03001295 channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan,
1296 data->scan_width);
Johannes Berg0172bb72012-11-23 14:23:30 +01001297 if (!channel)
1298 return NULL;
1299
Johannes Berg9caf0362012-11-29 01:25:20 +01001300 memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
1301 tmp.pub.channel = channel;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001302 tmp.pub.scan_width = data->scan_width;
1303 tmp.pub.signal = data->signal;
Johannes Berg9caf0362012-11-29 01:25:20 +01001304 tmp.pub.beacon_interval = beacon_interval;
1305 tmp.pub.capability = capability;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001306 tmp.ts_boottime = data->boottime_ns;
1307
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001308 /*
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02001309 * If we do not know here whether the IEs are from a Beacon or Probe
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001310 * Response frame, we need to pick one of the options and only use it
1311 * with the driver that does not provide the full Beacon/Probe Response
1312 * frame. Use Beacon frame pointer to avoid indicating that this should
Johannes Berg50521aa2013-01-30 21:33:19 +01001313 * override the IEs pointer should we have received an earlier
Johannes Berg9caf0362012-11-29 01:25:20 +01001314 * indication of Probe Response data.
Jouni Malinen34a6edd2010-01-06 16:19:24 +02001315 */
Johannes Berg0e227082014-08-12 20:34:30 +02001316 ies = kzalloc(sizeof(*ies) + ielen, gfp);
Johannes Berg9caf0362012-11-29 01:25:20 +01001317 if (!ies)
1318 return NULL;
1319 ies->len = ielen;
Johannes Berg8cef2c92013-02-05 16:54:31 +01001320 ies->tsf = tsf;
Johannes Berg0e227082014-08-12 20:34:30 +02001321 ies->from_beacon = false;
Johannes Berg9caf0362012-11-29 01:25:20 +01001322 memcpy(ies->data, ie, ielen);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001323
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02001324 switch (ftype) {
1325 case CFG80211_BSS_FTYPE_BEACON:
1326 ies->from_beacon = true;
Luca Coelho925b5972018-12-15 11:03:21 +02001327 /* fall through */
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02001328 case CFG80211_BSS_FTYPE_UNKNOWN:
1329 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
1330 break;
1331 case CFG80211_BSS_FTYPE_PRESP:
1332 rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
1333 break;
1334 }
Johannes Berg9caf0362012-11-29 01:25:20 +01001335 rcu_assign_pointer(tmp.pub.ies, ies);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001336
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001337 signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001338 wiphy->max_adj_channel_rssi_comp;
1339 res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001340 if (!res)
1341 return NULL;
1342
Johannes Berg57fbcce2016-04-12 15:56:15 +02001343 if (channel->band == NL80211_BAND_60GHZ) {
Dedy Lansky6eb18132015-02-08 15:52:03 +02001344 bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
1345 if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
1346 bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
1347 regulatory_hint_found_beacon(wiphy, channel, gfp);
1348 } else {
1349 if (res->pub.capability & WLAN_CAPABILITY_ESS)
1350 regulatory_hint_found_beacon(wiphy, channel, gfp);
1351 }
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001352
Peng Xu0b8fb822019-01-21 12:14:57 +02001353 if (trans_bss) {
1354 /* this is a nontransmitting bss, we need to add it to
1355 * transmitting bss' list if it is not there
1356 */
1357 trans_internal = container_of(trans_bss,
1358 struct cfg80211_internal_bss,
1359 pub);
1360 if (cfg80211_add_nontrans_list(trans_internal, res)) {
1361 if (__cfg80211_unlink_bss(rdev, res))
1362 rdev->bss_generation++;
1363 }
1364 }
1365
Beni Lev4ee3e062012-08-27 12:49:39 +03001366 trace_cfg80211_return_bss(&res->pub);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001367 /* cfg80211_bss_update gives us a referenced result */
1368 return &res->pub;
1369}
Peng Xu0b8fb822019-01-21 12:14:57 +02001370
1371static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
1372 struct cfg80211_inform_bss *data,
1373 enum cfg80211_bss_frame_type ftype,
1374 const u8 *bssid, u64 tsf,
1375 u16 beacon_interval, const u8 *ie,
1376 size_t ielen,
1377 struct cfg80211_bss *trans_bss,
1378 gfp_t gfp)
1379{
1380 const u8 *pos, *subelement, *mbssid_end_pos;
1381 const u8 *tmp, *mbssid_index_ie;
1382 size_t subie_len, new_ie_len;
1383 u8 new_bssid[ETH_ALEN];
1384 u8 *new_ie;
1385 u16 capability;
1386 struct cfg80211_bss *bss;
1387
1388 if (!trans_bss)
1389 return;
1390 if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1391 return;
1392
1393 pos = ie;
1394
1395 new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
1396 if (!new_ie)
1397 return;
1398
1399 while (pos < ie + ielen + 2) {
1400 tmp = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, pos,
1401 ielen - (pos - ie));
1402 if (!tmp)
1403 break;
1404
1405 mbssid_end_pos = tmp + tmp[1] + 2;
1406 /* Skip Element ID, Len, MaxBSSID Indicator */
1407 if (tmp[1] < 4)
1408 break;
1409 for (subelement = tmp + 3; subelement < mbssid_end_pos - 1;
1410 subelement += 2 + subelement[1]) {
1411 subie_len = subelement[1];
1412 if (mbssid_end_pos - subelement < 2 + subie_len)
1413 break;
1414 if (subelement[0] != 0 || subelement[1] < 4) {
1415 /* not a valid BSS profile */
1416 continue;
1417 }
1418
1419 if (subelement[2] != WLAN_EID_NON_TX_BSSID_CAP ||
1420 subelement[3] != 2) {
1421 /* The first element within the Nontransmitted
1422 * BSSID Profile is not the Nontransmitted
1423 * BSSID Capability element.
1424 */
1425 continue;
1426 }
1427
1428 /* found a Nontransmitted BSSID Profile */
1429 mbssid_index_ie = cfg80211_find_ie
1430 (WLAN_EID_MULTI_BSSID_IDX,
1431 subelement + 2, subie_len);
1432 if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
1433 mbssid_index_ie[2] == 0) {
1434 /* No valid Multiple BSSID-Index element */
1435 continue;
1436 }
1437
1438 cfg80211_gen_new_bssid(bssid, tmp[2],
1439 mbssid_index_ie[2],
1440 new_bssid);
1441 memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
1442 new_ie_len = cfg80211_gen_new_ie(ie, ielen,
1443 subelement + 2,
1444 subie_len, new_ie,
1445 gfp);
1446 if (!new_ie_len)
1447 continue;
1448
1449 capability = le16_to_cpup((const __le16 *)
1450 &subelement[4]);
1451 bss = cfg80211_inform_single_bss_data(wiphy, data,
1452 ftype,
1453 new_bssid, tsf,
1454 capability,
1455 beacon_interval,
1456 new_ie,
1457 new_ie_len,
1458 trans_bss, gfp);
1459 if (!bss)
1460 break;
1461 cfg80211_put_bss(wiphy, bss);
1462 }
1463
1464 pos = mbssid_end_pos;
1465 }
1466
1467 kfree(new_ie);
1468}
1469
1470struct cfg80211_bss *
1471cfg80211_inform_bss_data(struct wiphy *wiphy,
1472 struct cfg80211_inform_bss *data,
1473 enum cfg80211_bss_frame_type ftype,
1474 const u8 *bssid, u64 tsf, u16 capability,
1475 u16 beacon_interval, const u8 *ie, size_t ielen,
1476 gfp_t gfp)
1477{
1478 struct cfg80211_bss *res;
1479
1480 res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
1481 capability, beacon_interval, ie,
1482 ielen, NULL, gfp);
1483 cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
1484 beacon_interval, ie, ielen, res, gfp);
1485 return res;
1486}
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001487EXPORT_SYMBOL(cfg80211_inform_bss_data);
Jussi Kivilinna06aa7af2009-03-26 23:40:09 +02001488
Peng Xu0b8fb822019-01-21 12:14:57 +02001489static void
1490cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy,
1491 struct cfg80211_inform_bss *data,
1492 struct ieee80211_mgmt *mgmt, size_t len,
1493 struct cfg80211_bss *trans_bss,
1494 gfp_t gfp)
1495{
1496 enum cfg80211_bss_frame_type ftype;
1497 const u8 *ie = mgmt->u.probe_resp.variable;
1498 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1499 u.probe_resp.variable);
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001500
Peng Xu0b8fb822019-01-21 12:14:57 +02001501 ftype = ieee80211_is_beacon(mgmt->frame_control) ?
1502 CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
1503
1504 cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid,
1505 le64_to_cpu(mgmt->u.probe_resp.timestamp),
1506 le16_to_cpu(mgmt->u.probe_resp.beacon_int),
1507 ie, ielen, trans_bss, gfp);
1508}
1509
1510static void
1511cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
1512 struct cfg80211_internal_bss *nontrans_bss,
1513 struct ieee80211_mgmt *mgmt, size_t len,
1514 gfp_t gfp)
1515{
1516 u8 *ie, *new_ie, *pos;
1517 const u8 *nontrans_ssid, *trans_ssid, *mbssid;
1518 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1519 u.probe_resp.variable);
1520 size_t new_ie_len;
1521 struct cfg80211_bss_ies *new_ies;
1522 const struct cfg80211_bss_ies *old;
1523 u8 cpy_len;
1524
1525 ie = mgmt->u.probe_resp.variable;
1526
1527 new_ie_len = ielen;
1528 trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
1529 if (!trans_ssid)
1530 return;
1531 new_ie_len -= trans_ssid[1];
1532 mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
1533 if (!mbssid)
1534 return;
1535 new_ie_len -= mbssid[1];
1536 rcu_read_lock();
1537 nontrans_ssid = ieee80211_bss_get_ie(&nontrans_bss->pub, WLAN_EID_SSID);
1538 if (!nontrans_ssid) {
1539 rcu_read_unlock();
1540 return;
1541 }
1542 new_ie_len += nontrans_ssid[1];
1543 rcu_read_unlock();
1544
1545 /* generate new ie for nontrans BSS
1546 * 1. replace SSID with nontrans BSS' SSID
1547 * 2. skip MBSSID IE
1548 */
1549 new_ie = kzalloc(new_ie_len, gfp);
1550 if (!new_ie)
1551 return;
1552 new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, gfp);
1553 if (!new_ies) {
1554 kfree(new_ie);
1555 return;
1556 }
1557
1558 pos = new_ie;
1559
1560 /* copy the nontransmitted SSID */
1561 cpy_len = nontrans_ssid[1] + 2;
1562 memcpy(pos, nontrans_ssid, cpy_len);
1563 pos += cpy_len;
1564 /* copy the IEs between SSID and MBSSID */
1565 cpy_len = trans_ssid[1] + 2;
1566 memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
1567 pos += (mbssid - (trans_ssid + cpy_len));
1568 /* copy the IEs after MBSSID */
1569 cpy_len = mbssid[1] + 2;
1570 memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
1571
1572 /* update ie */
1573 new_ies->len = new_ie_len;
1574 new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
1575 new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
1576 memcpy(new_ies->data, new_ie, new_ie_len);
1577 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
1578 old = rcu_access_pointer(nontrans_bss->pub.proberesp_ies);
1579 rcu_assign_pointer(nontrans_bss->pub.proberesp_ies, new_ies);
1580 rcu_assign_pointer(nontrans_bss->pub.ies, new_ies);
1581 if (old)
1582 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1583 } else {
1584 old = rcu_access_pointer(nontrans_bss->pub.beacon_ies);
1585 rcu_assign_pointer(nontrans_bss->pub.beacon_ies, new_ies);
1586 rcu_assign_pointer(nontrans_bss->pub.ies, new_ies);
1587 if (old)
1588 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1589 }
1590}
1591
1592/* cfg80211_inform_bss_width_frame helper */
1593static struct cfg80211_bss *
1594cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
1595 struct cfg80211_inform_bss *data,
1596 struct ieee80211_mgmt *mgmt, size_t len,
1597 struct cfg80211_bss *trans_bss,
1598 gfp_t gfp)
Johannes Berg2a519312009-02-10 21:25:55 +01001599{
Johannes Berg9caf0362012-11-29 01:25:20 +01001600 struct cfg80211_internal_bss tmp = {}, *res;
1601 struct cfg80211_bss_ies *ies;
Emmanuel Grumbach3afc2162014-03-04 16:50:13 +02001602 struct ieee80211_channel *channel;
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001603 bool signal_valid;
Johannes Berg2a519312009-02-10 21:25:55 +01001604 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1605 u.probe_resp.variable);
Dedy Lansky6eb18132015-02-08 15:52:03 +02001606 int bss_type;
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001607
Johannes Berg0172bb72012-11-23 14:23:30 +01001608 BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
1609 offsetof(struct ieee80211_mgmt, u.beacon.variable));
1610
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001611 trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
Beni Lev4ee3e062012-08-27 12:49:39 +03001612
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001613 if (WARN_ON(!mgmt))
1614 return NULL;
1615
1616 if (WARN_ON(!wiphy))
1617 return NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001618
Sujith22fe88d2010-05-13 10:34:08 +05301619 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001620 (data->signal < 0 || data->signal > 100)))
Johannes Berg2a519312009-02-10 21:25:55 +01001621 return NULL;
1622
Mariusz Kozlowskibef9bac2011-03-26 19:26:55 +01001623 if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
Johannes Berg2a519312009-02-10 21:25:55 +01001624 return NULL;
1625
Johannes Berg0172bb72012-11-23 14:23:30 +01001626 channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
Jouni Malinen119f94a2018-09-05 18:52:22 +03001627 ielen, data->chan, data->scan_width);
Johannes Berg0172bb72012-11-23 14:23:30 +01001628 if (!channel)
1629 return NULL;
1630
Johannes Berg0e227082014-08-12 20:34:30 +02001631 ies = kzalloc(sizeof(*ies) + ielen, gfp);
Johannes Berg9caf0362012-11-29 01:25:20 +01001632 if (!ies)
Johannes Berg2a519312009-02-10 21:25:55 +01001633 return NULL;
Johannes Berg9caf0362012-11-29 01:25:20 +01001634 ies->len = ielen;
Johannes Berg8cef2c92013-02-05 16:54:31 +01001635 ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
Johannes Berg0e227082014-08-12 20:34:30 +02001636 ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
Johannes Berg9caf0362012-11-29 01:25:20 +01001637 memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
Johannes Berg2a519312009-02-10 21:25:55 +01001638
Johannes Berg9caf0362012-11-29 01:25:20 +01001639 if (ieee80211_is_probe_resp(mgmt->frame_control))
1640 rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
1641 else
1642 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
1643 rcu_assign_pointer(tmp.pub.ies, ies);
Arend Van Spriel505a2e82016-12-16 11:21:54 +00001644
Johannes Berg9caf0362012-11-29 01:25:20 +01001645 memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
1646 tmp.pub.channel = channel;
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001647 tmp.pub.scan_width = data->scan_width;
1648 tmp.pub.signal = data->signal;
Johannes Berg9caf0362012-11-29 01:25:20 +01001649 tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
1650 tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001651 tmp.ts_boottime = data->boottime_ns;
Avraham Stern1d762502016-07-05 17:10:13 +03001652 tmp.parent_tsf = data->parent_tsf;
Sunil Dutt983dafa2017-12-13 19:51:36 +02001653 tmp.pub.chains = data->chains;
1654 memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
Avraham Stern1d762502016-07-05 17:10:13 +03001655 ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
Johannes Berg2a519312009-02-10 21:25:55 +01001656
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001657 signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
Emmanuel Grumbach67af9812014-05-18 10:15:24 +03001658 wiphy->max_adj_channel_rssi_comp;
1659 res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
Johannes Berg2a519312009-02-10 21:25:55 +01001660 if (!res)
1661 return NULL;
1662
Johannes Berg57fbcce2016-04-12 15:56:15 +02001663 if (channel->band == NL80211_BAND_60GHZ) {
Dedy Lansky6eb18132015-02-08 15:52:03 +02001664 bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
1665 if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
1666 bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
1667 regulatory_hint_found_beacon(wiphy, channel, gfp);
1668 } else {
1669 if (res->pub.capability & WLAN_CAPABILITY_ESS)
1670 regulatory_hint_found_beacon(wiphy, channel, gfp);
1671 }
Luis R. Rodrigueze38f8a72009-02-21 00:20:39 -05001672
Beni Lev4ee3e062012-08-27 12:49:39 +03001673 trace_cfg80211_return_bss(&res->pub);
Johannes Berg2a519312009-02-10 21:25:55 +01001674 /* cfg80211_bss_update gives us a referenced result */
1675 return &res->pub;
1676}
Peng Xu0b8fb822019-01-21 12:14:57 +02001677
1678struct cfg80211_bss *
1679cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
1680 struct cfg80211_inform_bss *data,
1681 struct ieee80211_mgmt *mgmt, size_t len,
1682 gfp_t gfp)
1683{
1684 struct cfg80211_bss *res;
1685 struct cfg80211_internal_bss *trans_bss, *tmp_bss;
1686 const u8 *ie = mgmt->u.probe_resp.variable;
1687 const struct cfg80211_bss_ies *ies1, *ies2;
1688 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1689 u.probe_resp.variable);
1690
1691 res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
1692 len, NULL, gfp);
1693 if (!res || !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1694 return res;
1695
1696 /* process each non-transmitting bss */
1697 cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len, res, gfp);
1698
1699 /* check if the res has other nontransmitting bss which is not
1700 * in MBSSID IE
1701 */
1702 ies1 = rcu_access_pointer(res->ies);
1703 trans_bss = container_of(res, struct cfg80211_internal_bss, pub);
1704 if (!trans_bss)
1705 return res;
1706
1707 /* go through nontrans_list, if the timestamp of the BSS is
1708 * earlier than the timestamp of the transmitting BSS then
1709 * update it
1710 */
1711 list_for_each_entry(tmp_bss, &trans_bss->nontrans_list,
1712 nontrans_list) {
1713 ies2 = rcu_access_pointer(tmp_bss->pub.ies);
1714 if (ies2->tsf < ies1->tsf)
1715 cfg80211_update_notlisted_nontrans(wiphy, tmp_bss,
1716 mgmt, len, gfp);
1717 }
1718
1719 return res;
1720}
Dmitry Shmidt6e19bc42015-10-07 11:32:53 +02001721EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
Johannes Berg2a519312009-02-10 21:25:55 +01001722
Johannes Berg5b112d32013-02-01 01:49:58 +01001723void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001724{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001725 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001726 struct cfg80211_internal_bss *bss;
1727
1728 if (!pub)
1729 return;
1730
1731 bss = container_of(pub, struct cfg80211_internal_bss, pub);
Johannes Berg776b3582013-02-01 02:06:18 +01001732
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001733 spin_lock_bh(&rdev->bss_lock);
1734 bss_ref_get(rdev, bss);
1735 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg4c0c0b72012-01-20 13:55:26 +01001736}
1737EXPORT_SYMBOL(cfg80211_ref_bss);
1738
Johannes Berg5b112d32013-02-01 01:49:58 +01001739void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
Johannes Berg2a519312009-02-10 21:25:55 +01001740{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001741 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +01001742 struct cfg80211_internal_bss *bss;
1743
1744 if (!pub)
1745 return;
1746
1747 bss = container_of(pub, struct cfg80211_internal_bss, pub);
Johannes Berg776b3582013-02-01 02:06:18 +01001748
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001749 spin_lock_bh(&rdev->bss_lock);
1750 bss_ref_put(rdev, bss);
1751 spin_unlock_bh(&rdev->bss_lock);
Johannes Berg2a519312009-02-10 21:25:55 +01001752}
1753EXPORT_SYMBOL(cfg80211_put_bss);
1754
Johannes Bergd491af12009-02-10 21:25:58 +01001755void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1756{
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001757 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
Peng Xu0b8fb822019-01-21 12:14:57 +02001758 struct cfg80211_internal_bss *bss, *nontrans_bss, *tmp;
Johannes Bergd491af12009-02-10 21:25:58 +01001759
1760 if (WARN_ON(!pub))
1761 return;
1762
1763 bss = container_of(pub, struct cfg80211_internal_bss, pub);
1764
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001765 spin_lock_bh(&rdev->bss_lock);
Johannes Berg32073902010-10-06 21:18:04 +02001766 if (!list_empty(&bss->list)) {
Peng Xu0b8fb822019-01-21 12:14:57 +02001767 list_for_each_entry_safe(nontrans_bss, tmp,
1768 &bss->nontrans_list,
1769 nontrans_list) {
1770 if (__cfg80211_unlink_bss(rdev, nontrans_bss))
1771 rdev->bss_generation++;
1772 }
1773
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001774 if (__cfg80211_unlink_bss(rdev, bss))
1775 rdev->bss_generation++;
Johannes Berg32073902010-10-06 21:18:04 +02001776 }
Zhao, Gang1b8ec872014-04-21 12:53:02 +08001777 spin_unlock_bh(&rdev->bss_lock);
Johannes Bergd491af12009-02-10 21:25:58 +01001778}
1779EXPORT_SYMBOL(cfg80211_unlink_bss);
1780
Johannes Berg3d23e342009-09-29 23:27:28 +02001781#ifdef CONFIG_CFG80211_WEXT
Johannes Berg9f419f32013-05-08 21:34:22 +02001782static struct cfg80211_registered_device *
1783cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
1784{
Johannes Berg5fe231e2013-05-08 21:45:15 +02001785 struct cfg80211_registered_device *rdev;
Johannes Berg9f419f32013-05-08 21:34:22 +02001786 struct net_device *dev;
1787
Johannes Berg5fe231e2013-05-08 21:45:15 +02001788 ASSERT_RTNL();
1789
Johannes Berg9f419f32013-05-08 21:34:22 +02001790 dev = dev_get_by_index(net, ifindex);
1791 if (!dev)
Johannes Berg5fe231e2013-05-08 21:45:15 +02001792 return ERR_PTR(-ENODEV);
1793 if (dev->ieee80211_ptr)
Zhao, Gangf26cbf42014-04-21 12:53:03 +08001794 rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
Johannes Berg5fe231e2013-05-08 21:45:15 +02001795 else
Johannes Berg9f419f32013-05-08 21:34:22 +02001796 rdev = ERR_PTR(-ENODEV);
1797 dev_put(dev);
Johannes Berg9f419f32013-05-08 21:34:22 +02001798 return rdev;
1799}
1800
Johannes Berg2a519312009-02-10 21:25:55 +01001801int cfg80211_wext_siwscan(struct net_device *dev,
1802 struct iw_request_info *info,
1803 union iwreq_data *wrqu, char *extra)
1804{
1805 struct cfg80211_registered_device *rdev;
1806 struct wiphy *wiphy;
1807 struct iw_scan_req *wreq = NULL;
Johannes Berg65486c82009-12-23 15:33:35 +01001808 struct cfg80211_scan_request *creq = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01001809 int i, err, n_channels = 0;
Johannes Berg57fbcce2016-04-12 15:56:15 +02001810 enum nl80211_band band;
Johannes Berg2a519312009-02-10 21:25:55 +01001811
1812 if (!netif_running(dev))
1813 return -ENETDOWN;
1814
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001815 if (wrqu->data.length == sizeof(struct iw_scan_req))
1816 wreq = (struct iw_scan_req *)extra;
1817
Johannes Berg463d0182009-07-14 00:33:35 +02001818 rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01001819
1820 if (IS_ERR(rdev))
1821 return PTR_ERR(rdev);
1822
Johannes Bergf9d15d12014-01-22 11:14:19 +02001823 if (rdev->scan_req || rdev->scan_msg) {
Johannes Berg2a519312009-02-10 21:25:55 +01001824 err = -EBUSY;
1825 goto out;
1826 }
1827
1828 wiphy = &rdev->wiphy;
1829
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001830 /* Determine number of channels, needed to allocate creq */
1831 if (wreq && wreq->num_channels)
1832 n_channels = wreq->num_channels;
Ilan Peerbdfbec22014-01-09 11:37:23 +02001833 else
1834 n_channels = ieee80211_get_num_supported_channels(wiphy);
Johannes Berg2a519312009-02-10 21:25:55 +01001835
1836 creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
1837 n_channels * sizeof(void *),
1838 GFP_ATOMIC);
1839 if (!creq) {
1840 err = -ENOMEM;
1841 goto out;
1842 }
1843
1844 creq->wiphy = wiphy;
Johannes Bergfd014282012-06-18 19:17:03 +02001845 creq->wdev = dev->ieee80211_ptr;
Johannes Berg5ba63532009-08-07 17:54:07 +02001846 /* SSIDs come after channels */
1847 creq->ssids = (void *)&creq->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01001848 creq->n_channels = n_channels;
1849 creq->n_ssids = 1;
Sam Leffler15d60302012-10-11 21:03:34 -07001850 creq->scan_start = jiffies;
Johannes Berg2a519312009-02-10 21:25:55 +01001851
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001852 /* translate "Scan on frequencies" request */
Johannes Berg2a519312009-02-10 21:25:55 +01001853 i = 0;
Johannes Berg57fbcce2016-04-12 15:56:15 +02001854 for (band = 0; band < NUM_NL80211_BANDS; band++) {
Johannes Berg2a519312009-02-10 21:25:55 +01001855 int j;
Johannes Berg584991d2009-11-02 13:32:03 +01001856
Johannes Berg2a519312009-02-10 21:25:55 +01001857 if (!wiphy->bands[band])
1858 continue;
Johannes Berg584991d2009-11-02 13:32:03 +01001859
Johannes Berg2a519312009-02-10 21:25:55 +01001860 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01001861 /* ignore disabled channels */
1862 if (wiphy->bands[band]->channels[j].flags &
1863 IEEE80211_CHAN_DISABLED)
1864 continue;
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001865
1866 /* If we have a wireless request structure and the
1867 * wireless request specifies frequencies, then search
1868 * for the matching hardware channel.
1869 */
1870 if (wreq && wreq->num_channels) {
1871 int k;
1872 int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
1873 for (k = 0; k < wreq->num_channels; k++) {
Zhao, Gang96998e32014-04-09 09:28:06 +08001874 struct iw_freq *freq =
1875 &wreq->channel_list[k];
1876 int wext_freq =
1877 cfg80211_wext_freq(freq);
1878
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001879 if (wext_freq == wiphy_freq)
1880 goto wext_freq_found;
1881 }
1882 goto wext_freq_not_found;
1883 }
1884
1885 wext_freq_found:
Johannes Berg2a519312009-02-10 21:25:55 +01001886 creq->channels[i] = &wiphy->bands[band]->channels[j];
1887 i++;
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001888 wext_freq_not_found: ;
Johannes Berg2a519312009-02-10 21:25:55 +01001889 }
1890 }
Holger Schurig8862dc52009-09-11 10:13:55 +02001891 /* No channels found? */
1892 if (!i) {
1893 err = -EINVAL;
1894 goto out;
1895 }
Johannes Berg2a519312009-02-10 21:25:55 +01001896
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001897 /* Set real number of channels specified in creq->channels[] */
1898 creq->n_channels = i;
Johannes Berg2a519312009-02-10 21:25:55 +01001899
Holger Schurigb2e3abd2009-09-09 13:09:54 +02001900 /* translate "Scan for SSID" request */
1901 if (wreq) {
Johannes Berg2a519312009-02-10 21:25:55 +01001902 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
Johannes Berg65486c82009-12-23 15:33:35 +01001903 if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
1904 err = -EINVAL;
1905 goto out;
1906 }
Johannes Berg2a519312009-02-10 21:25:55 +01001907 memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
1908 creq->ssids[0].ssid_len = wreq->essid_len;
1909 }
1910 if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
1911 creq->n_ssids = 0;
1912 }
1913
Johannes Berg57fbcce2016-04-12 15:56:15 +02001914 for (i = 0; i < NUM_NL80211_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02001915 if (wiphy->bands[i])
1916 creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02001917
Jouni Malinen818965d2016-02-26 22:12:47 +02001918 eth_broadcast_addr(creq->bssid);
1919
Johannes Berg2a519312009-02-10 21:25:55 +01001920 rdev->scan_req = creq;
Hila Gonene35e4d22012-06-27 17:19:42 +03001921 err = rdev_scan(rdev, creq);
Johannes Berg2a519312009-02-10 21:25:55 +01001922 if (err) {
1923 rdev->scan_req = NULL;
Johannes Berg65486c82009-12-23 15:33:35 +01001924 /* creq will be freed below */
Johannes Berg463d0182009-07-14 00:33:35 +02001925 } else {
Johannes Bergfd014282012-06-18 19:17:03 +02001926 nl80211_send_scan_start(rdev, dev->ieee80211_ptr);
Johannes Berg65486c82009-12-23 15:33:35 +01001927 /* creq now owned by driver */
1928 creq = NULL;
Johannes Berg463d0182009-07-14 00:33:35 +02001929 dev_hold(dev);
1930 }
Johannes Berg2a519312009-02-10 21:25:55 +01001931 out:
Johannes Berg65486c82009-12-23 15:33:35 +01001932 kfree(creq);
Johannes Berg2a519312009-02-10 21:25:55 +01001933 return err;
1934}
Johannes Berg2afe38d2015-01-06 14:00:53 +01001935EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
Johannes Berg2a519312009-02-10 21:25:55 +01001936
James Minor76a70e92015-02-24 12:58:20 -06001937static char *ieee80211_scan_add_ies(struct iw_request_info *info,
1938 const struct cfg80211_bss_ies *ies,
1939 char *current_ev, char *end_buf)
Johannes Berg2a519312009-02-10 21:25:55 +01001940{
Johannes Berg9caf0362012-11-29 01:25:20 +01001941 const u8 *pos, *end, *next;
Johannes Berg2a519312009-02-10 21:25:55 +01001942 struct iw_event iwe;
1943
Johannes Berg9caf0362012-11-29 01:25:20 +01001944 if (!ies)
James Minor76a70e92015-02-24 12:58:20 -06001945 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01001946
1947 /*
1948 * If needed, fragment the IEs buffer (at IE boundaries) into short
1949 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
1950 */
Johannes Berg9caf0362012-11-29 01:25:20 +01001951 pos = ies->data;
1952 end = pos + ies->len;
Johannes Berg2a519312009-02-10 21:25:55 +01001953
1954 while (end - pos > IW_GENERIC_IE_MAX) {
1955 next = pos + 2 + pos[1];
1956 while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
1957 next = next + 2 + next[1];
1958
1959 memset(&iwe, 0, sizeof(iwe));
1960 iwe.cmd = IWEVGENIE;
1961 iwe.u.data.length = next - pos;
James Minor76a70e92015-02-24 12:58:20 -06001962 current_ev = iwe_stream_add_point_check(info, current_ev,
1963 end_buf, &iwe,
1964 (void *)pos);
1965 if (IS_ERR(current_ev))
1966 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01001967 pos = next;
1968 }
1969
1970 if (end > pos) {
1971 memset(&iwe, 0, sizeof(iwe));
1972 iwe.cmd = IWEVGENIE;
1973 iwe.u.data.length = end - pos;
James Minor76a70e92015-02-24 12:58:20 -06001974 current_ev = iwe_stream_add_point_check(info, current_ev,
1975 end_buf, &iwe,
1976 (void *)pos);
1977 if (IS_ERR(current_ev))
1978 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01001979 }
James Minor76a70e92015-02-24 12:58:20 -06001980
1981 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01001982}
1983
Johannes Berg2a519312009-02-10 21:25:55 +01001984static char *
Johannes Berg77965c972009-02-18 18:45:06 +01001985ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1986 struct cfg80211_internal_bss *bss, char *current_ev,
1987 char *end_buf)
Johannes Berg2a519312009-02-10 21:25:55 +01001988{
Johannes Berg9caf0362012-11-29 01:25:20 +01001989 const struct cfg80211_bss_ies *ies;
Johannes Berg2a519312009-02-10 21:25:55 +01001990 struct iw_event iwe;
Johannes Berg9caf0362012-11-29 01:25:20 +01001991 const u8 *ie;
James Minor76a70e92015-02-24 12:58:20 -06001992 u8 buf[50];
1993 u8 *cfg, *p, *tmp;
Johannes Berg9caf0362012-11-29 01:25:20 +01001994 int rem, i, sig;
Johannes Berg2a519312009-02-10 21:25:55 +01001995 bool ismesh = false;
1996
1997 memset(&iwe, 0, sizeof(iwe));
1998 iwe.cmd = SIOCGIWAP;
1999 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
2000 memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
James Minor76a70e92015-02-24 12:58:20 -06002001 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2002 IW_EV_ADDR_LEN);
2003 if (IS_ERR(current_ev))
2004 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002005
2006 memset(&iwe, 0, sizeof(iwe));
2007 iwe.cmd = SIOCGIWFREQ;
2008 iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
2009 iwe.u.freq.e = 0;
James Minor76a70e92015-02-24 12:58:20 -06002010 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2011 IW_EV_FREQ_LEN);
2012 if (IS_ERR(current_ev))
2013 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002014
2015 memset(&iwe, 0, sizeof(iwe));
2016 iwe.cmd = SIOCGIWFREQ;
2017 iwe.u.freq.m = bss->pub.channel->center_freq;
2018 iwe.u.freq.e = 6;
James Minor76a70e92015-02-24 12:58:20 -06002019 current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2020 IW_EV_FREQ_LEN);
2021 if (IS_ERR(current_ev))
2022 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002023
Johannes Berg77965c972009-02-18 18:45:06 +01002024 if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
Johannes Berg2a519312009-02-10 21:25:55 +01002025 memset(&iwe, 0, sizeof(iwe));
2026 iwe.cmd = IWEVQUAL;
2027 iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
2028 IW_QUAL_NOISE_INVALID |
Johannes Berga77b8552009-02-18 18:27:22 +01002029 IW_QUAL_QUAL_UPDATED;
Johannes Berg77965c972009-02-18 18:45:06 +01002030 switch (wiphy->signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01002031 case CFG80211_SIGNAL_TYPE_MBM:
Johannes Berga77b8552009-02-18 18:27:22 +01002032 sig = bss->pub.signal / 100;
2033 iwe.u.qual.level = sig;
Johannes Berg2a519312009-02-10 21:25:55 +01002034 iwe.u.qual.updated |= IW_QUAL_DBM;
Johannes Berga77b8552009-02-18 18:27:22 +01002035 if (sig < -110) /* rather bad */
2036 sig = -110;
2037 else if (sig > -40) /* perfect */
2038 sig = -40;
2039 /* will give a range of 0 .. 70 */
2040 iwe.u.qual.qual = sig + 110;
Johannes Berg2a519312009-02-10 21:25:55 +01002041 break;
2042 case CFG80211_SIGNAL_TYPE_UNSPEC:
2043 iwe.u.qual.level = bss->pub.signal;
Johannes Berga77b8552009-02-18 18:27:22 +01002044 /* will give range 0 .. 100 */
2045 iwe.u.qual.qual = bss->pub.signal;
Johannes Berg2a519312009-02-10 21:25:55 +01002046 break;
2047 default:
2048 /* not reached */
2049 break;
2050 }
James Minor76a70e92015-02-24 12:58:20 -06002051 current_ev = iwe_stream_add_event_check(info, current_ev,
2052 end_buf, &iwe,
2053 IW_EV_QUAL_LEN);
2054 if (IS_ERR(current_ev))
2055 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002056 }
2057
2058 memset(&iwe, 0, sizeof(iwe));
2059 iwe.cmd = SIOCGIWENCODE;
2060 if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
2061 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
2062 else
2063 iwe.u.data.flags = IW_ENCODE_DISABLED;
2064 iwe.u.data.length = 0;
James Minor76a70e92015-02-24 12:58:20 -06002065 current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2066 &iwe, "");
2067 if (IS_ERR(current_ev))
2068 return current_ev;
Johannes Berg2a519312009-02-10 21:25:55 +01002069
Johannes Berg9caf0362012-11-29 01:25:20 +01002070 rcu_read_lock();
2071 ies = rcu_dereference(bss->pub.ies);
Johannes Berg83c7aa12013-02-05 16:51:29 +01002072 rem = ies->len;
2073 ie = ies->data;
Johannes Berg9caf0362012-11-29 01:25:20 +01002074
Johannes Berg83c7aa12013-02-05 16:51:29 +01002075 while (rem >= 2) {
Johannes Berg2a519312009-02-10 21:25:55 +01002076 /* invalid data */
2077 if (ie[1] > rem - 2)
2078 break;
2079
2080 switch (ie[0]) {
2081 case WLAN_EID_SSID:
2082 memset(&iwe, 0, sizeof(iwe));
2083 iwe.cmd = SIOCGIWESSID;
2084 iwe.u.data.length = ie[1];
2085 iwe.u.data.flags = 1;
James Minor76a70e92015-02-24 12:58:20 -06002086 current_ev = iwe_stream_add_point_check(info,
2087 current_ev,
2088 end_buf, &iwe,
2089 (u8 *)ie + 2);
2090 if (IS_ERR(current_ev))
2091 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002092 break;
2093 case WLAN_EID_MESH_ID:
2094 memset(&iwe, 0, sizeof(iwe));
2095 iwe.cmd = SIOCGIWESSID;
2096 iwe.u.data.length = ie[1];
2097 iwe.u.data.flags = 1;
James Minor76a70e92015-02-24 12:58:20 -06002098 current_ev = iwe_stream_add_point_check(info,
2099 current_ev,
2100 end_buf, &iwe,
2101 (u8 *)ie + 2);
2102 if (IS_ERR(current_ev))
2103 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002104 break;
2105 case WLAN_EID_MESH_CONFIG:
2106 ismesh = true;
Rui Paulo136cfa22009-11-18 18:40:00 +00002107 if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
Johannes Berg2a519312009-02-10 21:25:55 +01002108 break;
Johannes Berg9caf0362012-11-29 01:25:20 +01002109 cfg = (u8 *)ie + 2;
Johannes Berg2a519312009-02-10 21:25:55 +01002110 memset(&iwe, 0, sizeof(iwe));
2111 iwe.cmd = IWEVCUSTOM;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002112 sprintf(buf, "Mesh Network Path Selection Protocol ID: "
2113 "0x%02X", cfg[0]);
Johannes Berg2a519312009-02-10 21:25:55 +01002114 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002115 current_ev = iwe_stream_add_point_check(info,
2116 current_ev,
2117 end_buf,
2118 &iwe, buf);
2119 if (IS_ERR(current_ev))
2120 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002121 sprintf(buf, "Path Selection Metric ID: 0x%02X",
2122 cfg[1]);
Johannes Berg2a519312009-02-10 21:25:55 +01002123 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002124 current_ev = iwe_stream_add_point_check(info,
2125 current_ev,
2126 end_buf,
2127 &iwe, buf);
2128 if (IS_ERR(current_ev))
2129 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002130 sprintf(buf, "Congestion Control Mode ID: 0x%02X",
2131 cfg[2]);
Johannes Berg2a519312009-02-10 21:25:55 +01002132 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002133 current_ev = iwe_stream_add_point_check(info,
2134 current_ev,
2135 end_buf,
2136 &iwe, buf);
2137 if (IS_ERR(current_ev))
2138 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002139 sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
Johannes Berg2a519312009-02-10 21:25:55 +01002140 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002141 current_ev = iwe_stream_add_point_check(info,
2142 current_ev,
2143 end_buf,
2144 &iwe, buf);
2145 if (IS_ERR(current_ev))
2146 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002147 sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
2148 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002149 current_ev = iwe_stream_add_point_check(info,
2150 current_ev,
2151 end_buf,
2152 &iwe, buf);
2153 if (IS_ERR(current_ev))
2154 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002155 sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
2156 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002157 current_ev = iwe_stream_add_point_check(info,
2158 current_ev,
2159 end_buf,
2160 &iwe, buf);
2161 if (IS_ERR(current_ev))
2162 goto unlock;
Rui Paulo76aa5e72009-11-18 18:22:59 +00002163 sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
Johannes Berg2a519312009-02-10 21:25:55 +01002164 iwe.u.data.length = strlen(buf);
James Minor76a70e92015-02-24 12:58:20 -06002165 current_ev = iwe_stream_add_point_check(info,
2166 current_ev,
2167 end_buf,
2168 &iwe, buf);
2169 if (IS_ERR(current_ev))
2170 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002171 break;
2172 case WLAN_EID_SUPP_RATES:
2173 case WLAN_EID_EXT_SUPP_RATES:
2174 /* display all supported rates in readable format */
2175 p = current_ev + iwe_stream_lcp_len(info);
2176
2177 memset(&iwe, 0, sizeof(iwe));
2178 iwe.cmd = SIOCGIWRATE;
2179 /* Those two flags are ignored... */
2180 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
2181
2182 for (i = 0; i < ie[1]; i++) {
2183 iwe.u.bitrate.value =
2184 ((ie[i + 2] & 0x7f) * 500000);
James Minor76a70e92015-02-24 12:58:20 -06002185 tmp = p;
Johannes Berg2a519312009-02-10 21:25:55 +01002186 p = iwe_stream_add_value(info, current_ev, p,
James Minor76a70e92015-02-24 12:58:20 -06002187 end_buf, &iwe,
2188 IW_EV_PARAM_LEN);
2189 if (p == tmp) {
2190 current_ev = ERR_PTR(-E2BIG);
2191 goto unlock;
2192 }
Johannes Berg2a519312009-02-10 21:25:55 +01002193 }
2194 current_ev = p;
2195 break;
2196 }
2197 rem -= ie[1] + 2;
2198 ie += ie[1] + 2;
2199 }
2200
Joe Perchesf64f9e72009-11-29 16:55:45 -08002201 if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
2202 ismesh) {
Johannes Berg2a519312009-02-10 21:25:55 +01002203 memset(&iwe, 0, sizeof(iwe));
2204 iwe.cmd = SIOCGIWMODE;
2205 if (ismesh)
2206 iwe.u.mode = IW_MODE_MESH;
2207 else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
2208 iwe.u.mode = IW_MODE_MASTER;
2209 else
2210 iwe.u.mode = IW_MODE_ADHOC;
James Minor76a70e92015-02-24 12:58:20 -06002211 current_ev = iwe_stream_add_event_check(info, current_ev,
2212 end_buf, &iwe,
2213 IW_EV_UINT_LEN);
2214 if (IS_ERR(current_ev))
2215 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002216 }
2217
James Minor76a70e92015-02-24 12:58:20 -06002218 memset(&iwe, 0, sizeof(iwe));
2219 iwe.cmd = IWEVCUSTOM;
2220 sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
2221 iwe.u.data.length = strlen(buf);
2222 current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2223 &iwe, buf);
2224 if (IS_ERR(current_ev))
2225 goto unlock;
2226 memset(&iwe, 0, sizeof(iwe));
2227 iwe.cmd = IWEVCUSTOM;
2228 sprintf(buf, " Last beacon: %ums ago",
2229 elapsed_jiffies_msecs(bss->ts));
2230 iwe.u.data.length = strlen(buf);
2231 current_ev = iwe_stream_add_point_check(info, current_ev,
2232 end_buf, &iwe, buf);
2233 if (IS_ERR(current_ev))
2234 goto unlock;
Johannes Berg2a519312009-02-10 21:25:55 +01002235
James Minor76a70e92015-02-24 12:58:20 -06002236 current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
2237
2238 unlock:
Johannes Berg9caf0362012-11-29 01:25:20 +01002239 rcu_read_unlock();
Johannes Berg2a519312009-02-10 21:25:55 +01002240 return current_ev;
2241}
2242
2243
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002244static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
Johannes Berg2a519312009-02-10 21:25:55 +01002245 struct iw_request_info *info,
2246 char *buf, size_t len)
2247{
2248 char *current_ev = buf;
2249 char *end_buf = buf + len;
2250 struct cfg80211_internal_bss *bss;
James Minor76a70e92015-02-24 12:58:20 -06002251 int err = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01002252
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002253 spin_lock_bh(&rdev->bss_lock);
2254 cfg80211_bss_expire(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01002255
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002256 list_for_each_entry(bss, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01002257 if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
James Minor76a70e92015-02-24 12:58:20 -06002258 err = -E2BIG;
2259 break;
Johannes Berg2a519312009-02-10 21:25:55 +01002260 }
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002261 current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
Johannes Berg77965c972009-02-18 18:45:06 +01002262 current_ev, end_buf);
James Minor76a70e92015-02-24 12:58:20 -06002263 if (IS_ERR(current_ev)) {
2264 err = PTR_ERR(current_ev);
2265 break;
2266 }
Johannes Berg2a519312009-02-10 21:25:55 +01002267 }
Zhao, Gang1b8ec872014-04-21 12:53:02 +08002268 spin_unlock_bh(&rdev->bss_lock);
James Minor76a70e92015-02-24 12:58:20 -06002269
2270 if (err)
2271 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01002272 return current_ev - buf;
2273}
2274
2275
2276int cfg80211_wext_giwscan(struct net_device *dev,
2277 struct iw_request_info *info,
2278 struct iw_point *data, char *extra)
2279{
2280 struct cfg80211_registered_device *rdev;
2281 int res;
2282
2283 if (!netif_running(dev))
2284 return -ENETDOWN;
2285
Johannes Berg463d0182009-07-14 00:33:35 +02002286 rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01002287
2288 if (IS_ERR(rdev))
2289 return PTR_ERR(rdev);
2290
Johannes Bergf9d15d12014-01-22 11:14:19 +02002291 if (rdev->scan_req || rdev->scan_msg)
Johannes Berg5fe231e2013-05-08 21:45:15 +02002292 return -EAGAIN;
Johannes Berg2a519312009-02-10 21:25:55 +01002293
2294 res = ieee80211_scan_results(rdev, info, extra, data->length);
2295 data->length = 0;
2296 if (res >= 0) {
2297 data->length = res;
2298 res = 0;
2299 }
2300
Johannes Berg2a519312009-02-10 21:25:55 +01002301 return res;
2302}
Johannes Berg2afe38d2015-01-06 14:00:53 +01002303EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
Johannes Berg2a519312009-02-10 21:25:55 +01002304#endif