Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fe806e49 authored by Sara Sharon's avatar Sara Sharon Committed by Johannes Berg
Browse files

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: default avatarSara Sharon <sara.sharon@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 671042a4
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -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,
				   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
 * @CFG80211_BSS_FTYPE_UNKNOWN: driver doesn't know whether the data is
+103 −6
Original line number Diff line number Diff line
@@ -1456,6 +1456,78 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
	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,
				       struct cfg80211_inform_bss *data,
				       enum cfg80211_bss_frame_type ftype,
@@ -1469,7 +1541,8 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
	const struct element *elem, *sub;
	size_t new_ie_len;
	u8 new_bssid[ETH_ALEN];
	u8 *new_ie;
	u8 *new_ie, *profile;
	u64 seen_indices = 0;
	u16 capability;
	struct cfg80211_bss *bss;

@@ -1487,10 +1560,16 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
	if (!new_ie)
		return;

	profile = kmalloc(ielen, gfp);
	if (!profile)
		goto out;

	for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
		if (elem->datalen < 4)
			continue;
		for_each_element(sub, elem->data + 1, elem->datalen - 1) {
			u8 profile_len;

			if (sub->id != 0 || sub->datalen < 4) {
				/* not a valid BSS profile */
				continue;
@@ -1505,16 +1584,31 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
				continue;
			}

			memset(profile, 0, ielen);
			profile_len = cfg80211_merge_profile(ie, ielen,
							     elem,
							     sub,
							     &profile,
							     ielen);

			/* found a Nontransmitted BSSID Profile */
			mbssid_index_ie = cfg80211_find_ie
				(WLAN_EID_MULTI_BSSID_IDX,
				 sub->data, sub->datalen);
				 profile, profile_len);
			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 */
				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->max_bssid_indicator = elem->data[0];

@@ -1523,13 +1617,14 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
					       non_tx_data->bssid_index,
					       new_bssid);
			memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
			new_ie_len = cfg80211_gen_new_ie(ie, ielen, sub->data,
							 sub->datalen, new_ie,
			new_ie_len = cfg80211_gen_new_ie(ie, ielen,
							 profile,
							 profile_len, new_ie,
							 gfp);
			if (!new_ie_len)
				continue;

			capability = get_unaligned_le16(sub->data + 2);
			capability = get_unaligned_le16(profile + 2);
			bss = cfg80211_inform_single_bss_data(wiphy, data,
							      ftype,
							      new_bssid, tsf,
@@ -1545,7 +1640,9 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
		}
	}

out:
	kfree(new_ie);
	kfree(profile);
}

struct cfg80211_bss *