diff options
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r-- | net/wireless/scan.c | 117 |
1 files changed, 114 insertions, 3 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index dc23b31594e0..31119e32e092 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -355,8 +355,8 @@ static bool is_mesh(struct cfg80211_bss *a, | |||
355 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; | 355 | sizeof(struct ieee80211_meshconf_ie) - 2) == 0; |
356 | } | 356 | } |
357 | 357 | ||
358 | static int cmp_bss(struct cfg80211_bss *a, | 358 | static int cmp_bss_core(struct cfg80211_bss *a, |
359 | struct cfg80211_bss *b) | 359 | struct cfg80211_bss *b) |
360 | { | 360 | { |
361 | int r; | 361 | int r; |
362 | 362 | ||
@@ -378,7 +378,15 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
378 | b->len_information_elements); | 378 | b->len_information_elements); |
379 | } | 379 | } |
380 | 380 | ||
381 | 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); | ||
382 | if (r) | 390 | if (r) |
383 | return r; | 391 | return r; |
384 | 392 | ||
@@ -389,6 +397,52 @@ static int cmp_bss(struct cfg80211_bss *a, | |||
389 | b->len_information_elements); | 397 | b->len_information_elements); |
390 | } | 398 | } |
391 | 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 | |||
392 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, | 446 | struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, |
393 | struct ieee80211_channel *channel, | 447 | struct ieee80211_channel *channel, |
394 | const u8 *bssid, | 448 | const u8 *bssid, |
@@ -505,6 +559,48 @@ rb_find_bss(struct cfg80211_registered_device *dev, | |||
505 | } | 559 | } |
506 | 560 | ||
507 | 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 * | ||
508 | cfg80211_bss_update(struct cfg80211_registered_device *dev, | 604 | cfg80211_bss_update(struct cfg80211_registered_device *dev, |
509 | struct cfg80211_internal_bss *res) | 605 | struct cfg80211_internal_bss *res) |
510 | { | 606 | { |
@@ -607,6 +703,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, | |||
607 | 703 | ||
608 | kref_put(&res->ref, bss_release); | 704 | kref_put(&res->ref, bss_release); |
609 | } 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 | |||
610 | /* this "consumes" the reference */ | 721 | /* this "consumes" the reference */ |
611 | list_add_tail(&res->list, &dev->bss_list); | 722 | list_add_tail(&res->list, &dev->bss_list); |
612 | rb_insert_bss(dev, res); | 723 | rb_insert_bss(dev, res); |