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