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.c295
1 files changed, 229 insertions, 66 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 36daacb31788..b7a167984986 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 *
@@ -573,7 +695,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
573 695
574 if (found) { 696 if (found) {
575 found->pub.beacon_interval = tmp->pub.beacon_interval; 697 found->pub.beacon_interval = tmp->pub.beacon_interval;
576 found->pub.tsf = tmp->pub.tsf;
577 found->pub.signal = tmp->pub.signal; 698 found->pub.signal = tmp->pub.signal;
578 found->pub.capability = tmp->pub.capability; 699 found->pub.capability = tmp->pub.capability;
579 found->ts = tmp->ts; 700 found->ts = tmp->ts;
@@ -594,6 +715,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
594 rcu_head); 715 rcu_head);
595 } else if (rcu_access_pointer(tmp->pub.beacon_ies)) { 716 } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
596 const struct cfg80211_bss_ies *old; 717 const struct cfg80211_bss_ies *old;
718 struct cfg80211_internal_bss *bss;
719
720 if (found->pub.hidden_beacon_bss &&
721 !list_empty(&found->hidden_list)) {
722 /*
723 * The found BSS struct is one of the probe
724 * response members of a group, but we're
725 * receiving a beacon (beacon_ies in the tmp
726 * bss is used). This can only mean that the
727 * AP changed its beacon from not having an
728 * SSID to showing it, which is confusing so
729 * drop this information.
730 */
731 goto drop;
732 }
597 733
598 old = rcu_access_pointer(found->pub.beacon_ies); 734 old = rcu_access_pointer(found->pub.beacon_ies);
599 735
@@ -605,6 +741,18 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
605 rcu_assign_pointer(found->pub.ies, 741 rcu_assign_pointer(found->pub.ies,
606 tmp->pub.beacon_ies); 742 tmp->pub.beacon_ies);
607 743
744 /* Assign beacon IEs to all sub entries */
745 list_for_each_entry(bss, &found->hidden_list,
746 hidden_list) {
747 const struct cfg80211_bss_ies *ies;
748
749 ies = rcu_access_pointer(bss->pub.beacon_ies);
750 WARN_ON(ies != old);
751
752 rcu_assign_pointer(bss->pub.beacon_ies,
753 tmp->pub.beacon_ies);
754 }
755
608 if (old) 756 if (old)
609 kfree_rcu((struct cfg80211_bss_ies *)old, 757 kfree_rcu((struct cfg80211_bss_ies *)old,
610 rcu_head); 758 rcu_head);
@@ -614,24 +762,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
614 struct cfg80211_internal_bss *hidden; 762 struct cfg80211_internal_bss *hidden;
615 struct cfg80211_bss_ies *ies; 763 struct cfg80211_bss_ies *ies;
616 764
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 /* 765 /*
636 * create a copy -- the "res" variable that is passed in 766 * create a copy -- the "res" variable that is passed in
637 * is allocated on the stack since it's not needed in the 767 * is allocated on the stack since it's not needed in the
@@ -646,21 +776,51 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
646 ies = (void *)rcu_dereference(tmp->pub.proberesp_ies); 776 ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
647 if (ies) 777 if (ies)
648 kfree_rcu(ies, rcu_head); 778 kfree_rcu(ies, rcu_head);
649 spin_unlock_bh(&dev->bss_lock); 779 goto drop;
650 return NULL;
651 } 780 }
652 memcpy(new, tmp, sizeof(*new)); 781 memcpy(new, tmp, sizeof(*new));
653 kref_init(&new->ref); 782 new->refcount = 1;
783 INIT_LIST_HEAD(&new->hidden_list);
784
785 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
786 hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
787 if (!hidden)
788 hidden = rb_find_bss(dev, tmp,
789 BSS_CMP_HIDE_NUL);
790 if (hidden) {
791 new->pub.hidden_beacon_bss = &hidden->pub;
792 list_add(&new->hidden_list,
793 &hidden->hidden_list);
794 hidden->refcount++;
795 rcu_assign_pointer(new->pub.beacon_ies,
796 hidden->pub.beacon_ies);
797 }
798 } else {
799 /*
800 * Ok so we found a beacon, and don't have an entry. If
801 * it's a beacon with hidden SSID, we might be in for an
802 * expensive search for any probe responses that should
803 * be grouped with this beacon for updates ...
804 */
805 if (!cfg80211_combine_bsses(dev, new)) {
806 kfree(new);
807 goto drop;
808 }
809 }
810
654 list_add_tail(&new->list, &dev->bss_list); 811 list_add_tail(&new->list, &dev->bss_list);
655 rb_insert_bss(dev, new); 812 rb_insert_bss(dev, new);
656 found = new; 813 found = new;
657 } 814 }
658 815
659 dev->bss_generation++; 816 dev->bss_generation++;
817 bss_ref_get(dev, found);
660 spin_unlock_bh(&dev->bss_lock); 818 spin_unlock_bh(&dev->bss_lock);
661 819
662 bss_ref_get(found);
663 return found; 820 return found;
821 drop:
822 spin_unlock_bh(&dev->bss_lock);
823 return NULL;
664} 824}
665 825
666static struct ieee80211_channel * 826static struct ieee80211_channel *
@@ -719,7 +879,6 @@ cfg80211_inform_bss(struct wiphy *wiphy,
719 memcpy(tmp.pub.bssid, bssid, ETH_ALEN); 879 memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
720 tmp.pub.channel = channel; 880 tmp.pub.channel = channel;
721 tmp.pub.signal = signal; 881 tmp.pub.signal = signal;
722 tmp.pub.tsf = tsf;
723 tmp.pub.beacon_interval = beacon_interval; 882 tmp.pub.beacon_interval = beacon_interval;
724 tmp.pub.capability = capability; 883 tmp.pub.capability = capability;
725 /* 884 /*
@@ -734,6 +893,7 @@ cfg80211_inform_bss(struct wiphy *wiphy,
734 if (!ies) 893 if (!ies)
735 return NULL; 894 return NULL;
736 ies->len = ielen; 895 ies->len = ielen;
896 ies->tsf = tsf;
737 memcpy(ies->data, ie, ielen); 897 memcpy(ies->data, ie, ielen);
738 898
739 rcu_assign_pointer(tmp.pub.beacon_ies, ies); 899 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
@@ -790,6 +950,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
790 if (!ies) 950 if (!ies)
791 return NULL; 951 return NULL;
792 ies->len = ielen; 952 ies->len = ielen;
953 ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
793 memcpy(ies->data, mgmt->u.probe_resp.variable, ielen); 954 memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
794 955
795 if (ieee80211_is_probe_resp(mgmt->frame_control)) 956 if (ieee80211_is_probe_resp(mgmt->frame_control))
@@ -801,7 +962,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
801 memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); 962 memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
802 tmp.pub.channel = channel; 963 tmp.pub.channel = channel;
803 tmp.pub.signal = signal; 964 tmp.pub.signal = signal;
804 tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
805 tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); 965 tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
806 tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); 966 tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
807 967
@@ -818,27 +978,35 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
818} 978}
819EXPORT_SYMBOL(cfg80211_inform_bss_frame); 979EXPORT_SYMBOL(cfg80211_inform_bss_frame);
820 980
821void cfg80211_ref_bss(struct cfg80211_bss *pub) 981void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
822{ 982{
983 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
823 struct cfg80211_internal_bss *bss; 984 struct cfg80211_internal_bss *bss;
824 985
825 if (!pub) 986 if (!pub)
826 return; 987 return;
827 988
828 bss = container_of(pub, struct cfg80211_internal_bss, pub); 989 bss = container_of(pub, struct cfg80211_internal_bss, pub);
829 bss_ref_get(bss); 990
991 spin_lock_bh(&dev->bss_lock);
992 bss_ref_get(dev, bss);
993 spin_unlock_bh(&dev->bss_lock);
830} 994}
831EXPORT_SYMBOL(cfg80211_ref_bss); 995EXPORT_SYMBOL(cfg80211_ref_bss);
832 996
833void cfg80211_put_bss(struct cfg80211_bss *pub) 997void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
834{ 998{
999 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
835 struct cfg80211_internal_bss *bss; 1000 struct cfg80211_internal_bss *bss;
836 1001
837 if (!pub) 1002 if (!pub)
838 return; 1003 return;
839 1004
840 bss = container_of(pub, struct cfg80211_internal_bss, pub); 1005 bss = container_of(pub, struct cfg80211_internal_bss, pub);
841 bss_ref_put(bss); 1006
1007 spin_lock_bh(&dev->bss_lock);
1008 bss_ref_put(dev, bss);
1009 spin_unlock_bh(&dev->bss_lock);
842} 1010}
843EXPORT_SYMBOL(cfg80211_put_bss); 1011EXPORT_SYMBOL(cfg80211_put_bss);
844 1012
@@ -854,8 +1022,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
854 1022
855 spin_lock_bh(&dev->bss_lock); 1023 spin_lock_bh(&dev->bss_lock);
856 if (!list_empty(&bss->list)) { 1024 if (!list_empty(&bss->list)) {
857 __cfg80211_unlink_bss(dev, bss); 1025 if (__cfg80211_unlink_bss(dev, bss))
858 dev->bss_generation++; 1026 dev->bss_generation++;
859 } 1027 }
860 spin_unlock_bh(&dev->bss_lock); 1028 spin_unlock_bh(&dev->bss_lock);
861} 1029}
@@ -1124,15 +1292,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1124 1292
1125 rcu_read_lock(); 1293 rcu_read_lock();
1126 ies = rcu_dereference(bss->pub.ies); 1294 ies = rcu_dereference(bss->pub.ies);
1127 if (ies) { 1295 rem = ies->len;
1128 rem = ies->len; 1296 ie = ies->data;
1129 ie = ies->data;
1130 } else {
1131 rem = 0;
1132 ie = NULL;
1133 }
1134 1297
1135 while (ies && rem >= 2) { 1298 while (rem >= 2) {
1136 /* invalid data */ 1299 /* invalid data */
1137 if (ie[1] > rem - 2) 1300 if (ie[1] > rem - 2)
1138 break; 1301 break;
@@ -1245,7 +1408,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1245 if (buf) { 1408 if (buf) {
1246 memset(&iwe, 0, sizeof(iwe)); 1409 memset(&iwe, 0, sizeof(iwe));
1247 iwe.cmd = IWEVCUSTOM; 1410 iwe.cmd = IWEVCUSTOM;
1248 sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); 1411 sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
1249 iwe.u.data.length = strlen(buf); 1412 iwe.u.data.length = strlen(buf);
1250 current_ev = iwe_stream_add_point(info, current_ev, end_buf, 1413 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
1251 &iwe, buf); 1414 &iwe, buf);