mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 21:24:08 +08:00
cfg80211: support profile split between elements
Since an element is limited to 255 octets, a profile may be split split to several elements. Support the split as defined in the 11ax draft 3. Detect legacy split and print a net-rate limited warning, since there is no ROI in supporting this probably non-existent split. Signed-off-by: Sara Sharon <sara.sharon@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
671042a4fb
commit
fe806e4992
@ -5542,6 +5542,20 @@ static inline void cfg80211_gen_new_bssid(const u8 *bssid, u8 max_bssid,
|
|||||||
bool cfg80211_is_element_inherited(const struct element *element,
|
bool cfg80211_is_element_inherited(const struct element *element,
|
||||||
const struct element *non_inherit_element);
|
const struct element *non_inherit_element);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cfg80211_merge_profile - merges a MBSSID profile if it is split between IEs
|
||||||
|
* @ie: ies
|
||||||
|
* @ielen: length of IEs
|
||||||
|
* @mbssid_elem: current MBSSID element
|
||||||
|
* @sub_elem: current MBSSID subelement (profile)
|
||||||
|
* @merged_ie: location of the merged profile
|
||||||
|
* @max_copy_len: max merged profile length
|
||||||
|
*/
|
||||||
|
size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
|
||||||
|
const struct element *mbssid_elem,
|
||||||
|
const struct element *sub_elem,
|
||||||
|
u8 **merged_ie, size_t max_copy_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum cfg80211_bss_frame_type - frame type that the BSS data came from
|
* enum cfg80211_bss_frame_type - frame type that the BSS data came from
|
||||||
* @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
|
* @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
|
||||||
|
@ -1456,6 +1456,78 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
|
|||||||
return &res->pub;
|
return &res->pub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct element
|
||||||
|
*cfg80211_get_profile_continuation(const u8 *ie, size_t ielen,
|
||||||
|
const struct element *mbssid_elem,
|
||||||
|
const struct element *sub_elem)
|
||||||
|
{
|
||||||
|
const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen;
|
||||||
|
const struct element *next_mbssid;
|
||||||
|
const struct element *next_sub;
|
||||||
|
|
||||||
|
next_mbssid = cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
|
||||||
|
mbssid_end,
|
||||||
|
ielen - (mbssid_end - ie));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If is is not the last subelement in current MBSSID IE or there isn't
|
||||||
|
* a next MBSSID IE - profile is complete.
|
||||||
|
*/
|
||||||
|
if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
|
||||||
|
!next_mbssid)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* For any length error, just return NULL */
|
||||||
|
|
||||||
|
if (next_mbssid->datalen < 4)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
next_sub = (void *)&next_mbssid->data[1];
|
||||||
|
|
||||||
|
if (next_mbssid->data + next_mbssid->datalen <
|
||||||
|
next_sub->data + next_sub->datalen)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (next_sub->id != 0 || next_sub->datalen < 2)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the first element in the next sub element is a start
|
||||||
|
* of a new profile
|
||||||
|
*/
|
||||||
|
return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
|
||||||
|
NULL : next_mbssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
|
||||||
|
const struct element *mbssid_elem,
|
||||||
|
const struct element *sub_elem,
|
||||||
|
u8 **merged_ie, size_t max_copy_len)
|
||||||
|
{
|
||||||
|
size_t copied_len = sub_elem->datalen;
|
||||||
|
const struct element *next_mbssid;
|
||||||
|
|
||||||
|
if (sub_elem->datalen > max_copy_len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memcpy(*merged_ie, sub_elem->data, sub_elem->datalen);
|
||||||
|
|
||||||
|
while ((next_mbssid = cfg80211_get_profile_continuation(ie, ielen,
|
||||||
|
mbssid_elem,
|
||||||
|
sub_elem))) {
|
||||||
|
const struct element *next_sub = (void *)&next_mbssid->data[1];
|
||||||
|
|
||||||
|
if (copied_len + next_sub->datalen > max_copy_len)
|
||||||
|
break;
|
||||||
|
memcpy(*merged_ie + copied_len, next_sub->data,
|
||||||
|
next_sub->datalen);
|
||||||
|
copied_len += next_sub->datalen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return copied_len;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(cfg80211_merge_profile);
|
||||||
|
|
||||||
static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
||||||
struct cfg80211_inform_bss *data,
|
struct cfg80211_inform_bss *data,
|
||||||
enum cfg80211_bss_frame_type ftype,
|
enum cfg80211_bss_frame_type ftype,
|
||||||
@ -1469,7 +1541,8 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
|||||||
const struct element *elem, *sub;
|
const struct element *elem, *sub;
|
||||||
size_t new_ie_len;
|
size_t new_ie_len;
|
||||||
u8 new_bssid[ETH_ALEN];
|
u8 new_bssid[ETH_ALEN];
|
||||||
u8 *new_ie;
|
u8 *new_ie, *profile;
|
||||||
|
u64 seen_indices = 0;
|
||||||
u16 capability;
|
u16 capability;
|
||||||
struct cfg80211_bss *bss;
|
struct cfg80211_bss *bss;
|
||||||
|
|
||||||
@ -1487,10 +1560,16 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
|||||||
if (!new_ie)
|
if (!new_ie)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
profile = kmalloc(ielen, gfp);
|
||||||
|
if (!profile)
|
||||||
|
goto out;
|
||||||
|
|
||||||
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
|
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
|
||||||
if (elem->datalen < 4)
|
if (elem->datalen < 4)
|
||||||
continue;
|
continue;
|
||||||
for_each_element(sub, elem->data + 1, elem->datalen - 1) {
|
for_each_element(sub, elem->data + 1, elem->datalen - 1) {
|
||||||
|
u8 profile_len;
|
||||||
|
|
||||||
if (sub->id != 0 || sub->datalen < 4) {
|
if (sub->id != 0 || sub->datalen < 4) {
|
||||||
/* not a valid BSS profile */
|
/* not a valid BSS profile */
|
||||||
continue;
|
continue;
|
||||||
@ -1505,16 +1584,31 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(profile, 0, ielen);
|
||||||
|
profile_len = cfg80211_merge_profile(ie, ielen,
|
||||||
|
elem,
|
||||||
|
sub,
|
||||||
|
&profile,
|
||||||
|
ielen);
|
||||||
|
|
||||||
/* found a Nontransmitted BSSID Profile */
|
/* found a Nontransmitted BSSID Profile */
|
||||||
mbssid_index_ie = cfg80211_find_ie
|
mbssid_index_ie = cfg80211_find_ie
|
||||||
(WLAN_EID_MULTI_BSSID_IDX,
|
(WLAN_EID_MULTI_BSSID_IDX,
|
||||||
sub->data, sub->datalen);
|
profile, profile_len);
|
||||||
if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
|
if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
|
||||||
mbssid_index_ie[2] == 0) {
|
mbssid_index_ie[2] == 0 ||
|
||||||
|
mbssid_index_ie[2] > 46) {
|
||||||
/* No valid Multiple BSSID-Index element */
|
/* No valid Multiple BSSID-Index element */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (seen_indices & BIT(mbssid_index_ie[2]))
|
||||||
|
/* We don't support legacy split of a profile */
|
||||||
|
net_dbg_ratelimited("Partial info for BSSID index %d\n",
|
||||||
|
mbssid_index_ie[2]);
|
||||||
|
|
||||||
|
seen_indices |= BIT(mbssid_index_ie[2]);
|
||||||
|
|
||||||
non_tx_data->bssid_index = mbssid_index_ie[2];
|
non_tx_data->bssid_index = mbssid_index_ie[2];
|
||||||
non_tx_data->max_bssid_indicator = elem->data[0];
|
non_tx_data->max_bssid_indicator = elem->data[0];
|
||||||
|
|
||||||
@ -1523,13 +1617,14 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
|||||||
non_tx_data->bssid_index,
|
non_tx_data->bssid_index,
|
||||||
new_bssid);
|
new_bssid);
|
||||||
memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
|
memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
|
||||||
new_ie_len = cfg80211_gen_new_ie(ie, ielen, sub->data,
|
new_ie_len = cfg80211_gen_new_ie(ie, ielen,
|
||||||
sub->datalen, new_ie,
|
profile,
|
||||||
|
profile_len, new_ie,
|
||||||
gfp);
|
gfp);
|
||||||
if (!new_ie_len)
|
if (!new_ie_len)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
capability = get_unaligned_le16(sub->data + 2);
|
capability = get_unaligned_le16(profile + 2);
|
||||||
bss = cfg80211_inform_single_bss_data(wiphy, data,
|
bss = cfg80211_inform_single_bss_data(wiphy, data,
|
||||||
ftype,
|
ftype,
|
||||||
new_bssid, tsf,
|
new_bssid, tsf,
|
||||||
@ -1545,7 +1640,9 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
kfree(new_ie);
|
kfree(new_ie);
|
||||||
|
kfree(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cfg80211_bss *
|
struct cfg80211_bss *
|
||||||
|
Loading…
Reference in New Issue
Block a user