aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.h4
-rw-r--r--net/wireless/scan.c273
2 files changed, 223 insertions, 54 deletions
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 8396f7671c8d..37d70dc2fe82 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -8,7 +8,6 @@
8#include <linux/mutex.h> 8#include <linux/mutex.h>
9#include <linux/list.h> 9#include <linux/list.h>
10#include <linux/netdevice.h> 10#include <linux/netdevice.h>
11#include <linux/kref.h>
12#include <linux/rbtree.h> 11#include <linux/rbtree.h>
13#include <linux/debugfs.h> 12#include <linux/debugfs.h>
14#include <linux/rfkill.h> 13#include <linux/rfkill.h>
@@ -124,9 +123,10 @@ static inline void assert_cfg80211_lock(void)
124 123
125struct cfg80211_internal_bss { 124struct cfg80211_internal_bss {
126 struct list_head list; 125 struct list_head list;
126 struct list_head hidden_list;
127 struct rb_node rbn; 127 struct rb_node rbn;
128 unsigned long ts; 128 unsigned long ts;
129 struct kref ref; 129 unsigned long refcount;
130 atomic_t hold; 130 atomic_t hold;
131 131
132 /* must be last because of priv member */ 132 /* must be last because of priv member */
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index dacb44ac2f74..5e0983d60428 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -19,46 +19,124 @@
19#include "wext-compat.h" 19#include "wext-compat.h"
20#include "rdev-ops.h" 20#include "rdev-ops.h"
21 21
22/**
23 * DOC: BSS tree/list structure
24 *
25 * At the top level, the BSS list is kept in both a list in each
26 * registered device (@bss_list) as well as an RB-tree for faster
27 * lookup. In the RB-tree, entries can be looked up using their
28 * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID
29 * for other BSSes.
30 *
31 * Due to the possibility of hidden SSIDs, there's a second level
32 * structure, the "hidden_list" and "hidden_beacon_bss" pointer.
33 * The hidden_list connects all BSSes belonging to a single AP
34 * that has a hidden SSID, and connects beacon and probe response
35 * entries. For a probe response entry for a hidden SSID, the
36 * hidden_beacon_bss pointer points to the BSS struct holding the
37 * beacon's information.
38 *
39 * Reference counting is done for all these references except for
40 * the hidden_list, so that a beacon BSS struct that is otherwise
41 * not referenced has one reference for being on the bss_list and
42 * one for each probe response entry that points to it using the
43 * hidden_beacon_bss pointer. When a BSS struct that has such a
44 * pointer is get/put, the refcount update is also propagated to
45 * the referenced struct, this ensure that it cannot get removed
46 * while somebody is using the probe response version.
47 *
48 * Note that the hidden_beacon_bss pointer never changes, due to
49 * the reference counting. Therefore, no locking is needed for
50 * it.
51 *
52 * Also note that the hidden_beacon_bss pointer is only relevant
53 * if the driver uses something other than the IEs, e.g. private
54 * data stored stored in the BSS struct, since the beacon IEs are
55 * also linked into the probe response struct.
56 */
57
22#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) 58#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
23 59
24static void bss_release(struct kref *ref) 60static void bss_free(struct cfg80211_internal_bss *bss)
25{ 61{
26 struct cfg80211_bss_ies *ies; 62 struct cfg80211_bss_ies *ies;
27 struct cfg80211_internal_bss *bss;
28
29 bss = container_of(ref, struct cfg80211_internal_bss, ref);
30 63
31 if (WARN_ON(atomic_read(&bss->hold))) 64 if (WARN_ON(atomic_read(&bss->hold)))
32 return; 65 return;
33 66
34 ies = (void *)rcu_access_pointer(bss->pub.beacon_ies); 67 ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
35 if (ies) 68 if (ies && !bss->pub.hidden_beacon_bss)
36 kfree_rcu(ies, rcu_head); 69 kfree_rcu(ies, rcu_head);
37 ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies); 70 ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
38 if (ies) 71 if (ies)
39 kfree_rcu(ies, rcu_head); 72 kfree_rcu(ies, rcu_head);
40 73
74 /*
75 * This happens when the module is removed, it doesn't
76 * really matter any more save for completeness
77 */
78 if (!list_empty(&bss->hidden_list))
79 list_del(&bss->hidden_list);
80
41 kfree(bss); 81 kfree(bss);
42} 82}
43 83
44static inline void bss_ref_get(struct cfg80211_internal_bss *bss) 84static inline void bss_ref_get(struct cfg80211_registered_device *dev,
85 struct cfg80211_internal_bss *bss)
45{ 86{
46 kref_get(&bss->ref); 87 lockdep_assert_held(&dev->bss_lock);
88
89 bss->refcount++;
90 if (bss->pub.hidden_beacon_bss) {
91 bss = container_of(bss->pub.hidden_beacon_bss,
92 struct cfg80211_internal_bss,
93 pub);
94 bss->refcount++;
95 }
47} 96}
48 97
49static inline void bss_ref_put(struct cfg80211_internal_bss *bss) 98static inline void bss_ref_put(struct cfg80211_registered_device *dev,
99 struct cfg80211_internal_bss *bss)
50{ 100{
51 kref_put(&bss->ref, bss_release); 101 lockdep_assert_held(&dev->bss_lock);
102
103 if (bss->pub.hidden_beacon_bss) {
104 struct cfg80211_internal_bss *hbss;
105 hbss = container_of(bss->pub.hidden_beacon_bss,
106 struct cfg80211_internal_bss,
107 pub);
108 hbss->refcount--;
109 if (hbss->refcount == 0)
110 bss_free(hbss);
111 }
112 bss->refcount--;
113 if (bss->refcount == 0)
114 bss_free(bss);
52} 115}
53 116
54static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, 117static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
55 struct cfg80211_internal_bss *bss) 118 struct cfg80211_internal_bss *bss)
56{ 119{
57 lockdep_assert_held(&dev->bss_lock); 120 lockdep_assert_held(&dev->bss_lock);
58 121
122 if (!list_empty(&bss->hidden_list)) {
123 /*
124 * don't remove the beacon entry if it has
125 * probe responses associated with it
126 */
127 if (!bss->pub.hidden_beacon_bss)
128 return false;
129 /*
130 * if it's a probe response entry break its
131 * link to the other entries in the group
132 */
133 list_del_init(&bss->hidden_list);
134 }
135
59 list_del_init(&bss->list); 136 list_del_init(&bss->list);
60 rb_erase(&bss->rbn, &dev->bss_tree); 137 rb_erase(&bss->rbn, &dev->bss_tree);
61 bss_ref_put(bss); 138 bss_ref_put(dev, bss);
139 return true;
62} 140}
63 141
64static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, 142static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
@@ -75,8 +153,8 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
75 if (!time_after(expire_time, bss->ts)) 153 if (!time_after(expire_time, bss->ts))
76 continue; 154 continue;
77 155
78 __cfg80211_unlink_bss(dev, bss); 156 if (__cfg80211_unlink_bss(dev, bss))
79 expired = true; 157 expired = true;
80 } 158 }
81 159
82 if (expired) 160 if (expired)
@@ -466,7 +544,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
466 continue; 544 continue;
467 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { 545 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
468 res = bss; 546 res = bss;
469 bss_ref_get(res); 547 bss_ref_get(dev, res);
470 break; 548 break;
471 } 549 }
472 } 550 }
@@ -532,23 +610,67 @@ rb_find_bss(struct cfg80211_registered_device *dev,
532 return NULL; 610 return NULL;
533} 611}
534 612
535static void 613static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
536copy_hidden_ies(struct cfg80211_internal_bss *res, 614 struct cfg80211_internal_bss *new)
537 struct cfg80211_internal_bss *hidden)
538{ 615{
539 const struct cfg80211_bss_ies *ies; 616 const struct cfg80211_bss_ies *ies;
617 struct cfg80211_internal_bss *bss;
618 const u8 *ie;
619 int i, ssidlen;
620 u8 fold = 0;
540 621
541 if (rcu_access_pointer(res->pub.beacon_ies)) 622 ies = rcu_access_pointer(new->pub.beacon_ies);
542 return;
543
544 ies = rcu_access_pointer(hidden->pub.beacon_ies);
545 if (WARN_ON(!ies)) 623 if (WARN_ON(!ies))
546 return; 624 return false;
547 625
548 ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC); 626 ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
549 if (unlikely(!ies)) 627 if (!ie) {
550 return; 628 /* nothing to do */
551 rcu_assign_pointer(res->pub.beacon_ies, ies); 629 return true;
630 }
631
632 ssidlen = ie[1];
633 for (i = 0; i < ssidlen; i++)
634 fold |= ie[2 + i];
635
636 if (fold) {
637 /* not a hidden SSID */
638 return true;
639 }
640
641 /* This is the bad part ... */
642
643 list_for_each_entry(bss, &dev->bss_list, list) {
644 if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
645 continue;
646 if (bss->pub.channel != new->pub.channel)
647 continue;
648 if (rcu_access_pointer(bss->pub.beacon_ies))
649 continue;
650 ies = rcu_access_pointer(bss->pub.ies);
651 if (!ies)
652 continue;
653 ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
654 if (!ie)
655 continue;
656 if (ssidlen && ie[1] != ssidlen)
657 continue;
658 /* that would be odd ... */
659 if (bss->pub.beacon_ies)
660 continue;
661 if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
662 continue;
663 if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
664 list_del(&bss->hidden_list);
665 /* combine them */
666 list_add(&bss->hidden_list, &new->hidden_list);
667 bss->pub.hidden_beacon_bss = &new->pub;
668 new->refcount += bss->refcount;
669 rcu_assign_pointer(bss->pub.beacon_ies,
670 new->pub.beacon_ies);
671 }
672
673 return true;
552} 674}
553 675
554static struct cfg80211_internal_bss * 676static struct cfg80211_internal_bss *
@@ -594,6 +716,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
594 rcu_head); 716 rcu_head);
595 } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { 717 } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
596 const struct cfg80211_bss_ies *old; 718 const struct cfg80211_bss_ies *old;
719 struct cfg80211_internal_bss *bss;
720
721 if (found->pub.hidden_beacon_bss &&
722 !list_empty(&found->hidden_list)) {
723 /*
724 * The found BSS struct is one of the probe
725 * response members of a group, but we're
726 * receiving a beacon (beacon_ies in the tmp
727 * bss is used). This can only mean that the
728 * AP changed its beacon from not having an
729 * SSID to showing it, which is confusing so
730 * drop this information.
731 */
732 goto drop;
733 }
597 734
598 old = rcu_access_pointer(found->pub.beacon_ies); 735 old = rcu_access_pointer(found->pub.beacon_ies);
599 736
@@ -605,6 +742,18 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
605 rcu_assign_pointer(found->pub.ies, 742 rcu_assign_pointer(found->pub.ies,
606 tmp->pub.beacon_ies); 743 tmp->pub.beacon_ies);
607 744
745 /* Assign beacon IEs to all sub entries */
746 list_for_each_entry(bss, &found->hidden_list,
747 hidden_list) {
748 const struct cfg80211_bss_ies *ies;
749
750 ies = rcu_access_pointer(bss->pub.beacon_ies);
751 WARN_ON(ies != old);
752
753 rcu_assign_pointer(bss->pub.beacon_ies,
754 tmp->pub.beacon_ies);
755 }
756
608 if (old) 757 if (old)
609 kfree_rcu((struct cfg80211_bss_ies *)old, 758 kfree_rcu((struct cfg80211_bss_ies *)old,
610 rcu_head); 759 rcu_head);
@@ -614,24 +763,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
614 struct cfg80211_internal_bss *hidden; 763 struct cfg80211_internal_bss *hidden;
615 struct cfg80211_bss_ies *ies; 764 struct cfg80211_bss_ies *ies;
616 765
617 /* First check if the beacon is a probe response from
618 * a hidden bss. If so, copy beacon ies (with nullified
619 * ssid) into the probe response bss entry (with real ssid).
620 * It is required basically for PSM implementation
621 * (probe responses do not contain tim ie) */
622
623 /* TODO: The code is not trying to update existing probe
624 * response bss entries when beacon ies are
625 * getting changed. */
626 hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
627 if (hidden) {
628 copy_hidden_ies(tmp, hidden);
629 } else {
630 hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_NUL);
631 if (hidden)
632 copy_hidden_ies(tmp, hidden);
633 }
634
635 /* 766 /*
636 * create a copy -- the "res" variable that is passed in 767 * create a copy -- the "res" variable that is passed in
637 * is allocated on the stack since it's not needed in the 768 * is allocated on the stack since it's not needed in the
@@ -646,21 +777,51 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
646 ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); 777 ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
647 if (ies) 778 if (ies)
648 kfree_rcu(ies, rcu_head); 779 kfree_rcu(ies, rcu_head);
649 spin_unlock_bh(&dev->bss_lock); 780 goto drop;
650 return NULL;
651 } 781 }
652 memcpy(new, tmp, sizeof(*new)); 782 memcpy(new, tmp, sizeof(*new));
653 kref_init(&new->ref); 783 new->refcount = 1;
784 INIT_LIST_HEAD(&new->hidden_list);
785
786 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
787 hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
788 if (!hidden)
789 hidden = rb_find_bss(dev, tmp,
790 BSS_CMP_HIDE_NUL);
791 if (hidden) {
792 new->pub.hidden_beacon_bss = &hidden->pub;
793 list_add(&new->hidden_list,
794 &hidden->hidden_list);
795 hidden->refcount++;
796 rcu_assign_pointer(new->pub.beacon_ies,
797 hidden->pub.beacon_ies);
798 }
799 } else {
800 /*
801 * Ok so we found a beacon, and don't have an entry. If
802 * it's a beacon with hidden SSID, we might be in for an
803 * expensive search for any probe responses that should
804 * be grouped with this beacon for updates ...
805 */
806 if (!cfg80211_combine_bsses(dev, new)) {
807 kfree(new);
808 goto drop;
809 }
810 }
811
654 list_add_tail(&new->list, &dev->bss_list); 812 list_add_tail(&new->list, &dev->bss_list);
655 rb_insert_bss(dev, new); 813 rb_insert_bss(dev, new);
656 found = new; 814 found = new;
657 } 815 }
658 816
659 dev->bss_generation++; 817 dev->bss_generation++;
818 bss_ref_get(dev, found);
660 spin_unlock_bh(&dev->bss_lock); 819 spin_unlock_bh(&dev->bss_lock);
661 820
662 bss_ref_get(found);
663 return found; 821 return found;
822 drop:
823 spin_unlock_bh(&dev->bss_lock);
824 return NULL;
664} 825}
665 826
666static struct ieee80211_channel * 827static struct ieee80211_channel *
@@ -820,25 +981,33 @@ EXPORT_SYMBOL(cfg80211_inform_bss_frame);
820 981
821void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) 982void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
822{ 983{
984 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
823 struct cfg80211_internal_bss *bss; 985 struct cfg80211_internal_bss *bss;
824 986
825 if (!pub) 987 if (!pub)
826 return; 988 return;
827 989
828 bss = container_of(pub, struct cfg80211_internal_bss, pub); 990 bss = container_of(pub, struct cfg80211_internal_bss, pub);
829 bss_ref_get(bss); 991
992 spin_lock_bh(&dev->bss_lock);
993 bss_ref_get(dev, bss);
994 spin_unlock_bh(&dev->bss_lock);
830} 995}
831EXPORT_SYMBOL(cfg80211_ref_bss); 996EXPORT_SYMBOL(cfg80211_ref_bss);
832 997
833void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) 998void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
834{ 999{
1000 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
835 struct cfg80211_internal_bss *bss; 1001 struct cfg80211_internal_bss *bss;
836 1002
837 if (!pub) 1003 if (!pub)
838 return; 1004 return;
839 1005
840 bss = container_of(pub, struct cfg80211_internal_bss, pub); 1006 bss = container_of(pub, struct cfg80211_internal_bss, pub);
841 bss_ref_put(bss); 1007
1008 spin_lock_bh(&dev->bss_lock);
1009 bss_ref_put(dev, bss);
1010 spin_unlock_bh(&dev->bss_lock);
842} 1011}
843EXPORT_SYMBOL(cfg80211_put_bss); 1012EXPORT_SYMBOL(cfg80211_put_bss);
844 1013
@@ -854,8 +1023,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
854 1023
855 spin_lock_bh(&dev->bss_lock); 1024 spin_lock_bh(&dev->bss_lock);
856 if (!list_empty(&bss->list)) { 1025 if (!list_empty(&bss->list)) {
857 __cfg80211_unlink_bss(dev, bss); 1026 if (__cfg80211_unlink_bss(dev, bss))
858 dev->bss_generation++; 1027 dev->bss_generation++;
859 } 1028 }
860 spin_unlock_bh(&dev->bss_lock); 1029 spin_unlock_bh(&dev->bss_lock);
861} 1030}