mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	wifi: cfg80211: handle BSS data contained in ML probe responses
The basic multi-link element within an multi-link probe response will contain full information about BSSes that are part of an MLD AP. This BSS information may be used to associate with a link of an MLD AP without having received a beacon from the BSS itself. This patch adds parsing of the data and adding/updating the BSS using the received elements. Doing this means that userspace can discover the BSSes using an ML probe request and request association on these links. Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Signed-off-by: Gregory Greenman <gregory.greenman@intel.com> Link: https://lore.kernel.org/r/20230618214436.29593bd0ae1f.Ic9a67b8f022360aa202b870a932897a389171b14@changeid [swap loop conditions smatch complained about] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
							parent
							
								
									dc92e54c30
								
							
						
					
					
						commit
						2481b5da9c
					
				
					 1 changed files with 354 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -1979,6 +1979,7 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
 | 
			
		|||
struct cfg80211_inform_single_bss_data {
 | 
			
		||||
	struct cfg80211_inform_bss *drv_data;
 | 
			
		||||
	enum cfg80211_bss_frame_type ftype;
 | 
			
		||||
	struct ieee80211_channel *channel;
 | 
			
		||||
	u8 bssid[ETH_ALEN];
 | 
			
		||||
	u64 tsf;
 | 
			
		||||
	u16 capability;
 | 
			
		||||
| 
						 | 
				
			
			@ -1986,7 +1987,12 @@ struct cfg80211_inform_single_bss_data {
 | 
			
		|||
	const u8 *ie;
 | 
			
		||||
	size_t ielen;
 | 
			
		||||
 | 
			
		||||
	/* Set for nontransmitted BSSIDs */
 | 
			
		||||
	enum {
 | 
			
		||||
		BSS_SOURCE_DIRECT = 0,
 | 
			
		||||
		BSS_SOURCE_MBSSID,
 | 
			
		||||
		BSS_SOURCE_STA_PROFILE,
 | 
			
		||||
	} bss_source;
 | 
			
		||||
	/* Set if reporting bss_source != BSS_SOURCE_DIRECT */
 | 
			
		||||
	struct cfg80211_bss *source_bss;
 | 
			
		||||
	u8 max_bssid_indicator;
 | 
			
		||||
	u8 bssid_index;
 | 
			
		||||
| 
						 | 
				
			
			@ -2014,22 +2020,31 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
 | 
			
		|||
		    (drv_data->signal < 0 || drv_data->signal > 100)))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	channel = cfg80211_get_bss_channel(wiphy, data->ie, data->ielen,
 | 
			
		||||
					   drv_data->chan, drv_data->scan_width);
 | 
			
		||||
	if (WARN_ON(data->bss_source != BSS_SOURCE_DIRECT && !data->source_bss))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	channel = data->channel;
 | 
			
		||||
	if (!channel)
 | 
			
		||||
		channel = cfg80211_get_bss_channel(wiphy, data->ie, data->ielen,
 | 
			
		||||
						   drv_data->chan,
 | 
			
		||||
						   drv_data->scan_width);
 | 
			
		||||
	if (!channel)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	memcpy(tmp.pub.bssid, data->bssid, ETH_ALEN);
 | 
			
		||||
	tmp.pub.channel = channel;
 | 
			
		||||
	tmp.pub.scan_width = drv_data->scan_width;
 | 
			
		||||
	tmp.pub.signal = drv_data->signal;
 | 
			
		||||
	if (data->bss_source != BSS_SOURCE_STA_PROFILE)
 | 
			
		||||
		tmp.pub.signal = drv_data->signal;
 | 
			
		||||
	else
 | 
			
		||||
		tmp.pub.signal = 0;
 | 
			
		||||
	tmp.pub.beacon_interval = data->beacon_interval;
 | 
			
		||||
	tmp.pub.capability = data->capability;
 | 
			
		||||
	tmp.ts_boottime = drv_data->boottime_ns;
 | 
			
		||||
	tmp.parent_tsf = drv_data->parent_tsf;
 | 
			
		||||
	ether_addr_copy(tmp.parent_bssid, drv_data->parent_bssid);
 | 
			
		||||
 | 
			
		||||
	if (data->source_bss) {
 | 
			
		||||
	if (data->bss_source != BSS_SOURCE_DIRECT) {
 | 
			
		||||
		tmp.pub.transmitted_bss = data->source_bss;
 | 
			
		||||
		ts = bss_from_pub(data->source_bss)->ts;
 | 
			
		||||
		tmp.pub.bssid_index = data->bssid_index;
 | 
			
		||||
| 
						 | 
				
			
			@ -2088,7 +2103,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
 | 
			
		|||
 | 
			
		||||
	rdev_inform_bss(rdev, &res->pub, ies, data->drv_data);
 | 
			
		||||
 | 
			
		||||
	if (data->source_bss) {
 | 
			
		||||
	if (data->bss_source == BSS_SOURCE_MBSSID) {
 | 
			
		||||
		/* this is a nontransmitting bss, we need to add it to
 | 
			
		||||
		 * transmitting bss' list if it is not there
 | 
			
		||||
		 */
 | 
			
		||||
| 
						 | 
				
			
			@ -2197,6 +2212,7 @@ cfg80211_parse_mbssid_data(struct wiphy *wiphy,
 | 
			
		|||
		.tsf = tx_data->tsf,
 | 
			
		||||
		.beacon_interval = tx_data->beacon_interval,
 | 
			
		||||
		.source_bss = source_bss,
 | 
			
		||||
		.bss_source = BSS_SOURCE_MBSSID,
 | 
			
		||||
	};
 | 
			
		||||
	const u8 *mbssid_index_ie;
 | 
			
		||||
	const struct element *elem, *sub;
 | 
			
		||||
| 
						 | 
				
			
			@ -2365,6 +2381,332 @@ ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_defragment_element);
 | 
			
		||||
 | 
			
		||||
struct cfg80211_mle {
 | 
			
		||||
	struct ieee80211_multi_link_elem *mle;
 | 
			
		||||
	struct ieee80211_mle_per_sta_profile
 | 
			
		||||
		*sta_prof[IEEE80211_MLD_MAX_NUM_LINKS];
 | 
			
		||||
	ssize_t sta_prof_len[IEEE80211_MLD_MAX_NUM_LINKS];
 | 
			
		||||
 | 
			
		||||
	u8 data[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct cfg80211_mle *
 | 
			
		||||
cfg80211_defrag_mle(const struct element *mle, const u8 *ie, size_t ielen,
 | 
			
		||||
		    gfp_t gfp)
 | 
			
		||||
{
 | 
			
		||||
	const struct element *elem;
 | 
			
		||||
	struct cfg80211_mle *res;
 | 
			
		||||
	size_t buf_len;
 | 
			
		||||
	ssize_t mle_len;
 | 
			
		||||
	u8 common_size, idx;
 | 
			
		||||
 | 
			
		||||
	if (!mle || !ieee80211_mle_size_ok(mle->data + 1, mle->datalen - 1))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Required length for first defragmentation */
 | 
			
		||||
	buf_len = mle->datalen - 1;
 | 
			
		||||
	for_each_element(elem, mle->data + mle->datalen,
 | 
			
		||||
			 ielen - sizeof(*mle) + mle->datalen) {
 | 
			
		||||
		if (elem->id != WLAN_EID_FRAGMENT)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		buf_len += elem->datalen;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res = kzalloc(struct_size(res, data, buf_len), gfp);
 | 
			
		||||
	if (!res)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	mle_len = cfg80211_defragment_element(mle, ie, ielen,
 | 
			
		||||
					      res->data, buf_len,
 | 
			
		||||
					      WLAN_EID_FRAGMENT);
 | 
			
		||||
	if (mle_len < 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	res->mle = (void *)res->data;
 | 
			
		||||
 | 
			
		||||
	/* Find the sub-element area in the buffer */
 | 
			
		||||
	common_size = ieee80211_mle_common_size((u8 *)res->mle);
 | 
			
		||||
	ie = res->data + common_size;
 | 
			
		||||
	ielen = mle_len - common_size;
 | 
			
		||||
 | 
			
		||||
	idx = 0;
 | 
			
		||||
	for_each_element_id(elem, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE,
 | 
			
		||||
			    ie, ielen) {
 | 
			
		||||
		res->sta_prof[idx] = (void *)elem->data;
 | 
			
		||||
		res->sta_prof_len[idx] = elem->datalen;
 | 
			
		||||
 | 
			
		||||
		idx++;
 | 
			
		||||
		if (idx >= IEEE80211_MLD_MAX_NUM_LINKS)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
	if (!for_each_element_completed(elem, ie, ielen))
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	/* Defragment sta_info in-place */
 | 
			
		||||
	for (idx = 0; idx < IEEE80211_MLD_MAX_NUM_LINKS && res->sta_prof[idx];
 | 
			
		||||
	     idx++) {
 | 
			
		||||
		if (res->sta_prof_len[idx] < 255)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		elem = (void *)res->sta_prof[idx] - 2;
 | 
			
		||||
 | 
			
		||||
		if (idx + 1 < ARRAY_SIZE(res->sta_prof) &&
 | 
			
		||||
		    res->sta_prof[idx + 1])
 | 
			
		||||
			buf_len = (u8 *)res->sta_prof[idx + 1] -
 | 
			
		||||
				  (u8 *)res->sta_prof[idx];
 | 
			
		||||
		else
 | 
			
		||||
			buf_len = ielen + ie - (u8 *)elem;
 | 
			
		||||
 | 
			
		||||
		res->sta_prof_len[idx] =
 | 
			
		||||
			cfg80211_defragment_element(elem,
 | 
			
		||||
						    (u8 *)elem, buf_len,
 | 
			
		||||
						    (u8 *)res->sta_prof[idx],
 | 
			
		||||
						    buf_len,
 | 
			
		||||
						    IEEE80211_MLE_SUBELEM_FRAGMENT);
 | 
			
		||||
		if (res->sta_prof_len[idx] < 0)
 | 
			
		||||
			goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	kfree(res);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
cfg80211_tbtt_info_for_mld_ap(const u8 *ie, size_t ielen, u8 mld_id, u8 link_id,
 | 
			
		||||
			      const struct ieee80211_neighbor_ap_info **ap_info,
 | 
			
		||||
			      const u8 **tbtt_info)
 | 
			
		||||
{
 | 
			
		||||
	const struct ieee80211_neighbor_ap_info *info;
 | 
			
		||||
	const struct element *rnr;
 | 
			
		||||
	const u8 *pos, *end;
 | 
			
		||||
 | 
			
		||||
	for_each_element_id(rnr, WLAN_EID_REDUCED_NEIGHBOR_REPORT, ie, ielen) {
 | 
			
		||||
		pos = rnr->data;
 | 
			
		||||
		end = rnr->data + rnr->datalen;
 | 
			
		||||
 | 
			
		||||
		/* RNR IE may contain more than one NEIGHBOR_AP_INFO */
 | 
			
		||||
		while (sizeof(*info) <= end - pos) {
 | 
			
		||||
			const struct ieee80211_rnr_mld_params *mld_params;
 | 
			
		||||
			u16 params;
 | 
			
		||||
			u8 length, i, count, mld_params_offset;
 | 
			
		||||
			u8 type, lid;
 | 
			
		||||
 | 
			
		||||
			info = (void *)pos;
 | 
			
		||||
			count = u8_get_bits(info->tbtt_info_hdr,
 | 
			
		||||
					    IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1;
 | 
			
		||||
			length = info->tbtt_info_len;
 | 
			
		||||
 | 
			
		||||
			pos += sizeof(*info);
 | 
			
		||||
 | 
			
		||||
			if (count * length > end - pos)
 | 
			
		||||
				return false;
 | 
			
		||||
 | 
			
		||||
			type = u8_get_bits(info->tbtt_info_hdr,
 | 
			
		||||
					   IEEE80211_AP_INFO_TBTT_HDR_TYPE);
 | 
			
		||||
 | 
			
		||||
			/* Only accept full TBTT information. NSTR mobile APs
 | 
			
		||||
			 * use the shortened version, but we ignore them here.
 | 
			
		||||
			 */
 | 
			
		||||
			if (type == IEEE80211_TBTT_INFO_TYPE_TBTT &&
 | 
			
		||||
			    length >=
 | 
			
		||||
			    offsetofend(struct ieee80211_tbtt_info_ge_11,
 | 
			
		||||
					mld_params)) {
 | 
			
		||||
				mld_params_offset =
 | 
			
		||||
					offsetof(struct ieee80211_tbtt_info_ge_11, mld_params);
 | 
			
		||||
			} else {
 | 
			
		||||
				pos += count * length;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (i = 0; i < count; i++) {
 | 
			
		||||
				mld_params = (void *)pos + mld_params_offset;
 | 
			
		||||
				params = le16_to_cpu(mld_params->params);
 | 
			
		||||
 | 
			
		||||
				lid = u16_get_bits(params,
 | 
			
		||||
						   IEEE80211_RNR_MLD_PARAMS_LINK_ID);
 | 
			
		||||
 | 
			
		||||
				if (mld_id == mld_params->mld_id &&
 | 
			
		||||
				    link_id == lid) {
 | 
			
		||||
					*ap_info = info;
 | 
			
		||||
					*tbtt_info = pos;
 | 
			
		||||
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				pos += length;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cfg80211_parse_ml_sta_data(struct wiphy *wiphy,
 | 
			
		||||
				       struct cfg80211_inform_single_bss_data *tx_data,
 | 
			
		||||
				       struct cfg80211_bss *source_bss,
 | 
			
		||||
				       gfp_t gfp)
 | 
			
		||||
{
 | 
			
		||||
	struct cfg80211_inform_single_bss_data data = {
 | 
			
		||||
		.drv_data = tx_data->drv_data,
 | 
			
		||||
		.ftype = tx_data->ftype,
 | 
			
		||||
		.source_bss = source_bss,
 | 
			
		||||
		.bss_source = BSS_SOURCE_STA_PROFILE,
 | 
			
		||||
	};
 | 
			
		||||
	struct ieee80211_multi_link_elem *ml_elem;
 | 
			
		||||
	const struct element *elem;
 | 
			
		||||
	struct cfg80211_mle *mle;
 | 
			
		||||
	u16 control;
 | 
			
		||||
	u8 *new_ie;
 | 
			
		||||
	struct cfg80211_bss *bss;
 | 
			
		||||
	int mld_id;
 | 
			
		||||
	u16 seen_links = 0;
 | 
			
		||||
	const u8 *pos;
 | 
			
		||||
	u8 i;
 | 
			
		||||
 | 
			
		||||
	if (!source_bss)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (tx_data->ftype != CFG80211_BSS_FTYPE_PRESP)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_MULTI_LINK,
 | 
			
		||||
				      tx_data->ie, tx_data->ielen);
 | 
			
		||||
	if (!elem || !ieee80211_mle_size_ok(elem->data + 1, elem->datalen - 1))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ml_elem = (void *)elem->data + 1;
 | 
			
		||||
	control = le16_to_cpu(ml_elem->control);
 | 
			
		||||
	if (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE) !=
 | 
			
		||||
	    IEEE80211_ML_CONTROL_TYPE_BASIC)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* Must be present when transmitted by an AP (in a probe response) */
 | 
			
		||||
	if (!(control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) ||
 | 
			
		||||
	    !(control & IEEE80211_MLC_BASIC_PRES_LINK_ID) ||
 | 
			
		||||
	    !(control & IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* length + MLD MAC address + link ID info + BSS Params Change Count */
 | 
			
		||||
	pos = ml_elem->variable + 1 + 6 + 1 + 1;
 | 
			
		||||
 | 
			
		||||
	if (u16_get_bits(control, IEEE80211_MLC_BASIC_PRES_MED_SYNC_DELAY))
 | 
			
		||||
		pos += 2;
 | 
			
		||||
	if (u16_get_bits(control, IEEE80211_MLC_BASIC_PRES_EML_CAPA))
 | 
			
		||||
		pos += 2;
 | 
			
		||||
 | 
			
		||||
	/* MLD capabilities and operations */
 | 
			
		||||
	pos += 2;
 | 
			
		||||
 | 
			
		||||
	/* Not included when the (nontransmitted) AP is responding itself,
 | 
			
		||||
	 * but defined to zero then (Draft P802.11be_D3.0, 9.4.2.170.2)
 | 
			
		||||
	 */
 | 
			
		||||
	if (u16_get_bits(control, IEEE80211_MLC_BASIC_PRES_MLD_ID)) {
 | 
			
		||||
		mld_id = *pos;
 | 
			
		||||
		pos += 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		mld_id = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Extended MLD capabilities and operations */
 | 
			
		||||
	pos += 2;
 | 
			
		||||
 | 
			
		||||
	/* Fully defrag the ML element for sta information/profile iteration */
 | 
			
		||||
	mle = cfg80211_defrag_mle(elem, tx_data->ie, tx_data->ielen, gfp);
 | 
			
		||||
	if (!mle)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
 | 
			
		||||
	if (!new_ie)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(mle->sta_prof) && mle->sta_prof[i]; i++) {
 | 
			
		||||
		const struct ieee80211_neighbor_ap_info *ap_info;
 | 
			
		||||
		enum nl80211_band band;
 | 
			
		||||
		u32 freq;
 | 
			
		||||
		const u8 *profile;
 | 
			
		||||
		const u8 *tbtt_info;
 | 
			
		||||
		ssize_t profile_len;
 | 
			
		||||
		u8 link_id;
 | 
			
		||||
 | 
			
		||||
		if (!ieee80211_mle_basic_sta_prof_size_ok((u8 *)mle->sta_prof[i],
 | 
			
		||||
							  mle->sta_prof_len[i]))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		control = le16_to_cpu(mle->sta_prof[i]->control);
 | 
			
		||||
 | 
			
		||||
		if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		link_id = u16_get_bits(control,
 | 
			
		||||
				       IEEE80211_MLE_STA_CONTROL_LINK_ID);
 | 
			
		||||
		if (seen_links & BIT(link_id))
 | 
			
		||||
			break;
 | 
			
		||||
		seen_links |= BIT(link_id);
 | 
			
		||||
 | 
			
		||||
		if (!(control & IEEE80211_MLE_STA_CONTROL_BEACON_INT_PRESENT) ||
 | 
			
		||||
		    !(control & IEEE80211_MLE_STA_CONTROL_TSF_OFFS_PRESENT) ||
 | 
			
		||||
		    !(control & IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		memcpy(data.bssid, mle->sta_prof[i]->variable, ETH_ALEN);
 | 
			
		||||
		data.beacon_interval =
 | 
			
		||||
			get_unaligned_le16(mle->sta_prof[i]->variable + 6);
 | 
			
		||||
		data.tsf = tx_data->tsf +
 | 
			
		||||
			   get_unaligned_le64(mle->sta_prof[i]->variable + 8);
 | 
			
		||||
 | 
			
		||||
		/* sta_info_len counts itself */
 | 
			
		||||
		profile = mle->sta_prof[i]->variable +
 | 
			
		||||
			  mle->sta_prof[i]->sta_info_len - 1;
 | 
			
		||||
		profile_len = (u8 *)mle->sta_prof[i] + mle->sta_prof_len[i] -
 | 
			
		||||
			      profile;
 | 
			
		||||
 | 
			
		||||
		if (profile_len < 2)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		data.capability = get_unaligned_le16(profile);
 | 
			
		||||
		profile += 2;
 | 
			
		||||
		profile_len -= 2;
 | 
			
		||||
 | 
			
		||||
		/* Find in RNR to look up channel information */
 | 
			
		||||
		if (!cfg80211_tbtt_info_for_mld_ap(tx_data->ie, tx_data->ielen,
 | 
			
		||||
						   mld_id, link_id,
 | 
			
		||||
						   &ap_info, &tbtt_info))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* We could sanity check the BSSID is included */
 | 
			
		||||
 | 
			
		||||
		if (!ieee80211_operating_class_to_band(ap_info->op_class,
 | 
			
		||||
						       &band))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		freq = ieee80211_channel_to_freq_khz(ap_info->channel, band);
 | 
			
		||||
		data.channel = ieee80211_get_channel_khz(wiphy, freq);
 | 
			
		||||
 | 
			
		||||
		/* Generate new elements */
 | 
			
		||||
		memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
 | 
			
		||||
		data.ie = new_ie;
 | 
			
		||||
		data.ielen = cfg80211_gen_new_ie(tx_data->ie, tx_data->ielen,
 | 
			
		||||
						 profile, profile_len,
 | 
			
		||||
						 new_ie,
 | 
			
		||||
						 IEEE80211_MAX_DATA_LEN);
 | 
			
		||||
		if (!data.ielen)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		bss = cfg80211_inform_single_bss_data(wiphy, &data, gfp);
 | 
			
		||||
		if (!bss)
 | 
			
		||||
			break;
 | 
			
		||||
		cfg80211_put_bss(wiphy, bss);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	kfree(new_ie);
 | 
			
		||||
	kfree(mle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct cfg80211_bss *
 | 
			
		||||
cfg80211_inform_bss_data(struct wiphy *wiphy,
 | 
			
		||||
			 struct cfg80211_inform_bss *data,
 | 
			
		||||
| 
						 | 
				
			
			@ -2391,6 +2733,9 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
 | 
			
		|||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	cfg80211_parse_mbssid_data(wiphy, &inform_data, res, gfp);
 | 
			
		||||
 | 
			
		||||
	cfg80211_parse_ml_sta_data(wiphy, &inform_data, res, gfp);
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_inform_bss_data);
 | 
			
		||||
| 
						 | 
				
			
			@ -2549,7 +2894,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
 | 
			
		|||
	if (!res)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* don't do any further MBSSID handling for S1G */
 | 
			
		||||
	/* don't do any further MBSSID/ML handling for S1G */
 | 
			
		||||
	if (ieee80211_is_s1g_beacon(mgmt->frame_control))
 | 
			
		||||
		return res;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2563,6 +2908,8 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
 | 
			
		|||
	/* process each non-transmitting bss */
 | 
			
		||||
	cfg80211_parse_mbssid_data(wiphy, &inform_data, res, gfp);
 | 
			
		||||
 | 
			
		||||
	cfg80211_parse_ml_sta_data(wiphy, &inform_data, res, gfp);
 | 
			
		||||
 | 
			
		||||
	return res;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue