Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * cfg80211 scan result handling
4 : : *
5 : : * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
6 : : * Copyright 2013-2014 Intel Mobile Communications GmbH
7 : : * Copyright 2016 Intel Deutschland GmbH
8 : : * Copyright (C) 2018-2019 Intel Corporation
9 : : */
10 : : #include <linux/kernel.h>
11 : : #include <linux/slab.h>
12 : : #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>
19 : : #include <net/cfg80211-wext.h>
20 : : #include <net/iw_handler.h>
21 : : #include "core.h"
22 : : #include "nl80211.h"
23 : : #include "wext-compat.h"
24 : : #include "rdev-ops.h"
25 : :
26 : : /**
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 : :
62 : : /*
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 : : */
70 : : static int bss_entries_limit = 1000;
71 : : module_param(bss_entries_limit, int, 0644);
72 : : MODULE_PARM_DESC(bss_entries_limit,
73 : : "limit to number of scan BSS entries (per wiphy, default 1000)");
74 : :
75 : : #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
76 : :
77 : 0 : static void bss_free(struct cfg80211_internal_bss *bss)
78 : : {
79 : : struct cfg80211_bss_ies *ies;
80 : :
81 [ # # # # ]: 0 : if (WARN_ON(atomic_read(&bss->hold)))
82 : 0 : return;
83 : :
84 : 0 : ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
85 [ # # # # ]: 0 : if (ies && !bss->pub.hidden_beacon_bss)
86 [ # # ]: 0 : kfree_rcu(ies, rcu_head);
87 : 0 : ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
88 [ # # ]: 0 : if (ies)
89 [ # # ]: 0 : kfree_rcu(ies, rcu_head);
90 : :
91 : : /*
92 : : * This happens when the module is removed, it doesn't
93 : : * really matter any more save for completeness
94 : : */
95 [ # # ]: 0 : if (!list_empty(&bss->hidden_list))
96 : : list_del(&bss->hidden_list);
97 : :
98 : 0 : kfree(bss);
99 : : }
100 : :
101 : 0 : static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
102 : : struct cfg80211_internal_bss *bss)
103 : : {
104 : : lockdep_assert_held(&rdev->bss_lock);
105 : :
106 : 0 : bss->refcount++;
107 [ # # ]: 0 : if (bss->pub.hidden_beacon_bss) {
108 : 0 : bss = container_of(bss->pub.hidden_beacon_bss,
109 : : struct cfg80211_internal_bss,
110 : : pub);
111 : 0 : bss->refcount++;
112 : : }
113 [ # # ]: 0 : if (bss->pub.transmitted_bss) {
114 : : bss = container_of(bss->pub.transmitted_bss,
115 : : struct cfg80211_internal_bss,
116 : : pub);
117 : 0 : bss->refcount++;
118 : : }
119 : 0 : }
120 : :
121 : 0 : static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
122 : : struct cfg80211_internal_bss *bss)
123 : : {
124 : : lockdep_assert_held(&rdev->bss_lock);
125 : :
126 [ # # ]: 0 : if (bss->pub.hidden_beacon_bss) {
127 : : struct cfg80211_internal_bss *hbss;
128 : 0 : hbss = container_of(bss->pub.hidden_beacon_bss,
129 : : struct cfg80211_internal_bss,
130 : : pub);
131 : 0 : hbss->refcount--;
132 [ # # ]: 0 : if (hbss->refcount == 0)
133 : 0 : bss_free(hbss);
134 : : }
135 : :
136 [ # # ]: 0 : if (bss->pub.transmitted_bss) {
137 : : struct cfg80211_internal_bss *tbss;
138 : :
139 : 0 : tbss = container_of(bss->pub.transmitted_bss,
140 : : struct cfg80211_internal_bss,
141 : : pub);
142 : 0 : tbss->refcount--;
143 [ # # ]: 0 : if (tbss->refcount == 0)
144 : 0 : bss_free(tbss);
145 : : }
146 : :
147 : 0 : bss->refcount--;
148 [ # # ]: 0 : if (bss->refcount == 0)
149 : 0 : bss_free(bss);
150 : 0 : }
151 : :
152 : 0 : static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
153 : : struct cfg80211_internal_bss *bss)
154 : : {
155 : : lockdep_assert_held(&rdev->bss_lock);
156 : :
157 [ # # ]: 0 : if (!list_empty(&bss->hidden_list)) {
158 : : /*
159 : : * don't remove the beacon entry if it has
160 : : * probe responses associated with it
161 : : */
162 [ # # ]: 0 : if (!bss->pub.hidden_beacon_bss)
163 : : return false;
164 : : /*
165 : : * if it's a probe response entry break its
166 : : * link to the other entries in the group
167 : : */
168 : : list_del_init(&bss->hidden_list);
169 : : }
170 : :
171 : 0 : list_del_init(&bss->list);
172 : 0 : list_del_init(&bss->pub.nontrans_list);
173 : 0 : rb_erase(&bss->rbn, &rdev->bss_tree);
174 : 0 : rdev->bss_entries--;
175 [ # # # # ]: 0 : WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
176 : : "rdev bss entries[%d]/list[empty:%d] corruption\n",
177 : : rdev->bss_entries, list_empty(&rdev->bss_list));
178 : 0 : bss_ref_put(rdev, bss);
179 : 0 : return true;
180 : : }
181 : :
182 : 0 : bool cfg80211_is_element_inherited(const struct element *elem,
183 : : const struct element *non_inherit_elem)
184 : : {
185 : : u8 id_len, ext_id_len, i, loop_len, id;
186 : : const u8 *list;
187 : :
188 [ # # ]: 0 : if (elem->id == WLAN_EID_MULTIPLE_BSSID)
189 : : return false;
190 : :
191 [ # # # # ]: 0 : if (!non_inherit_elem || non_inherit_elem->datalen < 2)
192 : : return true;
193 : :
194 : : /*
195 : : * non inheritance element format is:
196 : : * ext ID (56) | IDs list len | list | extension IDs list len | list
197 : : * Both lists are optional. Both lengths are mandatory.
198 : : * This means valid length is:
199 : : * elem_len = 1 (extension ID) + 2 (list len fields) + list lengths
200 : : */
201 : 0 : id_len = non_inherit_elem->data[1];
202 [ # # ]: 0 : if (non_inherit_elem->datalen < 3 + id_len)
203 : : return true;
204 : :
205 : 0 : ext_id_len = non_inherit_elem->data[2 + id_len];
206 [ # # ]: 0 : if (non_inherit_elem->datalen < 3 + id_len + ext_id_len)
207 : : return true;
208 : :
209 [ # # ]: 0 : if (elem->id == WLAN_EID_EXTENSION) {
210 [ # # ]: 0 : if (!ext_id_len)
211 : : return true;
212 : : loop_len = ext_id_len;
213 : 0 : list = &non_inherit_elem->data[3 + id_len];
214 : 0 : id = elem->data[0];
215 : : } else {
216 [ # # ]: 0 : if (!id_len)
217 : : return true;
218 : : loop_len = id_len;
219 : 0 : list = &non_inherit_elem->data[2];
220 : : id = elem->id;
221 : : }
222 : :
223 [ # # ]: 0 : for (i = 0; i < loop_len; i++) {
224 [ # # ]: 0 : if (list[i] == id)
225 : : return false;
226 : : }
227 : :
228 : : return true;
229 : : }
230 : : EXPORT_SYMBOL(cfg80211_is_element_inherited);
231 : :
232 : 0 : static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
233 : : const u8 *subelement, size_t subie_len,
234 : : u8 *new_ie, gfp_t gfp)
235 : : {
236 : : u8 *pos, *tmp;
237 : : const u8 *tmp_old, *tmp_new;
238 : : const struct element *non_inherit_elem;
239 : : u8 *sub_copy;
240 : :
241 : : /* copy subelement as we need to change its content to
242 : : * mark an ie after it is processed.
243 : : */
244 : 0 : sub_copy = kmemdup(subelement, subie_len, gfp);
245 [ # # ]: 0 : if (!sub_copy)
246 : : return 0;
247 : :
248 : : pos = &new_ie[0];
249 : :
250 : : /* set new ssid */
251 : : tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len);
252 [ # # ]: 0 : if (tmp_new) {
253 : 0 : memcpy(pos, tmp_new, tmp_new[1] + 2);
254 : 0 : pos += (tmp_new[1] + 2);
255 : : }
256 : :
257 : : /* get non inheritance list if exists */
258 : : non_inherit_elem =
259 : : cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
260 : : sub_copy, subie_len);
261 : :
262 : : /* go through IEs in ie (skip SSID) and subelement,
263 : : * merge them into new_ie
264 : : */
265 : : tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
266 [ # # ]: 0 : tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie;
267 : :
268 [ # # ]: 0 : while (tmp_old + tmp_old[1] + 2 - ie <= ielen) {
269 [ # # ]: 0 : if (tmp_old[0] == 0) {
270 : 0 : tmp_old++;
271 : 0 : continue;
272 : : }
273 : :
274 [ # # ]: 0 : if (tmp_old[0] == WLAN_EID_EXTENSION)
275 : 0 : tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy,
276 : : subie_len);
277 : : else
278 : : tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy,
279 : : subie_len);
280 : :
281 [ # # ]: 0 : if (!tmp) {
282 : : const struct element *old_elem = (void *)tmp_old;
283 : :
284 : : /* ie in old ie but not in subelement */
285 [ # # ]: 0 : if (cfg80211_is_element_inherited(old_elem,
286 : : non_inherit_elem)) {
287 : 0 : memcpy(pos, tmp_old, tmp_old[1] + 2);
288 : 0 : pos += tmp_old[1] + 2;
289 : : }
290 : : } else {
291 : : /* ie in transmitting ie also in subelement,
292 : : * copy from subelement and flag the ie in subelement
293 : : * as copied (by setting eid field to WLAN_EID_SSID,
294 : : * which is skipped anyway).
295 : : * For vendor ie, compare OUI + type + subType to
296 : : * determine if they are the same ie.
297 : : */
298 [ # # ]: 0 : if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) {
299 [ # # ]: 0 : if (!memcmp(tmp_old + 2, tmp + 2, 5)) {
300 : : /* same vendor ie, copy from
301 : : * subelement
302 : : */
303 : 0 : memcpy(pos, tmp, tmp[1] + 2);
304 : 0 : pos += tmp[1] + 2;
305 : 0 : tmp[0] = WLAN_EID_SSID;
306 : : } else {
307 : 0 : memcpy(pos, tmp_old, tmp_old[1] + 2);
308 : 0 : pos += tmp_old[1] + 2;
309 : : }
310 : : } else {
311 : : /* copy ie from subelement into new ie */
312 : 0 : memcpy(pos, tmp, tmp[1] + 2);
313 : 0 : pos += tmp[1] + 2;
314 : 0 : tmp[0] = WLAN_EID_SSID;
315 : : }
316 : : }
317 : :
318 [ # # ]: 0 : if (tmp_old + tmp_old[1] + 2 - ie == ielen)
319 : : break;
320 : :
321 : : tmp_old += tmp_old[1] + 2;
322 : : }
323 : :
324 : : /* go through subelement again to check if there is any ie not
325 : : * copied to new ie, skip ssid, capability, bssid-index ie
326 : : */
327 : : tmp_new = sub_copy;
328 [ # # ]: 0 : while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
329 [ # # ]: 0 : if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP ||
330 : : tmp_new[0] == WLAN_EID_SSID)) {
331 : 0 : memcpy(pos, tmp_new, tmp_new[1] + 2);
332 : 0 : pos += tmp_new[1] + 2;
333 : : }
334 [ # # ]: 0 : if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len)
335 : : break;
336 : : tmp_new += tmp_new[1] + 2;
337 : : }
338 : :
339 : 0 : kfree(sub_copy);
340 : 0 : return pos - new_ie;
341 : : }
342 : :
343 : 0 : static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
344 : : const u8 *ssid, size_t ssid_len)
345 : : {
346 : : const struct cfg80211_bss_ies *ies;
347 : : const u8 *ssidie;
348 : :
349 [ # # # # ]: 0 : if (bssid && !ether_addr_equal(a->bssid, bssid))
350 : : return false;
351 : :
352 [ # # ]: 0 : if (!ssid)
353 : : return true;
354 : :
355 : 0 : ies = rcu_access_pointer(a->ies);
356 [ # # ]: 0 : if (!ies)
357 : : return false;
358 : 0 : ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
359 [ # # ]: 0 : if (!ssidie)
360 : : return false;
361 [ # # ]: 0 : if (ssidie[1] != ssid_len)
362 : : return false;
363 : 0 : return memcmp(ssidie + 2, ssid, ssid_len) == 0;
364 : : }
365 : :
366 : : static int
367 : 0 : cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss,
368 : : struct cfg80211_bss *nontrans_bss)
369 : : {
370 : : const u8 *ssid;
371 : : size_t ssid_len;
372 : : struct cfg80211_bss *bss = NULL;
373 : :
374 : : rcu_read_lock();
375 : : ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID);
376 [ # # ]: 0 : if (!ssid) {
377 : : rcu_read_unlock();
378 : 0 : return -EINVAL;
379 : : }
380 : 0 : ssid_len = ssid[1];
381 : 0 : ssid = ssid + 2;
382 : : rcu_read_unlock();
383 : :
384 : : /* check if nontrans_bss is in the list */
385 [ # # ]: 0 : list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) {
386 [ # # ]: 0 : if (is_bss(bss, nontrans_bss->bssid, ssid, ssid_len))
387 : : return 0;
388 : : }
389 : :
390 : : /* add to the list */
391 : 0 : list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
392 : 0 : return 0;
393 : : }
394 : :
395 : 0 : static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
396 : : unsigned long expire_time)
397 : : {
398 : : struct cfg80211_internal_bss *bss, *tmp;
399 : : bool expired = false;
400 : :
401 : : lockdep_assert_held(&rdev->bss_lock);
402 : :
403 [ # # ]: 0 : list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {
404 [ # # ]: 0 : if (atomic_read(&bss->hold))
405 : 0 : continue;
406 [ # # ]: 0 : if (!time_after(expire_time, bss->ts))
407 : 0 : continue;
408 : :
409 [ # # ]: 0 : if (__cfg80211_unlink_bss(rdev, bss))
410 : : expired = true;
411 : : }
412 : :
413 [ # # ]: 0 : if (expired)
414 : 0 : rdev->bss_generation++;
415 : 0 : }
416 : :
417 : 0 : static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev)
418 : : {
419 : : struct cfg80211_internal_bss *bss, *oldest = NULL;
420 : : bool ret;
421 : :
422 : : lockdep_assert_held(&rdev->bss_lock);
423 : :
424 [ # # ]: 0 : list_for_each_entry(bss, &rdev->bss_list, list) {
425 [ # # ]: 0 : if (atomic_read(&bss->hold))
426 : 0 : continue;
427 : :
428 [ # # # # ]: 0 : if (!list_empty(&bss->hidden_list) &&
429 : 0 : !bss->pub.hidden_beacon_bss)
430 : 0 : continue;
431 : :
432 [ # # # # ]: 0 : if (oldest && time_before(oldest->ts, bss->ts))
433 : 0 : continue;
434 : : oldest = bss;
435 : : }
436 : :
437 [ # # # # ]: 0 : if (WARN_ON(!oldest))
438 : : return false;
439 : :
440 : : /*
441 : : * The callers make sure to increase rdev->bss_generation if anything
442 : : * gets removed (and a new entry added), so there's no need to also do
443 : : * it here.
444 : : */
445 : :
446 : 0 : ret = __cfg80211_unlink_bss(rdev, oldest);
447 [ # # ]: 0 : WARN_ON(!ret);
448 : 0 : return ret;
449 : : }
450 : :
451 : 0 : void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
452 : : bool send_message)
453 : : {
454 : : struct cfg80211_scan_request *request;
455 : : struct wireless_dev *wdev;
456 : : struct sk_buff *msg;
457 : : #ifdef CONFIG_CFG80211_WEXT
458 : : union iwreq_data wrqu;
459 : : #endif
460 : :
461 [ # # # # ]: 0 : ASSERT_RTNL();
462 : :
463 [ # # ]: 0 : if (rdev->scan_msg) {
464 : 0 : nl80211_send_scan_msg(rdev, rdev->scan_msg);
465 : 0 : rdev->scan_msg = NULL;
466 : 0 : return;
467 : : }
468 : :
469 : 0 : request = rdev->scan_req;
470 [ # # ]: 0 : if (!request)
471 : : return;
472 : :
473 : 0 : wdev = request->wdev;
474 : :
475 : : /*
476 : : * This must be before sending the other events!
477 : : * Otherwise, wpa_supplicant gets completely confused with
478 : : * wext events.
479 : : */
480 [ # # ]: 0 : if (wdev->netdev)
481 : 0 : cfg80211_sme_scan_done(wdev->netdev);
482 : :
483 [ # # # # ]: 0 : if (!request->info.aborted &&
484 : 0 : request->flags & NL80211_SCAN_FLAG_FLUSH) {
485 : : /* flush entries from previous scans */
486 : : spin_lock_bh(&rdev->bss_lock);
487 : 0 : __cfg80211_bss_expire(rdev, request->scan_start);
488 : : spin_unlock_bh(&rdev->bss_lock);
489 : : }
490 : :
491 : 0 : msg = nl80211_build_scan_msg(rdev, wdev, request->info.aborted);
492 : :
493 : : #ifdef CONFIG_CFG80211_WEXT
494 [ # # # # ]: 0 : if (wdev->netdev && !request->info.aborted) {
495 : 0 : memset(&wrqu, 0, sizeof(wrqu));
496 : :
497 : 0 : wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL);
498 : : }
499 : : #endif
500 : :
501 [ # # ]: 0 : if (wdev->netdev)
502 : 0 : dev_put(wdev->netdev);
503 : :
504 : 0 : rdev->scan_req = NULL;
505 : 0 : kfree(request);
506 : :
507 [ # # ]: 0 : if (!send_message)
508 : 0 : rdev->scan_msg = msg;
509 : : else
510 : 0 : nl80211_send_scan_msg(rdev, msg);
511 : : }
512 : :
513 : 0 : void __cfg80211_scan_done(struct work_struct *wk)
514 : : {
515 : : struct cfg80211_registered_device *rdev;
516 : :
517 : 0 : rdev = container_of(wk, struct cfg80211_registered_device,
518 : : scan_done_wk);
519 : :
520 : 0 : rtnl_lock();
521 : 0 : ___cfg80211_scan_done(rdev, true);
522 : 0 : rtnl_unlock();
523 : 0 : }
524 : :
525 : 0 : void cfg80211_scan_done(struct cfg80211_scan_request *request,
526 : : struct cfg80211_scan_info *info)
527 : : {
528 : 0 : trace_cfg80211_scan_done(request, info);
529 [ # # ]: 0 : WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);
530 : :
531 : 0 : request->info = *info;
532 : 0 : request->notified = true;
533 : 0 : queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
534 : 0 : }
535 : : EXPORT_SYMBOL(cfg80211_scan_done);
536 : :
537 : 0 : void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
538 : : struct cfg80211_sched_scan_request *req)
539 : : {
540 [ # # # # ]: 0 : ASSERT_RTNL();
541 : :
542 : 0 : list_add_rcu(&req->list, &rdev->sched_scan_req_list);
543 : 0 : }
544 : :
545 : 0 : static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
546 : : struct cfg80211_sched_scan_request *req)
547 : : {
548 [ # # # # ]: 0 : ASSERT_RTNL();
549 : :
550 : : list_del_rcu(&req->list);
551 [ # # ]: 0 : kfree_rcu(req, rcu_head);
552 : 0 : }
553 : :
554 : : static struct cfg80211_sched_scan_request *
555 : : cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
556 : : {
557 : : struct cfg80211_sched_scan_request *pos;
558 : :
559 : : WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_rtnl_is_held());
560 : :
561 [ # # # # ]: 0 : list_for_each_entry_rcu(pos, &rdev->sched_scan_req_list, list) {
562 [ # # # # ]: 0 : if (pos->reqid == reqid)
563 : 0 : return pos;
564 : : }
565 : : return NULL;
566 : : }
567 : :
568 : : /*
569 : : * Determines if a scheduled scan request can be handled. When a legacy
570 : : * scheduled scan is running no other scheduled scan is allowed regardless
571 : : * whether the request is for legacy or multi-support scan. When a multi-support
572 : : * scheduled scan is running a request for legacy scan is not allowed. In this
573 : : * case a request for multi-support scan can be handled if resources are
574 : : * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
575 : : */
576 : 0 : int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
577 : : bool want_multi)
578 : : {
579 : : struct cfg80211_sched_scan_request *pos;
580 : : int i = 0;
581 : :
582 [ # # ]: 0 : list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
583 : : /* request id zero means legacy in progress */
584 [ # # # # ]: 0 : if (!i && !pos->reqid)
585 : : return -EINPROGRESS;
586 : 0 : i++;
587 : : }
588 : :
589 [ # # ]: 0 : if (i) {
590 : : /* no legacy allowed when multi request(s) are active */
591 [ # # ]: 0 : if (!want_multi)
592 : : return -EINPROGRESS;
593 : :
594 : : /* resource limit reached */
595 [ # # ]: 0 : if (i == rdev->wiphy.max_sched_scan_reqs)
596 : : return -ENOSPC;
597 : : }
598 : 0 : return 0;
599 : : }
600 : :
601 : 0 : void cfg80211_sched_scan_results_wk(struct work_struct *work)
602 : : {
603 : : struct cfg80211_registered_device *rdev;
604 : : struct cfg80211_sched_scan_request *req, *tmp;
605 : :
606 : 0 : rdev = container_of(work, struct cfg80211_registered_device,
607 : : sched_scan_res_wk);
608 : :
609 : 0 : rtnl_lock();
610 [ # # ]: 0 : list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
611 [ # # ]: 0 : if (req->report_results) {
612 : 0 : req->report_results = false;
613 [ # # ]: 0 : if (req->flags & NL80211_SCAN_FLAG_FLUSH) {
614 : : /* flush entries from previous scans */
615 : : spin_lock_bh(&rdev->bss_lock);
616 : 0 : __cfg80211_bss_expire(rdev, req->scan_start);
617 : : spin_unlock_bh(&rdev->bss_lock);
618 : 0 : req->scan_start = jiffies;
619 : : }
620 : 0 : nl80211_send_sched_scan(req,
621 : : NL80211_CMD_SCHED_SCAN_RESULTS);
622 : : }
623 : : }
624 : 0 : rtnl_unlock();
625 : 0 : }
626 : :
627 : 0 : void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
628 : : {
629 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
630 : : struct cfg80211_sched_scan_request *request;
631 : :
632 : 0 : trace_cfg80211_sched_scan_results(wiphy, reqid);
633 : : /* ignore if we're not scanning */
634 : :
635 : : rcu_read_lock();
636 : : request = cfg80211_find_sched_scan_req(rdev, reqid);
637 [ # # ]: 0 : if (request) {
638 : 0 : request->report_results = true;
639 : 0 : queue_work(cfg80211_wq, &rdev->sched_scan_res_wk);
640 : : }
641 : : rcu_read_unlock();
642 : 0 : }
643 : : EXPORT_SYMBOL(cfg80211_sched_scan_results);
644 : :
645 : 0 : void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid)
646 : : {
647 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
648 : :
649 [ # # # # ]: 0 : ASSERT_RTNL();
650 : :
651 : 0 : trace_cfg80211_sched_scan_stopped(wiphy, reqid);
652 : :
653 : 0 : __cfg80211_stop_sched_scan(rdev, reqid, true);
654 : 0 : }
655 : : EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl);
656 : :
657 : 0 : void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid)
658 : : {
659 : 0 : rtnl_lock();
660 : 0 : cfg80211_sched_scan_stopped_rtnl(wiphy, reqid);
661 : 0 : rtnl_unlock();
662 : 0 : }
663 : : EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
664 : :
665 : 0 : int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
666 : : struct cfg80211_sched_scan_request *req,
667 : : bool driver_initiated)
668 : : {
669 [ # # # # ]: 0 : ASSERT_RTNL();
670 : :
671 [ # # ]: 0 : if (!driver_initiated) {
672 : 0 : int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid);
673 [ # # ]: 0 : if (err)
674 : : return err;
675 : : }
676 : :
677 : 0 : nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED);
678 : :
679 : 0 : cfg80211_del_sched_scan_req(rdev, req);
680 : :
681 : 0 : return 0;
682 : : }
683 : :
684 : 0 : int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
685 : : u64 reqid, bool driver_initiated)
686 : : {
687 : : struct cfg80211_sched_scan_request *sched_scan_req;
688 : :
689 [ # # # # ]: 0 : ASSERT_RTNL();
690 : :
691 : : sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
692 [ # # ]: 0 : if (!sched_scan_req)
693 : : return -ENOENT;
694 : :
695 : 0 : return cfg80211_stop_sched_scan_req(rdev, sched_scan_req,
696 : : driver_initiated);
697 : : }
698 : :
699 : 0 : void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
700 : : unsigned long age_secs)
701 : : {
702 : : struct cfg80211_internal_bss *bss;
703 : 0 : unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
704 : :
705 : : spin_lock_bh(&rdev->bss_lock);
706 [ # # ]: 0 : list_for_each_entry(bss, &rdev->bss_list, list)
707 : 0 : bss->ts -= age_jiffies;
708 : : spin_unlock_bh(&rdev->bss_lock);
709 : 0 : }
710 : :
711 : 0 : void cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
712 : : {
713 : 0 : __cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
714 : 0 : }
715 : :
716 : : const struct element *
717 : 0 : cfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len,
718 : : const u8 *match, unsigned int match_len,
719 : : unsigned int match_offset)
720 : : {
721 : : const struct element *elem;
722 : :
723 [ # # # # : 0 : for_each_element_id(elem, eid, ies, len) {
# # ]
724 [ # # # # ]: 0 : if (elem->datalen >= match_offset + match_len &&
725 : 0 : !memcmp(elem->data + match_offset, match, match_len))
726 : 0 : return elem;
727 : : }
728 : :
729 : : return NULL;
730 : : }
731 : : EXPORT_SYMBOL(cfg80211_find_elem_match);
732 : :
733 : 0 : const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type,
734 : : const u8 *ies,
735 : : unsigned int len)
736 : : {
737 : : const struct element *elem;
738 : 0 : u8 match[] = { oui >> 16, oui >> 8, oui, oui_type };
739 [ # # ]: 0 : int match_len = (oui_type < 0) ? 3 : sizeof(match);
740 : :
741 [ # # # # ]: 0 : if (WARN_ON(oui_type > 0xff))
742 : : return NULL;
743 : :
744 : 0 : elem = cfg80211_find_elem_match(WLAN_EID_VENDOR_SPECIFIC, ies, len,
745 : : match, match_len, 0);
746 : :
747 [ # # # # ]: 0 : if (!elem || elem->datalen < 4)
748 : : return NULL;
749 : :
750 : 0 : return elem;
751 : : }
752 : : EXPORT_SYMBOL(cfg80211_find_vendor_elem);
753 : :
754 : : /**
755 : : * enum bss_compare_mode - BSS compare mode
756 : : * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
757 : : * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
758 : : * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
759 : : */
760 : : enum bss_compare_mode {
761 : : BSS_CMP_REGULAR,
762 : : BSS_CMP_HIDE_ZLEN,
763 : : BSS_CMP_HIDE_NUL,
764 : : };
765 : :
766 : 0 : static int cmp_bss(struct cfg80211_bss *a,
767 : : struct cfg80211_bss *b,
768 : : enum bss_compare_mode mode)
769 : : {
770 : : const struct cfg80211_bss_ies *a_ies, *b_ies;
771 : : const u8 *ie1 = NULL;
772 : : const u8 *ie2 = NULL;
773 : : int i, r;
774 : :
775 [ # # ]: 0 : if (a->channel != b->channel)
776 : 0 : return b->channel->center_freq - a->channel->center_freq;
777 : :
778 : 0 : a_ies = rcu_access_pointer(a->ies);
779 [ # # ]: 0 : if (!a_ies)
780 : : return -1;
781 : 0 : b_ies = rcu_access_pointer(b->ies);
782 [ # # ]: 0 : if (!b_ies)
783 : : return 1;
784 : :
785 [ # # ]: 0 : if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
786 : 0 : ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
787 : 0 : a_ies->data, a_ies->len);
788 [ # # ]: 0 : if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
789 : 0 : ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
790 : 0 : b_ies->data, b_ies->len);
791 [ # # ]: 0 : if (ie1 && ie2) {
792 : : int mesh_id_cmp;
793 : :
794 [ # # ]: 0 : if (ie1[1] == ie2[1])
795 : 0 : mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
796 : : else
797 : 0 : mesh_id_cmp = ie2[1] - ie1[1];
798 : :
799 : 0 : ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
800 : 0 : a_ies->data, a_ies->len);
801 : 0 : ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
802 : 0 : b_ies->data, b_ies->len);
803 [ # # ]: 0 : if (ie1 && ie2) {
804 [ # # ]: 0 : if (mesh_id_cmp)
805 : : return mesh_id_cmp;
806 [ # # ]: 0 : if (ie1[1] != ie2[1])
807 : 0 : return ie2[1] - ie1[1];
808 : 0 : return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
809 : : }
810 : : }
811 : :
812 : 0 : r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
813 [ # # ]: 0 : if (r)
814 : : return r;
815 : :
816 : 0 : ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
817 : 0 : ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
818 : :
819 [ # # ]: 0 : if (!ie1 && !ie2)
820 : : return 0;
821 : :
822 : : /*
823 : : * Note that with "hide_ssid", the function returns a match if
824 : : * the already-present BSS ("b") is a hidden SSID beacon for
825 : : * the new BSS ("a").
826 : : */
827 : :
828 : : /* sort missing IE before (left of) present IE */
829 [ # # ]: 0 : if (!ie1)
830 : : return -1;
831 [ # # ]: 0 : if (!ie2)
832 : : return 1;
833 : :
834 [ # # # ]: 0 : switch (mode) {
835 : : case BSS_CMP_HIDE_ZLEN:
836 : : /*
837 : : * In ZLEN mode we assume the BSS entry we're
838 : : * looking for has a zero-length SSID. So if
839 : : * the one we're looking at right now has that,
840 : : * return 0. Otherwise, return the difference
841 : : * in length, but since we're looking for the
842 : : * 0-length it's really equivalent to returning
843 : : * the length of the one we're looking at.
844 : : *
845 : : * No content comparison is needed as we assume
846 : : * the content length is zero.
847 : : */
848 : 0 : return ie2[1];
849 : : case BSS_CMP_REGULAR:
850 : : default:
851 : : /* sort by length first, then by contents */
852 [ # # ]: 0 : if (ie1[1] != ie2[1])
853 : 0 : return ie2[1] - ie1[1];
854 : 0 : return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
855 : : case BSS_CMP_HIDE_NUL:
856 [ # # ]: 0 : if (ie1[1] != ie2[1])
857 : 0 : return ie2[1] - ie1[1];
858 : : /* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
859 [ # # ]: 0 : for (i = 0; i < ie2[1]; i++)
860 [ # # ]: 0 : if (ie2[i + 2])
861 : : return -1;
862 : : return 0;
863 : : }
864 : : }
865 : :
866 : 0 : static bool cfg80211_bss_type_match(u16 capability,
867 : : enum nl80211_band band,
868 : : enum ieee80211_bss_type bss_type)
869 : : {
870 : : bool ret = true;
871 : : u16 mask, val;
872 : :
873 [ # # ]: 0 : if (bss_type == IEEE80211_BSS_TYPE_ANY)
874 : : return ret;
875 : :
876 [ # # ]: 0 : if (band == NL80211_BAND_60GHZ) {
877 : : mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
878 [ # # # # ]: 0 : switch (bss_type) {
879 : : case IEEE80211_BSS_TYPE_ESS:
880 : : val = WLAN_CAPABILITY_DMG_TYPE_AP;
881 : : break;
882 : : case IEEE80211_BSS_TYPE_PBSS:
883 : : val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
884 : 0 : break;
885 : : case IEEE80211_BSS_TYPE_IBSS:
886 : : val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
887 : 0 : break;
888 : : default:
889 : : return false;
890 : : }
891 : : } else {
892 : : mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
893 [ # # # # ]: 0 : switch (bss_type) {
894 : : case IEEE80211_BSS_TYPE_ESS:
895 : : val = WLAN_CAPABILITY_ESS;
896 : : break;
897 : : case IEEE80211_BSS_TYPE_IBSS:
898 : : val = WLAN_CAPABILITY_IBSS;
899 : 0 : break;
900 : : case IEEE80211_BSS_TYPE_MBSS:
901 : : val = 0;
902 : 0 : break;
903 : : default:
904 : : return false;
905 : : }
906 : : }
907 : :
908 : 0 : ret = ((capability & mask) == val);
909 : 0 : return ret;
910 : : }
911 : :
912 : : /* Returned bss is reference counted and must be cleaned up appropriately. */
913 : 0 : struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
914 : : struct ieee80211_channel *channel,
915 : : const u8 *bssid,
916 : : const u8 *ssid, size_t ssid_len,
917 : : enum ieee80211_bss_type bss_type,
918 : : enum ieee80211_privacy privacy)
919 : : {
920 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
921 : : struct cfg80211_internal_bss *bss, *res = NULL;
922 : 0 : unsigned long now = jiffies;
923 : : int bss_privacy;
924 : :
925 : 0 : trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
926 : : privacy);
927 : :
928 : : spin_lock_bh(&rdev->bss_lock);
929 : :
930 [ # # ]: 0 : list_for_each_entry(bss, &rdev->bss_list, list) {
931 [ # # ]: 0 : if (!cfg80211_bss_type_match(bss->pub.capability,
932 : 0 : bss->pub.channel->band, bss_type))
933 : 0 : continue;
934 : :
935 : 0 : bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
936 [ # # # # ]: 0 : if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
937 : 0 : (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
938 : 0 : continue;
939 [ # # # # ]: 0 : if (channel && bss->pub.channel != channel)
940 : 0 : continue;
941 [ # # ]: 0 : if (!is_valid_ether_addr(bss->pub.bssid))
942 : 0 : continue;
943 : : /* Don't get expired BSS structs */
944 [ # # # # ]: 0 : if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
945 : 0 : !atomic_read(&bss->hold))
946 : 0 : continue;
947 [ # # ]: 0 : if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
948 : 0 : res = bss;
949 : 0 : bss_ref_get(rdev, res);
950 : 0 : break;
951 : : }
952 : : }
953 : :
954 : : spin_unlock_bh(&rdev->bss_lock);
955 [ # # ]: 0 : if (!res)
956 : : return NULL;
957 : 0 : trace_cfg80211_return_bss(&res->pub);
958 : 0 : return &res->pub;
959 : : }
960 : : EXPORT_SYMBOL(cfg80211_get_bss);
961 : :
962 : 0 : static void rb_insert_bss(struct cfg80211_registered_device *rdev,
963 : : struct cfg80211_internal_bss *bss)
964 : : {
965 : 0 : struct rb_node **p = &rdev->bss_tree.rb_node;
966 : : struct rb_node *parent = NULL;
967 : : struct cfg80211_internal_bss *tbss;
968 : : int cmp;
969 : :
970 [ # # ]: 0 : while (*p) {
971 : : parent = *p;
972 : : tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
973 : :
974 : 0 : cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
975 : :
976 [ # # # # ]: 0 : if (WARN_ON(!cmp)) {
977 : : /* will sort of leak this BSS */
978 : 0 : return;
979 : : }
980 : :
981 [ # # ]: 0 : if (cmp < 0)
982 : 0 : p = &(*p)->rb_left;
983 : : else
984 : 0 : p = &(*p)->rb_right;
985 : : }
986 : :
987 : 0 : rb_link_node(&bss->rbn, parent, p);
988 : 0 : rb_insert_color(&bss->rbn, &rdev->bss_tree);
989 : : }
990 : :
991 : : static struct cfg80211_internal_bss *
992 : 0 : rb_find_bss(struct cfg80211_registered_device *rdev,
993 : : struct cfg80211_internal_bss *res,
994 : : enum bss_compare_mode mode)
995 : : {
996 : 0 : struct rb_node *n = rdev->bss_tree.rb_node;
997 : : struct cfg80211_internal_bss *bss;
998 : : int r;
999 : :
1000 [ # # ]: 0 : while (n) {
1001 : 0 : bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
1002 : 0 : r = cmp_bss(&res->pub, &bss->pub, mode);
1003 : :
1004 [ # # ]: 0 : if (r == 0)
1005 : 0 : return bss;
1006 [ # # ]: 0 : else if (r < 0)
1007 : 0 : n = n->rb_left;
1008 : : else
1009 : 0 : n = n->rb_right;
1010 : : }
1011 : :
1012 : : return NULL;
1013 : : }
1014 : :
1015 : 0 : static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
1016 : : struct cfg80211_internal_bss *new)
1017 : : {
1018 : : const struct cfg80211_bss_ies *ies;
1019 : : struct cfg80211_internal_bss *bss;
1020 : : const u8 *ie;
1021 : : int i, ssidlen;
1022 : : u8 fold = 0;
1023 : : u32 n_entries = 0;
1024 : :
1025 : 0 : ies = rcu_access_pointer(new->pub.beacon_ies);
1026 [ # # # # ]: 0 : if (WARN_ON(!ies))
1027 : : return false;
1028 : :
1029 : 0 : ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
1030 [ # # ]: 0 : if (!ie) {
1031 : : /* nothing to do */
1032 : : return true;
1033 : : }
1034 : :
1035 : 0 : ssidlen = ie[1];
1036 [ # # ]: 0 : for (i = 0; i < ssidlen; i++)
1037 : 0 : fold |= ie[2 + i];
1038 : :
1039 [ # # ]: 0 : if (fold) {
1040 : : /* not a hidden SSID */
1041 : : return true;
1042 : : }
1043 : :
1044 : : /* This is the bad part ... */
1045 : :
1046 [ # # ]: 0 : list_for_each_entry(bss, &rdev->bss_list, list) {
1047 : : /*
1048 : : * we're iterating all the entries anyway, so take the
1049 : : * opportunity to validate the list length accounting
1050 : : */
1051 : 0 : n_entries++;
1052 : :
1053 [ # # ]: 0 : if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
1054 : 0 : continue;
1055 [ # # ]: 0 : if (bss->pub.channel != new->pub.channel)
1056 : 0 : continue;
1057 [ # # ]: 0 : if (bss->pub.scan_width != new->pub.scan_width)
1058 : 0 : continue;
1059 [ # # ]: 0 : if (rcu_access_pointer(bss->pub.beacon_ies))
1060 : 0 : continue;
1061 : 0 : ies = rcu_access_pointer(bss->pub.ies);
1062 [ # # ]: 0 : if (!ies)
1063 : 0 : continue;
1064 : 0 : ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
1065 [ # # ]: 0 : if (!ie)
1066 : 0 : continue;
1067 [ # # # # ]: 0 : if (ssidlen && ie[1] != ssidlen)
1068 : 0 : continue;
1069 [ # # # # : 0 : if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
# # ]
1070 : 0 : continue;
1071 [ # # # # : 0 : if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
# # ]
1072 : : list_del(&bss->hidden_list);
1073 : : /* combine them */
1074 : 0 : list_add(&bss->hidden_list, &new->hidden_list);
1075 : 0 : bss->pub.hidden_beacon_bss = &new->pub;
1076 : 0 : new->refcount += bss->refcount;
1077 : 0 : rcu_assign_pointer(bss->pub.beacon_ies,
1078 : : new->pub.beacon_ies);
1079 : : }
1080 : :
1081 [ # # # # ]: 0 : WARN_ONCE(n_entries != rdev->bss_entries,
1082 : : "rdev bss entries[%d]/list[len:%d] corruption\n",
1083 : : rdev->bss_entries, n_entries);
1084 : :
1085 : : return true;
1086 : : }
1087 : :
1088 : : struct cfg80211_non_tx_bss {
1089 : : struct cfg80211_bss *tx_bss;
1090 : : u8 max_bssid_indicator;
1091 : : u8 bssid_index;
1092 : : };
1093 : :
1094 : : static bool
1095 : 0 : cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
1096 : : struct cfg80211_internal_bss *known,
1097 : : struct cfg80211_internal_bss *new,
1098 : : bool signal_valid)
1099 : : {
1100 : : lockdep_assert_held(&rdev->bss_lock);
1101 : :
1102 : : /* Update IEs */
1103 [ # # ]: 0 : if (rcu_access_pointer(new->pub.proberesp_ies)) {
1104 : : const struct cfg80211_bss_ies *old;
1105 : :
1106 : 0 : old = rcu_access_pointer(known->pub.proberesp_ies);
1107 : :
1108 : 0 : rcu_assign_pointer(known->pub.proberesp_ies,
1109 : : new->pub.proberesp_ies);
1110 : : /* Override possible earlier Beacon frame IEs */
1111 : 0 : rcu_assign_pointer(known->pub.ies,
1112 : : new->pub.proberesp_ies);
1113 [ # # ]: 0 : if (old)
1114 [ # # ]: 0 : kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1115 [ # # ]: 0 : } else if (rcu_access_pointer(new->pub.beacon_ies)) {
1116 : : const struct cfg80211_bss_ies *old;
1117 : : struct cfg80211_internal_bss *bss;
1118 : :
1119 [ # # # # ]: 0 : if (known->pub.hidden_beacon_bss &&
1120 : 0 : !list_empty(&known->hidden_list)) {
1121 : : const struct cfg80211_bss_ies *f;
1122 : :
1123 : : /* The known BSS struct is one of the probe
1124 : : * response members of a group, but we're
1125 : : * receiving a beacon (beacon_ies in the new
1126 : : * bss is used). This can only mean that the
1127 : : * AP changed its beacon from not having an
1128 : : * SSID to showing it, which is confusing so
1129 : : * drop this information.
1130 : : */
1131 : :
1132 : 0 : f = rcu_access_pointer(new->pub.beacon_ies);
1133 [ # # ]: 0 : kfree_rcu((struct cfg80211_bss_ies *)f, rcu_head);
1134 : : return false;
1135 : : }
1136 : :
1137 : 0 : old = rcu_access_pointer(known->pub.beacon_ies);
1138 : :
1139 : 0 : rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
1140 : :
1141 : : /* Override IEs if they were from a beacon before */
1142 [ # # ]: 0 : if (old == rcu_access_pointer(known->pub.ies))
1143 : 0 : rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
1144 : :
1145 : : /* Assign beacon IEs to all sub entries */
1146 [ # # ]: 0 : list_for_each_entry(bss, &known->hidden_list, hidden_list) {
1147 : : const struct cfg80211_bss_ies *ies;
1148 : :
1149 : 0 : ies = rcu_access_pointer(bss->pub.beacon_ies);
1150 [ # # ]: 0 : WARN_ON(ies != old);
1151 : :
1152 : 0 : rcu_assign_pointer(bss->pub.beacon_ies,
1153 : : new->pub.beacon_ies);
1154 : : }
1155 : :
1156 [ # # ]: 0 : if (old)
1157 [ # # ]: 0 : kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1158 : : }
1159 : :
1160 : 0 : known->pub.beacon_interval = new->pub.beacon_interval;
1161 : :
1162 : : /* don't update the signal if beacon was heard on
1163 : : * adjacent channel.
1164 : : */
1165 [ # # ]: 0 : if (signal_valid)
1166 : 0 : known->pub.signal = new->pub.signal;
1167 : 0 : known->pub.capability = new->pub.capability;
1168 : 0 : known->ts = new->ts;
1169 : 0 : known->ts_boottime = new->ts_boottime;
1170 : 0 : known->parent_tsf = new->parent_tsf;
1171 : 0 : known->pub.chains = new->pub.chains;
1172 : 0 : memcpy(known->pub.chain_signal, new->pub.chain_signal,
1173 : : IEEE80211_MAX_CHAINS);
1174 : : ether_addr_copy(known->parent_bssid, new->parent_bssid);
1175 : 0 : known->pub.max_bssid_indicator = new->pub.max_bssid_indicator;
1176 : 0 : known->pub.bssid_index = new->pub.bssid_index;
1177 : :
1178 : 0 : return true;
1179 : : }
1180 : :
1181 : : /* Returned bss is reference counted and must be cleaned up appropriately. */
1182 : : struct cfg80211_internal_bss *
1183 : 0 : cfg80211_bss_update(struct cfg80211_registered_device *rdev,
1184 : : struct cfg80211_internal_bss *tmp,
1185 : : bool signal_valid, unsigned long ts)
1186 : : {
1187 : : struct cfg80211_internal_bss *found = NULL;
1188 : :
1189 [ # # # # ]: 0 : if (WARN_ON(!tmp->pub.channel))
1190 : : return NULL;
1191 : :
1192 : 0 : tmp->ts = ts;
1193 : :
1194 : : spin_lock_bh(&rdev->bss_lock);
1195 : :
1196 [ # # # # ]: 0 : if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
1197 : : spin_unlock_bh(&rdev->bss_lock);
1198 : 0 : return NULL;
1199 : : }
1200 : :
1201 : 0 : found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
1202 : :
1203 [ # # ]: 0 : if (found) {
1204 [ # # ]: 0 : if (!cfg80211_update_known_bss(rdev, found, tmp, signal_valid))
1205 : : goto drop;
1206 : : } else {
1207 : : struct cfg80211_internal_bss *new;
1208 : : struct cfg80211_internal_bss *hidden;
1209 : : struct cfg80211_bss_ies *ies;
1210 : :
1211 : : /*
1212 : : * create a copy -- the "res" variable that is passed in
1213 : : * is allocated on the stack since it's not needed in the
1214 : : * more common case of an update
1215 : : */
1216 : 0 : new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
1217 : : GFP_ATOMIC);
1218 [ # # ]: 0 : if (!new) {
1219 : 0 : ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
1220 [ # # ]: 0 : if (ies)
1221 [ # # ]: 0 : kfree_rcu(ies, rcu_head);
1222 : 0 : ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
1223 [ # # ]: 0 : if (ies)
1224 [ # # ]: 0 : kfree_rcu(ies, rcu_head);
1225 : : goto drop;
1226 : : }
1227 : 0 : memcpy(new, tmp, sizeof(*new));
1228 : 0 : new->refcount = 1;
1229 : 0 : INIT_LIST_HEAD(&new->hidden_list);
1230 : 0 : INIT_LIST_HEAD(&new->pub.nontrans_list);
1231 : :
1232 [ # # ]: 0 : if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
1233 : 0 : hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
1234 [ # # ]: 0 : if (!hidden)
1235 : 0 : hidden = rb_find_bss(rdev, tmp,
1236 : : BSS_CMP_HIDE_NUL);
1237 [ # # ]: 0 : if (hidden) {
1238 : 0 : new->pub.hidden_beacon_bss = &hidden->pub;
1239 : 0 : list_add(&new->hidden_list,
1240 : : &hidden->hidden_list);
1241 : 0 : hidden->refcount++;
1242 : 0 : rcu_assign_pointer(new->pub.beacon_ies,
1243 : : hidden->pub.beacon_ies);
1244 : : }
1245 : : } else {
1246 : : /*
1247 : : * Ok so we found a beacon, and don't have an entry. If
1248 : : * it's a beacon with hidden SSID, we might be in for an
1249 : : * expensive search for any probe responses that should
1250 : : * be grouped with this beacon for updates ...
1251 : : */
1252 [ # # ]: 0 : if (!cfg80211_combine_bsses(rdev, new)) {
1253 : 0 : kfree(new);
1254 : 0 : goto drop;
1255 : : }
1256 : : }
1257 : :
1258 [ # # # # ]: 0 : if (rdev->bss_entries >= bss_entries_limit &&
1259 : 0 : !cfg80211_bss_expire_oldest(rdev)) {
1260 : 0 : kfree(new);
1261 : 0 : goto drop;
1262 : : }
1263 : :
1264 : : /* This must be before the call to bss_ref_get */
1265 [ # # ]: 0 : if (tmp->pub.transmitted_bss) {
1266 : : struct cfg80211_internal_bss *pbss =
1267 : 0 : container_of(tmp->pub.transmitted_bss,
1268 : : struct cfg80211_internal_bss,
1269 : : pub);
1270 : :
1271 : 0 : new->pub.transmitted_bss = tmp->pub.transmitted_bss;
1272 : 0 : bss_ref_get(rdev, pbss);
1273 : : }
1274 : :
1275 : 0 : list_add_tail(&new->list, &rdev->bss_list);
1276 : 0 : rdev->bss_entries++;
1277 : 0 : rb_insert_bss(rdev, new);
1278 : : found = new;
1279 : : }
1280 : :
1281 : 0 : rdev->bss_generation++;
1282 : 0 : bss_ref_get(rdev, found);
1283 : : spin_unlock_bh(&rdev->bss_lock);
1284 : :
1285 : 0 : return found;
1286 : : drop:
1287 : : spin_unlock_bh(&rdev->bss_lock);
1288 : 0 : return NULL;
1289 : : }
1290 : :
1291 : : /*
1292 : : * Update RX channel information based on the available frame payload
1293 : : * information. This is mainly for the 2.4 GHz band where frames can be received
1294 : : * from neighboring channels and the Beacon frames use the DSSS Parameter Set
1295 : : * element to indicate the current (transmitting) channel, but this might also
1296 : : * be needed on other bands if RX frequency does not match with the actual
1297 : : * operating channel of a BSS.
1298 : : */
1299 : : static struct ieee80211_channel *
1300 : 0 : cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
1301 : : struct ieee80211_channel *channel,
1302 : : enum nl80211_bss_scan_width scan_width)
1303 : : {
1304 : : const u8 *tmp;
1305 : : u32 freq;
1306 : : int channel_number = -1;
1307 : : struct ieee80211_channel *alt_channel;
1308 : :
1309 : : tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
1310 [ # # # # ]: 0 : if (tmp && tmp[1] == 1) {
1311 : 0 : channel_number = tmp[2];
1312 : : } else {
1313 : : tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
1314 [ # # # # ]: 0 : if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
1315 : : struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
1316 : :
1317 : 0 : channel_number = htop->primary_chan;
1318 : : }
1319 : : }
1320 : :
1321 [ # # ]: 0 : if (channel_number < 0) {
1322 : : /* No channel information in frame payload */
1323 : : return channel;
1324 : : }
1325 : :
1326 : 0 : freq = ieee80211_channel_to_frequency(channel_number, channel->band);
1327 : 0 : alt_channel = ieee80211_get_channel(wiphy, freq);
1328 [ # # ]: 0 : if (!alt_channel) {
1329 [ # # ]: 0 : if (channel->band == NL80211_BAND_2GHZ) {
1330 : : /*
1331 : : * Better not allow unexpected channels when that could
1332 : : * be going beyond the 1-11 range (e.g., discovering
1333 : : * BSS on channel 12 when radio is configured for
1334 : : * channel 11.
1335 : : */
1336 : : return NULL;
1337 : : }
1338 : :
1339 : : /* No match for the payload channel number - ignore it */
1340 : 0 : return channel;
1341 : : }
1342 : :
1343 [ # # ]: 0 : if (scan_width == NL80211_BSS_CHAN_WIDTH_10 ||
1344 : : scan_width == NL80211_BSS_CHAN_WIDTH_5) {
1345 : : /*
1346 : : * Ignore channel number in 5 and 10 MHz channels where there
1347 : : * may not be an n:1 or 1:n mapping between frequencies and
1348 : : * channel numbers.
1349 : : */
1350 : : return channel;
1351 : : }
1352 : :
1353 : : /*
1354 : : * Use the channel determined through the payload channel number
1355 : : * instead of the RX channel reported by the driver.
1356 : : */
1357 [ # # ]: 0 : if (alt_channel->flags & IEEE80211_CHAN_DISABLED)
1358 : : return NULL;
1359 : 0 : return alt_channel;
1360 : : }
1361 : :
1362 : : /* Returned bss is reference counted and must be cleaned up appropriately. */
1363 : : static struct cfg80211_bss *
1364 : 0 : cfg80211_inform_single_bss_data(struct wiphy *wiphy,
1365 : : struct cfg80211_inform_bss *data,
1366 : : enum cfg80211_bss_frame_type ftype,
1367 : : const u8 *bssid, u64 tsf, u16 capability,
1368 : : u16 beacon_interval, const u8 *ie, size_t ielen,
1369 : : struct cfg80211_non_tx_bss *non_tx_data,
1370 : : gfp_t gfp)
1371 : : {
1372 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1373 : : struct cfg80211_bss_ies *ies;
1374 : : struct ieee80211_channel *channel;
1375 : 0 : struct cfg80211_internal_bss tmp = {}, *res;
1376 : : int bss_type;
1377 : : bool signal_valid;
1378 : : unsigned long ts;
1379 : :
1380 [ # # # # ]: 0 : if (WARN_ON(!wiphy))
1381 : : return NULL;
1382 : :
1383 [ # # # # : 0 : if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
# # # # ]
1384 : : (data->signal < 0 || data->signal > 100)))
1385 : : return NULL;
1386 : :
1387 : 0 : channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan,
1388 : : data->scan_width);
1389 [ # # ]: 0 : if (!channel)
1390 : : return NULL;
1391 : :
1392 : 0 : memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
1393 : 0 : tmp.pub.channel = channel;
1394 : 0 : tmp.pub.scan_width = data->scan_width;
1395 : 0 : tmp.pub.signal = data->signal;
1396 : 0 : tmp.pub.beacon_interval = beacon_interval;
1397 : 0 : tmp.pub.capability = capability;
1398 : 0 : tmp.ts_boottime = data->boottime_ns;
1399 [ # # ]: 0 : if (non_tx_data) {
1400 : 0 : tmp.pub.transmitted_bss = non_tx_data->tx_bss;
1401 : 0 : ts = bss_from_pub(non_tx_data->tx_bss)->ts;
1402 : 0 : tmp.pub.bssid_index = non_tx_data->bssid_index;
1403 : 0 : tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
1404 : : } else {
1405 : 0 : ts = jiffies;
1406 : : }
1407 : :
1408 : : /*
1409 : : * If we do not know here whether the IEs are from a Beacon or Probe
1410 : : * Response frame, we need to pick one of the options and only use it
1411 : : * with the driver that does not provide the full Beacon/Probe Response
1412 : : * frame. Use Beacon frame pointer to avoid indicating that this should
1413 : : * override the IEs pointer should we have received an earlier
1414 : : * indication of Probe Response data.
1415 : : */
1416 : 0 : ies = kzalloc(sizeof(*ies) + ielen, gfp);
1417 [ # # ]: 0 : if (!ies)
1418 : : return NULL;
1419 : 0 : ies->len = ielen;
1420 : 0 : ies->tsf = tsf;
1421 : 0 : ies->from_beacon = false;
1422 : 0 : memcpy(ies->data, ie, ielen);
1423 : :
1424 [ # # # # ]: 0 : switch (ftype) {
1425 : : case CFG80211_BSS_FTYPE_BEACON:
1426 : 0 : ies->from_beacon = true;
1427 : : /* fall through */
1428 : : case CFG80211_BSS_FTYPE_UNKNOWN:
1429 : 0 : rcu_assign_pointer(tmp.pub.beacon_ies, ies);
1430 : 0 : break;
1431 : : case CFG80211_BSS_FTYPE_PRESP:
1432 : 0 : rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
1433 : 0 : break;
1434 : : }
1435 : 0 : rcu_assign_pointer(tmp.pub.ies, ies);
1436 : :
1437 : 0 : signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
1438 : 0 : wiphy->max_adj_channel_rssi_comp;
1439 : 0 : res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid, ts);
1440 [ # # ]: 0 : if (!res)
1441 : : return NULL;
1442 : :
1443 [ # # ]: 0 : if (channel->band == NL80211_BAND_60GHZ) {
1444 : 0 : bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
1445 [ # # ]: 0 : if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
1446 : : bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
1447 : 0 : regulatory_hint_found_beacon(wiphy, channel, gfp);
1448 : : } else {
1449 [ # # ]: 0 : if (res->pub.capability & WLAN_CAPABILITY_ESS)
1450 : 0 : regulatory_hint_found_beacon(wiphy, channel, gfp);
1451 : : }
1452 : :
1453 [ # # ]: 0 : if (non_tx_data) {
1454 : : /* this is a nontransmitting bss, we need to add it to
1455 : : * transmitting bss' list if it is not there
1456 : : */
1457 [ # # ]: 0 : if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
1458 : : &res->pub)) {
1459 [ # # ]: 0 : if (__cfg80211_unlink_bss(rdev, res))
1460 : 0 : rdev->bss_generation++;
1461 : : }
1462 : : }
1463 : :
1464 : 0 : trace_cfg80211_return_bss(&res->pub);
1465 : : /* cfg80211_bss_update gives us a referenced result */
1466 : 0 : return &res->pub;
1467 : : }
1468 : :
1469 : : static const struct element
1470 : 0 : *cfg80211_get_profile_continuation(const u8 *ie, size_t ielen,
1471 : : const struct element *mbssid_elem,
1472 : : const struct element *sub_elem)
1473 : : {
1474 : 0 : const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen;
1475 : : const struct element *next_mbssid;
1476 : : const struct element *next_sub;
1477 : :
1478 : : next_mbssid = cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
1479 : : mbssid_end,
1480 : 0 : ielen - (mbssid_end - ie));
1481 : :
1482 : : /*
1483 : : * If is is not the last subelement in current MBSSID IE or there isn't
1484 : : * a next MBSSID IE - profile is complete.
1485 : : */
1486 [ # # # # ]: 0 : if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
1487 : : !next_mbssid)
1488 : : return NULL;
1489 : :
1490 : : /* For any length error, just return NULL */
1491 : :
1492 [ # # ]: 0 : if (next_mbssid->datalen < 4)
1493 : : return NULL;
1494 : :
1495 : : next_sub = (void *)&next_mbssid->data[1];
1496 : :
1497 [ # # ]: 0 : if (next_mbssid->data + next_mbssid->datalen <
1498 : 0 : next_sub->data + next_sub->datalen)
1499 : : return NULL;
1500 : :
1501 [ # # # # ]: 0 : if (next_sub->id != 0 || next_sub->datalen < 2)
1502 : : return NULL;
1503 : :
1504 : : /*
1505 : : * Check if the first element in the next sub element is a start
1506 : : * of a new profile
1507 : : */
1508 : 0 : return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
1509 [ # # ]: 0 : NULL : next_mbssid;
1510 : : }
1511 : :
1512 : 0 : size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
1513 : : const struct element *mbssid_elem,
1514 : : const struct element *sub_elem,
1515 : : u8 *merged_ie, size_t max_copy_len)
1516 : : {
1517 : 0 : size_t copied_len = sub_elem->datalen;
1518 : : const struct element *next_mbssid;
1519 : :
1520 [ # # ]: 0 : if (sub_elem->datalen > max_copy_len)
1521 : : return 0;
1522 : :
1523 : 0 : memcpy(merged_ie, sub_elem->data, sub_elem->datalen);
1524 : :
1525 [ # # ]: 0 : while ((next_mbssid = cfg80211_get_profile_continuation(ie, ielen,
1526 : : mbssid_elem,
1527 : : sub_elem))) {
1528 : : const struct element *next_sub = (void *)&next_mbssid->data[1];
1529 : :
1530 [ # # ]: 0 : if (copied_len + next_sub->datalen > max_copy_len)
1531 : : break;
1532 : 0 : memcpy(merged_ie + copied_len, next_sub->data,
1533 : : next_sub->datalen);
1534 : 0 : copied_len += next_sub->datalen;
1535 : : }
1536 : :
1537 : 0 : return copied_len;
1538 : : }
1539 : : EXPORT_SYMBOL(cfg80211_merge_profile);
1540 : :
1541 : 0 : static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
1542 : : struct cfg80211_inform_bss *data,
1543 : : enum cfg80211_bss_frame_type ftype,
1544 : : const u8 *bssid, u64 tsf,
1545 : : u16 beacon_interval, const u8 *ie,
1546 : : size_t ielen,
1547 : : struct cfg80211_non_tx_bss *non_tx_data,
1548 : : gfp_t gfp)
1549 : : {
1550 : : const u8 *mbssid_index_ie;
1551 : : const struct element *elem, *sub;
1552 : : size_t new_ie_len;
1553 : : u8 new_bssid[ETH_ALEN];
1554 : : u8 *new_ie, *profile;
1555 : : u64 seen_indices = 0;
1556 : : u16 capability;
1557 : : struct cfg80211_bss *bss;
1558 : :
1559 [ # # ]: 0 : if (!non_tx_data)
1560 : 0 : return;
1561 [ # # ]: 0 : if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1562 : : return;
1563 [ # # ]: 0 : if (!wiphy->support_mbssid)
1564 : : return;
1565 [ # # # # ]: 0 : if (wiphy->support_only_he_mbssid &&
1566 : : !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
1567 : : return;
1568 : :
1569 : : new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
1570 [ # # ]: 0 : if (!new_ie)
1571 : : return;
1572 : :
1573 : : profile = kmalloc(ielen, gfp);
1574 [ # # ]: 0 : if (!profile)
1575 : : goto out;
1576 : :
1577 [ # # # # : 0 : for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
# # ]
1578 [ # # ]: 0 : if (elem->datalen < 4)
1579 : 0 : continue;
1580 [ # # # # ]: 0 : for_each_element(sub, elem->data + 1, elem->datalen - 1) {
1581 : : u8 profile_len;
1582 : :
1583 [ # # # # ]: 0 : if (sub->id != 0 || sub->datalen < 4) {
1584 : : /* not a valid BSS profile */
1585 : 0 : continue;
1586 : : }
1587 : :
1588 [ # # # # ]: 0 : if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
1589 : 0 : sub->data[1] != 2) {
1590 : : /* The first element within the Nontransmitted
1591 : : * BSSID Profile is not the Nontransmitted
1592 : : * BSSID Capability element.
1593 : : */
1594 : 0 : continue;
1595 : : }
1596 : :
1597 : 0 : memset(profile, 0, ielen);
1598 : 0 : profile_len = cfg80211_merge_profile(ie, ielen,
1599 : : elem,
1600 : : sub,
1601 : : profile,
1602 : : ielen);
1603 : :
1604 : : /* found a Nontransmitted BSSID Profile */
1605 : : mbssid_index_ie = cfg80211_find_ie
1606 : : (WLAN_EID_MULTI_BSSID_IDX,
1607 : : profile, profile_len);
1608 [ # # # # : 0 : if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
# # ]
1609 [ # # ]: 0 : mbssid_index_ie[2] == 0 ||
1610 : : mbssid_index_ie[2] > 46) {
1611 : : /* No valid Multiple BSSID-Index element */
1612 : 0 : continue;
1613 : : }
1614 : :
1615 : : if (seen_indices & BIT_ULL(mbssid_index_ie[2]))
1616 : : /* We don't support legacy split of a profile */
1617 : : net_dbg_ratelimited("Partial info for BSSID index %d\n",
1618 : : mbssid_index_ie[2]);
1619 : :
1620 : : seen_indices |= BIT_ULL(mbssid_index_ie[2]);
1621 : :
1622 : 0 : non_tx_data->bssid_index = mbssid_index_ie[2];
1623 : 0 : non_tx_data->max_bssid_indicator = elem->data[0];
1624 : :
1625 : 0 : cfg80211_gen_new_bssid(bssid,
1626 : : non_tx_data->max_bssid_indicator,
1627 : : non_tx_data->bssid_index,
1628 : : new_bssid);
1629 : 0 : memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
1630 : 0 : new_ie_len = cfg80211_gen_new_ie(ie, ielen,
1631 : : profile,
1632 : : profile_len, new_ie,
1633 : : gfp);
1634 [ # # ]: 0 : if (!new_ie_len)
1635 : 0 : continue;
1636 : :
1637 : : capability = get_unaligned_le16(profile + 2);
1638 : 0 : bss = cfg80211_inform_single_bss_data(wiphy, data,
1639 : : ftype,
1640 : : new_bssid, tsf,
1641 : : capability,
1642 : : beacon_interval,
1643 : : new_ie,
1644 : : new_ie_len,
1645 : : non_tx_data,
1646 : : gfp);
1647 [ # # ]: 0 : if (!bss)
1648 : : break;
1649 : 0 : cfg80211_put_bss(wiphy, bss);
1650 : : }
1651 : : }
1652 : :
1653 : : out:
1654 : 0 : kfree(new_ie);
1655 : 0 : kfree(profile);
1656 : : }
1657 : :
1658 : : struct cfg80211_bss *
1659 : 0 : cfg80211_inform_bss_data(struct wiphy *wiphy,
1660 : : struct cfg80211_inform_bss *data,
1661 : : enum cfg80211_bss_frame_type ftype,
1662 : : const u8 *bssid, u64 tsf, u16 capability,
1663 : : u16 beacon_interval, const u8 *ie, size_t ielen,
1664 : : gfp_t gfp)
1665 : : {
1666 : : struct cfg80211_bss *res;
1667 : : struct cfg80211_non_tx_bss non_tx_data;
1668 : :
1669 : 0 : res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
1670 : : capability, beacon_interval, ie,
1671 : : ielen, NULL, gfp);
1672 [ # # ]: 0 : if (!res)
1673 : : return NULL;
1674 : 0 : non_tx_data.tx_bss = res;
1675 : 0 : cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
1676 : : beacon_interval, ie, ielen, &non_tx_data,
1677 : : gfp);
1678 : 0 : return res;
1679 : : }
1680 : : EXPORT_SYMBOL(cfg80211_inform_bss_data);
1681 : :
1682 : : static void
1683 : 0 : cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy,
1684 : : struct cfg80211_inform_bss *data,
1685 : : struct ieee80211_mgmt *mgmt, size_t len,
1686 : : struct cfg80211_non_tx_bss *non_tx_data,
1687 : : gfp_t gfp)
1688 : : {
1689 : : enum cfg80211_bss_frame_type ftype;
1690 : 0 : const u8 *ie = mgmt->u.probe_resp.variable;
1691 : 0 : size_t ielen = len - offsetof(struct ieee80211_mgmt,
1692 : : u.probe_resp.variable);
1693 : :
1694 [ # # ]: 0 : ftype = ieee80211_is_beacon(mgmt->frame_control) ?
1695 : : CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
1696 : :
1697 : 0 : cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid,
1698 : : le64_to_cpu(mgmt->u.probe_resp.timestamp),
1699 : : le16_to_cpu(mgmt->u.probe_resp.beacon_int),
1700 : : ie, ielen, non_tx_data, gfp);
1701 : 0 : }
1702 : :
1703 : : static void
1704 : 0 : cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
1705 : : struct cfg80211_bss *nontrans_bss,
1706 : : struct ieee80211_mgmt *mgmt, size_t len)
1707 : : {
1708 : : u8 *ie, *new_ie, *pos;
1709 : : const u8 *nontrans_ssid, *trans_ssid, *mbssid;
1710 : 0 : size_t ielen = len - offsetof(struct ieee80211_mgmt,
1711 : : u.probe_resp.variable);
1712 : : size_t new_ie_len;
1713 : : struct cfg80211_bss_ies *new_ies;
1714 : : const struct cfg80211_bss_ies *old;
1715 : : u8 cpy_len;
1716 : :
1717 : : lockdep_assert_held(&wiphy_to_rdev(wiphy)->bss_lock);
1718 : :
1719 : 0 : ie = mgmt->u.probe_resp.variable;
1720 : :
1721 : : new_ie_len = ielen;
1722 : : trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
1723 [ # # ]: 0 : if (!trans_ssid)
1724 : : return;
1725 : 0 : new_ie_len -= trans_ssid[1];
1726 : : mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
1727 : : /*
1728 : : * It's not valid to have the MBSSID element before SSID
1729 : : * ignore if that happens - the code below assumes it is
1730 : : * after (while copying things inbetween).
1731 : : */
1732 [ # # ]: 0 : if (!mbssid || mbssid < trans_ssid)
1733 : : return;
1734 : 0 : new_ie_len -= mbssid[1];
1735 : :
1736 : : nontrans_ssid = ieee80211_bss_get_ie(nontrans_bss, WLAN_EID_SSID);
1737 [ # # ]: 0 : if (!nontrans_ssid)
1738 : : return;
1739 : :
1740 : 0 : new_ie_len += nontrans_ssid[1];
1741 : :
1742 : : /* generate new ie for nontrans BSS
1743 : : * 1. replace SSID with nontrans BSS' SSID
1744 : : * 2. skip MBSSID IE
1745 : : */
1746 : 0 : new_ie = kzalloc(new_ie_len, GFP_ATOMIC);
1747 [ # # ]: 0 : if (!new_ie)
1748 : : return;
1749 : :
1750 : 0 : new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, GFP_ATOMIC);
1751 [ # # ]: 0 : if (!new_ies)
1752 : : goto out_free;
1753 : :
1754 : : pos = new_ie;
1755 : :
1756 : : /* copy the nontransmitted SSID */
1757 : 0 : cpy_len = nontrans_ssid[1] + 2;
1758 : 0 : memcpy(pos, nontrans_ssid, cpy_len);
1759 : 0 : pos += cpy_len;
1760 : : /* copy the IEs between SSID and MBSSID */
1761 : 0 : cpy_len = trans_ssid[1] + 2;
1762 : 0 : memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
1763 : 0 : pos += (mbssid - (trans_ssid + cpy_len));
1764 : : /* copy the IEs after MBSSID */
1765 : 0 : cpy_len = mbssid[1] + 2;
1766 : 0 : memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
1767 : :
1768 : : /* update ie */
1769 : 0 : new_ies->len = new_ie_len;
1770 : 0 : new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
1771 : 0 : new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
1772 : 0 : memcpy(new_ies->data, new_ie, new_ie_len);
1773 [ # # ]: 0 : if (ieee80211_is_probe_resp(mgmt->frame_control)) {
1774 : 0 : old = rcu_access_pointer(nontrans_bss->proberesp_ies);
1775 : 0 : rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies);
1776 : 0 : rcu_assign_pointer(nontrans_bss->ies, new_ies);
1777 [ # # ]: 0 : if (old)
1778 [ # # ]: 0 : kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1779 : : } else {
1780 : 0 : old = rcu_access_pointer(nontrans_bss->beacon_ies);
1781 : 0 : rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies);
1782 : 0 : rcu_assign_pointer(nontrans_bss->ies, new_ies);
1783 [ # # ]: 0 : if (old)
1784 [ # # ]: 0 : kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1785 : : }
1786 : :
1787 : : out_free:
1788 : 0 : kfree(new_ie);
1789 : : }
1790 : :
1791 : : /* cfg80211_inform_bss_width_frame helper */
1792 : : static struct cfg80211_bss *
1793 : 0 : cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
1794 : : struct cfg80211_inform_bss *data,
1795 : : struct ieee80211_mgmt *mgmt, size_t len,
1796 : : gfp_t gfp)
1797 : : {
1798 : 0 : struct cfg80211_internal_bss tmp = {}, *res;
1799 : : struct cfg80211_bss_ies *ies;
1800 : : struct ieee80211_channel *channel;
1801 : : bool signal_valid;
1802 : 0 : size_t ielen = len - offsetof(struct ieee80211_mgmt,
1803 : : u.probe_resp.variable);
1804 : : int bss_type;
1805 : :
1806 : : BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
1807 : : offsetof(struct ieee80211_mgmt, u.beacon.variable));
1808 : :
1809 : 0 : trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
1810 : :
1811 [ # # # # ]: 0 : if (WARN_ON(!mgmt))
1812 : : return NULL;
1813 : :
1814 [ # # # # ]: 0 : if (WARN_ON(!wiphy))
1815 : : return NULL;
1816 : :
1817 [ # # # # : 0 : if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
# # # # ]
1818 : : (data->signal < 0 || data->signal > 100)))
1819 : : return NULL;
1820 : :
1821 [ # # # # ]: 0 : if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
1822 : : return NULL;
1823 : :
1824 : 0 : channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
1825 : : ielen, data->chan, data->scan_width);
1826 [ # # ]: 0 : if (!channel)
1827 : : return NULL;
1828 : :
1829 : 0 : ies = kzalloc(sizeof(*ies) + ielen, gfp);
1830 [ # # ]: 0 : if (!ies)
1831 : : return NULL;
1832 : 0 : ies->len = ielen;
1833 : 0 : ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
1834 : 0 : ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
1835 : 0 : memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
1836 : :
1837 [ # # ]: 0 : if (ieee80211_is_probe_resp(mgmt->frame_control))
1838 : 0 : rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
1839 : : else
1840 : 0 : rcu_assign_pointer(tmp.pub.beacon_ies, ies);
1841 : 0 : rcu_assign_pointer(tmp.pub.ies, ies);
1842 : :
1843 : 0 : memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
1844 : 0 : tmp.pub.channel = channel;
1845 : 0 : tmp.pub.scan_width = data->scan_width;
1846 : 0 : tmp.pub.signal = data->signal;
1847 : 0 : tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
1848 : 0 : tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
1849 : 0 : tmp.ts_boottime = data->boottime_ns;
1850 : 0 : tmp.parent_tsf = data->parent_tsf;
1851 : 0 : tmp.pub.chains = data->chains;
1852 : 0 : memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
1853 : : ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
1854 : :
1855 : 0 : signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
1856 : 0 : wiphy->max_adj_channel_rssi_comp;
1857 : 0 : res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid,
1858 : : jiffies);
1859 [ # # ]: 0 : if (!res)
1860 : : return NULL;
1861 : :
1862 [ # # ]: 0 : if (channel->band == NL80211_BAND_60GHZ) {
1863 : 0 : bss_type = res->pub.capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
1864 [ # # ]: 0 : if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
1865 : : bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
1866 : 0 : regulatory_hint_found_beacon(wiphy, channel, gfp);
1867 : : } else {
1868 [ # # ]: 0 : if (res->pub.capability & WLAN_CAPABILITY_ESS)
1869 : 0 : regulatory_hint_found_beacon(wiphy, channel, gfp);
1870 : : }
1871 : :
1872 : 0 : trace_cfg80211_return_bss(&res->pub);
1873 : : /* cfg80211_bss_update gives us a referenced result */
1874 : 0 : return &res->pub;
1875 : : }
1876 : :
1877 : : struct cfg80211_bss *
1878 : 0 : cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
1879 : : struct cfg80211_inform_bss *data,
1880 : : struct ieee80211_mgmt *mgmt, size_t len,
1881 : : gfp_t gfp)
1882 : : {
1883 : : struct cfg80211_bss *res, *tmp_bss;
1884 : 0 : const u8 *ie = mgmt->u.probe_resp.variable;
1885 : : const struct cfg80211_bss_ies *ies1, *ies2;
1886 : 0 : size_t ielen = len - offsetof(struct ieee80211_mgmt,
1887 : : u.probe_resp.variable);
1888 : : struct cfg80211_non_tx_bss non_tx_data;
1889 : :
1890 : 0 : res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
1891 : : len, gfp);
1892 [ # # # # : 0 : if (!res || !wiphy->support_mbssid ||
# # ]
1893 : : !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1894 : : return res;
1895 [ # # # # ]: 0 : if (wiphy->support_only_he_mbssid &&
1896 : : !cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
1897 : : return res;
1898 : :
1899 : 0 : non_tx_data.tx_bss = res;
1900 : : /* process each non-transmitting bss */
1901 : 0 : cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len,
1902 : : &non_tx_data, gfp);
1903 : :
1904 : : spin_lock_bh(&wiphy_to_rdev(wiphy)->bss_lock);
1905 : :
1906 : : /* check if the res has other nontransmitting bss which is not
1907 : : * in MBSSID IE
1908 : : */
1909 : 0 : ies1 = rcu_access_pointer(res->ies);
1910 : :
1911 : : /* go through nontrans_list, if the timestamp of the BSS is
1912 : : * earlier than the timestamp of the transmitting BSS then
1913 : : * update it
1914 : : */
1915 [ # # ]: 0 : list_for_each_entry(tmp_bss, &res->nontrans_list,
1916 : : nontrans_list) {
1917 : 0 : ies2 = rcu_access_pointer(tmp_bss->ies);
1918 [ # # ]: 0 : if (ies2->tsf < ies1->tsf)
1919 : 0 : cfg80211_update_notlisted_nontrans(wiphy, tmp_bss,
1920 : : mgmt, len);
1921 : : }
1922 : : spin_unlock_bh(&wiphy_to_rdev(wiphy)->bss_lock);
1923 : :
1924 : 0 : return res;
1925 : : }
1926 : : EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
1927 : :
1928 : 0 : void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1929 : : {
1930 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1931 : : struct cfg80211_internal_bss *bss;
1932 : :
1933 [ # # ]: 0 : if (!pub)
1934 : 0 : return;
1935 : :
1936 : 0 : bss = container_of(pub, struct cfg80211_internal_bss, pub);
1937 : :
1938 : : spin_lock_bh(&rdev->bss_lock);
1939 : 0 : bss_ref_get(rdev, bss);
1940 : : spin_unlock_bh(&rdev->bss_lock);
1941 : : }
1942 : : EXPORT_SYMBOL(cfg80211_ref_bss);
1943 : :
1944 : 0 : void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1945 : : {
1946 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1947 : : struct cfg80211_internal_bss *bss;
1948 : :
1949 [ # # ]: 0 : if (!pub)
1950 : 0 : return;
1951 : :
1952 : 0 : bss = container_of(pub, struct cfg80211_internal_bss, pub);
1953 : :
1954 : : spin_lock_bh(&rdev->bss_lock);
1955 : 0 : bss_ref_put(rdev, bss);
1956 : : spin_unlock_bh(&rdev->bss_lock);
1957 : : }
1958 : : EXPORT_SYMBOL(cfg80211_put_bss);
1959 : :
1960 : 0 : void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1961 : : {
1962 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1963 : : struct cfg80211_internal_bss *bss, *tmp1;
1964 : : struct cfg80211_bss *nontrans_bss, *tmp;
1965 : :
1966 [ # # # # ]: 0 : if (WARN_ON(!pub))
1967 : 0 : return;
1968 : :
1969 : 0 : bss = container_of(pub, struct cfg80211_internal_bss, pub);
1970 : :
1971 : : spin_lock_bh(&rdev->bss_lock);
1972 [ # # ]: 0 : if (list_empty(&bss->list))
1973 : : goto out;
1974 : :
1975 [ # # ]: 0 : list_for_each_entry_safe(nontrans_bss, tmp,
1976 : : &pub->nontrans_list,
1977 : : nontrans_list) {
1978 : 0 : tmp1 = container_of(nontrans_bss,
1979 : : struct cfg80211_internal_bss, pub);
1980 [ # # ]: 0 : if (__cfg80211_unlink_bss(rdev, tmp1))
1981 : 0 : rdev->bss_generation++;
1982 : : }
1983 : :
1984 [ # # ]: 0 : if (__cfg80211_unlink_bss(rdev, bss))
1985 : 0 : rdev->bss_generation++;
1986 : : out:
1987 : : spin_unlock_bh(&rdev->bss_lock);
1988 : : }
1989 : : EXPORT_SYMBOL(cfg80211_unlink_bss);
1990 : :
1991 : 0 : void cfg80211_bss_iter(struct wiphy *wiphy,
1992 : : struct cfg80211_chan_def *chandef,
1993 : : void (*iter)(struct wiphy *wiphy,
1994 : : struct cfg80211_bss *bss,
1995 : : void *data),
1996 : : void *iter_data)
1997 : : {
1998 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1999 : : struct cfg80211_internal_bss *bss;
2000 : :
2001 : : spin_lock_bh(&rdev->bss_lock);
2002 : :
2003 [ # # ]: 0 : list_for_each_entry(bss, &rdev->bss_list, list) {
2004 [ # # # # ]: 0 : if (!chandef || cfg80211_is_sub_chan(chandef, bss->pub.channel))
2005 : 0 : iter(wiphy, &bss->pub, iter_data);
2006 : : }
2007 : :
2008 : : spin_unlock_bh(&rdev->bss_lock);
2009 : 0 : }
2010 : : EXPORT_SYMBOL(cfg80211_bss_iter);
2011 : :
2012 : 0 : void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
2013 : : struct ieee80211_channel *chan)
2014 : : {
2015 : 0 : struct wiphy *wiphy = wdev->wiphy;
2016 : : struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
2017 : 0 : struct cfg80211_internal_bss *cbss = wdev->current_bss;
2018 : : struct cfg80211_internal_bss *new = NULL;
2019 : : struct cfg80211_internal_bss *bss;
2020 : : struct cfg80211_bss *nontrans_bss;
2021 : : struct cfg80211_bss *tmp;
2022 : :
2023 : : spin_lock_bh(&rdev->bss_lock);
2024 : :
2025 : : /*
2026 : : * Some APs use CSA also for bandwidth changes, i.e., without actually
2027 : : * changing the control channel, so no need to update in such a case.
2028 : : */
2029 [ # # ]: 0 : if (cbss->pub.channel == chan)
2030 : : goto done;
2031 : :
2032 : : /* use transmitting bss */
2033 [ # # ]: 0 : if (cbss->pub.transmitted_bss)
2034 : 0 : cbss = container_of(cbss->pub.transmitted_bss,
2035 : : struct cfg80211_internal_bss,
2036 : : pub);
2037 : :
2038 : 0 : cbss->pub.channel = chan;
2039 : :
2040 [ # # ]: 0 : list_for_each_entry(bss, &rdev->bss_list, list) {
2041 [ # # ]: 0 : if (!cfg80211_bss_type_match(bss->pub.capability,
2042 : 0 : bss->pub.channel->band,
2043 : : wdev->conn_bss_type))
2044 : 0 : continue;
2045 : :
2046 [ # # ]: 0 : if (bss == cbss)
2047 : 0 : continue;
2048 : :
2049 [ # # ]: 0 : if (!cmp_bss(&bss->pub, &cbss->pub, BSS_CMP_REGULAR)) {
2050 : 0 : new = bss;
2051 : 0 : break;
2052 : : }
2053 : : }
2054 : :
2055 [ # # ]: 0 : if (new) {
2056 : : /* to save time, update IEs for transmitting bss only */
2057 [ # # ]: 0 : if (cfg80211_update_known_bss(rdev, cbss, new, false)) {
2058 : 0 : new->pub.proberesp_ies = NULL;
2059 : 0 : new->pub.beacon_ies = NULL;
2060 : : }
2061 : :
2062 [ # # ]: 0 : list_for_each_entry_safe(nontrans_bss, tmp,
2063 : : &new->pub.nontrans_list,
2064 : : nontrans_list) {
2065 : 0 : bss = container_of(nontrans_bss,
2066 : : struct cfg80211_internal_bss, pub);
2067 [ # # ]: 0 : if (__cfg80211_unlink_bss(rdev, bss))
2068 : 0 : rdev->bss_generation++;
2069 : : }
2070 : :
2071 [ # # ]: 0 : WARN_ON(atomic_read(&new->hold));
2072 [ # # # # ]: 0 : if (!WARN_ON(!__cfg80211_unlink_bss(rdev, new)))
2073 : 0 : rdev->bss_generation++;
2074 : : }
2075 : :
2076 : 0 : rb_erase(&cbss->rbn, &rdev->bss_tree);
2077 : 0 : rb_insert_bss(rdev, cbss);
2078 : 0 : rdev->bss_generation++;
2079 : :
2080 [ # # ]: 0 : list_for_each_entry_safe(nontrans_bss, tmp,
2081 : : &cbss->pub.nontrans_list,
2082 : : nontrans_list) {
2083 : 0 : bss = container_of(nontrans_bss,
2084 : : struct cfg80211_internal_bss, pub);
2085 : 0 : bss->pub.channel = chan;
2086 : 0 : rb_erase(&bss->rbn, &rdev->bss_tree);
2087 : 0 : rb_insert_bss(rdev, bss);
2088 : 0 : rdev->bss_generation++;
2089 : : }
2090 : :
2091 : : done:
2092 : : spin_unlock_bh(&rdev->bss_lock);
2093 : 0 : }
2094 : :
2095 : : #ifdef CONFIG_CFG80211_WEXT
2096 : : static struct cfg80211_registered_device *
2097 : 0 : cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
2098 : : {
2099 : : struct cfg80211_registered_device *rdev;
2100 : : struct net_device *dev;
2101 : :
2102 [ # # # # ]: 0 : ASSERT_RTNL();
2103 : :
2104 : 0 : dev = dev_get_by_index(net, ifindex);
2105 [ # # ]: 0 : if (!dev)
2106 : : return ERR_PTR(-ENODEV);
2107 [ # # ]: 0 : if (dev->ieee80211_ptr)
2108 : 0 : rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
2109 : : else
2110 : : rdev = ERR_PTR(-ENODEV);
2111 : 0 : dev_put(dev);
2112 : 0 : return rdev;
2113 : : }
2114 : :
2115 : 0 : int cfg80211_wext_siwscan(struct net_device *dev,
2116 : : struct iw_request_info *info,
2117 : : union iwreq_data *wrqu, char *extra)
2118 : : {
2119 : : struct cfg80211_registered_device *rdev;
2120 : : struct wiphy *wiphy;
2121 : : struct iw_scan_req *wreq = NULL;
2122 : : struct cfg80211_scan_request *creq = NULL;
2123 : : int i, err, n_channels = 0;
2124 : : enum nl80211_band band;
2125 : :
2126 [ # # ]: 0 : if (!netif_running(dev))
2127 : : return -ENETDOWN;
2128 : :
2129 [ # # ]: 0 : if (wrqu->data.length == sizeof(struct iw_scan_req))
2130 : : wreq = (struct iw_scan_req *)extra;
2131 : :
2132 : 0 : rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
2133 : :
2134 [ # # ]: 0 : if (IS_ERR(rdev))
2135 : 0 : return PTR_ERR(rdev);
2136 : :
2137 [ # # # # ]: 0 : if (rdev->scan_req || rdev->scan_msg) {
2138 : : err = -EBUSY;
2139 : : goto out;
2140 : : }
2141 : :
2142 : 0 : wiphy = &rdev->wiphy;
2143 : :
2144 : : /* Determine number of channels, needed to allocate creq */
2145 [ # # # # ]: 0 : if (wreq && wreq->num_channels)
2146 : 0 : n_channels = wreq->num_channels;
2147 : : else
2148 : 0 : n_channels = ieee80211_get_num_supported_channels(wiphy);
2149 : :
2150 : 0 : creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
2151 : 0 : n_channels * sizeof(void *),
2152 : : GFP_ATOMIC);
2153 [ # # ]: 0 : if (!creq) {
2154 : : err = -ENOMEM;
2155 : : goto out;
2156 : : }
2157 : :
2158 : 0 : creq->wiphy = wiphy;
2159 : 0 : creq->wdev = dev->ieee80211_ptr;
2160 : : /* SSIDs come after channels */
2161 : 0 : creq->ssids = (void *)&creq->channels[n_channels];
2162 : 0 : creq->n_channels = n_channels;
2163 : 0 : creq->n_ssids = 1;
2164 : 0 : creq->scan_start = jiffies;
2165 : :
2166 : : /* translate "Scan on frequencies" request */
2167 : : i = 0;
2168 [ # # ]: 0 : for (band = 0; band < NUM_NL80211_BANDS; band++) {
2169 : : int j;
2170 : :
2171 [ # # ]: 0 : if (!wiphy->bands[band])
2172 : 0 : continue;
2173 : :
2174 [ # # ]: 0 : for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
2175 : : /* ignore disabled channels */
2176 [ # # ]: 0 : if (wiphy->bands[band]->channels[j].flags &
2177 : : IEEE80211_CHAN_DISABLED)
2178 : 0 : continue;
2179 : :
2180 : : /* If we have a wireless request structure and the
2181 : : * wireless request specifies frequencies, then search
2182 : : * for the matching hardware channel.
2183 : : */
2184 [ # # # # ]: 0 : if (wreq && wreq->num_channels) {
2185 : : int k;
2186 : 0 : int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
2187 [ # # ]: 0 : for (k = 0; k < wreq->num_channels; k++) {
2188 : 0 : struct iw_freq *freq =
2189 : : &wreq->channel_list[k];
2190 : 0 : int wext_freq =
2191 : : cfg80211_wext_freq(freq);
2192 : :
2193 [ # # ]: 0 : if (wext_freq == wiphy_freq)
2194 : : goto wext_freq_found;
2195 : : }
2196 : : goto wext_freq_not_found;
2197 : : }
2198 : :
2199 : : wext_freq_found:
2200 : 0 : creq->channels[i] = &wiphy->bands[band]->channels[j];
2201 : 0 : i++;
2202 : : wext_freq_not_found: ;
2203 : : }
2204 : : }
2205 : : /* No channels found? */
2206 [ # # ]: 0 : if (!i) {
2207 : : err = -EINVAL;
2208 : : goto out;
2209 : : }
2210 : :
2211 : : /* Set real number of channels specified in creq->channels[] */
2212 : 0 : creq->n_channels = i;
2213 : :
2214 : : /* translate "Scan for SSID" request */
2215 [ # # ]: 0 : if (wreq) {
2216 [ # # ]: 0 : if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
2217 [ # # ]: 0 : if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
2218 : : err = -EINVAL;
2219 : : goto out;
2220 : : }
2221 : 0 : memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
2222 : 0 : creq->ssids[0].ssid_len = wreq->essid_len;
2223 : : }
2224 [ # # ]: 0 : if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
2225 : 0 : creq->n_ssids = 0;
2226 : : }
2227 : :
2228 [ # # ]: 0 : for (i = 0; i < NUM_NL80211_BANDS; i++)
2229 [ # # ]: 0 : if (wiphy->bands[i])
2230 : 0 : creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
2231 : :
2232 : 0 : eth_broadcast_addr(creq->bssid);
2233 : :
2234 : 0 : rdev->scan_req = creq;
2235 : 0 : err = rdev_scan(rdev, creq);
2236 [ # # ]: 0 : if (err) {
2237 : 0 : rdev->scan_req = NULL;
2238 : : /* creq will be freed below */
2239 : : } else {
2240 : 0 : nl80211_send_scan_start(rdev, dev->ieee80211_ptr);
2241 : : /* creq now owned by driver */
2242 : : creq = NULL;
2243 : 0 : dev_hold(dev);
2244 : : }
2245 : : out:
2246 : 0 : kfree(creq);
2247 : 0 : return err;
2248 : : }
2249 : : EXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
2250 : :
2251 : 0 : static char *ieee80211_scan_add_ies(struct iw_request_info *info,
2252 : : const struct cfg80211_bss_ies *ies,
2253 : : char *current_ev, char *end_buf)
2254 : : {
2255 : : const u8 *pos, *end, *next;
2256 : : struct iw_event iwe;
2257 : :
2258 [ # # ]: 0 : if (!ies)
2259 : : return current_ev;
2260 : :
2261 : : /*
2262 : : * If needed, fragment the IEs buffer (at IE boundaries) into short
2263 : : * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
2264 : : */
2265 : 0 : pos = ies->data;
2266 : 0 : end = pos + ies->len;
2267 : :
2268 [ # # ]: 0 : while (end - pos > IW_GENERIC_IE_MAX) {
2269 : 0 : next = pos + 2 + pos[1];
2270 [ # # ]: 0 : while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
2271 : : next = next + 2 + next[1];
2272 : :
2273 : 0 : memset(&iwe, 0, sizeof(iwe));
2274 : 0 : iwe.cmd = IWEVGENIE;
2275 : 0 : iwe.u.data.length = next - pos;
2276 : : current_ev = iwe_stream_add_point_check(info, current_ev,
2277 : : end_buf, &iwe,
2278 : : (void *)pos);
2279 [ # # ]: 0 : if (IS_ERR(current_ev))
2280 : 0 : return current_ev;
2281 : 0 : pos = next;
2282 : : }
2283 : :
2284 [ # # ]: 0 : if (end > pos) {
2285 : 0 : memset(&iwe, 0, sizeof(iwe));
2286 : 0 : iwe.cmd = IWEVGENIE;
2287 : 0 : iwe.u.data.length = end - pos;
2288 : : current_ev = iwe_stream_add_point_check(info, current_ev,
2289 : : end_buf, &iwe,
2290 : : (void *)pos);
2291 [ # # ]: 0 : if (IS_ERR(current_ev))
2292 : : return current_ev;
2293 : : }
2294 : :
2295 : 0 : return current_ev;
2296 : : }
2297 : :
2298 : : static char *
2299 : 0 : ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
2300 : : struct cfg80211_internal_bss *bss, char *current_ev,
2301 : : char *end_buf)
2302 : : {
2303 : : const struct cfg80211_bss_ies *ies;
2304 : : struct iw_event iwe;
2305 : : const u8 *ie;
2306 : : u8 buf[50];
2307 : : u8 *cfg, *p, *tmp;
2308 : : int rem, i, sig;
2309 : : bool ismesh = false;
2310 : :
2311 : 0 : memset(&iwe, 0, sizeof(iwe));
2312 : 0 : iwe.cmd = SIOCGIWAP;
2313 : 0 : iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
2314 : 0 : memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
2315 : : current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2316 : : IW_EV_ADDR_LEN);
2317 [ # # ]: 0 : if (IS_ERR(current_ev))
2318 : : return current_ev;
2319 : :
2320 : 0 : memset(&iwe, 0, sizeof(iwe));
2321 : 0 : iwe.cmd = SIOCGIWFREQ;
2322 : 0 : iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
2323 : 0 : iwe.u.freq.e = 0;
2324 : : current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2325 : : IW_EV_FREQ_LEN);
2326 [ # # ]: 0 : if (IS_ERR(current_ev))
2327 : : return current_ev;
2328 : :
2329 : 0 : memset(&iwe, 0, sizeof(iwe));
2330 : 0 : iwe.cmd = SIOCGIWFREQ;
2331 : 0 : iwe.u.freq.m = bss->pub.channel->center_freq;
2332 : 0 : iwe.u.freq.e = 6;
2333 : : current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
2334 : : IW_EV_FREQ_LEN);
2335 [ # # ]: 0 : if (IS_ERR(current_ev))
2336 : : return current_ev;
2337 : :
2338 [ # # ]: 0 : if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
2339 : 0 : memset(&iwe, 0, sizeof(iwe));
2340 : 0 : iwe.cmd = IWEVQUAL;
2341 : 0 : iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
2342 : : IW_QUAL_NOISE_INVALID |
2343 : : IW_QUAL_QUAL_UPDATED;
2344 [ # # # ]: 0 : switch (wiphy->signal_type) {
2345 : : case CFG80211_SIGNAL_TYPE_MBM:
2346 : 0 : sig = bss->pub.signal / 100;
2347 : 0 : iwe.u.qual.level = sig;
2348 : 0 : iwe.u.qual.updated |= IW_QUAL_DBM;
2349 [ # # ]: 0 : if (sig < -110) /* rather bad */
2350 : : sig = -110;
2351 [ # # ]: 0 : else if (sig > -40) /* perfect */
2352 : : sig = -40;
2353 : : /* will give a range of 0 .. 70 */
2354 : 0 : iwe.u.qual.qual = sig + 110;
2355 : 0 : break;
2356 : : case CFG80211_SIGNAL_TYPE_UNSPEC:
2357 : 0 : iwe.u.qual.level = bss->pub.signal;
2358 : : /* will give range 0 .. 100 */
2359 : 0 : iwe.u.qual.qual = bss->pub.signal;
2360 : 0 : break;
2361 : : default:
2362 : : /* not reached */
2363 : : break;
2364 : : }
2365 : : current_ev = iwe_stream_add_event_check(info, current_ev,
2366 : : end_buf, &iwe,
2367 : : IW_EV_QUAL_LEN);
2368 [ # # ]: 0 : if (IS_ERR(current_ev))
2369 : : return current_ev;
2370 : : }
2371 : :
2372 : 0 : memset(&iwe, 0, sizeof(iwe));
2373 : 0 : iwe.cmd = SIOCGIWENCODE;
2374 [ # # ]: 0 : if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
2375 : 0 : iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
2376 : : else
2377 : 0 : iwe.u.data.flags = IW_ENCODE_DISABLED;
2378 : : iwe.u.data.length = 0;
2379 : : current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2380 : : &iwe, "");
2381 [ # # ]: 0 : if (IS_ERR(current_ev))
2382 : : return current_ev;
2383 : :
2384 : : rcu_read_lock();
2385 : 0 : ies = rcu_dereference(bss->pub.ies);
2386 : 0 : rem = ies->len;
2387 : 0 : ie = ies->data;
2388 : :
2389 [ # # ]: 0 : while (rem >= 2) {
2390 : : /* invalid data */
2391 [ # # ]: 0 : if (ie[1] > rem - 2)
2392 : : break;
2393 : :
2394 [ # # # # : 0 : switch (ie[0]) {
# ]
2395 : : case WLAN_EID_SSID:
2396 : 0 : memset(&iwe, 0, sizeof(iwe));
2397 : 0 : iwe.cmd = SIOCGIWESSID;
2398 : 0 : iwe.u.data.length = ie[1];
2399 : 0 : iwe.u.data.flags = 1;
2400 : 0 : current_ev = iwe_stream_add_point_check(info,
2401 : : current_ev,
2402 : : end_buf, &iwe,
2403 : : (u8 *)ie + 2);
2404 [ # # ]: 0 : if (IS_ERR(current_ev))
2405 : : goto unlock;
2406 : : break;
2407 : : case WLAN_EID_MESH_ID:
2408 : 0 : memset(&iwe, 0, sizeof(iwe));
2409 : 0 : iwe.cmd = SIOCGIWESSID;
2410 : 0 : iwe.u.data.length = ie[1];
2411 : 0 : iwe.u.data.flags = 1;
2412 : 0 : current_ev = iwe_stream_add_point_check(info,
2413 : : current_ev,
2414 : : end_buf, &iwe,
2415 : : (u8 *)ie + 2);
2416 [ # # ]: 0 : if (IS_ERR(current_ev))
2417 : : goto unlock;
2418 : : break;
2419 : : case WLAN_EID_MESH_CONFIG:
2420 : : ismesh = true;
2421 [ # # ]: 0 : if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
2422 : : break;
2423 : : cfg = (u8 *)ie + 2;
2424 : 0 : memset(&iwe, 0, sizeof(iwe));
2425 : 0 : iwe.cmd = IWEVCUSTOM;
2426 : 0 : sprintf(buf, "Mesh Network Path Selection Protocol ID: "
2427 : 0 : "0x%02X", cfg[0]);
2428 : 0 : iwe.u.data.length = strlen(buf);
2429 : : current_ev = iwe_stream_add_point_check(info,
2430 : : current_ev,
2431 : : end_buf,
2432 : : &iwe, buf);
2433 [ # # ]: 0 : if (IS_ERR(current_ev))
2434 : : goto unlock;
2435 : 0 : sprintf(buf, "Path Selection Metric ID: 0x%02X",
2436 : 0 : cfg[1]);
2437 : 0 : iwe.u.data.length = strlen(buf);
2438 : : current_ev = iwe_stream_add_point_check(info,
2439 : : current_ev,
2440 : : end_buf,
2441 : : &iwe, buf);
2442 [ # # ]: 0 : if (IS_ERR(current_ev))
2443 : : goto unlock;
2444 : 0 : sprintf(buf, "Congestion Control Mode ID: 0x%02X",
2445 : 0 : cfg[2]);
2446 : 0 : iwe.u.data.length = strlen(buf);
2447 : : current_ev = iwe_stream_add_point_check(info,
2448 : : current_ev,
2449 : : end_buf,
2450 : : &iwe, buf);
2451 [ # # ]: 0 : if (IS_ERR(current_ev))
2452 : : goto unlock;
2453 : 0 : sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
2454 : 0 : iwe.u.data.length = strlen(buf);
2455 : : current_ev = iwe_stream_add_point_check(info,
2456 : : current_ev,
2457 : : end_buf,
2458 : : &iwe, buf);
2459 [ # # ]: 0 : if (IS_ERR(current_ev))
2460 : : goto unlock;
2461 : 0 : sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
2462 : 0 : iwe.u.data.length = strlen(buf);
2463 : : current_ev = iwe_stream_add_point_check(info,
2464 : : current_ev,
2465 : : end_buf,
2466 : : &iwe, buf);
2467 [ # # ]: 0 : if (IS_ERR(current_ev))
2468 : : goto unlock;
2469 : 0 : sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
2470 : 0 : iwe.u.data.length = strlen(buf);
2471 : : current_ev = iwe_stream_add_point_check(info,
2472 : : current_ev,
2473 : : end_buf,
2474 : : &iwe, buf);
2475 [ # # ]: 0 : if (IS_ERR(current_ev))
2476 : : goto unlock;
2477 : 0 : sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
2478 : 0 : iwe.u.data.length = strlen(buf);
2479 : : current_ev = iwe_stream_add_point_check(info,
2480 : : current_ev,
2481 : : end_buf,
2482 : : &iwe, buf);
2483 [ # # ]: 0 : if (IS_ERR(current_ev))
2484 : : goto unlock;
2485 : : break;
2486 : : case WLAN_EID_SUPP_RATES:
2487 : : case WLAN_EID_EXT_SUPP_RATES:
2488 : : /* display all supported rates in readable format */
2489 : 0 : p = current_ev + iwe_stream_lcp_len(info);
2490 : :
2491 : 0 : memset(&iwe, 0, sizeof(iwe));
2492 : 0 : iwe.cmd = SIOCGIWRATE;
2493 : : /* Those two flags are ignored... */
2494 : : iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
2495 : :
2496 [ # # ]: 0 : for (i = 0; i < ie[1]; i++) {
2497 : 0 : iwe.u.bitrate.value =
2498 : 0 : ((ie[i + 2] & 0x7f) * 500000);
2499 : : tmp = p;
2500 : 0 : p = iwe_stream_add_value(info, current_ev, p,
2501 : : end_buf, &iwe,
2502 : : IW_EV_PARAM_LEN);
2503 [ # # ]: 0 : if (p == tmp) {
2504 : : current_ev = ERR_PTR(-E2BIG);
2505 : : goto unlock;
2506 : : }
2507 : : }
2508 : 0 : current_ev = p;
2509 : 0 : break;
2510 : : }
2511 : 0 : rem -= ie[1] + 2;
2512 : 0 : ie += ie[1] + 2;
2513 : : }
2514 : :
2515 [ # # # # ]: 0 : if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
2516 : : ismesh) {
2517 : 0 : memset(&iwe, 0, sizeof(iwe));
2518 : 0 : iwe.cmd = SIOCGIWMODE;
2519 [ # # ]: 0 : if (ismesh)
2520 : 0 : iwe.u.mode = IW_MODE_MESH;
2521 [ # # ]: 0 : else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
2522 : 0 : iwe.u.mode = IW_MODE_MASTER;
2523 : : else
2524 : 0 : iwe.u.mode = IW_MODE_ADHOC;
2525 : : current_ev = iwe_stream_add_event_check(info, current_ev,
2526 : : end_buf, &iwe,
2527 : : IW_EV_UINT_LEN);
2528 [ # # ]: 0 : if (IS_ERR(current_ev))
2529 : : goto unlock;
2530 : : }
2531 : :
2532 : 0 : memset(&iwe, 0, sizeof(iwe));
2533 : 0 : iwe.cmd = IWEVCUSTOM;
2534 : 0 : sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
2535 : 0 : iwe.u.data.length = strlen(buf);
2536 : : current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
2537 : : &iwe, buf);
2538 [ # # ]: 0 : if (IS_ERR(current_ev))
2539 : : goto unlock;
2540 : 0 : memset(&iwe, 0, sizeof(iwe));
2541 : 0 : iwe.cmd = IWEVCUSTOM;
2542 : 0 : sprintf(buf, " Last beacon: %ums ago",
2543 : : elapsed_jiffies_msecs(bss->ts));
2544 : 0 : iwe.u.data.length = strlen(buf);
2545 : : current_ev = iwe_stream_add_point_check(info, current_ev,
2546 : : end_buf, &iwe, buf);
2547 [ # # ]: 0 : if (IS_ERR(current_ev))
2548 : : goto unlock;
2549 : :
2550 : 0 : current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
2551 : :
2552 : : unlock:
2553 : : rcu_read_unlock();
2554 : 0 : return current_ev;
2555 : : }
2556 : :
2557 : :
2558 : 0 : static int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
2559 : : struct iw_request_info *info,
2560 : : char *buf, size_t len)
2561 : : {
2562 : : char *current_ev = buf;
2563 : 0 : char *end_buf = buf + len;
2564 : : struct cfg80211_internal_bss *bss;
2565 : : int err = 0;
2566 : :
2567 : : spin_lock_bh(&rdev->bss_lock);
2568 : : cfg80211_bss_expire(rdev);
2569 : :
2570 [ # # ]: 0 : list_for_each_entry(bss, &rdev->bss_list, list) {
2571 [ # # ]: 0 : if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
2572 : : err = -E2BIG;
2573 : : break;
2574 : : }
2575 : 0 : current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
2576 : : current_ev, end_buf);
2577 [ # # ]: 0 : if (IS_ERR(current_ev)) {
2578 : : err = PTR_ERR(current_ev);
2579 : 0 : break;
2580 : : }
2581 : : }
2582 : : spin_unlock_bh(&rdev->bss_lock);
2583 : :
2584 [ # # ]: 0 : if (err)
2585 : : return err;
2586 : 0 : return current_ev - buf;
2587 : : }
2588 : :
2589 : :
2590 : 0 : int cfg80211_wext_giwscan(struct net_device *dev,
2591 : : struct iw_request_info *info,
2592 : : struct iw_point *data, char *extra)
2593 : : {
2594 : : struct cfg80211_registered_device *rdev;
2595 : : int res;
2596 : :
2597 [ # # ]: 0 : if (!netif_running(dev))
2598 : : return -ENETDOWN;
2599 : :
2600 : 0 : rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
2601 : :
2602 [ # # ]: 0 : if (IS_ERR(rdev))
2603 : 0 : return PTR_ERR(rdev);
2604 : :
2605 [ # # # # ]: 0 : if (rdev->scan_req || rdev->scan_msg)
2606 : : return -EAGAIN;
2607 : :
2608 : 0 : res = ieee80211_scan_results(rdev, info, extra, data->length);
2609 : 0 : data->length = 0;
2610 [ # # ]: 0 : if (res >= 0) {
2611 : 0 : data->length = res;
2612 : : res = 0;
2613 : : }
2614 : :
2615 : 0 : return res;
2616 : : }
2617 : : EXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
2618 : : #endif
|