aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r--net/wireless/scan.c117
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
358static int cmp_bss(struct cfg80211_bss *a, 358static 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
384static 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
400static 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
392struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, 446struct 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
507static struct cfg80211_internal_bss * 561static struct cfg80211_internal_bss *
562rb_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
584static void
585copy_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
603static struct cfg80211_internal_bss *
508cfg80211_bss_update(struct cfg80211_registered_device *dev, 604cfg80211_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);