aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-01-31 20:06:18 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-11 12:44:57 -0500
commit776b3580178f2065838fa0db0eb7a41b57495c0a (patch)
tree7a12015c647583690755f53482e40de7f57b3a31 /net/wireless
parent1f4ac5a63f897a480fffd0d5c843b03f02c384a5 (diff)
cfg80211: track hidden SSID networks properly
Currently, cfg80211 will copy beacon IEs from a previously received hidden SSID beacon to a probe response entry, if that entry is created after the beacon entry. However, if it is the other way around, or if the beacon is updated, such changes aren't propagated. Fix this by tracking the relation between the probe response and beacon BSS structs in this case. In case drivers have private data stored in a BSS struct and need access to such data from a beacon entry, cfg80211 now provides the hidden_beacon_bss pointer from the probe response entry to the beacon entry. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
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}