From: Gregory Greenman <[email protected]>
Hi,
A bunch of patches from our internal tree with mac80211 and
cfg80211 changes. It's the usual developement, cleanups and
bugfixes.
Thanks,
Gregory
Benjamin Berg (11):
wifi: ieee80211: add helper to validate ML element type and size
wifi: ieee80211: use default for medium synchronization delay
wifi: cfg80211: Always ignore ML element
wifi: ieee80211: add definitions for RNR MLD params
wifi: cfg80211: use a struct for inform_single_bss data
wifi: ieee80211: add structs for TBTT information access
wifi: cfg80211: use structs for TBTT information access
wifi: cfg80211: handle BSS data contained in ML probe responses
wifi: cfg80211: do not scan disabled links on 6GHz
wifi: cfg80211: stop parsing after allocation failure
wifi: cfg80211: search all RNR elements for colocated APs
Ilan Peer (4):
wifi: cfg80211: Support changes in AP MLD link state change
wifi: mac80211: Include Multi-Link in CRC calculation
wifi: ieee80211: Fix the common size calculation for reconfiguration ML
wifi: mac80211: Support link removal using Reconfiguration ML element
Johannes Berg (5):
wifi: ieee80211: reorder presence checks in MLE per-STA profile
wifi: mac80211: agg-tx: add a few locking assertions
wifi: mac80211: agg-tx: prevent start/stop race
wifi: update multi-link element STA reconfig
wifi: mac80211: check EHT basic MCS/NSS set
include/linux/ieee80211.h | 174 ++++++--
include/net/cfg80211.h | 14 +
include/uapi/linux/nl80211.h | 3 +
net/mac80211/agg-tx.c | 14 +-
net/mac80211/ieee80211_i.h | 3 +
net/mac80211/mlme.c | 251 +++++++++++-
net/mac80211/util.c | 4 +
net/wireless/nl80211.c | 43 ++
net/wireless/scan.c | 748 ++++++++++++++++++++++++++---------
net/wireless/trace.h | 16 +
10 files changed, 1045 insertions(+), 225 deletions(-)
--
2.38.1
From: Johannes Berg <[email protected]>
Check that all the NSS in the EHT basic MCS/NSS set
are actually supported, otherwise disable EHT for the
connection.
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 28 ++++++++----
net/mac80211/mlme.c | 89 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 108 insertions(+), 9 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index d2025c986b0f..fa679613c562 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1996,12 +1996,18 @@ struct ieee80211_mu_edca_param_set {
* @rx_tx_mcs13_max_nss: indicates the maximum number of spatial streams
* supported for reception and the maximum number of spatial streams
* supported for transmission for MCS 12 - 13.
+ * @rx_tx_max_nss: array of the previous fields for easier loop access
*/
struct ieee80211_eht_mcs_nss_supp_20mhz_only {
- u8 rx_tx_mcs7_max_nss;
- u8 rx_tx_mcs9_max_nss;
- u8 rx_tx_mcs11_max_nss;
- u8 rx_tx_mcs13_max_nss;
+ union {
+ struct {
+ u8 rx_tx_mcs7_max_nss;
+ u8 rx_tx_mcs9_max_nss;
+ u8 rx_tx_mcs11_max_nss;
+ u8 rx_tx_mcs13_max_nss;
+ };
+ u8 rx_tx_max_nss[4];
+ };
};
/**
@@ -2021,11 +2027,17 @@ struct ieee80211_eht_mcs_nss_supp_20mhz_only {
* @rx_tx_mcs13_max_nss: indicates the maximum number of spatial streams
* supported for reception and the maximum number of spatial streams
* supported for transmission for MCS 12 - 13.
+ * @rx_tx_max_nss: array of the previous fields for easier loop access
*/
struct ieee80211_eht_mcs_nss_supp_bw {
- u8 rx_tx_mcs9_max_nss;
- u8 rx_tx_mcs11_max_nss;
- u8 rx_tx_mcs13_max_nss;
+ union {
+ struct {
+ u8 rx_tx_mcs9_max_nss;
+ u8 rx_tx_mcs11_max_nss;
+ u8 rx_tx_mcs13_max_nss;
+ };
+ u8 rx_tx_max_nss[3];
+ };
};
/**
@@ -2078,7 +2090,7 @@ struct ieee80211_eht_cap_elem {
*/
struct ieee80211_eht_operation {
u8 params;
- __le32 basic_mcs_nss;
+ struct ieee80211_eht_mcs_nss_supp_20mhz_only basic_mcs_nss;
u8 optional[];
} __packed;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 06ba809f83a1..13226349e80e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -4692,6 +4692,89 @@ ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata,
return false;
}
+static u8
+ieee80211_get_eht_cap_mcs_nss(const struct ieee80211_sta_he_cap *sta_he_cap,
+ const struct ieee80211_sta_eht_cap *sta_eht_cap,
+ unsigned int idx, int bw)
+{
+ u8 he_phy_cap0 = sta_he_cap->he_cap_elem.phy_cap_info[0];
+ u8 eht_phy_cap0 = sta_eht_cap->eht_cap_elem.phy_cap_info[0];
+
+ /* handle us being a 20 MHz-only EHT STA - with four values
+ * for MCS 0-7, 8-9, 10-11, 12-13.
+ */
+ if (!(he_phy_cap0 & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL))
+ return sta_eht_cap->eht_mcs_nss_supp.only_20mhz.rx_tx_max_nss[idx];
+
+ /* the others have MCS 0-9 together, rather than separately from 0-7 */
+ if (idx > 0)
+ idx--;
+
+ switch (bw) {
+ case 0:
+ return sta_eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_max_nss[idx];
+ case 1:
+ if (!(he_phy_cap0 &
+ (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)))
+ return 0xff; /* pass check */
+ return sta_eht_cap->eht_mcs_nss_supp.bw._160.rx_tx_max_nss[idx];
+ case 2:
+ if (!(eht_phy_cap0 & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ))
+ return 0xff; /* pass check */
+ return sta_eht_cap->eht_mcs_nss_supp.bw._320.rx_tx_max_nss[idx];
+ }
+
+ WARN_ON(1);
+ return 0;
+}
+
+static bool
+ieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ const struct ieee80211_eht_operation *eht_op)
+{
+ const struct ieee80211_sta_he_cap *sta_he_cap =
+ ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
+ const struct ieee80211_sta_eht_cap *sta_eht_cap =
+ ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
+ const struct ieee80211_eht_mcs_nss_supp_20mhz_only *req;
+ unsigned int i;
+
+ if (!sta_he_cap || !sta_eht_cap || !eht_op)
+ return false;
+
+ req = &eht_op->basic_mcs_nss;
+
+ for (i = 0; i < ARRAY_SIZE(req->rx_tx_max_nss); i++) {
+ u8 req_rx_nss, req_tx_nss;
+ unsigned int bw;
+
+ req_rx_nss = u8_get_bits(req->rx_tx_max_nss[i],
+ IEEE80211_EHT_MCS_NSS_RX);
+ req_tx_nss = u8_get_bits(req->rx_tx_max_nss[i],
+ IEEE80211_EHT_MCS_NSS_TX);
+
+ for (bw = 0; bw < 3; bw++) {
+ u8 have, have_rx_nss, have_tx_nss;
+
+ have = ieee80211_get_eht_cap_mcs_nss(sta_he_cap,
+ sta_eht_cap,
+ i, bw);
+ have_rx_nss = u8_get_bits(have,
+ IEEE80211_EHT_MCS_NSS_RX);
+ have_tx_nss = u8_get_bits(have,
+ IEEE80211_EHT_MCS_NSS_TX);
+
+ if (req_rx_nss > have_rx_nss ||
+ req_tx_nss > have_tx_nss)
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
struct ieee80211_link_data *link,
struct cfg80211_bss *cbss,
@@ -4847,11 +4930,15 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
else
eht_oper = NULL;
+ if (!ieee80211_verify_sta_eht_mcs_support(sdata, sband, eht_oper))
+ *conn_flags |= IEEE80211_CONN_DISABLE_EHT;
+
eht_ml_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_MULTI_LINK,
cbss_ies->data, cbss_ies->len);
/* data + 1 / datalen - 1 since it's an extended element */
- if (eht_ml_elem &&
+ if (!(*conn_flags & IEEE80211_CONN_DISABLE_EHT) &&
+ eht_ml_elem &&
ieee80211_mle_type_ok(eht_ml_elem->data + 1,
IEEE80211_ML_CONTROL_TYPE_BASIC,
eht_ml_elem->datalen - 1)) {
--
2.38.1
From: Benjamin Berg <[email protected]>
An AP reporting colocated APs may send more than one reduced neighbor
report element. As such, iterate all elements instead of only parsing
the first one when looking for colocated APs.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 127 ++++++++++++++++++++++----------------------
1 file changed, 63 insertions(+), 64 deletions(-)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index b2cf7abd5ad0..465334b3960e 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -643,90 +643,89 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
int n_coloc = 0, ret;
LIST_HEAD(ap_list);
- elem = cfg80211_find_elem(WLAN_EID_REDUCED_NEIGHBOR_REPORT, ies->data,
- ies->len);
- if (!elem)
- return 0;
-
- pos = elem->data;
- end = pos + elem->datalen;
-
ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp);
if (ret)
return ret;
- /* RNR IE may contain more than one NEIGHBOR_AP_INFO */
- while (pos + sizeof(*ap_info) <= end) {
- enum nl80211_band band;
- int freq;
- u8 length, i, count;
+ for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
+ ies->data, ies->len) {
+ pos = elem->data;
+ end = elem->data + elem->datalen;
- 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;
+ /* RNR IE may contain more than one NEIGHBOR_AP_INFO */
+ while (pos + sizeof(*ap_info) <= end) {
+ enum nl80211_band band;
+ int freq;
+ u8 length, i, count;
- pos += sizeof(*ap_info);
+ 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;
- if (!ieee80211_operating_class_to_band(ap_info->op_class,
- &band))
- break;
+ pos += sizeof(*ap_info);
- freq = ieee80211_channel_to_frequency(ap_info->channel, band);
+ if (!ieee80211_operating_class_to_band(ap_info->op_class,
+ &band))
+ break;
- if (end - pos < count * length)
- break;
+ freq = ieee80211_channel_to_frequency(ap_info->channel,
+ band);
- if (u8_get_bits(ap_info->tbtt_info_hdr,
- IEEE80211_AP_INFO_TBTT_HDR_TYPE) !=
- IEEE80211_TBTT_INFO_TYPE_TBTT) {
- pos += count * length;
- continue;
- }
+ if (end - pos < count * length)
+ break;
- /*
- * 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 (u8_get_bits(ap_info->tbtt_info_hdr,
+ IEEE80211_AP_INFO_TBTT_HDR_TYPE) !=
+ IEEE80211_TBTT_INFO_TYPE_TBTT) {
+ pos += count * length;
+ continue;
+ }
- for (i = 0; i < count; i++) {
- struct cfg80211_colocated_ap *entry;
+ /* 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;
+ }
- entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN,
- GFP_ATOMIC);
+ for (i = 0; i < count; i++) {
+ struct cfg80211_colocated_ap *entry;
- if (!entry)
- goto error;
+ entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN,
+ GFP_ATOMIC);
- entry->center_freq = freq;
+ if (!entry)
+ goto error;
- if (!cfg80211_parse_ap_info(entry, pos, length,
- ssid_elem, s_ssid_tmp)) {
- n_coloc++;
- list_add_tail(&entry->list, &ap_list);
- } else {
- kfree(entry);
- }
+ entry->center_freq = freq;
+
+ if (!cfg80211_parse_ap_info(entry, pos, length,
+ ssid_elem,
+ s_ssid_tmp)) {
+ n_coloc++;
+ list_add_tail(&entry->list, &ap_list);
+ } else {
+ kfree(entry);
+ }
- pos += length;
+ pos += length;
+ }
}
- }
error:
- if (pos != end) {
- cfg80211_free_coloc_ap_list(&ap_list);
- return 0;
+ if (pos != end) {
+ cfg80211_free_coloc_ap_list(&ap_list);
+ return 0;
+ }
}
list_splice_tail(&ap_list, list);
--
2.38.1
From: Ilan Peer <[email protected]>
Include the Multi-Link elements found in beacon frames
in the CRC calculation, as these elements are intended
to reflect changes in the AP MLD state.
Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/util.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 2c53f6e17cfe..35701316dccf 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -987,6 +987,10 @@ ieee80211_parse_extension_element(u32 *crc,
const struct ieee80211_multi_link_elem *mle =
(void *)data;
+ if (crc)
+ *crc = crc32_be(*crc, (void *)elem,
+ elem->datalen + 2);
+
switch (le16_get_bits(mle->control,
IEEE80211_ML_CONTROL_TYPE)) {
case IEEE80211_ML_CONTROL_TYPE_BASIC:
--
2.38.1
From: Ilan Peer <[email protected]>
The common information length is found in the first octet of the common
information.
Fixes: 0f48b8b88aa9 ("wifi: ieee80211: add definitions for multi-link element")
Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index aeedd49e5101..97edc3b404dd 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4617,15 +4617,12 @@ static inline u8 ieee80211_mle_common_size(const u8 *data)
case IEEE80211_ML_CONTROL_TYPE_BASIC:
case IEEE80211_ML_CONTROL_TYPE_PREQ:
case IEEE80211_ML_CONTROL_TYPE_TDLS:
+ case IEEE80211_ML_CONTROL_TYPE_RECONF:
/*
* The length is the first octet pointed by mle->variable so no
* need to add anything
*/
break;
- case IEEE80211_ML_CONTROL_TYPE_RECONF:
- if (control & IEEE80211_MLC_RECONF_PRES_MLD_MAC_ADDR)
- common += ETH_ALEN;
- return common;
case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS:
if (control & IEEE80211_MLC_PRIO_ACCESS_PRES_AP_MLD_MAC_ADDR)
common += ETH_ALEN;
--
2.38.1
From: Benjamin Berg <[email protected]>
Add the definitions necessary to parse the MLD parameters
included in an RNR element.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 251998be24d0..7afd08d2de2f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4485,6 +4485,7 @@ static inline bool for_each_element_completed(const struct element *element,
#define IEEE80211_TBTT_INFO_TYPE_MLD 1
#define IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM 9
#define IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM 13
+#define IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM_MLD_PARAM 16
#define IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED 0x01
#define IEEE80211_RNR_TBTT_PARAMS_SAME_SSID 0x02
@@ -4508,6 +4509,20 @@ enum ieee80211_range_params_max_total_ltf {
IEEE80211_RANGE_PARAMS_MAX_TOTAL_LTF_UNSPECIFIED,
};
+/*
+ * reduced neighbor report, based on Draft P802.11be_D3.0,
+ * section 9.4.2.170.2.
+ */
+struct ieee80211_rnr_mld_params {
+ u8 mld_id;
+ __le16 params;
+} __packed;
+
+#define IEEE80211_RNR_MLD_PARAMS_LINK_ID 0x000F
+#define IEEE80211_RNR_MLD_PARAMS_BSS_CHANGE_COUNT 0x0FF0
+#define IEEE80211_RNR_MLD_PARAMS_UPDATES_INCLUDED 0x1000
+#define IEEE80211_RNR_MLD_PARAMS_DISABLED_LINK 0x2000
+
/* multi-link device */
#define IEEE80211_MLD_MAX_NUM_LINKS 15
--
2.38.1
From: Johannes Berg <[email protected]>
In ieee80211_mle_sta_prof_size_ok(), the presence
checks aren't ordered by field order, so that's a
bit confusing. Reorder them.
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 98223b665456..fc3c26f1b718 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4876,9 +4876,6 @@ static inline bool ieee80211_mle_basic_sta_prof_size_ok(const u8 *data,
info_len += 8;
if (control & IEEE80211_MLE_STA_CONTROL_DTIM_INFO_PRESENT)
info_len += 2;
- if (control & IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT)
- info_len += 1;
-
if (control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE &&
control & IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE) {
if (control & IEEE80211_MLE_STA_CONTROL_NSTR_BITMAP_SIZE)
@@ -4886,6 +4883,8 @@ static inline bool ieee80211_mle_basic_sta_prof_size_ok(const u8 *data,
else
info_len += 1;
}
+ if (control & IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT)
+ info_len += 1;
return prof->sta_info_len >= info_len &&
fixed + prof->sta_info_len <= len;
--
2.38.1
From: Benjamin Berg <[email protected]>
Make the data access a bit nicer overall by using structs. There is a
small change here to also accept a TBTT information length of eight
bytes as we do not require the 20 MHz PSD information.
This also fixes a bug reading the short SSID on big endian machines.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 3 --
net/wireless/scan.c | 61 +++++++++++++++++++++------------------
2 files changed, 33 insertions(+), 31 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 5a27c232afdb..e145af7448a3 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4483,9 +4483,6 @@ static inline bool for_each_element_completed(const struct element *element,
#define IEEE80211_AP_INFO_TBTT_HDR_COUNT 0xF0
#define IEEE80211_TBTT_INFO_TYPE_TBTT 0
#define IEEE80211_TBTT_INFO_TYPE_MLD 1
-#define IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM 9
-#define IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM 13
-#define IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM_MLD_PARAM 16
#define IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED 0x01
#define IEEE80211_RNR_TBTT_PARAMS_SAME_SSID 0x02
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 974a6a8240dd..f0b4d7671d17 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -574,39 +574,41 @@ static void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list)
static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
const u8 *pos, u8 length,
const struct element *ssid_elem,
- int s_ssid_tmp)
+ u32 s_ssid_tmp)
{
- /* skip the TBTT offset */
- pos++;
+ u8 bss_params;
- /* ignore entries with invalid BSSID */
- if (!is_valid_ether_addr(pos))
- return -EINVAL;
-
- memcpy(entry->bssid, pos, ETH_ALEN);
- pos += ETH_ALEN;
+ /* The length is already verified by the caller to contain bss_params */
+ if (length > sizeof(struct ieee80211_tbtt_info_7_8_9)) {
+ struct ieee80211_tbtt_info_ge_11 *tbtt_info = (void *)pos;
- if (length >= IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM) {
- memcpy(&entry->short_ssid, pos,
- sizeof(entry->short_ssid));
+ memcpy(entry->bssid, tbtt_info->bssid, ETH_ALEN);
+ entry->short_ssid = le32_to_cpu(tbtt_info->short_ssid);
entry->short_ssid_valid = true;
- pos += 4;
+
+ bss_params = tbtt_info->bss_params;
+ } else {
+ struct ieee80211_tbtt_info_7_8_9 *tbtt_info = (void *)pos;
+
+ memcpy(entry->bssid, tbtt_info->bssid, ETH_ALEN);
+
+ bss_params = tbtt_info->bss_params;
}
+ /* ignore entries with invalid BSSID */
+ if (!is_valid_ether_addr(entry->bssid))
+ return -EINVAL;
+
/* skip non colocated APs */
- if (!cfg80211_parse_bss_param(*pos, entry))
+ if (!cfg80211_parse_bss_param(bss_params, entry))
return -EINVAL;
- pos++;
- if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM) {
- /*
- * no information about the short ssid. Consider the entry valid
- * for now. It would later be dropped in case there are explicit
- * SSIDs that need to be matched
- */
- if (!entry->same_ssid)
- return 0;
- }
+ /* no information about the short ssid. Consider the entry valid
+ * for now. It would later be dropped in case there are explicit
+ * SSIDs that need to be matched
+ */
+ if (!entry->same_ssid && !entry->short_ssid_valid)
+ return 0;
if (entry->same_ssid) {
entry->short_ssid = s_ssid_tmp;
@@ -617,10 +619,10 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
* cfg80211_parse_colocated_ap(), before calling this
* function.
*/
- memcpy(&entry->ssid, &ssid_elem->data,
- ssid_elem->datalen);
+ memcpy(&entry->ssid, &ssid_elem->data, ssid_elem->datalen);
entry->ssid_len = ssid_elem->datalen;
}
+
return 0;
}
@@ -682,8 +684,11 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
* next AP info
*/
if (band != NL80211_BAND_6GHZ ||
- (length != IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM &&
- length < IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM)) {
+ !(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;
}
--
2.38.1
From: Benjamin Berg <[email protected]>
The argument is getting quite large, so use a struct internally to pass
around the information.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 209 ++++++++++++++++++++++++--------------------
1 file changed, 112 insertions(+), 97 deletions(-)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 095dc9db8750..974a6a8240dd 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1644,12 +1644,6 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
return true;
}
-struct cfg80211_non_tx_bss {
- struct cfg80211_bss *tx_bss;
- u8 max_bssid_indicator;
- u8 bssid_index;
-};
-
static void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known,
const struct cfg80211_bss_ies *new_ies,
const struct cfg80211_bss_ies *old_ies)
@@ -1977,17 +1971,30 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
return alt_channel;
}
+struct cfg80211_inform_single_bss_data {
+ struct cfg80211_inform_bss *drv_data;
+ enum cfg80211_bss_frame_type ftype;
+ u8 bssid[ETH_ALEN];
+ u64 tsf;
+ u16 capability;
+ u16 beacon_interval;
+ const u8 *ie;
+ size_t ielen;
+
+ /* Set for nontransmitted BSSIDs */
+ struct cfg80211_bss *source_bss;
+ u8 max_bssid_indicator;
+ u8 bssid_index;
+};
+
/* Returned bss is reference counted and must be cleaned up appropriately. */
static struct cfg80211_bss *
cfg80211_inform_single_bss_data(struct wiphy *wiphy,
- struct cfg80211_inform_bss *data,
- enum cfg80211_bss_frame_type ftype,
- const u8 *bssid, u64 tsf, u16 capability,
- u16 beacon_interval, const u8 *ie, size_t ielen,
- struct cfg80211_non_tx_bss *non_tx_data,
+ struct cfg80211_inform_single_bss_data *data,
gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_inform_bss *drv_data = data->drv_data;
struct cfg80211_bss_ies *ies;
struct ieee80211_channel *channel;
struct cfg80211_internal_bss tmp = {}, *res;
@@ -1999,40 +2006,41 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
return NULL;
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
- (data->signal < 0 || data->signal > 100)))
+ (drv_data->signal < 0 || drv_data->signal > 100)))
return NULL;
- channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan,
- data->scan_width);
+ 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, bssid, ETH_ALEN);
+ memcpy(tmp.pub.bssid, data->bssid, ETH_ALEN);
tmp.pub.channel = channel;
- tmp.pub.scan_width = data->scan_width;
- tmp.pub.signal = data->signal;
- tmp.pub.beacon_interval = beacon_interval;
- tmp.pub.capability = capability;
- tmp.ts_boottime = data->boottime_ns;
- tmp.parent_tsf = data->parent_tsf;
- ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
-
- if (non_tx_data) {
- tmp.pub.transmitted_bss = non_tx_data->tx_bss;
- ts = bss_from_pub(non_tx_data->tx_bss)->ts;
- tmp.pub.bssid_index = non_tx_data->bssid_index;
- tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
+ tmp.pub.scan_width = drv_data->scan_width;
+ tmp.pub.signal = drv_data->signal;
+ 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) {
+ tmp.pub.transmitted_bss = data->source_bss;
+ ts = bss_from_pub(data->source_bss)->ts;
+ tmp.pub.bssid_index = data->bssid_index;
+ tmp.pub.max_bssid_indicator = data->max_bssid_indicator;
} else {
ts = jiffies;
if (channel->band == NL80211_BAND_60GHZ) {
- bss_type = capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
+ bss_type = data->capability &
+ WLAN_CAPABILITY_DMG_TYPE_MASK;
if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
regulatory_hint_found_beacon(wiphy, channel,
gfp);
} else {
- if (capability & WLAN_CAPABILITY_ESS)
+ if (data->capability & WLAN_CAPABILITY_ESS)
regulatory_hint_found_beacon(wiphy, channel,
gfp);
}
@@ -2046,15 +2054,15 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
* override the IEs pointer should we have received an earlier
* indication of Probe Response data.
*/
- ies = kzalloc(sizeof(*ies) + ielen, gfp);
+ ies = kzalloc(sizeof(*ies) + data->ielen, gfp);
if (!ies)
return NULL;
- ies->len = ielen;
- ies->tsf = tsf;
+ ies->len = data->ielen;
+ ies->tsf = data->tsf;
ies->from_beacon = false;
- memcpy(ies->data, ie, ielen);
+ memcpy(ies->data, data->ie, data->ielen);
- switch (ftype) {
+ switch (data->ftype) {
case CFG80211_BSS_FTYPE_BEACON:
ies->from_beacon = true;
fallthrough;
@@ -2067,7 +2075,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
}
rcu_assign_pointer(tmp.pub.ies, ies);
- signal_valid = data->chan == channel;
+ signal_valid = drv_data->chan == channel;
spin_lock_bh(&rdev->bss_lock);
res = __cfg80211_bss_update(rdev, &tmp, signal_valid, ts);
if (!res)
@@ -2075,12 +2083,11 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
rdev_inform_bss(rdev, &res->pub, ies, data->drv_data);
- if (non_tx_data) {
+ if (data->source_bss) {
/* this is a nontransmitting bss, we need to add it to
* transmitting bss' list if it is not there
*/
- if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
- &res->pub)) {
+ if (cfg80211_add_nontrans_list(data->source_bss, &res->pub)) {
if (__cfg80211_unlink_bss(rdev, res)) {
rdev->bss_generation++;
res = NULL;
@@ -2173,43 +2180,47 @@ size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
}
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,
- const u8 *bssid, u64 tsf,
- u16 beacon_interval, const u8 *ie,
- size_t ielen,
- struct cfg80211_non_tx_bss *non_tx_data,
- gfp_t gfp)
-{
+static void
+cfg80211_parse_mbssid_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,
+ .tsf = tx_data->tsf,
+ .beacon_interval = tx_data->beacon_interval,
+ .source_bss = source_bss,
+ };
const u8 *mbssid_index_ie;
const struct element *elem, *sub;
- size_t new_ie_len;
- u8 new_bssid[ETH_ALEN];
u8 *new_ie, *profile;
u64 seen_indices = 0;
- u16 capability;
struct cfg80211_bss *bss;
- if (!non_tx_data)
+ if (!source_bss)
return;
- if (!cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
+ if (!cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
+ tx_data->ie, tx_data->ielen))
return;
if (!wiphy->support_mbssid)
return;
if (wiphy->support_only_he_mbssid &&
- !cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
+ !cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
+ tx_data->ie, tx_data->ielen))
return;
new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
if (!new_ie)
return;
- profile = kmalloc(ielen, gfp);
+ profile = kmalloc(tx_data->ielen, gfp);
if (!profile)
goto out;
- for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, ie, ielen) {
+ for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID,
+ tx_data->ie, tx_data->ielen) {
if (elem->datalen < 4)
continue;
if (elem->data[0] < 1 || (int)elem->data[0] > 8)
@@ -2231,12 +2242,13 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
continue;
}
- memset(profile, 0, ielen);
- profile_len = cfg80211_merge_profile(ie, ielen,
+ memset(profile, 0, tx_data->ielen);
+ profile_len = cfg80211_merge_profile(tx_data->ie,
+ tx_data->ielen,
elem,
sub,
profile,
- ielen);
+ tx_data->ielen);
/* found a Nontransmitted BSSID Profile */
mbssid_index_ie = cfg80211_find_ie
@@ -2256,31 +2268,27 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
seen_indices |= BIT_ULL(mbssid_index_ie[2]);
- non_tx_data->bssid_index = mbssid_index_ie[2];
- non_tx_data->max_bssid_indicator = elem->data[0];
+ data.bssid_index = mbssid_index_ie[2];
+ data.max_bssid_indicator = elem->data[0];
+
+ cfg80211_gen_new_bssid(tx_data->bssid,
+ data.max_bssid_indicator,
+ data.bssid_index,
+ data.bssid);
- cfg80211_gen_new_bssid(bssid,
- non_tx_data->max_bssid_indicator,
- non_tx_data->bssid_index,
- new_bssid);
memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
- new_ie_len = cfg80211_gen_new_ie(ie, ielen,
+ data.ie = new_ie;
+ data.ielen = cfg80211_gen_new_ie(tx_data->ie,
+ tx_data->ielen,
profile,
- profile_len, new_ie,
+ profile_len,
+ new_ie,
IEEE80211_MAX_DATA_LEN);
- if (!new_ie_len)
+ if (!data.ielen)
continue;
- capability = get_unaligned_le16(profile + 2);
- bss = cfg80211_inform_single_bss_data(wiphy, data,
- ftype,
- new_bssid, tsf,
- capability,
- beacon_interval,
- new_ie,
- new_ie_len,
- non_tx_data,
- gfp);
+ data.capability = get_unaligned_le16(profile + 2);
+ bss = cfg80211_inform_single_bss_data(wiphy, &data, gfp);
if (!bss)
break;
cfg80211_put_bss(wiphy, bss);
@@ -2360,18 +2368,24 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
u16 beacon_interval, const u8 *ie, size_t ielen,
gfp_t gfp)
{
+ struct cfg80211_inform_single_bss_data inform_data = {
+ .drv_data = data,
+ .ftype = ftype,
+ .tsf = tsf,
+ .capability = capability,
+ .beacon_interval = beacon_interval,
+ .ie = ie,
+ .ielen = ielen,
+ };
struct cfg80211_bss *res;
- struct cfg80211_non_tx_bss non_tx_data;
- res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
- capability, beacon_interval, ie,
- ielen, NULL, gfp);
+ memcpy(inform_data.bssid, bssid, ETH_ALEN);
+
+ res = cfg80211_inform_single_bss_data(wiphy, &inform_data, gfp);
if (!res)
return NULL;
- non_tx_data.tx_bss = res;
- cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
- beacon_interval, ie, ielen, &non_tx_data,
- gfp);
+
+ cfg80211_parse_mbssid_data(wiphy, &inform_data, res, gfp);
return res;
}
EXPORT_SYMBOL(cfg80211_inform_bss_data);
@@ -2517,12 +2531,13 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp)
{
+ struct cfg80211_inform_single_bss_data inform_data = {
+ .drv_data = data,
+ .ie = mgmt->u.probe_resp.variable,
+ .ielen = len - offsetof(struct ieee80211_mgmt,
+ u.probe_resp.variable),
+ };
struct cfg80211_bss *res;
- const u8 *ie = mgmt->u.probe_resp.variable;
- size_t ielen = len - offsetof(struct ieee80211_mgmt,
- u.probe_resp.variable);
- enum cfg80211_bss_frame_type ftype;
- struct cfg80211_non_tx_bss non_tx_data = {};
res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
len, gfp);
@@ -2533,15 +2548,15 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
if (ieee80211_is_s1g_beacon(mgmt->frame_control))
return res;
- ftype = ieee80211_is_beacon(mgmt->frame_control) ?
+ inform_data.ftype = ieee80211_is_beacon(mgmt->frame_control) ?
CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
- non_tx_data.tx_bss = res;
+ memcpy(inform_data.bssid, mgmt->bssid, ETH_ALEN);
+ inform_data.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
+ inform_data.beacon_interval =
+ le16_to_cpu(mgmt->u.probe_resp.beacon_int);
/* process each non-transmitting bss */
- cfg80211_parse_mbssid_data(wiphy, data, ftype, mgmt->bssid,
- le64_to_cpu(mgmt->u.probe_resp.timestamp),
- le16_to_cpu(mgmt->u.probe_resp.beacon_int),
- ie, ielen, &non_tx_data, gfp);
+ cfg80211_parse_mbssid_data(wiphy, &inform_data, res, gfp);
return res;
}
--
2.38.1
From: Benjamin Berg <[email protected]>
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 <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 361 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 354 insertions(+), 7 deletions(-)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index f0b4d7671d17..46488650ecbc 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -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; res->sta_prof[idx] && idx < IEEE80211_MLD_MAX_NUM_LINKS;
+ 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);
--
2.38.1
From: Ilan Peer <[email protected]>
Add support for handling link removal indicated by the
Reconfiguration Multi-Link element.
Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 33 ++++++++
net/mac80211/ieee80211_i.h | 3 +
net/mac80211/mlme.c | 159 +++++++++++++++++++++++++++++++++++++
3 files changed, 195 insertions(+)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index e145af7448a3..98223b665456 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4891,6 +4891,39 @@ static inline bool ieee80211_mle_basic_sta_prof_size_ok(const u8 *data,
fixed + prof->sta_info_len <= len;
}
+#define IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID 0x000f
+#define IEEE80211_MLE_STA_RECONF_CONTROL_COMPLETE_PROFILE 0x0010
+#define IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT 0x0020
+#define IEEE80211_MLE_STA_RECONF_CONTROL_DELETE_TIMER_PRESENT 0x0040
+
+/**
+ * ieee80211_mle_reconf_sta_prof_size_ok - validate reconfiguration multi-link
+ * element sta profile size.
+ * @data: pointer to the sub element data
+ * @len: length of the containing sub element
+ */
+static inline bool ieee80211_mle_reconf_sta_prof_size_ok(const u8 *data,
+ size_t len)
+{
+ const struct ieee80211_mle_per_sta_profile *prof = (const void *)data;
+ u16 control;
+ u8 fixed = sizeof(*prof);
+ u8 info_len = 1;
+
+ if (len < fixed)
+ return false;
+
+ control = le16_to_cpu(prof->control);
+
+ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT)
+ info_len += ETH_ALEN;
+ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_DELETE_TIMER_PRESENT)
+ info_len += 2;
+
+ return prof->sta_info_len >= info_len &&
+ fixed + prof->sta_info_len - 1 <= len;
+}
+
#define for_each_mle_subelement(_elem, _data, _len) \
if (ieee80211_mle_size_ok(_data, _len)) \
for_each_element(_elem, \
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index be3294719cb4..b5678f2d83f5 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -551,6 +551,9 @@ struct ieee80211_if_managed {
*/
u8 *assoc_req_ies;
size_t assoc_req_ies_len;
+
+ struct delayed_work ml_reconf_work;
+ u16 removed_links;
};
struct ieee80211_if_ibss {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 30588703ffd3..f0fa1f0991ed 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5606,6 +5606,161 @@ static bool ieee80211_config_puncturing(struct ieee80211_link_data *link,
return true;
}
+static void ieee80211_ml_reconf_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.ml_reconf_work.work);
+ u16 new_valid_links, new_active_links, new_dormant_links;
+ int ret;
+
+ if (!sdata->u.mgd.removed_links)
+ return;
+
+ sdata_info(sdata,
+ "MLO Reconfiguration: work: valid=0x%x, removed=0x%x\n",
+ sdata->vif.valid_links, sdata->u.mgd.removed_links);
+
+ new_valid_links = sdata->vif.valid_links & ~sdata->u.mgd.removed_links;
+ if (new_valid_links == sdata->vif.valid_links)
+ return;
+
+ if (!new_valid_links ||
+ !(new_valid_links & ~sdata->vif.dormant_links)) {
+ sdata_info(sdata, "No valid links after reconfiguration\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ new_active_links = sdata->vif.active_links & ~sdata->u.mgd.removed_links;
+ if (new_active_links != sdata->vif.active_links) {
+ if (!new_active_links)
+ new_active_links = ffs(new_valid_links &
+ ~sdata->vif.dormant_links) - 1;
+
+ ret = ieee80211_set_active_links(&sdata->vif,
+ new_active_links);
+ if (ret) {
+ sdata_info(sdata,
+ "Failed setting active links\n");
+ goto out;
+ }
+ }
+
+ new_dormant_links = sdata->vif.dormant_links & ~sdata->u.mgd.removed_links;
+
+ ret = ieee80211_vif_set_links(sdata, new_valid_links,
+ new_dormant_links);
+ if (ret)
+ sdata_info(sdata, "Failed setting valid links\n");
+
+out:
+ if (!ret) {
+ sdata_lock(sdata);
+ cfg80211_cqm_links_state_change_notify(sdata->dev,
+ sdata->u.mgd.removed_links);
+ sdata_unlock(sdata);
+ } else {
+ __ieee80211_disconnect(sdata);
+ }
+
+ sdata->u.mgd.removed_links = 0;
+}
+
+static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata,
+ struct ieee802_11_elems *elems)
+{
+ const struct ieee80211_multi_link_elem *ml;
+ const struct element *sub;
+ size_t ml_len;
+ unsigned long removed_links = 0;
+ u16 link_removal_timeout[IEEE80211_MLD_MAX_NUM_LINKS] = {};
+ u8 link_id;
+ u32 delay;
+
+ if (!ieee80211_vif_is_mld(&sdata->vif) || !elems->ml_reconf)
+ return;
+
+ ml_len = cfg80211_defragment_element(elems->ml_reconf_elem,
+ elems->ie_start,
+ elems->total_len,
+ elems->scratch_pos,
+ elems->scratch + elems->scratch_len -
+ elems->scratch_pos,
+ WLAN_EID_FRAGMENT);
+
+ elems->ml_reconf = (const void *)elems->scratch_pos;
+ elems->ml_reconf_len = ml_len;
+ ml = elems->ml_reconf;
+
+ /* Directly parse the sub elements as the common information doesn't
+ * hold any useful information.
+ */
+ for_each_mle_subelement(sub, (u8 *)ml, ml_len) {
+ struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
+ u8 *pos = prof->variable;
+ u16 control;
+
+ if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
+ continue;
+
+ if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data,
+ sub->datalen))
+ return;
+
+ control = le16_to_cpu(prof->control);
+ link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID;
+
+ removed_links |= BIT(link_id);
+
+ /* the MAC address should not be included, but handle it */
+ if (control &
+ IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT)
+ pos += 6;
+
+ /* According to Draft P802.11be_D3.0, the control should
+ * include the AP Removal Timer present. If the AP Removal Timer
+ * is not present assume immediate removal.
+ */
+ if (control &
+ IEEE80211_MLE_STA_RECONF_CONTROL_DELETE_TIMER_PRESENT)
+ link_removal_timeout[link_id] = le16_to_cpu(*(__le16 *)pos);
+ }
+
+ removed_links &= sdata->vif.valid_links;
+ if (!removed_links) {
+ /* In case the removal was cancelled, abort it */
+ if (sdata->u.mgd.removed_links) {
+ sdata->u.mgd.removed_links = 0;
+ cancel_delayed_work(&sdata->u.mgd.ml_reconf_work);
+ }
+ return;
+ }
+
+ delay = 0;
+ for_each_set_bit(link_id, &removed_links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct ieee80211_bss_conf *link_conf =
+ sdata_dereference(sdata->vif.link_conf[link_id], sdata);
+ u32 link_delay;
+
+ if (!link_conf) {
+ removed_links &= ~BIT(link_id);
+ continue;
+ }
+
+ link_delay = link_conf->beacon_int *
+ link_removal_timeout[link_id];
+
+ if (!delay)
+ delay = link_delay;
+ else
+ delay = min(delay, link_delay);
+ }
+
+ sdata->u.mgd.removed_links = removed_links;
+ schedule_delayed_work(&sdata->u.mgd.ml_reconf_work, TU_TO_JIFFIES(delay));
+}
+
static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
struct ieee80211_hdr *hdr, size_t len,
struct ieee80211_rx_status *rx_status)
@@ -5935,6 +6090,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
}
}
+ ieee80211_ml_reconfiguration(sdata, elems);
+
ieee80211_link_info_change_notify(sdata, link, changed);
free:
kfree(elems);
@@ -6561,6 +6718,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ieee80211_csa_connection_drop_work);
INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
ieee80211_tdls_peer_del_work);
+ INIT_DELAYED_WORK(&ifmgd->ml_reconf_work, ieee80211_ml_reconf_work);
timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0);
timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0);
timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0);
@@ -7573,6 +7731,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
wiphy_work_cancel(sdata->local->hw.wiphy,
&ifmgd->csa_connection_drop_work);
cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
+ cancel_delayed_work_sync(&ifmgd->ml_reconf_work);
sdata_lock(sdata);
if (ifmgd->assoc_data)
--
2.38.1
From: Johannes Berg <[email protected]>
This is all true today, but difficult to understand since
the callers are in other files etc. Add two new lockdep
assertions to make things easier to read.
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/agg-tx.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 3b651e7f5a73..118ad2e24dbb 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <[email protected]>
* Copyright 2007-2010, Intel Corporation
* Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2022 Intel Corporation
+ * Copyright (C) 2018 - 2023 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -457,6 +457,8 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
u8 tid = tid_tx->tid;
u16 buf_size;
+ lockdep_assert_held(&sta->ampdu_mlme.mtx);
+
/* activate the timer for the recipient's addBA response */
mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
@@ -795,6 +797,8 @@ void ieee80211_start_tx_ba_cb(struct sta_info *sta, int tid,
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
+ lockdep_assert_held(&sta->ampdu_mlme.mtx);
+
if (WARN_ON(test_and_set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state)))
return;
--
2.38.1
From: Johannes Berg <[email protected]>
There were crashes reported in this code, and the timer_shutdown()
warning in one of the previous patches indicates that the timeout
timer for the AP response (addba_resp_timer) is still armed while
we're stopping the aggregation session.
After a very long deliberation of the code, so far the only way I
could find that might cause this would be the following sequence:
- session start requested
- session start indicated to driver, but driver returns
IEEE80211_AMPDU_TX_START_DELAY_ADDBA
- session stop requested, sets HT_AGG_STATE_WANT_STOP
- session stop worker runs ___ieee80211_stop_tx_ba_session(),
sets HT_AGG_STATE_STOPPING
From here on, the order doesn't matter exactly, but:
1. driver calls ieee80211_start_tx_ba_cb_irqsafe(),
setting HT_AGG_STATE_START_CB
2. driver calls ieee80211_stop_tx_ba_cb_irqsafe(),
setting HT_AGG_STATE_STOP_CB
3. the worker will run ieee80211_start_tx_ba_cb() for
HT_AGG_STATE_START_CB
4. the worker will run ieee80211_stop_tx_ba_cb() for
HT_AGG_STATE_STOP_CB
(the order could also be 1./3./2./4.)
This will cause ieee80211_start_tx_ba_cb() to send out the AddBA
request frame to the AP and arm the timer, but we're already in
the middle of stopping and so the ieee80211_stop_tx_ba_cb() will
no longer assume it needs to stop anything.
Prevent this by checking for WANT_STOP/STOPPING in the start CB,
and warn if we're sending a frame on a stopping session.
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/agg-tx.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 118ad2e24dbb..b6b772685881 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -457,6 +457,10 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta,
u8 tid = tid_tx->tid;
u16 buf_size;
+ if (WARN_ON_ONCE(test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state) ||
+ test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)))
+ return;
+
lockdep_assert_held(&sta->ampdu_mlme.mtx);
/* activate the timer for the recipient's addBA response */
@@ -802,6 +806,10 @@ void ieee80211_start_tx_ba_cb(struct sta_info *sta, int tid,
if (WARN_ON(test_and_set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state)))
return;
+ if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state) ||
+ test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state))
+ return;
+
if (!test_bit(HT_AGG_STATE_SENT_ADDBA, &tid_tx->state)) {
ieee80211_send_addba_with_timeout(sta, tid_tx);
/* RESPONSE_RECEIVED state whould trigger the flow again */
--
2.38.1
From: Benjamin Berg <[email protected]>
The error handling code would break out of the loop incorrectly,
causing the rest of the message to be misinterpreted. Fix this by
also jumping out of the surrounding while loop, which will trigger
the error detection code.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index cf0ad544cb99..b2cf7abd5ad0 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -707,7 +707,7 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
GFP_ATOMIC);
if (!entry)
- break;
+ goto error;
entry->center_freq = freq;
@@ -723,6 +723,7 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
}
}
+error:
if (pos != end) {
cfg80211_free_coloc_ap_list(&ap_list);
return 0;
--
2.38.1
From: Benjamin Berg <[email protected]>
If a link is disabled on 6GHz, we should not send a probe request on the
channel to resolve it. Simply skip such RNR entries so that the link is
ignored.
Userspace can still see the link in the RNR and may generate an ML probe
request in order to associate to the (currently) disabled link.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 46488650ecbc..cf0ad544cb99 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -587,6 +587,13 @@ static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
entry->short_ssid_valid = true;
bss_params = tbtt_info->bss_params;
+
+ /* Ignore disabled links */
+ if (length >= offsetofend(typeof(*tbtt_info), mld_params)) {
+ if (le16_get_bits(tbtt_info->mld_params.params,
+ IEEE80211_RNR_MLD_PARAMS_DISABLED_LINK))
+ return -EINVAL;
+ }
} else {
struct ieee80211_tbtt_info_7_8_9 *tbtt_info = (void *)pos;
--
2.38.1
From: Benjamin Berg <[email protected]>
The helper functions to retrieve the EML capabilities and medium
synchronization delay both assume that the type is correct. Instead of
assuming the length is correct and still checking the type, add a new
helper to check both and don't do any verification.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 50 ++++++++++++++++++++++++---------------
net/mac80211/mlme.c | 3 ++-
2 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 97edc3b404dd..b107f21e1233 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4639,10 +4639,10 @@ static inline u8 ieee80211_mle_common_size(const u8 *data)
* ieee80211_mle_get_eml_sync_delay - returns the medium sync delay
* @data: pointer to the multi link EHT IE
*
- * The element is assumed to be big enough. This must be checked by
- * ieee80211_mle_size_ok().
- * If the medium synchronization can't be found (the type is not basic, or
- * the medium sync presence bit is clear), 0 will be returned.
+ * The element is assumed to be of the correct type (BASIC) and big enough,
+ * this must be checked using ieee80211_mle_type_ok().
+ *
+ * If the medium synchronization is not present, then 0 is returned.
*/
static inline u16 ieee80211_mle_get_eml_med_sync_delay(const u8 *data)
{
@@ -4650,13 +4650,7 @@ static inline u16 ieee80211_mle_get_eml_med_sync_delay(const u8 *data)
u16 control = le16_to_cpu(mle->control);
const u8 *common = mle->variable;
- if (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE) !=
- IEEE80211_ML_CONTROL_TYPE_BASIC)
- return 0;
-
- /* common points now at the beginning of
- * ieee80211_mle_basic_common_info
- */
+ /* common points now at the beginning of ieee80211_mle_basic_common_info */
common += sizeof(struct ieee80211_mle_basic_common_info);
if (!(control & IEEE80211_MLC_BASIC_PRES_MED_SYNC_DELAY))
@@ -4674,10 +4668,10 @@ static inline u16 ieee80211_mle_get_eml_med_sync_delay(const u8 *data)
* ieee80211_mle_get_eml_cap - returns the EML capability
* @data: pointer to the multi link EHT IE
*
- * The element is assumed to be big enough. This must be checked by
- * ieee80211_mle_size_ok().
- * If the EML capability can't be found (the type is not basic, or
- * the EML capability presence bit is clear), 0 will be returned.
+ * The element is assumed to be of the correct type (BASIC) and big enough,
+ * this must be checked using ieee80211_mle_type_ok().
+ *
+ * If the EML capability is not present, 0 will be returned.
*/
static inline u16 ieee80211_mle_get_eml_cap(const u8 *data)
{
@@ -4685,10 +4679,6 @@ static inline u16 ieee80211_mle_get_eml_cap(const u8 *data)
u16 control = le16_to_cpu(mle->control);
const u8 *common = mle->variable;
- if (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE) !=
- IEEE80211_ML_CONTROL_TYPE_BASIC)
- return 0;
-
/* common points now at the beginning of ieee80211_mle_basic_common_info */
common += sizeof(struct ieee80211_mle_basic_common_info);
@@ -4773,6 +4763,28 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len)
return mle->variable[0] >= common;
}
+/**
+ * ieee80211_mle_type_ok - validate multi-link element type and size
+ * @data: pointer to the element data
+ * @type: expected type of the element
+ * @len: length of the containing element
+ */
+static inline bool ieee80211_mle_type_ok(const u8 *data, u8 type, size_t len)
+{
+ const struct ieee80211_multi_link_elem *mle = (const void *)data;
+ u16 control;
+
+ if (!ieee80211_mle_size_ok(data, len))
+ return false;
+
+ control = le16_to_cpu(mle->control);
+
+ if (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE) == type)
+ return true;
+
+ return false;
+}
+
enum ieee80211_mle_subelems {
IEEE80211_MLE_SUBELEM_PER_STA_PROFILE = 0,
IEEE80211_MLE_SUBELEM_FRAGMENT = 254,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b8f8220cd9ff..30588703ffd3 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -4852,7 +4852,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
/* data + 1 / datalen - 1 since it's an extended element */
if (eht_ml_elem &&
- ieee80211_mle_size_ok(eht_ml_elem->data + 1,
+ ieee80211_mle_type_ok(eht_ml_elem->data + 1,
+ IEEE80211_ML_CONTROL_TYPE_BASIC,
eht_ml_elem->datalen - 1)) {
sdata->vif.cfg.eml_cap =
ieee80211_mle_get_eml_cap(eht_ml_elem->data + 1);
--
2.38.1
From: Benjamin Berg <[email protected]>
Default values are defined for the information included in the Medium
Synchronization Delay Information subfield. The spec says to
initialize the values to these defaults and only change them when
included.
Return the default value instead of zero so that the defaults are
used when the field is not included in the association response.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index b107f21e1233..251998be24d0 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4535,6 +4535,14 @@ struct ieee80211_multi_link_elem {
#define IEEE80211_MED_SYNC_DELAY_SYNC_OFDM_ED_THRESH 0x0f00
#define IEEE80211_MED_SYNC_DELAY_SYNC_MAX_NUM_TXOPS 0xf000
+/*
+ * Described in P802.11be_D3.0
+ * dot11MSDTimerDuration should default to 5484 (i.e. 171.375)
+ * dot11MSDOFDMEDthreshold defaults to -72 (i.e. 0)
+ * dot11MSDTXOPMAX defaults to 1
+ */
+#define IEEE80211_MED_SYNC_DELAY_DEFAULT 0x10ac
+
#define IEEE80211_EML_CAP_EMLSR_SUPP 0x0001
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY 0x000e
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_0US 0
@@ -4642,7 +4650,8 @@ static inline u8 ieee80211_mle_common_size(const u8 *data)
* The element is assumed to be of the correct type (BASIC) and big enough,
* this must be checked using ieee80211_mle_type_ok().
*
- * If the medium synchronization is not present, then 0 is returned.
+ * If the medium synchronization is not present, then the default value is
+ * returned.
*/
static inline u16 ieee80211_mle_get_eml_med_sync_delay(const u8 *data)
{
@@ -4654,7 +4663,7 @@ static inline u16 ieee80211_mle_get_eml_med_sync_delay(const u8 *data)
common += sizeof(struct ieee80211_mle_basic_common_info);
if (!(control & IEEE80211_MLC_BASIC_PRES_MED_SYNC_DELAY))
- return 0;
+ return IEEE80211_MED_SYNC_DELAY_DEFAULT;
if (control & IEEE80211_MLC_BASIC_PRES_LINK_ID)
common += 1;
--
2.38.1
From: Johannes Berg <[email protected]>
Update the MLE STA reconfig sub-type to 802.11be D3.0
format, which includes the operation update field.
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 8 ++++++--
net/mac80211/mlme.c | 2 +-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index fc3c26f1b718..d2025c986b0f 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4893,7 +4893,9 @@ static inline bool ieee80211_mle_basic_sta_prof_size_ok(const u8 *data,
#define IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID 0x000f
#define IEEE80211_MLE_STA_RECONF_CONTROL_COMPLETE_PROFILE 0x0010
#define IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT 0x0020
-#define IEEE80211_MLE_STA_RECONF_CONTROL_DELETE_TIMER_PRESENT 0x0040
+#define IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT 0x0040
+#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_UPDATE_TYPE 0x0780
+#define IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_PARAMS_PRESENT 0x0800
/**
* ieee80211_mle_reconf_sta_prof_size_ok - validate reconfiguration multi-link
@@ -4916,7 +4918,9 @@ static inline bool ieee80211_mle_reconf_sta_prof_size_ok(const u8 *data,
if (control & IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT)
info_len += ETH_ALEN;
- if (control & IEEE80211_MLE_STA_RECONF_CONTROL_DELETE_TIMER_PRESENT)
+ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT)
+ info_len += 2;
+ if (control & IEEE80211_MLE_STA_RECONF_CONTROL_OPERATION_PARAMS_PRESENT)
info_len += 2;
return prof->sta_info_len >= info_len &&
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index f0fa1f0991ed..06ba809f83a1 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5723,7 +5723,7 @@ static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata,
* is not present assume immediate removal.
*/
if (control &
- IEEE80211_MLE_STA_RECONF_CONTROL_DELETE_TIMER_PRESENT)
+ IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT)
link_removal_timeout[link_id] = le16_to_cpu(*(__le16 *)pos);
}
--
2.38.1
From: Benjamin Berg <[email protected]>
The element should never be inherited, so always exclude it.
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index dc71c6ac5bf5..095dc9db8750 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -218,6 +218,10 @@ bool cfg80211_is_element_inherited(const struct element *elem,
if (elem->id == WLAN_EID_MULTIPLE_BSSID)
return false;
+ if (elem->id == WLAN_EID_EXTENSION && elem->datalen > 1 &&
+ elem->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK)
+ return false;
+
if (!non_inherit_elem || non_inherit_elem->datalen < 2)
return true;
--
2.38.1