|
@@ -100,8 +100,10 @@ static void bss_release(struct kref *ref)
|
|
|
if (bss->pub.free_priv)
|
|
|
bss->pub.free_priv(&bss->pub);
|
|
|
|
|
|
- if (bss->ies_allocated)
|
|
|
- kfree(bss->pub.information_elements);
|
|
|
+ if (bss->beacon_ies_allocated)
|
|
|
+ kfree(bss->pub.beacon_ies);
|
|
|
+ if (bss->proberesp_ies_allocated)
|
|
|
+ kfree(bss->pub.proberesp_ies);
|
|
|
|
|
|
BUG_ON(atomic_read(&bss->hold));
|
|
|
|
|
@@ -375,8 +377,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
|
|
|
|
|
|
static struct cfg80211_internal_bss *
|
|
|
cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|
|
- struct cfg80211_internal_bss *res,
|
|
|
- bool overwrite)
|
|
|
+ struct cfg80211_internal_bss *res)
|
|
|
{
|
|
|
struct cfg80211_internal_bss *found = NULL;
|
|
|
const u8 *meshid, *meshcfg;
|
|
@@ -418,28 +419,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|
|
found->pub.capability = res->pub.capability;
|
|
|
found->ts = res->ts;
|
|
|
|
|
|
- /* overwrite IEs */
|
|
|
- if (overwrite) {
|
|
|
+ /* Update IEs */
|
|
|
+ if (res->pub.proberesp_ies) {
|
|
|
size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
|
|
|
- size_t ielen = res->pub.len_information_elements;
|
|
|
+ size_t ielen = res->pub.len_proberesp_ies;
|
|
|
+
|
|
|
+ if (found->pub.proberesp_ies &&
|
|
|
+ !found->proberesp_ies_allocated &&
|
|
|
+ ksize(found) >= used + ielen) {
|
|
|
+ memcpy(found->pub.proberesp_ies,
|
|
|
+ res->pub.proberesp_ies, ielen);
|
|
|
+ found->pub.len_proberesp_ies = ielen;
|
|
|
+ } else {
|
|
|
+ u8 *ies = found->pub.proberesp_ies;
|
|
|
+
|
|
|
+ if (found->proberesp_ies_allocated)
|
|
|
+ ies = krealloc(ies, ielen, GFP_ATOMIC);
|
|
|
+ else
|
|
|
+ ies = kmalloc(ielen, GFP_ATOMIC);
|
|
|
+
|
|
|
+ if (ies) {
|
|
|
+ memcpy(ies, res->pub.proberesp_ies,
|
|
|
+ ielen);
|
|
|
+ found->proberesp_ies_allocated = true;
|
|
|
+ found->pub.proberesp_ies = ies;
|
|
|
+ found->pub.len_proberesp_ies = ielen;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (!found->ies_allocated && ksize(found) >= used + ielen) {
|
|
|
- memcpy(found->pub.information_elements,
|
|
|
- res->pub.information_elements, ielen);
|
|
|
- found->pub.len_information_elements = ielen;
|
|
|
+ /* Override possible earlier Beacon frame IEs */
|
|
|
+ found->pub.information_elements =
|
|
|
+ found->pub.proberesp_ies;
|
|
|
+ found->pub.len_information_elements =
|
|
|
+ found->pub.len_proberesp_ies;
|
|
|
+ }
|
|
|
+ if (res->pub.beacon_ies) {
|
|
|
+ size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
|
|
|
+ size_t ielen = res->pub.len_beacon_ies;
|
|
|
+
|
|
|
+ if (found->pub.beacon_ies &&
|
|
|
+ !found->beacon_ies_allocated &&
|
|
|
+ ksize(found) >= used + ielen) {
|
|
|
+ memcpy(found->pub.beacon_ies,
|
|
|
+ res->pub.beacon_ies, ielen);
|
|
|
+ found->pub.len_beacon_ies = ielen;
|
|
|
} else {
|
|
|
- u8 *ies = found->pub.information_elements;
|
|
|
+ u8 *ies = found->pub.beacon_ies;
|
|
|
|
|
|
- if (found->ies_allocated)
|
|
|
+ if (found->beacon_ies_allocated)
|
|
|
ies = krealloc(ies, ielen, GFP_ATOMIC);
|
|
|
else
|
|
|
ies = kmalloc(ielen, GFP_ATOMIC);
|
|
|
|
|
|
if (ies) {
|
|
|
- memcpy(ies, res->pub.information_elements, ielen);
|
|
|
- found->ies_allocated = true;
|
|
|
- found->pub.information_elements = ies;
|
|
|
- found->pub.len_information_elements = ielen;
|
|
|
+ memcpy(ies, res->pub.beacon_ies,
|
|
|
+ ielen);
|
|
|
+ found->beacon_ies_allocated = true;
|
|
|
+ found->pub.beacon_ies = ies;
|
|
|
+ found->pub.len_beacon_ies = ielen;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -489,14 +526,26 @@ cfg80211_inform_bss(struct wiphy *wiphy,
|
|
|
res->pub.tsf = timestamp;
|
|
|
res->pub.beacon_interval = beacon_interval;
|
|
|
res->pub.capability = capability;
|
|
|
- /* point to after the private area */
|
|
|
- res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
|
|
|
- memcpy(res->pub.information_elements, ie, ielen);
|
|
|
- res->pub.len_information_elements = ielen;
|
|
|
+ /*
|
|
|
+ * Since we do not know here whether the IEs are from a Beacon or Probe
|
|
|
+ * Response frame, we need to pick one of the options and only use it
|
|
|
+ * with the driver that does not provide the full Beacon/Probe Response
|
|
|
+ * frame. Use Beacon frame pointer to avoid indicating that this should
|
|
|
+ * override the information_elements pointer should we have received an
|
|
|
+ * earlier indication of Probe Response data.
|
|
|
+ *
|
|
|
+ * The initial buffer for the IEs is allocated with the BSS entry and
|
|
|
+ * is located after the private area.
|
|
|
+ */
|
|
|
+ res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
|
|
|
+ memcpy(res->pub.beacon_ies, ie, ielen);
|
|
|
+ res->pub.len_beacon_ies = ielen;
|
|
|
+ res->pub.information_elements = res->pub.beacon_ies;
|
|
|
+ res->pub.len_information_elements = res->pub.len_beacon_ies;
|
|
|
|
|
|
kref_init(&res->ref);
|
|
|
|
|
|
- res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0);
|
|
|
+ res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
|
|
|
if (!res)
|
|
|
return NULL;
|
|
|
|
|
@@ -517,7 +566,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
|
|
|
struct cfg80211_internal_bss *res;
|
|
|
size_t ielen = len - offsetof(struct ieee80211_mgmt,
|
|
|
u.probe_resp.variable);
|
|
|
- bool overwrite;
|
|
|
size_t privsz = wiphy->bss_priv_size;
|
|
|
|
|
|
if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
|
|
@@ -538,16 +586,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
|
|
|
res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
|
|
|
res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
|
|
|
res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
|
|
|
- /* point to after the private area */
|
|
|
- res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
|
|
|
- memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen);
|
|
|
- res->pub.len_information_elements = ielen;
|
|
|
+ /*
|
|
|
+ * The initial buffer for the IEs is allocated with the BSS entry and
|
|
|
+ * is located after the private area.
|
|
|
+ */
|
|
|
+ if (ieee80211_is_probe_resp(mgmt->frame_control)) {
|
|
|
+ res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
|
|
|
+ memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
|
|
|
+ ielen);
|
|
|
+ res->pub.len_proberesp_ies = ielen;
|
|
|
+ res->pub.information_elements = res->pub.proberesp_ies;
|
|
|
+ res->pub.len_information_elements = res->pub.len_proberesp_ies;
|
|
|
+ } else {
|
|
|
+ res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
|
|
|
+ memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
|
|
|
+ res->pub.len_beacon_ies = ielen;
|
|
|
+ res->pub.information_elements = res->pub.beacon_ies;
|
|
|
+ res->pub.len_information_elements = res->pub.len_beacon_ies;
|
|
|
+ }
|
|
|
|
|
|
kref_init(&res->ref);
|
|
|
|
|
|
- overwrite = ieee80211_is_probe_resp(mgmt->frame_control);
|
|
|
-
|
|
|
- res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
|
|
|
+ res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
|
|
|
if (!res)
|
|
|
return NULL;
|
|
|
|