|
@@ -355,8 +355,8 @@ static bool is_mesh(struct cfg80211_bss *a,
|
|
|
sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
|
|
|
}
|
|
|
|
|
|
-static int cmp_bss(struct cfg80211_bss *a,
|
|
|
- struct cfg80211_bss *b)
|
|
|
+static int cmp_bss_core(struct cfg80211_bss *a,
|
|
|
+ struct cfg80211_bss *b)
|
|
|
{
|
|
|
int r;
|
|
|
|
|
@@ -378,7 +378,15 @@ static int cmp_bss(struct cfg80211_bss *a,
|
|
|
b->len_information_elements);
|
|
|
}
|
|
|
|
|
|
- r = memcmp(a->bssid, b->bssid, ETH_ALEN);
|
|
|
+ return memcmp(a->bssid, b->bssid, ETH_ALEN);
|
|
|
+}
|
|
|
+
|
|
|
+static int cmp_bss(struct cfg80211_bss *a,
|
|
|
+ struct cfg80211_bss *b)
|
|
|
+{
|
|
|
+ int r;
|
|
|
+
|
|
|
+ r = cmp_bss_core(a, b);
|
|
|
if (r)
|
|
|
return r;
|
|
|
|
|
@@ -389,6 +397,52 @@ static int cmp_bss(struct cfg80211_bss *a,
|
|
|
b->len_information_elements);
|
|
|
}
|
|
|
|
|
|
+static int cmp_hidden_bss(struct cfg80211_bss *a,
|
|
|
+ struct cfg80211_bss *b)
|
|
|
+{
|
|
|
+ const u8 *ie1;
|
|
|
+ const u8 *ie2;
|
|
|
+ int i;
|
|
|
+ int r;
|
|
|
+
|
|
|
+ r = cmp_bss_core(a, b);
|
|
|
+ if (r)
|
|
|
+ return r;
|
|
|
+
|
|
|
+ ie1 = cfg80211_find_ie(WLAN_EID_SSID,
|
|
|
+ a->information_elements,
|
|
|
+ a->len_information_elements);
|
|
|
+ ie2 = cfg80211_find_ie(WLAN_EID_SSID,
|
|
|
+ b->information_elements,
|
|
|
+ b->len_information_elements);
|
|
|
+
|
|
|
+ /* Key comparator must use same algorithm in any rb-tree
|
|
|
+ * search function (order is important), otherwise ordering
|
|
|
+ * of items in the tree is broken and search gives incorrect
|
|
|
+ * results. This code uses same order as cmp_ies() does. */
|
|
|
+
|
|
|
+ /* sort missing IE before (left of) present IE */
|
|
|
+ if (!ie1)
|
|
|
+ return -1;
|
|
|
+ if (!ie2)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /* zero-size SSID is used as an indication of the hidden bss */
|
|
|
+ if (!ie2[1])
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* sort by length first, then by contents */
|
|
|
+ if (ie1[1] != ie2[1])
|
|
|
+ return ie2[1] - ie1[1];
|
|
|
+
|
|
|
+ /* zeroed SSID ie is another indication of a hidden bss */
|
|
|
+ for (i = 0; i < ie2[1]; i++)
|
|
|
+ if (ie2[i + 2])
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
|
|
|
struct ieee80211_channel *channel,
|
|
|
const u8 *bssid,
|
|
@@ -504,6 +558,48 @@ rb_find_bss(struct cfg80211_registered_device *dev,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static struct cfg80211_internal_bss *
|
|
|
+rb_find_hidden_bss(struct cfg80211_registered_device *dev,
|
|
|
+ struct cfg80211_internal_bss *res)
|
|
|
+{
|
|
|
+ struct rb_node *n = dev->bss_tree.rb_node;
|
|
|
+ struct cfg80211_internal_bss *bss;
|
|
|
+ int r;
|
|
|
+
|
|
|
+ while (n) {
|
|
|
+ bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
|
|
|
+ r = cmp_hidden_bss(&res->pub, &bss->pub);
|
|
|
+
|
|
|
+ if (r == 0)
|
|
|
+ return bss;
|
|
|
+ else if (r < 0)
|
|
|
+ n = n->rb_left;
|
|
|
+ else
|
|
|
+ n = n->rb_right;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+copy_hidden_ies(struct cfg80211_internal_bss *res,
|
|
|
+ struct cfg80211_internal_bss *hidden)
|
|
|
+{
|
|
|
+ if (unlikely(res->pub.beacon_ies))
|
|
|
+ return;
|
|
|
+ if (WARN_ON(!hidden->pub.beacon_ies))
|
|
|
+ return;
|
|
|
+
|
|
|
+ res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC);
|
|
|
+ if (unlikely(!res->pub.beacon_ies))
|
|
|
+ return;
|
|
|
+
|
|
|
+ res->beacon_ies_allocated = true;
|
|
|
+ res->pub.len_beacon_ies = hidden->pub.len_beacon_ies;
|
|
|
+ memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies,
|
|
|
+ res->pub.len_beacon_ies);
|
|
|
+}
|
|
|
+
|
|
|
static struct cfg80211_internal_bss *
|
|
|
cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|
|
struct cfg80211_internal_bss *res)
|
|
@@ -607,6 +703,21 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|
|
|
|
|
kref_put(&res->ref, bss_release);
|
|
|
} else {
|
|
|
+ struct cfg80211_internal_bss *hidden;
|
|
|
+
|
|
|
+ /* First check if the beacon is a probe response from
|
|
|
+ * a hidden bss. If so, copy beacon ies (with nullified
|
|
|
+ * ssid) into the probe response bss entry (with real ssid).
|
|
|
+ * It is required basically for PSM implementation
|
|
|
+ * (probe responses do not contain tim ie) */
|
|
|
+
|
|
|
+ /* TODO: The code is not trying to update existing probe
|
|
|
+ * response bss entries when beacon ies are
|
|
|
+ * getting changed. */
|
|
|
+ hidden = rb_find_hidden_bss(dev, res);
|
|
|
+ if (hidden)
|
|
|
+ copy_hidden_ies(res, hidden);
|
|
|
+
|
|
|
/* this "consumes" the reference */
|
|
|
list_add_tail(&res->list, &dev->bss_list);
|
|
|
rb_insert_bss(dev, res);
|