mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	wifi: mac80211: Parse station profile from association response
When processing an association response frame for a Multi-Link connection, extract the per station profile for each additional link, and use it for parsing the link elements. As the Multi-Link element might be fragmented, add support for reassembling a fragmented element. To simplify memory management logic, extend 'struct ieee802_11_elems' to hold a scratch buffer, which is used for the defragmentation. Once an element is reconstructed in the scratch area, point the corresponding element pointer to it. Currently only defragmentation of Multi-Link element and the contained per-STA profile subelement is supported. Signed-off-by: Ilan Peer <ilan.peer@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
							parent
							
								
									fb99c7d4d6
								
							
						
					
					
						commit
						45ebac4f05
					
				
					 4 changed files with 188 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -4666,6 +4666,7 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
 | 
			
		|||
 | 
			
		||||
enum ieee80211_mle_subelems {
 | 
			
		||||
	IEEE80211_MLE_SUBELEM_PER_STA_PROFILE		= 0,
 | 
			
		||||
	IEEE80211_MLE_SUBELEM_FRAGMENT		        = 254,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define IEEE80211_MLE_STA_CONTROL_LINK_ID			0x000f
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1707,8 +1707,27 @@ struct ieee802_11_elems {
 | 
			
		|||
	u8 tx_pwr_env_num;
 | 
			
		||||
	u8 eht_cap_len;
 | 
			
		||||
 | 
			
		||||
	/* mult-link element can be de-fragmented and thus u8 is not sufficient */
 | 
			
		||||
	size_t multi_link_len;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * store the per station profile pointer and length in case that the
 | 
			
		||||
	 * parsing also handled Multi-Link element parsing for a specific link
 | 
			
		||||
	 * ID.
 | 
			
		||||
	 */
 | 
			
		||||
	struct ieee80211_mle_per_sta_profile *prof;
 | 
			
		||||
	size_t sta_prof_len;
 | 
			
		||||
 | 
			
		||||
	/* whether a parse error occurred while retrieving these elements */
 | 
			
		||||
	bool parse_error;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * scratch buffer that can be used for various element parsing related
 | 
			
		||||
	 * tasks, e.g., element de-fragmentation etc.
 | 
			
		||||
	 */
 | 
			
		||||
	size_t scratch_len;
 | 
			
		||||
	u8 *scratch_pos;
 | 
			
		||||
	u8 scratch[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline struct ieee80211_local *hw_to_local(
 | 
			
		||||
| 
						 | 
				
			
			@ -2197,9 +2216,13 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
 | 
			
		|||
 *	represent a non-transmitting BSS in which case the data
 | 
			
		||||
 *	for that non-transmitting BSS is returned
 | 
			
		||||
 * @link_id: the link ID to parse elements for, if a STA profile
 | 
			
		||||
 *	is present in the multi-link element, or -1 to ignore
 | 
			
		||||
 *	is present in the multi-link element, or -1 to ignore;
 | 
			
		||||
 *	note that the code currently assumes parsing an association
 | 
			
		||||
 *	(or re-association) response frame if this is given
 | 
			
		||||
 * @from_ap: frame is received from an AP (currently used only
 | 
			
		||||
 *	for EHT capabilities parsing)
 | 
			
		||||
 * @scratch_len: if non zero, specifies the requested length of the scratch
 | 
			
		||||
 *      buffer; otherwise, 'len' is used.
 | 
			
		||||
 */
 | 
			
		||||
struct ieee80211_elems_parse_params {
 | 
			
		||||
	const u8 *start;
 | 
			
		||||
| 
						 | 
				
			
			@ -2210,6 +2233,7 @@ struct ieee80211_elems_parse_params {
 | 
			
		|||
	struct cfg80211_bss *bss;
 | 
			
		||||
	int link_id;
 | 
			
		||||
	bool from_ap;
 | 
			
		||||
	size_t scratch_len;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ieee802_11_elems *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3923,11 +3923,12 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
 | 
			
		|||
	struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
 | 
			
		||||
	struct ieee80211_bss_conf *bss_conf = link->conf;
 | 
			
		||||
	struct ieee80211_local *local = sdata->local;
 | 
			
		||||
	unsigned int link_id = link->link_id;
 | 
			
		||||
	struct ieee80211_elems_parse_params parse_params = {
 | 
			
		||||
		.start = elem_start,
 | 
			
		||||
		.len = elem_len,
 | 
			
		||||
		.bss = cbss,
 | 
			
		||||
		.link_id = link == &sdata->deflink ? -1 : link->link_id,
 | 
			
		||||
		.link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id,
 | 
			
		||||
		.from_ap = true,
 | 
			
		||||
	};
 | 
			
		||||
	bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
 | 
			
		||||
| 
						 | 
				
			
			@ -3942,8 +3943,18 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
 | 
			
		|||
	if (!elems)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: use from STA profile element after parsing that */
 | 
			
		||||
	capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 | 
			
		||||
	if (link_id == assoc_data->assoc_link_id) {
 | 
			
		||||
		capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 | 
			
		||||
	} else if (!elems->prof) {
 | 
			
		||||
		ret = false;
 | 
			
		||||
		goto out;
 | 
			
		||||
	} else {
 | 
			
		||||
		const u8 *ptr = elems->prof->variable +
 | 
			
		||||
				elems->prof->sta_info_len - 1;
 | 
			
		||||
 | 
			
		||||
		/* FIXME: need to also handle the status code */
 | 
			
		||||
		capab_info = get_unaligned_le16(ptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!is_s1g && !elems->supp_rates) {
 | 
			
		||||
		sdata_info(sdata, "no SuppRates element in AssocResp\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1026,8 +1026,10 @@ ieee80211_parse_extension_element(u32 *crc,
 | 
			
		|||
			elems->eht_operation = data;
 | 
			
		||||
		break;
 | 
			
		||||
	case WLAN_EID_EXT_EHT_MULTI_LINK:
 | 
			
		||||
		if (ieee80211_mle_size_ok(data, len))
 | 
			
		||||
		if (ieee80211_mle_size_ok(data, len)) {
 | 
			
		||||
			elems->multi_link = (void *)data;
 | 
			
		||||
			elems->multi_link_len = len;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1497,6 +1499,145 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
 | 
			
		|||
	return found ? profile_len : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ieee80211_defragment_element(struct ieee802_11_elems *elems,
 | 
			
		||||
					 void **elem_ptr, size_t *len,
 | 
			
		||||
					 size_t total_len, u8 frag_id)
 | 
			
		||||
{
 | 
			
		||||
	u8 *data = *elem_ptr, *pos, *start;
 | 
			
		||||
	const struct element *elem;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Since 'data' points to the data of the element, not the element
 | 
			
		||||
	 * itself, allow 254 in case it was an extended element where the
 | 
			
		||||
	 * extended ID isn't part of the data we see here and thus not part of
 | 
			
		||||
	 * 'len' either.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!data || (*len != 254 && *len != 255))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	start = elems->scratch_pos;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(*len > (elems->scratch + elems->scratch_len -
 | 
			
		||||
			    elems->scratch_pos)))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	memcpy(elems->scratch_pos, data, *len);
 | 
			
		||||
	elems->scratch_pos += *len;
 | 
			
		||||
 | 
			
		||||
	pos = data + *len;
 | 
			
		||||
	total_len -= *len;
 | 
			
		||||
	for_each_element(elem, pos, total_len) {
 | 
			
		||||
		if (elem->id != frag_id)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (WARN_ON(elem->datalen >
 | 
			
		||||
			    (elems->scratch + elems->scratch_len -
 | 
			
		||||
			     elems->scratch_pos)))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		memcpy(elems->scratch_pos, elem->data, elem->datalen);
 | 
			
		||||
		elems->scratch_pos += elem->datalen;
 | 
			
		||||
 | 
			
		||||
		*len += elem->datalen;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*elem_ptr = start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,
 | 
			
		||||
				       u8 link_id)
 | 
			
		||||
{
 | 
			
		||||
	const struct ieee80211_multi_link_elem *ml = elems->multi_link;
 | 
			
		||||
	size_t ml_len = elems->multi_link_len;
 | 
			
		||||
	const struct element *sub;
 | 
			
		||||
 | 
			
		||||
	if (!ml || !ml_len)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (le16_get_bits(ml->control, IEEE80211_ML_CONTROL_TYPE) !=
 | 
			
		||||
	    IEEE80211_ML_CONTROL_TYPE_BASIC)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	for_each_mle_subelement(sub, (u8 *)ml, ml_len) {
 | 
			
		||||
		struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
 | 
			
		||||
		u16 control;
 | 
			
		||||
 | 
			
		||||
		if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!ieee80211_mle_sta_prof_size_ok(sub->data, sub->datalen))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		control = le16_to_cpu(prof->control);
 | 
			
		||||
 | 
			
		||||
		if (link_id != u16_get_bits(control,
 | 
			
		||||
					    IEEE80211_MLE_STA_CONTROL_LINK_ID))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		elems->prof = prof;
 | 
			
		||||
		elems->sta_prof_len = sub->datalen;
 | 
			
		||||
 | 
			
		||||
		/* the sub element can be fragmented */
 | 
			
		||||
		ieee80211_defragment_element(elems, (void **)&elems->prof,
 | 
			
		||||
					     &elems->sta_prof_len,
 | 
			
		||||
					     ml_len - (sub->data - (u8 *)ml),
 | 
			
		||||
					     IEEE80211_MLE_SUBELEM_FRAGMENT);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems,
 | 
			
		||||
				     struct ieee80211_elems_parse_params *params)
 | 
			
		||||
{
 | 
			
		||||
	struct ieee80211_mle_per_sta_profile *prof;
 | 
			
		||||
	struct ieee80211_elems_parse_params sub = {
 | 
			
		||||
		.action = params->action,
 | 
			
		||||
		.from_ap = params->from_ap,
 | 
			
		||||
		.link_id = -1,
 | 
			
		||||
	};
 | 
			
		||||
	const struct element *non_inherit = NULL;
 | 
			
		||||
	const u8 *end;
 | 
			
		||||
 | 
			
		||||
	if (params->link_id == -1)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ieee80211_defragment_element(elems, (void **)&elems->multi_link,
 | 
			
		||||
				     &elems->multi_link_len,
 | 
			
		||||
				     elems->total_len - ((u8 *)elems->multi_link -
 | 
			
		||||
							 elems->ie_start),
 | 
			
		||||
				     WLAN_EID_FRAGMENT);
 | 
			
		||||
 | 
			
		||||
	ieee80211_mle_get_sta_prof(elems, params->link_id);
 | 
			
		||||
	prof = elems->prof;
 | 
			
		||||
 | 
			
		||||
	if (!prof)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* check if we have the 4 bytes for the fixed part in assoc response */
 | 
			
		||||
	if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) {
 | 
			
		||||
		elems->prof = NULL;
 | 
			
		||||
		elems->sta_prof_len = 0;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Skip the capability information and the status code that are expected
 | 
			
		||||
	 * as part of the station profile in association response frames. Note
 | 
			
		||||
	 * the -1 is because the 'sta_info_len' is accounted to as part of the
 | 
			
		||||
	 * per-STA profile, but not part of the 'u8 variable[]' portion.
 | 
			
		||||
	 */
 | 
			
		||||
	sub.start = prof->variable + prof->sta_info_len - 1 + 4;
 | 
			
		||||
	end = (const u8 *)prof + elems->sta_prof_len;
 | 
			
		||||
	sub.len = end - sub.start;
 | 
			
		||||
 | 
			
		||||
	non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
 | 
			
		||||
					     sub.start, sub.len);
 | 
			
		||||
	_ieee802_11_parse_elems_full(&sub, elems, non_inherit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ieee802_11_elems *
 | 
			
		||||
ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1504,12 +1645,15 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
 | 
			
		|||
	const struct element *non_inherit = NULL;
 | 
			
		||||
	u8 *nontransmitted_profile;
 | 
			
		||||
	int nontransmitted_profile_len = 0;
 | 
			
		||||
	size_t scratch_len = params->scratch_len ?: 2 * params->len;
 | 
			
		||||
 | 
			
		||||
	elems = kzalloc(sizeof(*elems), GFP_ATOMIC);
 | 
			
		||||
	elems = kzalloc(sizeof(*elems) + scratch_len, GFP_ATOMIC);
 | 
			
		||||
	if (!elems)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	elems->ie_start = params->start;
 | 
			
		||||
	elems->total_len = params->len;
 | 
			
		||||
	elems->scratch_len = scratch_len;
 | 
			
		||||
	elems->scratch_pos = elems->scratch;
 | 
			
		||||
 | 
			
		||||
	nontransmitted_profile = kmalloc(params->len, GFP_ATOMIC);
 | 
			
		||||
	if (nontransmitted_profile) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1537,6 +1681,8 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
 | 
			
		|||
		_ieee802_11_parse_elems_full(&sub, elems, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ieee80211_mle_parse_link(elems, params);
 | 
			
		||||
 | 
			
		||||
	if (elems->tim && !elems->parse_error) {
 | 
			
		||||
		const struct ieee80211_tim_ie *tim_ie = elems->tim;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue