diff options
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r-- | net/wireless/scan.c | 130 |
1 files changed, 122 insertions, 8 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0fb14241040..31119e32e09 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -259,17 +259,20 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) | |||
259 | { | 259 | { |
260 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); | 260 | const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); |
261 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); | 261 | const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); |
262 | int r; | ||
263 | 262 | ||
263 | /* equal if both missing */ | ||
264 | if (!ie1 && !ie2) | 264 | if (!ie1 && !ie2) |
265 | return 0; | 265 | return 0; |
266 | if (!ie1 || !ie2) | 266 | /* sort missing IE before (left of) present IE */ |
267 | if (!ie1) | ||
267 | return -1; | 268 | return -1; |
269 | if (!ie2) | ||
270 | return 1; | ||
268 | 271 | ||
269 | r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1])); | 272 | /* sort by length first, then by contents */ |
270 | if (r == 0 && ie1[1] != ie2[1]) | 273 | if (ie1[1] != ie2[1]) |
271 | return ie2[1] - ie1[1]; | 274 | return ie2[1] - ie1[1]; |
272 | return r; | 275 | return memcmp(ie1 + 2, ie2 + 2, ie1[1]); |
273 | } | 276 | } |
274 | 277 | ||
275 | static bool is_bss(struct cfg80211_bss *a, | 278 | static bool is_bss(struct cfg80211_bss *a, |
@@ -352,8 +355,8 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
352 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; | 355 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; |
353 | } | 356 | } |
354 | 357 | ||
355 | static int cmp_bss(struct cfg80211_bss *a, | 358 | static int cmp_bss_core(struct cfg80211_bss *a, |
356 | struct cfg80211_bss *b) | 359 | struct cfg80211_bss *b) |
357 | { | 360 | { |
358 | int r; | 361 | int r; |
359 | 362 | ||
@@ -375,7 +378,15 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
375 | b->len_information_elements); | 378 | b->len_information_elements); |
376 | } | 379 | } |
377 | 380 | ||
378 | r = memcmp(a->bssid, b->bssid, ETH_ALEN); | 381 | return memcmp(a->bssid, b->bssid, ETH_ALEN); |
382 | } | ||
383 | |||
384 | static int cmp_bss(struct cfg80211_bss *a, | ||
385 | struct cfg80211_bss *b) | ||
386 | { | ||
387 | int r; | ||
388 | |||
389 | r = cmp_bss_core(a, b); | ||
379 | if (r) | 390 | if (r) |
380 | return r; | 391 | return r; |
381 | 392 | ||
@@ -386,6 +397,52 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
386 | b->len_information_elements); | 397 | b->len_information_elements); |
387 | } | 398 | } |
388 | 399 | ||
400 | static int cmp_hidden_bss(struct cfg80211_bss *a, | ||
401 | struct cfg80211_bss *b) | ||
402 | { | ||
403 | const u8 *ie1; | ||
404 | const u8 *ie2; | ||
405 | int i; | ||
406 | int r; | ||
407 | |||
408 | r = cmp_bss_core(a, b); | ||
409 | if (r) | ||
410 | return r; | ||
411 | |||
412 | ie1 = cfg80211_find_ie(WLAN_EID_SSID, | ||
413 | a->information_elements, | ||
414 | a->len_information_elements); | ||
415 | ie2 = cfg80211_find_ie(WLAN_EID_SSID, | ||
416 | b->information_elements, | ||
417 | b->len_information_elements); | ||
418 | |||
419 | /* Key comparator must use same algorithm in any rb-tree | ||
420 | * search function (order is important), otherwise ordering | ||
421 | * of items in the tree is broken and search gives incorrect | ||
422 | * results. This code uses same order as cmp_ies() does. */ | ||
423 | |||
424 | /* sort missing IE before (left of) present IE */ | ||
425 | if (!ie1) | ||
426 | return -1; | ||
427 | if (!ie2) | ||
428 | return 1; | ||
429 | |||
430 | /* zero-size SSID is used as an indication of the hidden bss */ | ||
431 | if (!ie2[1]) | ||
432 | return 0; | ||
433 | |||
434 | /* sort by length first, then by contents */ | ||
435 | if (ie1[1] != ie2[1]) | ||
436 | return ie2[1] - ie1[1]; | ||
437 | |||
438 | /* zeroed SSID ie is another indication of a hidden bss */ | ||
439 | for (i = 0; i < ie2[1]; i++) | ||
440 | if (ie2[i + 2]) | ||
441 | return -1; | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
389 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | 446 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, |
390 | struct ieee80211_channel *channel, | 447 | struct ieee80211_channel *channel, |
391 | const u8 *bssid, | 448 | const u8 *bssid, |
@@ -502,6 +559,48 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
502 | } | 559 | } |
503 | 560 | ||
504 | static struct cfg80211_internal_bss * | 561 | static struct cfg80211_internal_bss * |
562 | rb_find_hidden_bss(struct cfg80211_registered_device *dev, | ||
563 | struct cfg80211_internal_bss *res) | ||
564 | { | ||
565 | struct rb_node *n = dev->bss_tree.rb_node; | ||
566 | struct cfg80211_internal_bss *bss; | ||
567 | int r; | ||
568 | |||
569 | while (n) { | ||
570 | bss = rb_entry(n, struct cfg80211_internal_bss, rbn); | ||
571 | r = cmp_hidden_bss(&res->pub, &bss->pub); | ||
572 | |||
573 | if (r == 0) | ||
574 | return bss; | ||
575 | else if (r < 0) | ||
576 | n = n->rb_left; | ||
577 | else | ||
578 | n = n->rb_right; | ||
579 | } | ||
580 | |||
581 | return NULL; | ||
582 | } | ||
583 | |||
584 | static void | ||
585 | copy_hidden_ies(struct cfg80211_internal_bss *res, | ||
586 | struct cfg80211_internal_bss *hidden) | ||
587 | { | ||
588 | if (unlikely(res->pub.beacon_ies)) | ||
589 | return; | ||
590 | if (WARN_ON(!hidden->pub.beacon_ies)) | ||
591 | return; | ||
592 | |||
593 | res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); | ||
594 | if (unlikely(!res->pub.beacon_ies)) | ||
595 | return; | ||
596 | |||
597 | res->beacon_ies_allocated = true; | ||
598 | res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; | ||
599 | memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, | ||
600 | res->pub.len_beacon_ies); | ||
601 | } | ||
602 | |||
603 | static struct cfg80211_internal_bss * | ||
505 | cfg80211_bss_update(struct cfg80211_registered_device *dev, | 604 | cfg80211_bss_update(struct cfg80211_registered_device *dev, |
506 | struct cfg80211_internal_bss *res) | 605 | struct cfg80211_internal_bss *res) |
507 | { | 606 | { |
@@ -604,6 +703,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
604 | 703 | ||
605 | kref_put(&res->ref, bss_release); | 704 | kref_put(&res->ref, bss_release); |
606 | } else { | 705 | } else { |
706 | struct cfg80211_internal_bss *hidden; | ||
707 | |||
708 | /* First check if the beacon is a probe response from | ||
709 | * a hidden bss. If so, copy beacon ies (with nullified | ||
710 | * ssid) into the probe response bss entry (with real ssid). | ||
711 | * It is required basically for PSM implementation | ||
712 | * (probe responses do not contain tim ie) */ | ||
713 | |||
714 | /* TODO: The code is not trying to update existing probe | ||
715 | * response bss entries when beacon ies are | ||
716 | * getting changed. */ | ||
717 | hidden = rb_find_hidden_bss(dev, res); | ||
718 | if (hidden) | ||
719 | copy_hidden_ies(res, hidden); | ||
720 | |||
607 | /* this "consumes" the reference */ | 721 | /* this "consumes" the reference */ |
608 | list_add_tail(&res->list, &dev->bss_list); | 722 | list_add_tail(&res->list, &dev->bss_list); |
609 | rb_insert_bss(dev, res); | 723 | rb_insert_bss(dev, res); |