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

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@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 7e899c1d
...@@ -611,104 +611,144 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry, ...@@ -611,104 +611,144 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
return 0; return 0;
} }
VISIBLE_IF_CFG80211_KUNIT int enum cfg80211_rnr_iter_ret {
cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, RNR_ITER_CONTINUE,
struct list_head *list) 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 *rnr;
const struct element *elem, *ssid_elem;
const u8 *pos, *end; 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); for_each_element_id(rnr, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
if (ret) elems, elems_len) {
return 0; const struct ieee80211_neighbor_ap_info *info;
for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT, pos = rnr->data;
ies->data, ies->len) { end = rnr->data + rnr->datalen;
pos = elem->data;
end = elem->data + elem->datalen;
/* RNR IE may contain more than one NEIGHBOR_AP_INFO */ /* RNR IE may contain more than one NEIGHBOR_AP_INFO */
while (pos + sizeof(*ap_info) <= end) { while (sizeof(*info) <= end - pos) {
enum nl80211_band band;
int freq;
u8 length, i, count; u8 length, i, count;
u8 type;
ap_info = (void *)pos; info = (void *)pos;
count = u8_get_bits(ap_info->tbtt_info_hdr, count = u8_get_bits(info->tbtt_info_hdr,
IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1; IEEE80211_AP_INFO_TBTT_HDR_COUNT) +
length = ap_info->tbtt_info_len; 1;
length = info->tbtt_info_len;
pos += sizeof(*ap_info); pos += sizeof(*info);
if (!ieee80211_operating_class_to_band(ap_info->op_class, if (count * length > end - pos)
&band)) return false;
break;
freq = ieee80211_channel_to_frequency(ap_info->channel, type = u8_get_bits(info->tbtt_info_hdr,
band); IEEE80211_AP_INFO_TBTT_HDR_TYPE);
if (end - pos < count * length) for (i = 0; i < count; i++) {
break; 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, pos += length;
IEEE80211_AP_INFO_TBTT_HDR_TYPE) !=
IEEE80211_TBTT_INFO_TYPE_TBTT) {
pos += count * length;
continue;
} }
}
/* TBTT info must include bss param + BSSID + if (pos != end)
* (short SSID or same_ssid bit to be set). return false;
* 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;
}
for (i = 0; i < count; i++) { return true;
struct cfg80211_colocated_ap *entry; }
struct colocated_ap_data {
const struct element *ssid_elem;
struct list_head ap_list;
u32 s_ssid_tmp;
int n_coloc;
};
entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN, static enum cfg80211_rnr_iter_ret
GFP_ATOMIC); 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;
if (!entry) if (type != IEEE80211_TBTT_INFO_TYPE_TBTT)
goto error; return RNR_ITER_CONTINUE;
entry->center_freq = freq; if (!ieee80211_operating_class_to_band(info->op_class, &band))
return RNR_ITER_CONTINUE;
if (!cfg80211_parse_ap_info(entry, pos, length, /* TBTT info must include bss param + BSSID + (short SSID or
ssid_elem, * same_ssid bit to be set). Ignore other options, and move to
s_ssid_tmp)) { * the next AP info
n_coloc++; */
list_add_tail(&entry->list, &ap_list); if (band != NL80211_BAND_6GHZ ||
} else { !(tbtt_info_len == offsetofend(struct ieee80211_tbtt_info_7_8_9,
kfree(entry); 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)
return RNR_ITER_ERROR;
entry->center_freq =
ieee80211_channel_to_frequency(info->channel, band);
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: VISIBLE_IF_CFG80211_KUNIT int
if (pos != end) { cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
cfg80211_free_coloc_ap_list(&ap_list); struct list_head *list)
return 0; {
} 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); list_splice_tail(&data.ap_list, list);
return n_coloc; return data.n_coloc;
} }
EXPORT_SYMBOL_IF_CFG80211_KUNIT(cfg80211_parse_colocated_ap); 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, ...@@ -2607,79 +2647,71 @@ cfg80211_defrag_mle(const struct element *mle, const u8 *ie, size_t ielen,
return NULL; return NULL;
} }
static u8 struct tbtt_info_iter_data {
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;
const struct ieee80211_neighbor_ap_info **ap_info, u8 param_ch_count;
u8 *param_ch_count) u32 use_for;
{ u8 mld_id, link_id;
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;
u32 use_for;
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); 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 &&
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;
if (count * length > end - pos) link_id = le16_get_bits(mld_params->params,
return 0; IEEE80211_RNR_MLD_PARAMS_LINK_ID);
type = u8_get_bits(info->tbtt_info_hdr, if (data->mld_id != mld_params->mld_id)
IEEE80211_AP_INFO_TBTT_HDR_TYPE); return RNR_ITER_CONTINUE;
if (type == IEEE80211_TBTT_INFO_TYPE_TBTT && if (data->link_id != link_id)
length >= return RNR_ITER_CONTINUE;
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++) { data->ap_info = info;
mld_params = (void *)pos + mld_params_offset; data->param_ch_count =
params = le16_to_cpu(mld_params->params); le16_get_bits(mld_params->params,
IEEE80211_RNR_MLD_PARAMS_BSS_CHANGE_COUNT);
lid = u16_get_bits(params, if (type == IEEE80211_TBTT_INFO_TYPE_TBTT)
IEEE80211_RNR_MLD_PARAMS_LINK_ID); data->use_for = NL80211_BSS_USE_FOR_ALL;
else
data->use_for = NL80211_BSS_USE_FOR_MLD_LINK;
return RNR_ITER_BREAK;
}
if (mld_id == mld_params->mld_id && static u8
link_id == lid) { cfg80211_rnr_info_for_mld_ap(const u8 *ie, size_t ielen, u8 mld_id, u8 link_id,
*ap_info = info; const struct ieee80211_neighbor_ap_info **ap_info,
*param_ch_count = u8 *param_ch_count)
le16_get_bits(mld_params->params, {
IEEE80211_RNR_MLD_PARAMS_BSS_CHANGE_COUNT); struct tbtt_info_iter_data data = {
.mld_id = mld_id,
.link_id = link_id,
};
return use_for; cfg80211_iter_rnr(ie, ielen, cfg802121_mld_ap_rnr_iter, &data);
}
pos += length; *ap_info = data.ap_info;
} *param_ch_count = data.param_ch_count;
}
}
return 0; return data.use_for;
} }
static struct element * static struct element *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment