summaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorPeng Xu <pxu@codeaurora.org>2019-01-21 05:14:57 -0500
committerJohannes Berg <johannes.berg@intel.com>2019-02-08 07:51:50 -0500
commit0b8fb8235be8be99a197e8d948fc0a2df8dc261a (patch)
treef82782572078836b3166c1aaec02e113b6f30528 /net/wireless
parentfcea7db50467e72338beb262fe6342ed01643399 (diff)
cfg80211: Parsing of Multiple BSSID information in scanning
This extends cfg80211 BSS table processing to be able to parse Multiple BSSID element from Beacon and Probe Response frames and to update the BSS profiles in internal database for non-transmitted BSSs. Signed-off-by: Peng Xu <pxu@codeaurora.org> Signed-off-by: Sara Sharon <sara.sharon@intel.com> Signed-off-by: Jouni Malinen <jouni@codeaurora.org> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.h3
-rw-r--r--net/wireless/scan.c508
2 files changed, 472 insertions, 39 deletions
diff --git a/net/wireless/core.h b/net/wireless/core.h
index c5d6f3418601..a50b92ac77a1 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -3,7 +3,7 @@
3 * Wireless configuration interface internals. 3 * Wireless configuration interface internals.
4 * 4 *
5 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 5 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
6 * Copyright (C) 2018 Intel Corporation 6 * Copyright (C) 2018-2019 Intel Corporation
7 */ 7 */
8#ifndef __NET_WIRELESS_CORE_H 8#ifndef __NET_WIRELESS_CORE_H
9#define __NET_WIRELESS_CORE_H 9#define __NET_WIRELESS_CORE_H
@@ -152,6 +152,7 @@ extern int cfg80211_rdev_list_generation;
152struct cfg80211_internal_bss { 152struct cfg80211_internal_bss {
153 struct list_head list; 153 struct list_head list;
154 struct list_head hidden_list; 154 struct list_head hidden_list;
155 struct list_head nontrans_list;
155 struct rb_node rbn; 156 struct rb_node rbn;
156 u64 ts_boottime; 157 u64 ts_boottime;
157 unsigned long ts; 158 unsigned long ts;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index d2c9ca5f4f57..531c2e56413f 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -5,6 +5,7 @@
5 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> 5 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
6 * Copyright 2013-2014 Intel Mobile Communications GmbH 6 * Copyright 2013-2014 Intel Mobile Communications GmbH
7 * Copyright 2016 Intel Deutschland GmbH 7 * Copyright 2016 Intel Deutschland GmbH
8 * Copyright (C) 2018-2019 Intel Corporation
8 */ 9 */
9#include <linux/kernel.h> 10#include <linux/kernel.h>
10#include <linux/slab.h> 11#include <linux/slab.h>
@@ -150,6 +151,7 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
150 } 151 }
151 152
152 list_del_init(&bss->list); 153 list_del_init(&bss->list);
154 list_del_init(&bss->nontrans_list);
153 rb_erase(&bss->rbn, &rdev->bss_tree); 155 rb_erase(&bss->rbn, &rdev->bss_tree);
154 rdev->bss_entries--; 156 rdev->bss_entries--;
155 WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list), 157 WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
@@ -159,6 +161,172 @@ static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
159 return true; 161 return true;
160} 162}
161 163
164static void cfg80211_gen_new_bssid(const u8 *bssid, u8 max_bssid,
165 u8 mbssid_index, u8 *new_bssid_addr)
166{
167 u64 bssid_tmp, new_bssid = 0;
168 u64 lsb_n;
169
170 bssid_tmp = ether_addr_to_u64(bssid);
171
172 lsb_n = bssid_tmp & ((1 << max_bssid) - 1);
173 new_bssid = bssid_tmp;
174 new_bssid &= ~((1 << max_bssid) - 1);
175 new_bssid |= (lsb_n + mbssid_index) % (1 << max_bssid);
176
177 u64_to_ether_addr(new_bssid, new_bssid_addr);
178}
179
180static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
181 const u8 *subelement, size_t subie_len,
182 u8 *new_ie, gfp_t gfp)
183{
184 u8 *pos, *tmp;
185 const u8 *tmp_old, *tmp_new;
186 u8 *sub_copy;
187
188 /* copy subelement as we need to change its content to
189 * mark an ie after it is processed.
190 */
191 sub_copy = kmalloc(subie_len, gfp);
192 if (!sub_copy)
193 return 0;
194 memcpy(sub_copy, subelement, subie_len);
195
196 pos = &new_ie[0];
197
198 /* set new ssid */
199 tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len);
200 if (tmp_new) {
201 memcpy(pos, tmp_new, tmp_new[1] + 2);
202 pos += (tmp_new[1] + 2);
203 }
204
205 /* go through IEs in ie (skip SSID) and subelement,
206 * merge them into new_ie
207 */
208 tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
209 tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie;
210
211 while (tmp_old + tmp_old[1] + 2 - ie <= ielen) {
212 if (tmp_old[0] == 0) {
213 tmp_old++;
214 continue;
215 }
216
217 tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy, subie_len);
218 if (!tmp) {
219 /* ie in old ie but not in subelement */
220 if (tmp_old[0] != WLAN_EID_MULTIPLE_BSSID) {
221 memcpy(pos, tmp_old, tmp_old[1] + 2);
222 pos += tmp_old[1] + 2;
223 }
224 } else {
225 /* ie in transmitting ie also in subelement,
226 * copy from subelement and flag the ie in subelement
227 * as copied (by setting eid field to 0xff). For
228 * vendor ie, compare OUI + type + subType to
229 * determine if they are the same ie.
230 */
231 if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) {
232 if (!memcmp(tmp_old + 2, tmp + 2, 5)) {
233 /* same vendor ie, copy from
234 * subelement
235 */
236 memcpy(pos, tmp, tmp[1] + 2);
237 pos += tmp[1] + 2;
238 tmp[0] = 0xff;
239 } else {
240 memcpy(pos, tmp_old, tmp_old[1] + 2);
241 pos += tmp_old[1] + 2;
242 }
243 } else {
244 /* copy ie from subelement into new ie */
245 memcpy(pos, tmp, tmp[1] + 2);
246 pos += tmp[1] + 2;
247 tmp[0] = 0xff;
248 }
249 }
250
251 if (tmp_old + tmp_old[1] + 2 - ie == ielen)
252 break;
253
254 tmp_old += tmp_old[1] + 2;
255 }
256
257 /* go through subelement again to check if there is any ie not
258 * copied to new ie, skip ssid, capability, bssid-index ie
259 */
260 tmp_new = sub_copy;
261 while (tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
262 if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP ||
263 tmp_new[0] == WLAN_EID_SSID ||
264 tmp_new[0] == WLAN_EID_MULTI_BSSID_IDX ||
265 tmp_new[0] == 0xff)) {
266 memcpy(pos, tmp_new, tmp_new[1] + 2);
267 pos += tmp_new[1] + 2;
268 }
269 if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len)
270 break;
271 tmp_new += tmp_new[1] + 2;
272 }
273
274 kfree(sub_copy);
275 return pos - new_ie;
276}
277
278static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
279 const u8 *ssid, size_t ssid_len)
280{
281 const struct cfg80211_bss_ies *ies;
282 const u8 *ssidie;
283
284 if (bssid && !ether_addr_equal(a->bssid, bssid))
285 return false;
286
287 if (!ssid)
288 return true;
289
290 ies = rcu_access_pointer(a->ies);
291 if (!ies)
292 return false;
293 ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
294 if (!ssidie)
295 return false;
296 if (ssidie[1] != ssid_len)
297 return false;
298 return memcmp(ssidie + 2, ssid, ssid_len) == 0;
299}
300
301static int
302cfg80211_add_nontrans_list(struct cfg80211_internal_bss *trans_bss,
303 struct cfg80211_internal_bss *nontrans_bss)
304{
305 const u8 *ssid;
306 size_t ssid_len;
307 struct cfg80211_internal_bss *bss = NULL;
308
309 rcu_read_lock();
310 ssid = ieee80211_bss_get_ie(&nontrans_bss->pub, WLAN_EID_SSID);
311 if (!ssid) {
312 rcu_read_unlock();
313 return -EINVAL;
314 }
315 ssid_len = ssid[1];
316 ssid = ssid + 2;
317 rcu_read_unlock();
318
319 /* check if nontrans_bss is in the list */
320 list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) {
321 if (is_bss(&bss->pub, nontrans_bss->pub.bssid, ssid, ssid_len))
322 return 0;
323 }
324
325 /* add to the list */
326 list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
327 return 0;
328}
329
162static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev, 330static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
163 unsigned long expire_time) 331 unsigned long expire_time)
164{ 332{
@@ -518,29 +686,6 @@ const struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type,
518} 686}
519EXPORT_SYMBOL(cfg80211_find_vendor_elem); 687EXPORT_SYMBOL(cfg80211_find_vendor_elem);
520 688
521static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
522 const u8 *ssid, size_t ssid_len)
523{
524 const struct cfg80211_bss_ies *ies;
525 const u8 *ssidie;
526
527 if (bssid && !ether_addr_equal(a->bssid, bssid))
528 return false;
529
530 if (!ssid)
531 return true;
532
533 ies = rcu_access_pointer(a->ies);
534 if (!ies)
535 return false;
536 ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
537 if (!ssidie)
538 return false;
539 if (ssidie[1] != ssid_len)
540 return false;
541 return memcmp(ssidie + 2, ssid, ssid_len) == 0;
542}
543
544/** 689/**
545 * enum bss_compare_mode - BSS compare mode 690 * enum bss_compare_mode - BSS compare mode
546 * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find) 691 * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
@@ -1002,6 +1147,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
1002 memcpy(new, tmp, sizeof(*new)); 1147 memcpy(new, tmp, sizeof(*new));
1003 new->refcount = 1; 1148 new->refcount = 1;
1004 INIT_LIST_HEAD(&new->hidden_list); 1149 INIT_LIST_HEAD(&new->hidden_list);
1150 INIT_LIST_HEAD(&new->nontrans_list);
1005 1151
1006 if (rcu_access_pointer(tmp->pub.proberesp_ies)) { 1152 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
1007 hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN); 1153 hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
@@ -1123,17 +1269,19 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
1123} 1269}
1124 1270
1125/* Returned bss is reference counted and must be cleaned up appropriately. */ 1271/* Returned bss is reference counted and must be cleaned up appropriately. */
1126struct cfg80211_bss * 1272static struct cfg80211_bss *
1127cfg80211_inform_bss_data(struct wiphy *wiphy, 1273cfg80211_inform_single_bss_data(struct wiphy *wiphy,
1128 struct cfg80211_inform_bss *data, 1274 struct cfg80211_inform_bss *data,
1129 enum cfg80211_bss_frame_type ftype, 1275 enum cfg80211_bss_frame_type ftype,
1130 const u8 *bssid, u64 tsf, u16 capability, 1276 const u8 *bssid, u64 tsf, u16 capability,
1131 u16 beacon_interval, const u8 *ie, size_t ielen, 1277 u16 beacon_interval, const u8 *ie, size_t ielen,
1132 gfp_t gfp) 1278 struct cfg80211_bss *trans_bss,
1279 gfp_t gfp)
1133{ 1280{
1281 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1134 struct cfg80211_bss_ies *ies; 1282 struct cfg80211_bss_ies *ies;
1135 struct ieee80211_channel *channel; 1283 struct ieee80211_channel *channel;
1136 struct cfg80211_internal_bss tmp = {}, *res; 1284 struct cfg80211_internal_bss tmp = {}, *res, *trans_internal;
1137 int bss_type; 1285 int bss_type;
1138 bool signal_valid; 1286 bool signal_valid;
1139 1287
@@ -1202,19 +1350,252 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
1202 regulatory_hint_found_beacon(wiphy, channel, gfp); 1350 regulatory_hint_found_beacon(wiphy, channel, gfp);
1203 } 1351 }
1204 1352
1353 if (trans_bss) {
1354 /* this is a nontransmitting bss, we need to add it to
1355 * transmitting bss' list if it is not there
1356 */
1357 trans_internal = container_of(trans_bss,
1358 struct cfg80211_internal_bss,
1359 pub);
1360 if (cfg80211_add_nontrans_list(trans_internal, res)) {
1361 if (__cfg80211_unlink_bss(rdev, res))
1362 rdev->bss_generation++;
1363 }
1364 }
1365
1205 trace_cfg80211_return_bss(&res->pub); 1366 trace_cfg80211_return_bss(&res->pub);
1206 /* cfg80211_bss_update gives us a referenced result */ 1367 /* cfg80211_bss_update gives us a referenced result */
1207 return &res->pub; 1368 return &res->pub;
1208} 1369}
1209EXPORT_SYMBOL(cfg80211_inform_bss_data);
1210 1370
1211/* cfg80211_inform_bss_width_frame helper */ 1371static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
1372 struct cfg80211_inform_bss *data,
1373 enum cfg80211_bss_frame_type ftype,
1374 const u8 *bssid, u64 tsf,
1375 u16 beacon_interval, const u8 *ie,
1376 size_t ielen,
1377 struct cfg80211_bss *trans_bss,
1378 gfp_t gfp)
1379{
1380 const u8 *pos, *subelement, *mbssid_end_pos;
1381 const u8 *tmp, *mbssid_index_ie;
1382 size_t subie_len, new_ie_len;
1383 u8 new_bssid[ETH_ALEN];
1384 u8 *new_ie;
1385 u16 capability;
1386 struct cfg80211_bss *bss;
1387
1388 if (!trans_bss)
1389 return;
1390 if (!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1391 return;
1392
1393 pos = ie;
1394
1395 new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
1396 if (!new_ie)
1397 return;
1398
1399 while (pos < ie + ielen + 2) {
1400 tmp = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, pos,
1401 ielen - (pos - ie));
1402 if (!tmp)
1403 break;
1404
1405 mbssid_end_pos = tmp + tmp[1] + 2;
1406 /* Skip Element ID, Len, MaxBSSID Indicator */
1407 if (tmp[1] < 4)
1408 break;
1409 for (subelement = tmp + 3; subelement < mbssid_end_pos - 1;
1410 subelement += 2 + subelement[1]) {
1411 subie_len = subelement[1];
1412 if (mbssid_end_pos - subelement < 2 + subie_len)
1413 break;
1414 if (subelement[0] != 0 || subelement[1] < 4) {
1415 /* not a valid BSS profile */
1416 continue;
1417 }
1418
1419 if (subelement[2] != WLAN_EID_NON_TX_BSSID_CAP ||
1420 subelement[3] != 2) {
1421 /* The first element within the Nontransmitted
1422 * BSSID Profile is not the Nontransmitted
1423 * BSSID Capability element.
1424 */
1425 continue;
1426 }
1427
1428 /* found a Nontransmitted BSSID Profile */
1429 mbssid_index_ie = cfg80211_find_ie
1430 (WLAN_EID_MULTI_BSSID_IDX,
1431 subelement + 2, subie_len);
1432 if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
1433 mbssid_index_ie[2] == 0) {
1434 /* No valid Multiple BSSID-Index element */
1435 continue;
1436 }
1437
1438 cfg80211_gen_new_bssid(bssid, tmp[2],
1439 mbssid_index_ie[2],
1440 new_bssid);
1441 memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
1442 new_ie_len = cfg80211_gen_new_ie(ie, ielen,
1443 subelement + 2,
1444 subie_len, new_ie,
1445 gfp);
1446 if (!new_ie_len)
1447 continue;
1448
1449 capability = le16_to_cpup((const __le16 *)
1450 &subelement[4]);
1451 bss = cfg80211_inform_single_bss_data(wiphy, data,
1452 ftype,
1453 new_bssid, tsf,
1454 capability,
1455 beacon_interval,
1456 new_ie,
1457 new_ie_len,
1458 trans_bss, gfp);
1459 if (!bss)
1460 break;
1461 cfg80211_put_bss(wiphy, bss);
1462 }
1463
1464 pos = mbssid_end_pos;
1465 }
1466
1467 kfree(new_ie);
1468}
1469
1212struct cfg80211_bss * 1470struct cfg80211_bss *
1213cfg80211_inform_bss_frame_data(struct wiphy *wiphy, 1471cfg80211_inform_bss_data(struct wiphy *wiphy,
1214 struct cfg80211_inform_bss *data, 1472 struct cfg80211_inform_bss *data,
1215 struct ieee80211_mgmt *mgmt, size_t len, 1473 enum cfg80211_bss_frame_type ftype,
1216 gfp_t gfp) 1474 const u8 *bssid, u64 tsf, u16 capability,
1475 u16 beacon_interval, const u8 *ie, size_t ielen,
1476 gfp_t gfp)
1477{
1478 struct cfg80211_bss *res;
1479
1480 res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
1481 capability, beacon_interval, ie,
1482 ielen, NULL, gfp);
1483 cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
1484 beacon_interval, ie, ielen, res, gfp);
1485 return res;
1486}
1487EXPORT_SYMBOL(cfg80211_inform_bss_data);
1217 1488
1489static void
1490cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy,
1491 struct cfg80211_inform_bss *data,
1492 struct ieee80211_mgmt *mgmt, size_t len,
1493 struct cfg80211_bss *trans_bss,
1494 gfp_t gfp)
1495{
1496 enum cfg80211_bss_frame_type ftype;
1497 const u8 *ie = mgmt->u.probe_resp.variable;
1498 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1499 u.probe_resp.variable);
1500
1501 ftype = ieee80211_is_beacon(mgmt->frame_control) ?
1502 CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
1503
1504 cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid,
1505 le64_to_cpu(mgmt->u.probe_resp.timestamp),
1506 le16_to_cpu(mgmt->u.probe_resp.beacon_int),
1507 ie, ielen, trans_bss, gfp);
1508}
1509
1510static void
1511cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
1512 struct cfg80211_internal_bss *nontrans_bss,
1513 struct ieee80211_mgmt *mgmt, size_t len,
1514 gfp_t gfp)
1515{
1516 u8 *ie, *new_ie, *pos;
1517 const u8 *nontrans_ssid, *trans_ssid, *mbssid;
1518 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1519 u.probe_resp.variable);
1520 size_t new_ie_len;
1521 struct cfg80211_bss_ies *new_ies;
1522 const struct cfg80211_bss_ies *old;
1523 u8 cpy_len;
1524
1525 ie = mgmt->u.probe_resp.variable;
1526
1527 new_ie_len = ielen;
1528 trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
1529 if (!trans_ssid)
1530 return;
1531 new_ie_len -= trans_ssid[1];
1532 mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
1533 if (!mbssid)
1534 return;
1535 new_ie_len -= mbssid[1];
1536 rcu_read_lock();
1537 nontrans_ssid = ieee80211_bss_get_ie(&nontrans_bss->pub, WLAN_EID_SSID);
1538 if (!nontrans_ssid) {
1539 rcu_read_unlock();
1540 return;
1541 }
1542 new_ie_len += nontrans_ssid[1];
1543 rcu_read_unlock();
1544
1545 /* generate new ie for nontrans BSS
1546 * 1. replace SSID with nontrans BSS' SSID
1547 * 2. skip MBSSID IE
1548 */
1549 new_ie = kzalloc(new_ie_len, gfp);
1550 if (!new_ie)
1551 return;
1552 new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, gfp);
1553 if (!new_ies) {
1554 kfree(new_ie);
1555 return;
1556 }
1557
1558 pos = new_ie;
1559
1560 /* copy the nontransmitted SSID */
1561 cpy_len = nontrans_ssid[1] + 2;
1562 memcpy(pos, nontrans_ssid, cpy_len);
1563 pos += cpy_len;
1564 /* copy the IEs between SSID and MBSSID */
1565 cpy_len = trans_ssid[1] + 2;
1566 memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
1567 pos += (mbssid - (trans_ssid + cpy_len));
1568 /* copy the IEs after MBSSID */
1569 cpy_len = mbssid[1] + 2;
1570 memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
1571
1572 /* update ie */
1573 new_ies->len = new_ie_len;
1574 new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
1575 new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
1576 memcpy(new_ies->data, new_ie, new_ie_len);
1577 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
1578 old = rcu_access_pointer(nontrans_bss->pub.proberesp_ies);
1579 rcu_assign_pointer(nontrans_bss->pub.proberesp_ies, new_ies);
1580 rcu_assign_pointer(nontrans_bss->pub.ies, new_ies);
1581 if (old)
1582 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1583 } else {
1584 old = rcu_access_pointer(nontrans_bss->pub.beacon_ies);
1585 rcu_assign_pointer(nontrans_bss->pub.beacon_ies, new_ies);
1586 rcu_assign_pointer(nontrans_bss->pub.ies, new_ies);
1587 if (old)
1588 kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
1589 }
1590}
1591
1592/* cfg80211_inform_bss_width_frame helper */
1593static struct cfg80211_bss *
1594cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
1595 struct cfg80211_inform_bss *data,
1596 struct ieee80211_mgmt *mgmt, size_t len,
1597 struct cfg80211_bss *trans_bss,
1598 gfp_t gfp)
1218{ 1599{
1219 struct cfg80211_internal_bss tmp = {}, *res; 1600 struct cfg80211_internal_bss tmp = {}, *res;
1220 struct cfg80211_bss_ies *ies; 1601 struct cfg80211_bss_ies *ies;
@@ -1293,6 +1674,50 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
1293 /* cfg80211_bss_update gives us a referenced result */ 1674 /* cfg80211_bss_update gives us a referenced result */
1294 return &res->pub; 1675 return &res->pub;
1295} 1676}
1677
1678struct cfg80211_bss *
1679cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
1680 struct cfg80211_inform_bss *data,
1681 struct ieee80211_mgmt *mgmt, size_t len,
1682 gfp_t gfp)
1683{
1684 struct cfg80211_bss *res;
1685 struct cfg80211_internal_bss *trans_bss, *tmp_bss;
1686 const u8 *ie = mgmt->u.probe_resp.variable;
1687 const struct cfg80211_bss_ies *ies1, *ies2;
1688 size_t ielen = len - offsetof(struct ieee80211_mgmt,
1689 u.probe_resp.variable);
1690
1691 res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
1692 len, NULL, gfp);
1693 if (!res || !cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
1694 return res;
1695
1696 /* process each non-transmitting bss */
1697 cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len, res, gfp);
1698
1699 /* check if the res has other nontransmitting bss which is not
1700 * in MBSSID IE
1701 */
1702 ies1 = rcu_access_pointer(res->ies);
1703 trans_bss = container_of(res, struct cfg80211_internal_bss, pub);
1704 if (!trans_bss)
1705 return res;
1706
1707 /* go through nontrans_list, if the timestamp of the BSS is
1708 * earlier than the timestamp of the transmitting BSS then
1709 * update it
1710 */
1711 list_for_each_entry(tmp_bss, &trans_bss->nontrans_list,
1712 nontrans_list) {
1713 ies2 = rcu_access_pointer(tmp_bss->pub.ies);
1714 if (ies2->tsf < ies1->tsf)
1715 cfg80211_update_notlisted_nontrans(wiphy, tmp_bss,
1716 mgmt, len, gfp);
1717 }
1718
1719 return res;
1720}
1296EXPORT_SYMBOL(cfg80211_inform_bss_frame_data); 1721EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
1297 1722
1298void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) 1723void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
@@ -1330,7 +1755,7 @@ EXPORT_SYMBOL(cfg80211_put_bss);
1330void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) 1755void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1331{ 1756{
1332 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 1757 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
1333 struct cfg80211_internal_bss *bss; 1758 struct cfg80211_internal_bss *bss, *nontrans_bss, *tmp;
1334 1759
1335 if (WARN_ON(!pub)) 1760 if (WARN_ON(!pub))
1336 return; 1761 return;
@@ -1339,6 +1764,13 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
1339 1764
1340 spin_lock_bh(&rdev->bss_lock); 1765 spin_lock_bh(&rdev->bss_lock);
1341 if (!list_empty(&bss->list)) { 1766 if (!list_empty(&bss->list)) {
1767 list_for_each_entry_safe(nontrans_bss, tmp,
1768 &bss->nontrans_list,
1769 nontrans_list) {
1770 if (__cfg80211_unlink_bss(rdev, nontrans_bss))
1771 rdev->bss_generation++;
1772 }
1773
1342 if (__cfg80211_unlink_bss(rdev, bss)) 1774 if (__cfg80211_unlink_bss(rdev, bss))
1343 rdev->bss_generation++; 1775 rdev->bss_generation++;
1344 } 1776 }