diff options
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r-- | net/wireless/scan.c | 508 |
1 files changed, 470 insertions, 38 deletions
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 | ||
164 | static 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 | |||
180 | static 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 | |||
278 | static 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 | |||
301 | static int | ||
302 | cfg80211_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 | |||
162 | static void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev, | 330 | static 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 | } |
519 | EXPORT_SYMBOL(cfg80211_find_vendor_elem); | 687 | EXPORT_SYMBOL(cfg80211_find_vendor_elem); |
520 | 688 | ||
521 | static 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. */ |
1126 | struct cfg80211_bss * | 1272 | static struct cfg80211_bss * |
1127 | cfg80211_inform_bss_data(struct wiphy *wiphy, | 1273 | cfg80211_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 | } |
1209 | EXPORT_SYMBOL(cfg80211_inform_bss_data); | ||
1210 | 1370 | ||
1211 | /* cfg80211_inform_bss_width_frame helper */ | 1371 | static 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 | |||
1212 | struct cfg80211_bss * | 1470 | struct cfg80211_bss * |
1213 | cfg80211_inform_bss_frame_data(struct wiphy *wiphy, | 1471 | cfg80211_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 | } | ||
1487 | EXPORT_SYMBOL(cfg80211_inform_bss_data); | ||
1217 | 1488 | ||
1489 | static void | ||
1490 | cfg80211_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 | |||
1510 | static void | ||
1511 | cfg80211_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 */ | ||
1593 | static struct cfg80211_bss * | ||
1594 | cfg80211_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 | |||
1678 | struct cfg80211_bss * | ||
1679 | cfg80211_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 | } | ||
1296 | EXPORT_SYMBOL(cfg80211_inform_bss_frame_data); | 1721 | EXPORT_SYMBOL(cfg80211_inform_bss_frame_data); |
1297 | 1722 | ||
1298 | void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) | 1723 | void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) |
@@ -1330,7 +1755,7 @@ EXPORT_SYMBOL(cfg80211_put_bss); | |||
1330 | void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) | 1755 | void 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 | } |