Commit 6b756efc authored by Johannes Berg's avatar Johannes Berg
Browse files

wifi: cfg80211: refactor RNR parsing



We'll need more parsing of the reduced neighbor report element,
and we already have two places doing pretty much the same.
Combine by refactoring the parsing into a separate function
with a callback for each item found.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Reviewed-by: default avatarBenjamin Berg <benjamin.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240216135047.cfff14b692fc.Ibe25be88a769eab29ebb17b9d19af666df6a2227@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 7e899c1d
Loading
Loading
Loading
Loading
+169 −137
Original line number Diff line number Diff line
@@ -611,104 +611,144 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
	return 0;
}

VISIBLE_IF_CFG80211_KUNIT int
cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
			    struct list_head *list)
enum cfg80211_rnr_iter_ret {
	RNR_ITER_CONTINUE,
	RNR_ITER_BREAK,
	RNR_ITER_ERROR,
};

static bool
cfg80211_iter_rnr(const u8 *elems, size_t elems_len,
		  enum cfg80211_rnr_iter_ret
		  (*iter)(void *data, u8 type,
			  const struct ieee80211_neighbor_ap_info *info,
			  const u8 *tbtt_info, u8 tbtt_info_len),
		  void *iter_data)
{
	struct ieee80211_neighbor_ap_info *ap_info;
	const struct element *elem, *ssid_elem;
	const struct element *rnr;
	const u8 *pos, *end;
	u32 s_ssid_tmp;
	int n_coloc = 0, ret;
	LIST_HEAD(ap_list);

	ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp);
	if (ret)
		return 0;
	for_each_element_id(rnr, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
			    elems, elems_len) {
		const struct ieee80211_neighbor_ap_info *info;

	for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
			    ies->data, ies->len) {
		pos = elem->data;
		end = elem->data + elem->datalen;
		pos = rnr->data;
		end = rnr->data + rnr->datalen;

		/* RNR IE may contain more than one NEIGHBOR_AP_INFO */
		while (pos + sizeof(*ap_info) <= end) {
			enum nl80211_band band;
			int freq;
		while (sizeof(*info) <= end - pos) {
			u8 length, i, count;
			u8 type;

			ap_info = (void *)pos;
			count = u8_get_bits(ap_info->tbtt_info_hdr,
					    IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1;
			length = ap_info->tbtt_info_len;
			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(*ap_info);
			pos += sizeof(*info);

			if (!ieee80211_operating_class_to_band(ap_info->op_class,
							       &band))
				break;
			if (count * length > end - pos)
				return false;

			freq = ieee80211_channel_to_frequency(ap_info->channel,
							      band);
			type = u8_get_bits(info->tbtt_info_hdr,
					   IEEE80211_AP_INFO_TBTT_HDR_TYPE);

			if (end - pos < count * length)
			for (i = 0; i < count; i++) {
				switch (iter(iter_data, type, info,
					     pos, length)) {
				case RNR_ITER_CONTINUE:
					break;
				case RNR_ITER_BREAK:
					return true;
				case RNR_ITER_ERROR:
					return false;
				}

			if (u8_get_bits(ap_info->tbtt_info_hdr,
					IEEE80211_AP_INFO_TBTT_HDR_TYPE) !=
			    IEEE80211_TBTT_INFO_TYPE_TBTT) {
				pos += count * length;
				continue;
				pos += length;
			}
		}

			/* TBTT info must include bss param + BSSID +
			 * (short SSID or same_ssid bit to be set).
			 * ignore other options, and move to the
			 * next AP info
			 */
			if (band != NL80211_BAND_6GHZ ||
			    !(length == offsetofend(struct ieee80211_tbtt_info_7_8_9,
						    bss_params) ||
			      length == sizeof(struct ieee80211_tbtt_info_7_8_9) ||
			      length >= offsetofend(struct ieee80211_tbtt_info_ge_11,
						    bss_params))) {
				pos += count * length;
				continue;
		if (pos != end)
			return false;
	}

			for (i = 0; i < count; i++) {
	return true;
}

struct colocated_ap_data {
	const struct element *ssid_elem;
	struct list_head ap_list;
	u32 s_ssid_tmp;
	int n_coloc;
};

static enum cfg80211_rnr_iter_ret
cfg80211_parse_colocated_ap_iter(void *_data, u8 type,
				 const struct ieee80211_neighbor_ap_info *info,
				 const u8 *tbtt_info, u8 tbtt_info_len)
{
	struct colocated_ap_data *data = _data;
	struct cfg80211_colocated_ap *entry;
	enum nl80211_band band;

				entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN,
						GFP_ATOMIC);
	if (type != IEEE80211_TBTT_INFO_TYPE_TBTT)
		return RNR_ITER_CONTINUE;

	if (!ieee80211_operating_class_to_band(info->op_class, &band))
		return RNR_ITER_CONTINUE;

	/* TBTT info must include bss param + BSSID + (short SSID or
	 * same_ssid bit to be set). Ignore other options, and move to
	 * the next AP info
	 */
	if (band != NL80211_BAND_6GHZ ||
	    !(tbtt_info_len == offsetofend(struct ieee80211_tbtt_info_7_8_9,
					   bss_params) ||
	      tbtt_info_len == sizeof(struct ieee80211_tbtt_info_7_8_9) ||
	      tbtt_info_len >= offsetofend(struct ieee80211_tbtt_info_ge_11,
					   bss_params)))
		return RNR_ITER_CONTINUE;

	entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN, GFP_ATOMIC);
	if (!entry)
					goto error;
		return RNR_ITER_ERROR;

				entry->center_freq = freq;
	entry->center_freq =
		ieee80211_channel_to_frequency(info->channel, band);

				if (!cfg80211_parse_ap_info(entry, pos, length,
							    ssid_elem,
							    s_ssid_tmp)) {
					n_coloc++;
					list_add_tail(&entry->list, &ap_list);
	if (!cfg80211_parse_ap_info(entry, tbtt_info, tbtt_info_len,
				    data->ssid_elem, data->s_ssid_tmp)) {
		data->n_coloc++;
		list_add_tail(&entry->list, &data->ap_list);
	} else {
		kfree(entry);
	}

				pos += length;
			}
	return RNR_ITER_CONTINUE;
}

error:
		if (pos != end) {
			cfg80211_free_coloc_ap_list(&ap_list);
VISIBLE_IF_CFG80211_KUNIT int
cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
			    struct list_head *list)
{
	struct colocated_ap_data data = {};
	int ret;

	INIT_LIST_HEAD(&data.ap_list);

	ret = cfg80211_calc_short_ssid(ies, &data.ssid_elem, &data.s_ssid_tmp);
	if (ret)
		return 0;

	if (!cfg80211_iter_rnr(ies->data, ies->len,
			       cfg80211_parse_colocated_ap_iter, &data)) {
		cfg80211_free_coloc_ap_list(&data.ap_list);
		return 0;
		}
	}

	list_splice_tail(&ap_list, list);
	return n_coloc;
	list_splice_tail(&data.ap_list, list);
	return data.n_coloc;
}
EXPORT_SYMBOL_IF_CFG80211_KUNIT(cfg80211_parse_colocated_ap);

@@ -2607,79 +2647,71 @@ cfg80211_defrag_mle(const struct element *mle, const u8 *ie, size_t ielen,
	return NULL;
}

static u8
cfg80211_rnr_info_for_mld_ap(const u8 *ie, size_t ielen, u8 mld_id, u8 link_id,
			     const struct ieee80211_neighbor_ap_info **ap_info,
			     u8 *param_ch_count)
{
	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;
struct tbtt_info_iter_data {
	const struct ieee80211_neighbor_ap_info *ap_info;
	u8 param_ch_count;
	u32 use_for;
	u8 mld_id, link_id;
};

			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 0;

			type = u8_get_bits(info->tbtt_info_hdr,
					   IEEE80211_AP_INFO_TBTT_HDR_TYPE);
static enum cfg80211_rnr_iter_ret
cfg802121_mld_ap_rnr_iter(void *_data, u8 type,
			  const struct ieee80211_neighbor_ap_info *info,
			  const u8 *tbtt_info, u8 tbtt_info_len)
{
	const struct ieee80211_rnr_mld_params *mld_params;
	struct tbtt_info_iter_data *data = _data;
	u8 link_id;

	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);
				use_for = NL80211_BSS_USE_FOR_ALL;
			} else if (type == IEEE80211_TBTT_INFO_TYPE_MLD &&
				   length >= sizeof(struct ieee80211_rnr_mld_params)) {
				mld_params_offset = 0;
				use_for = NL80211_BSS_USE_FOR_MLD_LINK;
			} 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);
	    tbtt_info_len >= offsetofend(struct ieee80211_tbtt_info_ge_11,
					 mld_params))
		mld_params = (void *)(tbtt_info +
				      offsetof(struct ieee80211_tbtt_info_ge_11,
					       mld_params));
	else if (type == IEEE80211_TBTT_INFO_TYPE_MLD &&
		 tbtt_info_len >= sizeof(struct ieee80211_rnr_mld_params))
		mld_params = (void *)tbtt_info;
	else
		return RNR_ITER_CONTINUE;

				lid = u16_get_bits(params,
	link_id = le16_get_bits(mld_params->params,
				IEEE80211_RNR_MLD_PARAMS_LINK_ID);

				if (mld_id == mld_params->mld_id &&
				    link_id == lid) {
					*ap_info = info;
					*param_ch_count =
	if (data->mld_id != mld_params->mld_id)
		return RNR_ITER_CONTINUE;

	if (data->link_id != link_id)
		return RNR_ITER_CONTINUE;

	data->ap_info = info;
	data->param_ch_count =
		le16_get_bits(mld_params->params,
			      IEEE80211_RNR_MLD_PARAMS_BSS_CHANGE_COUNT);

					return use_for;
	if (type == IEEE80211_TBTT_INFO_TYPE_TBTT)
		data->use_for = NL80211_BSS_USE_FOR_ALL;
	else
		data->use_for = NL80211_BSS_USE_FOR_MLD_LINK;
	return RNR_ITER_BREAK;
}

				pos += length;
			}
		}
	}
static u8
cfg80211_rnr_info_for_mld_ap(const u8 *ie, size_t ielen, u8 mld_id, u8 link_id,
			     const struct ieee80211_neighbor_ap_info **ap_info,
			     u8 *param_ch_count)
{
	struct tbtt_info_iter_data data = {
		.mld_id = mld_id,
		.link_id = link_id,
	};

	return 0;
	cfg80211_iter_rnr(ie, ielen, cfg802121_mld_ap_rnr_iter, &data);

	*ap_info = data.ap_info;
	*param_ch_count = data.param_ch_count;

	return data.use_for;
}

static struct element *