2023-06-16 06:56:26

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 00/20] cfg80211/mac80211 patches from our internal tree 2023-06-16

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

Abhishek Naik (2):
wifi: mac80211: handle TDLS data frames with MLO
wifi: mac80211: Add HE and EHT capa elements in TDLS frames

Anjaneyulu (1):
wifi: mac80211: add consistency check for compat chandef

Benjamin Berg (10):
wifi: mac80211: stop passing cbss to parser
wifi: cfg80211: move regulatory_hint_found_beacon to be earlier
wifi: cfg80211: keep bss_lock held when informing
wifi: cfg80211: add inform_bss op to update BSS
wifi: mac80211: use new inform_bss callback
wifi: cfg80211: ignore invalid TBTT info field types
wifi: cfg80211: rewrite merging of inherited elements
wifi: cfg80211: drop incorrect nontransmitted BSS update code
wifi: cfg80211: add element defragmentation helper
wifi: mac80211: use cfg80211 defragmentation helper

Emmanuel Grumbach (1):
wifi: mac80211: feed the link_id to cfg80211_ch_switch_started_notify

Ilan Peer (3):
wifi: mac80211: Rename multi_link
wifi: mac80211: Add support for parsing Reconfiguration Multi Link element
wifi: mac80211: Rename ieee80211_mle_sta_prof_size_ok()

Mukesh Sisodiya (3):
wifi: cfg80211: make TDLS management link-aware
wifi: mac80211: handle TDLS negotiation with MLO
wifi: mac80211: Extend AID element addition for TDLS frames

.../net/wireless/marvell/mwifiex/cfg80211.c | 8 +-
include/linux/ieee80211.h | 8 +-
include/net/cfg80211.h | 42 +-
net/mac80211/cfg.c | 1 +
net/mac80211/chan.c | 5 +
net/mac80211/ieee80211_i.h | 24 +-
net/mac80211/mlme.c | 14 +-
net/mac80211/scan.c | 93 ++-
net/mac80211/tdls.c | 257 ++++++---
net/mac80211/tx.c | 22 +-
net/mac80211/util.c | 118 ++--
net/wireless/nl80211.c | 7 +-
net/wireless/rdev-ops.h | 27 +-
net/wireless/scan.c | 542 +++++++++---------
net/wireless/trace.h | 30 +-
15 files changed, 697 insertions(+), 501 deletions(-)

--
2.38.1



2023-06-16 06:56:26

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 01/20] wifi: cfg80211: make TDLS management link-aware

From: Mukesh Sisodiya <[email protected]>

For multi-link operation(MLO) TDLS management
frames need to be transmitted on a specific link.
The TDLS setup request will add BSSID along with
peer address and userspace will pass the link-id
based on BSSID value to the driver(or mac80211).

Signed-off-by: Mukesh Sisodiya <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
drivers/net/wireless/marvell/mwifiex/cfg80211.c | 8 ++++----
include/net/cfg80211.h | 7 ++++---
net/mac80211/ieee80211_i.h | 8 ++++----
net/mac80211/tdls.c | 10 +++++-----
net/wireless/nl80211.c | 7 +++++--
net/wireless/rdev-ops.h | 15 ++++++++-------
net/wireless/trace.h | 13 ++++++++-----
7 files changed, 38 insertions(+), 30 deletions(-)

diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index bcd564dc3554..1ef89cdcaa59 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -3753,10 +3753,10 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
*/
static int
mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, u32 peer_capability,
- bool initiator, const u8 *extra_ies,
- size_t extra_ies_len)
+ const u8 *peer, int link_id, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ u32 peer_capability, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
int ret;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6d5b104212b3..5b1d76583dbb 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4595,9 +4595,10 @@ struct cfg80211_ops {
struct cfg80211_gtk_rekey_data *data);

int (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, u32 peer_capability,
- bool initiator, const u8 *buf, size_t len);
+ const u8 *peer, int link_id,
+ u8 action_code, u8 dialog_token, u16 status_code,
+ u32 peer_capability, bool initiator,
+ const u8 *buf, size_t len);
int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4afd5095366c..ca8a1e1c8bbd 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2565,10 +2565,10 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,

/* TDLS */
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, u32 peer_capability,
- bool initiator, const u8 *extra_ies,
- size_t extra_ies_len);
+ const u8 *peer, int link_id,
+ u8 action_code, u8 dialog_token, u16 status_code,
+ u32 peer_capability, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len);
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 52c47674a554..6575b2801676 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -6,7 +6,7 @@
* Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2019, 2021-2022 Intel Corporation
+ * Copyright (C) 2019, 2021-2023 Intel Corporation
*/

#include <linux/ieee80211.h>
@@ -1185,10 +1185,10 @@ ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
}

int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
- u16 status_code, u32 peer_capability,
- bool initiator, const u8 *extra_ies,
- size_t extra_ies_len)
+ const u8 *peer, int link_id,
+ u8 action_code, u8 dialog_token, u16 status_code,
+ u32 peer_capability, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
int ret;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 2941187a7a3d..515e76e870b3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -12239,6 +12239,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
u32 peer_capability = 0;
u16 status_code;
u8 *peer;
+ int link_id;
bool initiator;

if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
@@ -12260,8 +12261,9 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
peer_capability =
nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
+ link_id = nl80211_link_id_or_invalid(info->attrs);

- return rdev_tdls_mgmt(rdev, dev, peer, action_code,
+ return rdev_tdls_mgmt(rdev, dev, peer, link_id, action_code,
dialog_token, status_code, peer_capability,
initiator,
nla_data(info->attrs[NL80211_ATTR_IE]),
@@ -17128,7 +17130,8 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = nl80211_tdls_mgmt,
.flags = GENL_UNS_ADMIN_PERM,
- .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_MLO_VALID_LINK_ID),
},
{
.cmd = NL80211_CMD_TDLS_OPER,
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 69b508743e57..f8c310849438 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -899,17 +899,18 @@ static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev,

static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
struct net_device *dev, u8 *peer,
- u8 action_code, u8 dialog_token,
- u16 status_code, u32 peer_capability,
- bool initiator, const u8 *buf, size_t len)
+ int link_id, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ u32 peer_capability, bool initiator,
+ const u8 *buf, size_t len)
{
int ret;
- trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
+ trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, link_id, action_code,
dialog_token, status_code, peer_capability,
initiator, buf, len);
- ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
- dialog_token, status_code, peer_capability,
- initiator, buf, len);
+ ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, link_id,
+ action_code, dialog_token, status_code,
+ peer_capability, initiator, buf, len);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 716a1fa70069..63aed8d59d25 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1779,15 +1779,16 @@ DEFINE_EVENT(wiphy_netdev_id_evt, rdev_sched_scan_stop,

TRACE_EVENT(rdev_tdls_mgmt,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
- u8 *peer, u8 action_code, u8 dialog_token,
+ u8 *peer, int link_id, u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
bool initiator, const u8 *buf, size_t len),
- TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
- peer_capability, initiator, buf, len),
+ TP_ARGS(wiphy, netdev, peer, link_id, action_code, dialog_token,
+ status_code, peer_capability, initiator, buf, len),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
MAC_ENTRY(peer)
+ __field(int, link_id)
__field(u8, action_code)
__field(u8, dialog_token)
__field(u16, status_code)
@@ -1799,6 +1800,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
WIPHY_ASSIGN;
NETDEV_ASSIGN;
MAC_ASSIGN(peer, peer);
+ __entry->link_id = link_id;
__entry->action_code = action_code;
__entry->dialog_token = dialog_token;
__entry->status_code = status_code;
@@ -1806,11 +1808,12 @@ TRACE_EVENT(rdev_tdls_mgmt,
__entry->initiator = initiator;
memcpy(__get_dynamic_array(buf), buf, len);
),
- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %pM, action_code: %u, "
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %pM"
+ ", link_id: %d, action_code: %u "
"dialog_token: %u, status_code: %u, peer_capability: %u "
"initiator: %s buf: %#.2x ",
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->peer,
- __entry->action_code, __entry->dialog_token,
+ __entry->link_id, __entry->action_code, __entry->dialog_token,
__entry->status_code, __entry->peer_capability,
BOOL_TO_STR(__entry->initiator),
((u8 *)__get_dynamic_array(buf))[0])
--
2.38.1


2023-06-16 06:56:28

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 03/20] wifi: mac80211: handle TDLS data frames with MLO

From: Abhishek Naik <[email protected]>

If the device is associated with an AP MLD, then TDLS data frames
should have
- A1 = peer address,
- A2 = own MLD address (since the peer may now know about MLO), and
- A3 = BSSID.

Change the code to do that.

Signed-off-by: Abhishek Naik <[email protected]>
Signed-off-by: Mukesh Sisodiya <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/tx.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index d8460a14b6bd..b5183f6365aa 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2753,10 +2753,20 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);

if (tdls_peer) {
+ /* For TDLS only one link can be valid with peer STA */
+ int tdls_link_id = sta->sta.valid_links ?
+ __ffs(sta->sta.valid_links) : 0;
+ struct ieee80211_link_data *link;
+
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ link = rcu_dereference(sdata->link[tdls_link_id]);
+ if (WARN_ON_ONCE(!link)) {
+ ret = -EINVAL;
+ goto free;
+ }
+ memcpy(hdr.addr3, link->u.mgd.bssid, ETH_ALEN);
hdrlen = 24;
} else if (sdata->u.mgd.use_4addr &&
cpu_to_be16(ethertype) != sdata->control_port_protocol) {
@@ -3066,10 +3076,18 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
break;
case NL80211_IFTYPE_STATION:
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+ /* For TDLS only one link can be valid with peer STA */
+ int tdls_link_id = sta->sta.valid_links ?
+ __ffs(sta->sta.valid_links) : 0;
+ struct ieee80211_link_data *link;
+
/* DA SA BSSID */
build.da_offs = offsetof(struct ieee80211_hdr, addr1);
build.sa_offs = offsetof(struct ieee80211_hdr, addr2);
- memcpy(hdr->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ link = rcu_dereference(sdata->link[tdls_link_id]);
+ if (WARN_ON_ONCE(!link))
+ break;
+ memcpy(hdr->addr3, link->u.mgd.bssid, ETH_ALEN);
build.hdr_len = 24;
break;
}
--
2.38.1


2023-06-16 06:56:28

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 04/20] wifi: mac80211: Add HE and EHT capa elements in TDLS frames

From: Abhishek Naik <[email protected]>

Add HE and EHT capabilities IE in TDLS setup request,
response, confirm and discovery response frames.

Signed-off-by: Abhishek Naik <[email protected]>
Signed-off-by: Mukesh Sisodiya <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/tdls.c | 85 ++++++++++++++++++++++++++++++++++++++
net/mac80211/util.c | 2 +-
3 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index ca8a1e1c8bbd..f8d5f37ebe9a 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2426,6 +2426,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
const u8 *da, const u8 *bssid,
u16 stype, u16 reason,
bool send_frame, u8 *frame_buf);
+u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end);

enum {
IEEE80211_PROBE_FLAG_DIRECTED = BIT(0),
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 8f59f92ea9d9..c085c076aaef 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -372,6 +372,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
struct ieee80211_local *local = sdata->local;
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
+ const struct ieee80211_sta_he_cap *he_cap;
+ const struct ieee80211_sta_eht_cap *eht_cap;
struct sta_info *sta = NULL;
size_t offset = 0, noffset;
u8 *pos;
@@ -533,6 +535,82 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
}

+ /* add any custom IEs that go before HE capabilities */
+ if (extra_ies_len) {
+ static const u8 before_he_cap[] = {
+ WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_FILS_REQ_PARAMS,
+ WLAN_EID_AP_CSN,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_he_cap,
+ ARRAY_SIZE(before_he_cap),
+ offset);
+ skb_put_data(skb, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ /* build the HE-cap from sband */
+ he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
+ if (he_cap &&
+ (action_code == WLAN_TDLS_SETUP_REQUEST ||
+ action_code == WLAN_TDLS_SETUP_RESPONSE ||
+ action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) {
+ __le16 he_6ghz_capa;
+ u8 cap_size;
+
+ cap_size =
+ 2 + 1 + sizeof(he_cap->he_cap_elem) +
+ ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) +
+ ieee80211_he_ppe_size(he_cap->ppe_thres[0],
+ he_cap->he_cap_elem.phy_cap_info);
+ pos = skb_put(skb, cap_size);
+ pos = ieee80211_ie_build_he_cap(0, pos, he_cap, pos + cap_size);
+
+ /* Build HE 6Ghz capa IE from sband */
+ if (sband->band == NL80211_BAND_6GHZ) {
+ cap_size = 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa);
+ pos = skb_put(skb, cap_size);
+ he_6ghz_capa =
+ ieee80211_get_he_6ghz_capa_vif(sband, &sdata->vif);
+ pos = ieee80211_write_he_6ghz_cap(pos, he_6ghz_capa,
+ pos + cap_size);
+ }
+ }
+
+ /* add any custom IEs that go before EHT capabilities */
+ if (extra_ies_len) {
+ static const u8 before_he_cap[] = {
+ WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_FILS_REQ_PARAMS,
+ WLAN_EID_AP_CSN,
+ };
+
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_he_cap,
+ ARRAY_SIZE(before_he_cap),
+ offset);
+ skb_put_data(skb, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ /* build the EHT-cap from sband */
+ eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
+ if (he_cap && eht_cap &&
+ (action_code == WLAN_TDLS_SETUP_REQUEST ||
+ action_code == WLAN_TDLS_SETUP_RESPONSE ||
+ action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) {
+ u8 cap_size;
+
+ cap_size =
+ 2 + 1 + sizeof(eht_cap->eht_cap_elem) +
+ ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
+ &eht_cap->eht_cap_elem, false) +
+ ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
+ eht_cap->eht_cap_elem.phy_cap_info);
+ pos = skb_put(skb, cap_size);
+ ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + cap_size, false);
+ }
mutex_unlock(&local->sta_mtx);

/* add any remaining IEs */
@@ -897,6 +975,13 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
sizeof(struct ieee80211_ht_operation)) +
2 + max(sizeof(struct ieee80211_vht_cap),
sizeof(struct ieee80211_vht_operation)) +
+ 2 + 1 + sizeof(struct ieee80211_he_cap_elem) +
+ sizeof(struct ieee80211_he_mcs_nss_supp) +
+ IEEE80211_HE_PPE_THRES_MAX_LEN +
+ 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
+ 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) +
+ sizeof(struct ieee80211_eht_mcs_nss_supp) +
+ IEEE80211_EHT_PPE_THRES_MAX_LEN +
50 + /* supported channels */
3 + /* 40/20 BSS coex */
4 + /* AID */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 2fc07717bcad..b81089fc51e1 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1918,7 +1918,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
}
}

-static u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end)
+u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end)
{
if ((end - pos) < 5)
return pos;
--
2.38.1


2023-06-16 06:56:33

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 02/20] wifi: mac80211: handle TDLS negotiation with MLO

From: Mukesh Sisodiya <[email protected]>

Userspace can now select the link to use for TDLS management
frames (indicating e.g. which BSSID should be used), use the
link_id received from cfg80211 to build the frames.

Signed-off-by: Mukesh Sisodiya <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/tdls.c | 147 +++++++++++++++++++++++++++-----------------
1 file changed, 89 insertions(+), 58 deletions(-)

diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 6575b2801676..8f59f92ea9d9 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -39,9 +39,10 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
mutex_unlock(&local->mtx);
}

-static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_ext_capab(struct ieee80211_link_data *link,
struct sk_buff *skb)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
bool chan_switch = local->hw.wiphy->features &
@@ -50,7 +51,7 @@ static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
!ifmgd->tdls_wider_bw_prohibited;
bool buffer_sta = ieee80211_hw_check(&local->hw,
SUPPORTS_TDLS_BUFFER_STA);
- struct ieee80211_supported_band *sband = ieee80211_get_sband(sdata);
+ struct ieee80211_supported_band *sband = ieee80211_get_link_sband(link);
bool vht = sband && sband->vht_cap.vht_supported;
u8 *pos = skb_put(skb, 10);

@@ -152,13 +153,13 @@ ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
*pos = 2 * subband_cnt;
}

-static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_oper_classes(struct ieee80211_link_data *link,
struct sk_buff *skb)
{
u8 *pos;
u8 op_class;

- if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef,
+ if (!ieee80211_chandef_to_operating_class(&link->conf->chandef,
&op_class))
return;

@@ -180,7 +181,7 @@ static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
*pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
}

-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_link_data *link,
u16 status_code)
{
struct ieee80211_supported_band *sband;
@@ -189,7 +190,8 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
if (status_code != 0)
return 0;

- sband = ieee80211_get_sband(sdata);
+ sband = ieee80211_get_link_sband(link);
+
if (sband && sband->band == NL80211_BAND_2GHZ) {
return WLAN_CAPABILITY_SHORT_SLOT_TIME |
WLAN_CAPABILITY_SHORT_PREAMBLE;
@@ -198,10 +200,11 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
return 0;
}

-static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_link_ie(struct ieee80211_link_data *link,
struct sk_buff *skb, const u8 *peer,
bool initiator)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_tdls_lnkie *lnkid;
const u8 *init_addr, *rsp_addr;

@@ -218,7 +221,7 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
lnkid->ie_type = WLAN_EID_LINK_ID;
lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;

- memcpy(lnkid->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+ memcpy(lnkid->bssid, link->u.mgd.bssid, ETH_ALEN);
memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
}
@@ -359,11 +362,12 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
}

static void
-ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
struct sk_buff *skb, const u8 *peer,
u8 action_code, bool initiator,
const u8 *extra_ies, size_t extra_ies_len)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_supported_band *sband;
struct ieee80211_local *local = sdata->local;
struct ieee80211_sta_ht_cap ht_cap;
@@ -372,8 +376,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
size_t offset = 0, noffset;
u8 *pos;

- sband = ieee80211_get_sband(sdata);
- if (!sband)
+ sband = ieee80211_get_link_sband(link);
+ if (WARN_ON_ONCE(!sband))
return;

ieee80211_add_srates_ie(sdata, skb, false, sband->band);
@@ -397,7 +401,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset;
}

- ieee80211_tdls_add_ext_capab(sdata, skb);
+ ieee80211_tdls_add_ext_capab(link, skb);

/* add the QoS element if we support it */
if (local->hw.queues >= IEEE80211_NUM_ACS &&
@@ -436,10 +440,10 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
return;
}

- sta->tdls_chandef = sdata->vif.bss_conf.chandef;
+ sta->tdls_chandef = link->conf->chandef;
}

- ieee80211_tdls_add_oper_classes(sdata, skb);
+ ieee80211_tdls_add_oper_classes(link, skb);

/*
* with TDLS we can switch channels, and HT-caps are not necessarily
@@ -472,7 +476,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
(ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
ieee80211_tdls_add_bss_coex_ie(skb);

- ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+ ieee80211_tdls_add_link_ie(link, skb, peer, initiator);

/* add any custom IEs that go before VHT capabilities */
if (extra_ies_len) {
@@ -540,31 +544,33 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
}

static void
-ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data *link,
struct sk_buff *skb, const u8 *peer,
bool initiator, const u8 *extra_ies,
size_t extra_ies_len)
{
+ struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_local *local = sdata->local;
size_t offset = 0, noffset;
struct sta_info *sta, *ap_sta;
struct ieee80211_supported_band *sband;
u8 *pos;

- sband = ieee80211_get_sband(sdata);
- if (!sband)
+ sband = ieee80211_get_link_sband(link);
+ if (WARN_ON_ONCE(!sband))
return;

mutex_lock(&local->sta_mtx);

sta = sta_info_get(sdata, peer);
- ap_sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
+ ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
+
if (WARN_ON_ONCE(!sta || !ap_sta)) {
mutex_unlock(&local->sta_mtx);
return;
}

- sta->tdls_chandef = sdata->vif.bss_conf.chandef;
+ sta->tdls_chandef = link->conf->chandef;

/* add any custom IEs that go before the QoS IE */
if (extra_ies_len) {
@@ -610,11 +616,11 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,

pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
ieee80211_ie_build_ht_oper(pos, &sta->sta.deflink.ht_cap,
- &sdata->vif.bss_conf.chandef, prot,
+ &link->conf->chandef, prot,
true);
}

- ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+ ieee80211_tdls_add_link_ie(link, skb, peer, initiator);

/* only include VHT-operation if not on the 2.4GHz band */
if (sband->band != NL80211_BAND_2GHZ &&
@@ -641,7 +647,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
}

static void
-ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_link_data *link,
struct sk_buff *skb, const u8 *peer,
bool initiator, const u8 *extra_ies,
size_t extra_ies_len, u8 oper_class,
@@ -670,7 +676,7 @@ ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset;
}

- ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+ ieee80211_tdls_add_link_ie(link, skb, peer, initiator);

/* add any remaining IEs */
if (extra_ies_len) {
@@ -680,20 +686,20 @@ ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
}

static void
-ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_link_data *link,
struct sk_buff *skb, const u8 *peer,
u16 status_code, bool initiator,
const u8 *extra_ies,
size_t extra_ies_len)
{
if (status_code == 0)
- ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+ ieee80211_tdls_add_link_ie(link, skb, peer, initiator);

if (extra_ies_len)
skb_put_data(skb, extra_ies, extra_ies_len);
}

-static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_ies(struct ieee80211_link_data *link,
struct sk_buff *skb, const u8 *peer,
u8 action_code, u16 status_code,
bool initiator, const u8 *extra_ies,
@@ -705,7 +711,8 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
case WLAN_TDLS_SETUP_RESPONSE:
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
if (status_code == 0)
- ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
+ ieee80211_tdls_add_setup_start_ies(link,
+ skb, peer,
action_code,
initiator,
extra_ies,
@@ -713,7 +720,7 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
break;
case WLAN_TDLS_SETUP_CONFIRM:
if (status_code == 0)
- ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
+ ieee80211_tdls_add_setup_cfm_ies(link, skb, peer,
initiator, extra_ies,
extra_ies_len);
break;
@@ -722,16 +729,17 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
if (extra_ies_len)
skb_put_data(skb, extra_ies, extra_ies_len);
if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
- ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+ ieee80211_tdls_add_link_ie(link, skb,
+ peer, initiator);
break;
case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
- ieee80211_tdls_add_chan_switch_req_ies(sdata, skb, peer,
+ ieee80211_tdls_add_chan_switch_req_ies(link, skb, peer,
initiator, extra_ies,
extra_ies_len,
oper_class, chandef);
break;
case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
- ieee80211_tdls_add_chan_switch_resp_ies(sdata, skb, peer,
+ ieee80211_tdls_add_chan_switch_resp_ies(link, skb, peer,
status_code,
initiator, extra_ies,
extra_ies_len);
@@ -742,6 +750,7 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,

static int
ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
+ struct ieee80211_link_data *link,
const u8 *peer, u8 action_code, u8 dialog_token,
u16 status_code, struct sk_buff *skb)
{
@@ -766,7 +775,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
skb_put(skb, sizeof(tf->u.setup_req));
tf->u.setup_req.dialog_token = dialog_token;
tf->u.setup_req.capability =
- cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+ cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
status_code));
break;
case WLAN_TDLS_SETUP_RESPONSE:
@@ -777,7 +786,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_resp.status_code = cpu_to_le16(status_code);
tf->u.setup_resp.dialog_token = dialog_token;
tf->u.setup_resp.capability =
- cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+ cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
status_code));
break;
case WLAN_TDLS_SETUP_CONFIRM:
@@ -824,7 +833,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,

static int
ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
+ const u8 *peer, struct ieee80211_link_data *link,
+ u8 action_code, u8 dialog_token,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -833,8 +843,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
mgmt = skb_put_zero(skb, 24);
memcpy(mgmt->da, peer, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
- memcpy(mgmt->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN);
-
+ memcpy(mgmt->bssid, link->u.mgd.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);

@@ -847,7 +856,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
mgmt->u.action.u.tdls_discover_resp.dialog_token =
dialog_token;
mgmt->u.action.u.tdls_discover_resp.capability =
- cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+ cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
status_code));
break;
default:
@@ -859,15 +868,23 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,

static struct sk_buff *
ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
- const u8 *peer, u8 action_code,
- u8 dialog_token, u16 status_code,
- bool initiator, const u8 *extra_ies,
- size_t extra_ies_len, u8 oper_class,
+ const u8 *peer, int link_id,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, bool initiator,
+ const u8 *extra_ies, size_t extra_ies_len,
+ u8 oper_class,
struct cfg80211_chan_def *chandef)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
int ret;
+ struct ieee80211_link_data *link;
+
+ link_id = link_id >= 0 ? link_id : 0;
+ rcu_read_lock();
+ link = rcu_dereference(sdata->link[link_id]);
+ if (WARN_ON(!link))
+ goto unlock;

skb = netdev_alloc_skb(sdata->dev,
local->hw.extra_tx_headroom +
@@ -887,7 +904,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
extra_ies_len +
sizeof(struct ieee80211_tdls_lnkie));
if (!skb)
- return NULL;
+ goto unlock;

skb_reserve(skb, local->hw.extra_tx_headroom);

@@ -900,13 +917,13 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy,
- sdata->dev, peer,
+ sdata->dev, link, peer,
action_code, dialog_token,
status_code, skb);
break;
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev,
- peer, action_code,
+ peer, link, action_code,
dialog_token, status_code,
skb);
break;
@@ -918,19 +935,23 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
if (ret < 0)
goto fail;

- ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+ ieee80211_tdls_add_ies(link, skb, peer, action_code, status_code,
initiator, extra_ies, extra_ies_len, oper_class,
chandef);
+ rcu_read_unlock();
return skb;

fail:
dev_kfree_skb(skb);
+unlock:
+ rcu_read_unlock();
return NULL;
}

static int
ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
+ const u8 *peer, int link_id,
+ u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
bool initiator, const u8 *extra_ies,
size_t extra_ies_len, u8 oper_class,
@@ -988,7 +1009,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
if (ret < 0)
goto fail;

- skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code,
+ skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer,
+ link_id, action_code,
dialog_token, status_code,
initiator, extra_ies,
extra_ies_len, oper_class,
@@ -999,7 +1021,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
}

if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
- ieee80211_tx_skb(sdata, skb);
+ ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
return 0;
}

@@ -1066,7 +1088,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,

static int
ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
+ const u8 *peer, int link_id,
+ u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability, bool initiator,
const u8 *extra_ies, size_t extra_ies_len)
{
@@ -1115,7 +1138,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
mutex_unlock(&local->mtx);

/* we cannot take the mutex while preparing the setup packet */
- ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+ link_id, action_code,
dialog_token, status_code,
peer_capability, initiator,
extra_ies, extra_ies_len, 0,
@@ -1139,7 +1163,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,

static int
ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code, u8 dialog_token,
+ const u8 *peer, int link_id,
+ u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
bool initiator, const u8 *extra_ies,
size_t extra_ies_len)
@@ -1159,7 +1184,8 @@ ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
ieee80211_flush_queues(local, sdata, false);

- ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+ link_id, action_code,
dialog_token, status_code,
peer_capability, initiator,
extra_ies, extra_ies_len, 0,
@@ -1204,13 +1230,14 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
case WLAN_TDLS_SETUP_RESPONSE:
- ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code,
+ ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer,
+ link_id, action_code,
dialog_token, status_code,
peer_capability, initiator,
extra_ies, extra_ies_len);
break;
case WLAN_TDLS_TEARDOWN:
- ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer,
+ ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer, link_id,
action_code, dialog_token,
status_code,
peer_capability, initiator,
@@ -1228,7 +1255,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
/* no special handling */
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
- action_code,
+ link_id, action_code,
dialog_token,
status_code,
peer_capability,
@@ -1240,8 +1267,8 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
break;
}

- tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n",
- action_code, peer, ret);
+ tdls_dbg(sdata, "TDLS mgmt action %d peer %pM link_id %d status %d\n",
+ action_code, peer, link_id, ret);
return ret;
}

@@ -1497,6 +1524,7 @@ ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing);
u8 *pos = extra_ies;
struct sk_buff *skb;
+ int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;

/*
* if chandef points to a wide channel add a Secondary-Channel
@@ -1524,6 +1552,7 @@ ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
iee80211_tdls_add_ch_switch_timing(pos, 0, 0);

skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+ link_id,
WLAN_TDLS_CHANNEL_SWITCH_REQUEST,
0, 0, !sta->sta.tdls_initiator,
extra_ies, extra_ies_len,
@@ -1644,11 +1673,13 @@ ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta,
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct sk_buff *skb;
u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)];
+ int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;

/* initial timing are always zero in the template */
iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0);

skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+ link_id,
WLAN_TDLS_CHANNEL_SWITCH_RESPONSE,
0, 0, !sta->sta.tdls_initiator,
extra_ies, sizeof(extra_ies), 0, NULL);
--
2.38.1


2023-06-16 06:56:40

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 05/20] wifi: mac80211: Extend AID element addition for TDLS frames

From: Mukesh Sisodiya <[email protected]>

Extend AID element addition in TDLS setup request and response
frames to add it when HE or EHT capabilities are supported.

Signed-off-by: Mukesh Sisodiya <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/tdls.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index c085c076aaef..5ffa3b752720 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -503,17 +503,21 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
offset = noffset;
}

- /* build the VHT-cap similarly to the HT-cap */
+ /* add AID if VHT, HE or EHT capabilities supported */
memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+ he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
+ eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
+ if ((vht_cap.vht_supported || he_cap || eht_cap) &&
+ (action_code == WLAN_TDLS_SETUP_REQUEST ||
+ action_code == WLAN_TDLS_SETUP_RESPONSE))
+ ieee80211_tdls_add_aid(sdata, skb);
+
+ /* build the VHT-cap similarly to the HT-cap */
if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) &&
vht_cap.vht_supported) {
ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);

- /* the AID is present only when VHT is implemented */
- if (action_code == WLAN_TDLS_SETUP_REQUEST)
- ieee80211_tdls_add_aid(sdata, skb);
-
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
@@ -521,9 +525,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
/* the peer caps are already intersected with our own */
memcpy(&vht_cap, &sta->sta.deflink.vht_cap, sizeof(vht_cap));

- /* the AID is present only when VHT is implemented */
- ieee80211_tdls_add_aid(sdata, skb);
-
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);

@@ -551,7 +552,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
}

/* build the HE-cap from sband */
- he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif);
if (he_cap &&
(action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_TDLS_SETUP_RESPONSE ||
@@ -595,7 +595,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
}

/* build the EHT-cap from sband */
- eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif);
if (he_cap && eht_cap &&
(action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_TDLS_SETUP_RESPONSE ||
--
2.38.1


2023-06-16 06:56:46

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 07/20] wifi: mac80211: add consistency check for compat chandef

From: Anjaneyulu <[email protected]>

Add NULL check for compat variable to avoid crash in
cfg80211_chandef_compatible() if it got called with
some mixed up channel context where not all the users
compatible with each other, which shouldn't happen.

Signed-off-by: Anjaneyulu <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/chan.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 168bf3edd4b4..68952752b599 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -802,6 +802,11 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
}
}

+ if (WARN_ON_ONCE(!compat)) {
+ rcu_read_unlock();
+ return;
+ }
+
/* TDLS peers can sometimes affect the chandef width */
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (!sta->uploaded ||
--
2.38.1


2023-06-16 06:56:48

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 06/20] wifi: mac80211: stop passing cbss to parser

From: Benjamin Berg <[email protected]>

In both of these cases (config_link, prep_channel) it is not needed
to parse the MBSSID data for a nontransmitted BSS. In the config_link
case the frame does not contain any MBSSID element and inheritance
rules are only needed for the ML STA profile. While in the
prep_channel case the IEs have already been processed by cfg80211 and
are already exploded.

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/mlme.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 738822b82d3e..171ba9d237c2 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -4709,7 +4709,6 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
struct ieee80211_bss *bss = (void *)cbss->priv;
struct ieee80211_elems_parse_params parse_params = {
- .bss = cbss,
.link_id = -1,
.from_ap = true,
};
--
2.38.1


2023-06-16 06:56:50

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 08/20] wifi: mac80211: feed the link_id to cfg80211_ch_switch_started_notify

From: Emmanuel Grumbach <[email protected]>

For now, fix this only in station mode. We'll need to fix
the AP mode later.

Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/mlme.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 171ba9d237c2..1fc66f09cbb8 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1981,8 +1981,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
IEEE80211_QUEUE_STOP_REASON_CSA);
mutex_unlock(&local->mtx);

- cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, 0,
- csa_ie.count, csa_ie.mode, 0);
+ cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
+ link->link_id, csa_ie.count,
+ csa_ie.mode, 0);

if (local->ops->channel_switch) {
/* use driver's channel switch callback */
--
2.38.1


2023-06-16 06:56:50

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 09/20] wifi: cfg80211: move regulatory_hint_found_beacon to be earlier

From: Benjamin Berg <[email protected]>

These calls do not require any locking, so move them in preparation for
the next patches.

A minor change/bugfix is to not hint a beacon for nontransmitted BSSes

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 42 ++++++++++++++++++++++--------------------
1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index ce2104dc05c6..19e7014f8bc3 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1971,6 +1971,18 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
} else {
ts = jiffies;
+
+ if (channel->band == NL80211_BAND_60GHZ) {
+ bss_type = 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)
+ regulatory_hint_found_beacon(wiphy, channel,
+ gfp);
+ }
}

/*
@@ -2007,16 +2019,6 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
if (!res)
return NULL;

- if (channel->band == NL80211_BAND_60GHZ) {
- bss_type = res->pub.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 (res->pub.capability & WLAN_CAPABILITY_ESS)
- regulatory_hint_found_beacon(wiphy, channel, gfp);
- }
-
if (non_tx_data) {
/* this is a nontransmitting bss, we need to add it to
* transmitting bss' list if it is not there
@@ -2445,6 +2447,16 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
}

+ if (channel->band == NL80211_BAND_60GHZ) {
+ bss_type = 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)
+ regulatory_hint_found_beacon(wiphy, channel, gfp);
+ }
+
ies = kzalloc(sizeof(*ies) + ielen, gfp);
if (!ies)
return NULL;
@@ -2478,16 +2490,6 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
if (!res)
return NULL;

- if (channel->band == NL80211_BAND_60GHZ) {
- bss_type = res->pub.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 (res->pub.capability & WLAN_CAPABILITY_ESS)
- regulatory_hint_found_beacon(wiphy, channel, gfp);
- }
-
trace_cfg80211_return_bss(&res->pub);
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
--
2.38.1


2023-06-16 06:56:52

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 10/20] wifi: cfg80211: keep bss_lock held when informing

From: Benjamin Berg <[email protected]>

It is reasonable to hold bss_lock for a little bit longer after
cfg80211_bss_update is done. Right now, this does not make any big
difference, but doing so in preparation for the next patch which adds
a call to the driver.

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 66 ++++++++++++++++++++++++++++-----------------
1 file changed, 42 insertions(+), 24 deletions(-)

diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 19e7014f8bc3..8984f74da891 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1701,10 +1701,10 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
}

/* Returned bss is reference counted and must be cleaned up appropriately. */
-struct cfg80211_internal_bss *
-cfg80211_bss_update(struct cfg80211_registered_device *rdev,
- struct cfg80211_internal_bss *tmp,
- bool signal_valid, unsigned long ts)
+static struct cfg80211_internal_bss *
+__cfg80211_bss_update(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *tmp,
+ bool signal_valid, unsigned long ts)
{
struct cfg80211_internal_bss *found = NULL;

@@ -1713,10 +1713,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,

tmp->ts = ts;

- spin_lock_bh(&rdev->bss_lock);
-
if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
- spin_unlock_bh(&rdev->bss_lock);
return NULL;
}

@@ -1724,7 +1721,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,

if (found) {
if (!cfg80211_update_known_bss(rdev, found, tmp, signal_valid))
- goto drop;
+ return NULL;
} else {
struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden;
@@ -1744,7 +1741,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
if (ies)
kfree_rcu(ies, rcu_head);
- goto drop;
+ return NULL;
}
memcpy(new, tmp, sizeof(*new));
new->refcount = 1;
@@ -1775,14 +1772,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
*/
if (!cfg80211_combine_bsses(rdev, new)) {
bss_ref_put(rdev, new);
- goto drop;
+ return NULL;
}
}

if (rdev->bss_entries >= bss_entries_limit &&
!cfg80211_bss_expire_oldest(rdev)) {
bss_ref_put(rdev, new);
- goto drop;
+ return NULL;
}

/* This must be before the call to bss_ref_get */
@@ -1799,12 +1796,22 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,

rdev->bss_generation++;
bss_ref_get(rdev, found);
- spin_unlock_bh(&rdev->bss_lock);

return found;
- drop:
+}
+
+struct cfg80211_internal_bss *
+cfg80211_bss_update(struct cfg80211_registered_device *rdev,
+ struct cfg80211_internal_bss *tmp,
+ bool signal_valid, unsigned long ts)
+{
+ struct cfg80211_internal_bss *res;
+
+ spin_lock_bh(&rdev->bss_lock);
+ res = __cfg80211_bss_update(rdev, tmp, signal_valid, ts);
spin_unlock_bh(&rdev->bss_lock);
- return NULL;
+
+ return res;
}

int cfg80211_get_ies_channel_number(const u8 *ie, size_t ielen,
@@ -2015,15 +2022,15 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
rcu_assign_pointer(tmp.pub.ies, ies);

signal_valid = data->chan == channel;
- res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid, ts);
+ spin_lock_bh(&rdev->bss_lock);
+ res = __cfg80211_bss_update(rdev, &tmp, signal_valid, ts);
if (!res)
- return NULL;
+ goto drop;

if (non_tx_data) {
/* this is a nontransmitting bss, we need to add it to
* transmitting bss' list if it is not there
*/
- spin_lock_bh(&rdev->bss_lock);
if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
&res->pub)) {
if (__cfg80211_unlink_bss(rdev, res)) {
@@ -2031,15 +2038,19 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
res = NULL;
}
}
- spin_unlock_bh(&rdev->bss_lock);

if (!res)
- return NULL;
+ goto drop;
}
+ spin_unlock_bh(&rdev->bss_lock);

trace_cfg80211_return_bss(&res->pub);
- /* cfg80211_bss_update gives us a referenced result */
+ /* __cfg80211_bss_update gives us a referenced result */
return &res->pub;
+
+drop:
+ spin_unlock_bh(&rdev->bss_lock);
+ return NULL;
}

static const struct element
@@ -2376,6 +2387,7 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp)
{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies;
struct ieee80211_channel *channel;
@@ -2485,14 +2497,20 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
ether_addr_copy(tmp.parent_bssid, data->parent_bssid);

signal_valid = data->chan == channel;
- res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid,
- jiffies);
+ spin_lock_bh(&rdev->bss_lock);
+ res = __cfg80211_bss_update(rdev, &tmp, signal_valid, jiffies);
if (!res)
- return NULL;
+ goto drop;
+
+ spin_unlock_bh(&rdev->bss_lock);

trace_cfg80211_return_bss(&res->pub);
- /* cfg80211_bss_update gives us a referenced result */
+ /* __cfg80211_bss_update gives us a referenced result */
return &res->pub;
+
+drop:
+ spin_unlock_bh(&rdev->bss_lock);
+ return NULL;
}

struct cfg80211_bss *
--
2.38.1


2023-06-16 06:56:54

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 12/20] wifi: mac80211: use new inform_bss callback

From: Benjamin Berg <[email protected]>

Doing this simplifies the code somewhat, as iteration over the
nontransmitted BSSs is not required anymore. Also, mac80211 should
not be iterating over the nontrans_list as it should only be accessed
while the bss_lock is held.

It also simplifies parsing of the IEs somewhat, as cfg80211 already
extracts the IEs and passes them to the callback.

Note that the only user left requiring parsing a specific BSS is the
association code if a beacon is required by the hardware.

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/cfg.c | 1 +
net/mac80211/ieee80211_i.h | 3 ++
net/mac80211/scan.c | 93 ++++++++++++++++----------------------
3 files changed, 42 insertions(+), 55 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 359589d525d5..eea7028a46a7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -5050,6 +5050,7 @@ const struct cfg80211_ops mac80211_config_ops = {
.join_ocb = ieee80211_join_ocb,
.leave_ocb = ieee80211_leave_ocb,
.change_bss = ieee80211_change_bss,
+ .inform_bss = ieee80211_inform_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_monitor_channel = ieee80211_set_monitor_channel,
.suspend = ieee80211_suspend,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f8d5f37ebe9a..b05dfdcfff11 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1929,6 +1929,9 @@ void ieee80211_scan_cancel(struct ieee80211_local *local);
void ieee80211_run_deferred_scan(struct ieee80211_local *local);
void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb);

+void ieee80211_inform_bss(struct wiphy *wiphy, struct cfg80211_bss *bss,
+ const struct cfg80211_bss_ies *ies, void *data);
+
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
struct ieee80211_bss *
ieee80211_bss_info_update(struct ieee80211_local *local,
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index ea5383136fff..0805aa8603c6 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <[email protected]>
* Copyright 2013-2015 Intel Mobile Communications GmbH
* Copyright 2016-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2022 Intel Corporation
+ * Copyright (C) 2018-2023 Intel Corporation
*/

#include <linux/if_arp.h>
@@ -55,27 +55,45 @@ static bool is_uapsd_supported(struct ieee802_11_elems *elems)
return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
}

-static void
-ieee80211_update_bss_from_elems(struct ieee80211_local *local,
- struct ieee80211_bss *bss,
- struct ieee802_11_elems *elems,
- struct ieee80211_rx_status *rx_status,
- bool beacon)
+struct inform_bss_update_data {
+ struct ieee80211_rx_status *rx_status;
+ bool beacon;
+};
+
+void ieee80211_inform_bss(struct wiphy *wiphy,
+ struct cfg80211_bss *cbss,
+ const struct cfg80211_bss_ies *ies,
+ void *data)
{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct inform_bss_update_data *update_data = data;
+ struct ieee80211_bss *bss = (void *)cbss->priv;
+ struct ieee80211_rx_status *rx_status;
+ struct ieee802_11_elems *elems;
int clen, srlen;

- if (beacon)
+ /* This happens while joining an IBSS */
+ if (!update_data)
+ return;
+
+ elems = ieee802_11_parse_elems(ies->data, ies->len, false, NULL);
+ if (!elems)
+ return;
+
+ rx_status = update_data->rx_status;
+
+ if (update_data->beacon)
bss->device_ts_beacon = rx_status->device_timestamp;
else
bss->device_ts_presp = rx_status->device_timestamp;

if (elems->parse_error) {
- if (beacon)
+ if (update_data->beacon)
bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON;
else
bss->corrupt_data |= IEEE80211_BSS_CORRUPT_PROBE_RESP;
} else {
- if (beacon)
+ if (update_data->beacon)
bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_BEACON;
else
bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_PROBE_RESP;
@@ -124,7 +142,7 @@ ieee80211_update_bss_from_elems(struct ieee80211_local *local,
bss->valid_data |= IEEE80211_BSS_VALID_WMM;
}

- if (beacon) {
+ if (update_data->beacon) {
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[rx_status->band];
if (!(rx_status->encoding == RX_ENC_HT) &&
@@ -138,6 +156,8 @@ ieee80211_update_bss_from_elems(struct ieee80211_local *local,
le32_to_cpu(elems->vht_cap_elem->vht_cap_info);
else
bss->vht_cap_info = 0;
+
+ kfree(elems);
}

struct ieee80211_bss *
@@ -148,16 +168,17 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
{
bool beacon = ieee80211_is_beacon(mgmt->frame_control) ||
ieee80211_is_s1g_beacon(mgmt->frame_control);
- struct cfg80211_bss *cbss, *non_tx_cbss;
- struct ieee80211_bss *bss, *non_tx_bss;
+ struct cfg80211_bss *cbss;
+ struct inform_bss_update_data update_data = {
+ .rx_status = rx_status,
+ .beacon = beacon,
+ };
struct cfg80211_inform_bss bss_meta = {
.boottime_ns = rx_status->boottime_ns,
+ .drv_data = (void *)&update_data,
};
bool signal_valid;
struct ieee80211_sub_if_data *scan_sdata;
- struct ieee802_11_elems *elems;
- size_t baselen;
- u8 *elements;

if (rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)
bss_meta.signal = 0; /* invalid signal indication */
@@ -192,50 +213,12 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
if (!cbss)
return NULL;

- if (ieee80211_is_probe_resp(mgmt->frame_control)) {
- elements = mgmt->u.probe_resp.variable;
- baselen = offsetof(struct ieee80211_mgmt,
- u.probe_resp.variable);
- } else if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
- struct ieee80211_ext *ext = (void *) mgmt;
-
- baselen = offsetof(struct ieee80211_ext, u.s1g_beacon.variable);
- elements = ext->u.s1g_beacon.variable;
- } else {
- baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
- elements = mgmt->u.beacon.variable;
- }
-
- if (baselen > len)
- return NULL;
-
- elems = ieee802_11_parse_elems(elements, len - baselen, false, cbss);
- if (!elems)
- return NULL;
-
/* In case the signal is invalid update the status */
signal_valid = channel == cbss->channel;
if (!signal_valid)
rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;

- bss = (void *)cbss->priv;
- ieee80211_update_bss_from_elems(local, bss, elems, rx_status, beacon);
- kfree(elems);
-
- list_for_each_entry(non_tx_cbss, &cbss->nontrans_list, nontrans_list) {
- non_tx_bss = (void *)non_tx_cbss->priv;
-
- elems = ieee802_11_parse_elems(elements, len - baselen, false,
- non_tx_cbss);
- if (!elems)
- continue;
-
- ieee80211_update_bss_from_elems(local, non_tx_bss, elems,
- rx_status, beacon);
- kfree(elems);
- }
-
- return bss;
+ return (void *)cbss->priv;
}

static bool ieee80211_scan_accept_presp(struct ieee80211_sub_if_data *sdata,
--
2.38.1


2023-06-16 06:56:57

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 13/20] wifi: cfg80211: ignore invalid TBTT info field types

From: Benjamin Berg <[email protected]>

The TBTT information field type must be zero. This is only changed in
the 802.11be draft specification where the value 1 is used to indicate
that only the MLD parameters are included.

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 2 ++
net/wireless/scan.c | 7 +++++++
2 files changed, 9 insertions(+)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 5dfed1a6625c..47ddc65b443b 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4481,6 +4481,8 @@ static inline bool for_each_element_completed(const struct element *element,
#define IEEE80211_AP_INFO_TBTT_HDR_FILTERED 0x04
#define IEEE80211_AP_INFO_TBTT_HDR_COLOC 0x08
#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

diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index d9abbf123ad1..2212e6d24204 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -629,6 +629,13 @@ static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
if (end - pos < count * length)
break;

+ if (u8_get_bits(ap_info->tbtt_info_hdr,
+ IEEE80211_AP_INFO_TBTT_HDR_TYPE) !=
+ IEEE80211_TBTT_INFO_TYPE_TBTT) {
+ pos += count * length;
+ continue;
+ }
+
/*
* TBTT info must include bss param + BSSID +
* (short SSID or same_ssid bit to be set).
--
2.38.1


2023-06-16 06:57:00

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 11/20] wifi: cfg80211: add inform_bss op to update BSS

From: Benjamin Berg <[email protected]>

This new function is called from within the inform_bss(_frame)_data
functions in order for the driver to update data that it is tracking.

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/net/cfg80211.h | 13 +++++++++++++
net/wireless/rdev-ops.h | 12 ++++++++++++
net/wireless/scan.c | 4 ++++
net/wireless/trace.h | 17 +++++++++++++++++
4 files changed, 46 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5b1d76583dbb..94ca5cb340f6 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2708,6 +2708,7 @@ enum cfg80211_signal_type {
* the BSS that requested the scan in which the beacon/probe was received.
* @chains: bitmask for filled values in @chain_signal.
* @chain_signal: per-chain signal strength of last received BSS in dBm.
+ * @drv_data: Data to be passed through to @inform_bss
*/
struct cfg80211_inform_bss {
struct ieee80211_channel *chan;
@@ -2718,6 +2719,8 @@ struct cfg80211_inform_bss {
u8 parent_bssid[ETH_ALEN] __aligned(2);
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
+
+ void *drv_data;
};

/**
@@ -4089,6 +4092,13 @@ struct mgmt_frame_regs {
*
* @change_bss: Modify parameters for a given BSS.
*
+ * @inform_bss: Called by cfg80211 while being informed about new BSS data
+ * for every BSS found within the reported data or frame. This is called
+ * from within the cfg8011 inform_bss handlers while holding the bss_lock.
+ * The data parameter is passed through from drv_data inside
+ * struct cfg80211_inform_bss.
+ * The new IE data for the BSS is explicitly passed.
+ *
* @set_txq_params: Set TX queue parameters
*
* @libertas_set_mesh_channel: Only for backward compatibility for libertas,
@@ -4476,6 +4486,9 @@ struct cfg80211_ops {
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
struct bss_parameters *params);

+ void (*inform_bss)(struct wiphy *wiphy, struct cfg80211_bss *bss,
+ const struct cfg80211_bss_ies *ies, void *data);
+
int (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_txq_params *params);

diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index f8c310849438..90bb7ac4b930 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -407,6 +407,18 @@ static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
return ret;
}

+static inline void rdev_inform_bss(struct cfg80211_registered_device *rdev,
+ struct cfg80211_bss *bss,
+ const struct cfg80211_bss_ies *ies,
+ void *drv_data)
+
+{
+ trace_rdev_inform_bss(&rdev->wiphy, bss);
+ if (rdev->ops->inform_bss)
+ rdev->ops->inform_bss(&rdev->wiphy, bss, ies, drv_data);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
static inline int rdev_set_txq_params(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_txq_params *params)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 8984f74da891..d9abbf123ad1 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -2027,6 +2027,8 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
if (!res)
goto drop;

+ rdev_inform_bss(rdev, &res->pub, ies, data->drv_data);
+
if (non_tx_data) {
/* this is a nontransmitting bss, we need to add it to
* transmitting bss' list if it is not there
@@ -2502,6 +2504,8 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
if (!res)
goto drop;

+ rdev_inform_bss(rdev, &res->pub, ies, data->drv_data);
+
spin_unlock_bh(&rdev->bss_lock);

trace_cfg80211_return_bss(&res->pub);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 63aed8d59d25..41dc87ee62f0 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1159,6 +1159,23 @@ TRACE_EVENT(rdev_change_bss,
__entry->ap_isolate, __entry->ht_opmode)
);

+TRACE_EVENT(rdev_inform_bss,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_bss *bss),
+ TP_ARGS(wiphy, bss),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ MAC_ENTRY(bssid)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ MAC_ASSIGN(bssid, bss->bssid);
+ CHAN_ASSIGN(bss->channel);
+ ),
+ TP_printk(WIPHY_PR_FMT ", %pM, " CHAN_PR_FMT,
+ WIPHY_PR_ARG, __entry->bssid, CHAN_PR_ARG)
+);
+
TRACE_EVENT(rdev_set_txq_params,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
struct ieee80211_txq_params *params),
--
2.38.1


2023-06-16 06:57:08

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 14/20] wifi: cfg80211: rewrite merging of inherited elements

From: Benjamin Berg <[email protected]>

The cfg80211_gen_new_ie function merges the IEs using inheritance rules.
Rewrite this function to fix issues around inheritance rules. In
particular, vendor elements do not require any special handling, as they
are either all inherited or overridden by the subprofile.
Also, add fragmentation handling as this may be needed in some cases.

This also changes the function to not require making a copy. The new
version could be optimized a bit by explicitly tracking which IEs have
been handled already rather than looking that up again every time.

Note that a small behavioural change is the removal of the SSID special
handling. This should be fine for the MBSSID element, as the SSID must
be included in the subelement.

Fixes: 0b8fb8235be8 ("cfg80211: Parsing of Multiple BSSID information in scanning")
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 213 ++++++++++++++++++++++++++------------------
1 file changed, 124 insertions(+), 89 deletions(-)

diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 2212e6d24204..b9eb91f485f8 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -259,117 +259,152 @@ bool cfg80211_is_element_inherited(const struct element *elem,
}
EXPORT_SYMBOL(cfg80211_is_element_inherited);

-static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
- const u8 *subelement, size_t subie_len,
- u8 *new_ie, gfp_t gfp)
+static size_t cfg80211_copy_elem_with_frags(const struct element *elem,
+ const u8 *ie, size_t ie_len,
+ u8 **pos, u8 *buf, size_t buf_len)
{
- u8 *pos, *tmp;
- const u8 *tmp_old, *tmp_new;
- const struct element *non_inherit_elem;
- u8 *sub_copy;
+ if (WARN_ON((u8 *)elem < ie || elem->data > ie + ie_len ||
+ elem->data + elem->datalen > ie + ie_len))
+ return 0;

- /* copy subelement as we need to change its content to
- * mark an ie after it is processed.
- */
- sub_copy = kmemdup(subelement, subie_len, gfp);
- if (!sub_copy)
+ if (elem->datalen + 2 > buf + buf_len - *pos)
return 0;

- pos = &new_ie[0];
+ memcpy(*pos, elem, elem->datalen + 2);
+ *pos += elem->datalen + 2;

- /* set new ssid */
- tmp_new = cfg80211_find_ie(WLAN_EID_SSID, sub_copy, subie_len);
- if (tmp_new) {
- memcpy(pos, tmp_new, tmp_new[1] + 2);
- pos += (tmp_new[1] + 2);
+ /* Finish if it is not fragmented */
+ if (elem->datalen != 255)
+ return *pos - buf;
+
+ ie_len = ie + ie_len - elem->data - elem->datalen;
+ ie = (const u8 *)elem->data + elem->datalen;
+
+ for_each_element(elem, ie, ie_len) {
+ if (elem->id != WLAN_EID_FRAGMENT)
+ break;
+
+ if (elem->datalen + 2 > buf + buf_len - *pos)
+ return 0;
+
+ memcpy(*pos, elem, elem->datalen + 2);
+ *pos += elem->datalen + 2;
+
+ if (elem->datalen != 255)
+ break;
}

- /* get non inheritance list if exists */
- non_inherit_elem =
- cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
- sub_copy, subie_len);
+ return *pos - buf;
+}

- /* go through IEs in ie (skip SSID) and subelement,
- * merge them into new_ie
+static size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
+ const u8 *subie, size_t subie_len,
+ u8 *new_ie, size_t new_ie_len)
+{
+ const struct element *non_inherit_elem, *parent, *sub;
+ u8 *pos = new_ie;
+ u8 id, ext_id;
+ unsigned int match_len;
+
+ non_inherit_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
+ subie, subie_len);
+
+ /* We copy the elements one by one from the parent to the generated
+ * elements.
+ * If they are not inherited (included in subie or in the non
+ * inheritance element), then we copy all occurrences the first time
+ * we see this element type.
*/
- tmp_old = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
- tmp_old = (tmp_old) ? tmp_old + tmp_old[1] + 2 : ie;
-
- while (tmp_old + 2 - ie <= ielen &&
- tmp_old + tmp_old[1] + 2 - ie <= ielen) {
- if (tmp_old[0] == 0) {
- tmp_old++;
+ for_each_element(parent, ie, ielen) {
+ if (parent->id == WLAN_EID_FRAGMENT)
continue;
+
+ if (parent->id == WLAN_EID_EXTENSION) {
+ if (parent->datalen < 1)
+ continue;
+
+ id = WLAN_EID_EXTENSION;
+ ext_id = parent->data[0];
+ match_len = 1;
+ } else {
+ id = parent->id;
+ match_len = 0;
}

- if (tmp_old[0] == WLAN_EID_EXTENSION)
- tmp = (u8 *)cfg80211_find_ext_ie(tmp_old[2], sub_copy,
- subie_len);
- else
- tmp = (u8 *)cfg80211_find_ie(tmp_old[0], sub_copy,
- subie_len);
+ /* Find first occurrence in subie */
+ sub = cfg80211_find_elem_match(id, subie, subie_len,
+ &ext_id, match_len, 0);

- if (!tmp) {
- const struct element *old_elem = (void *)tmp_old;
+ /* Copy from parent if not in subie and inherited */
+ if (!sub &&
+ cfg80211_is_element_inherited(parent, non_inherit_elem)) {
+ if (!cfg80211_copy_elem_with_frags(parent,
+ ie, ielen,
+ &pos, new_ie,
+ new_ie_len))
+ return 0;

- /* ie in old ie but not in subelement */
- if (cfg80211_is_element_inherited(old_elem,
- non_inherit_elem)) {
- memcpy(pos, tmp_old, tmp_old[1] + 2);
- pos += tmp_old[1] + 2;
- }
- } else {
- /* ie in transmitting ie also in subelement,
- * copy from subelement and flag the ie in subelement
- * as copied (by setting eid field to WLAN_EID_SSID,
- * which is skipped anyway).
- * For vendor ie, compare OUI + type + subType to
- * determine if they are the same ie.
- */
- if (tmp_old[0] == WLAN_EID_VENDOR_SPECIFIC) {
- if (tmp_old[1] >= 5 && tmp[1] >= 5 &&
- !memcmp(tmp_old + 2, tmp + 2, 5)) {
- /* same vendor ie, copy from
- * subelement
- */
- memcpy(pos, tmp, tmp[1] + 2);
- pos += tmp[1] + 2;
- tmp[0] = WLAN_EID_SSID;
- } else {
- memcpy(pos, tmp_old, tmp_old[1] + 2);
- pos += tmp_old[1] + 2;
- }
- } else {
- /* copy ie from subelement into new ie */
- memcpy(pos, tmp, tmp[1] + 2);
- pos += tmp[1] + 2;
- tmp[0] = WLAN_EID_SSID;
- }
+ continue;
}

- if (tmp_old + tmp_old[1] + 2 - ie == ielen)
- break;
+ /* Already copied if an earlier element had the same type */
+ if (cfg80211_find_elem_match(id, ie, (u8 *)parent - ie,
+ &ext_id, match_len, 0))
+ continue;

- tmp_old += tmp_old[1] + 2;
+ /* Not inheriting, copy all similar elements from subie */
+ while (sub) {
+ if (!cfg80211_copy_elem_with_frags(sub,
+ subie, subie_len,
+ &pos, new_ie,
+ new_ie_len))
+ return 0;
+
+ sub = cfg80211_find_elem_match(id,
+ sub->data + sub->datalen,
+ subie_len + subie -
+ (sub->data +
+ sub->datalen),
+ &ext_id, match_len, 0);
+ }
}

- /* go through subelement again to check if there is any ie not
- * copied to new ie, skip ssid, capability, bssid-index ie
+ /* The above misses elements that are included in subie but not in the
+ * parent, so do a pass over subie and append those.
+ * Skip the non-tx BSSID caps and non-inheritance element.
*/
- tmp_new = sub_copy;
- while (tmp_new + 2 - sub_copy <= subie_len &&
- tmp_new + tmp_new[1] + 2 - sub_copy <= subie_len) {
- if (!(tmp_new[0] == WLAN_EID_NON_TX_BSSID_CAP ||
- tmp_new[0] == WLAN_EID_SSID)) {
- memcpy(pos, tmp_new, tmp_new[1] + 2);
- pos += tmp_new[1] + 2;
+ for_each_element(sub, subie, subie_len) {
+ if (sub->id == WLAN_EID_NON_TX_BSSID_CAP)
+ continue;
+
+ if (sub->id == WLAN_EID_FRAGMENT)
+ continue;
+
+ if (sub->id == WLAN_EID_EXTENSION) {
+ if (sub->datalen < 1)
+ continue;
+
+ id = WLAN_EID_EXTENSION;
+ ext_id = sub->data[0];
+ match_len = 1;
+
+ if (ext_id == WLAN_EID_EXT_NON_INHERITANCE)
+ continue;
+ } else {
+ id = sub->id;
+ match_len = 0;
}
- if (tmp_new + tmp_new[1] + 2 - sub_copy == subie_len)
- break;
- tmp_new += tmp_new[1] + 2;
+
+ /* Processed if one was included in the parent */
+ if (cfg80211_find_elem_match(id, ie, ielen,
+ &ext_id, match_len, 0))
+ continue;
+
+ if (!cfg80211_copy_elem_with_frags(sub, subie, subie_len,
+ &pos, new_ie, new_ie_len))
+ return 0;
}

- kfree(sub_copy);
return pos - new_ie;
}

@@ -2228,7 +2263,7 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
new_ie_len = cfg80211_gen_new_ie(ie, ielen,
profile,
profile_len, new_ie,
- gfp);
+ IEEE80211_MAX_DATA_LEN);
if (!new_ie_len)
continue;

--
2.38.1


2023-06-16 06:57:15

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 15/20] wifi: cfg80211: drop incorrect nontransmitted BSS update code

From: Benjamin Berg <[email protected]>

The removed code ran for any BSS that was not included in the MBSSID
element in order to update it. However, instead of using the correct
inheritance rules, it would simply copy the elements from the
transmitting AP. The result is that we would report incorrect elements
in this case.

After some discussions, it seems that there are likely not even APs
actually using this feature. Either way, removing the code decreases
complexity and makes the cfg80211 behaviour more correct.

Fixes: 0b8fb8235be8 ("cfg80211: Parsing of Multiple BSSID information in scanning")
Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/wireless/scan.c | 154 ++++----------------------------------------
1 file changed, 11 insertions(+), 143 deletions(-)

diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index b9eb91f485f8..75e6e032bb3a 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -2312,118 +2312,6 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_inform_bss_data);

-static void
-cfg80211_parse_mbssid_frame_data(struct wiphy *wiphy,
- struct cfg80211_inform_bss *data,
- struct ieee80211_mgmt *mgmt, size_t len,
- struct cfg80211_non_tx_bss *non_tx_data,
- gfp_t gfp)
-{
- enum cfg80211_bss_frame_type ftype;
- const u8 *ie = mgmt->u.probe_resp.variable;
- size_t ielen = len - offsetof(struct ieee80211_mgmt,
- u.probe_resp.variable);
-
- ftype = ieee80211_is_beacon(mgmt->frame_control) ?
- CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
-
- 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);
-}
-
-static void
-cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
- struct cfg80211_bss *nontrans_bss,
- struct ieee80211_mgmt *mgmt, size_t len)
-{
- u8 *ie, *new_ie, *pos;
- const struct element *nontrans_ssid;
- const u8 *trans_ssid, *mbssid;
- size_t ielen = len - offsetof(struct ieee80211_mgmt,
- u.probe_resp.variable);
- size_t new_ie_len;
- struct cfg80211_bss_ies *new_ies;
- const struct cfg80211_bss_ies *old;
- size_t cpy_len;
-
- lockdep_assert_held(&wiphy_to_rdev(wiphy)->bss_lock);
-
- ie = mgmt->u.probe_resp.variable;
-
- new_ie_len = ielen;
- trans_ssid = cfg80211_find_ie(WLAN_EID_SSID, ie, ielen);
- if (!trans_ssid)
- return;
- new_ie_len -= trans_ssid[1];
- mbssid = cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen);
- /*
- * It's not valid to have the MBSSID element before SSID
- * ignore if that happens - the code below assumes it is
- * after (while copying things inbetween).
- */
- if (!mbssid || mbssid < trans_ssid)
- return;
- new_ie_len -= mbssid[1];
-
- nontrans_ssid = ieee80211_bss_get_elem(nontrans_bss, WLAN_EID_SSID);
- if (!nontrans_ssid)
- return;
-
- new_ie_len += nontrans_ssid->datalen;
-
- /* generate new ie for nontrans BSS
- * 1. replace SSID with nontrans BSS' SSID
- * 2. skip MBSSID IE
- */
- new_ie = kzalloc(new_ie_len, GFP_ATOMIC);
- if (!new_ie)
- return;
-
- new_ies = kzalloc(sizeof(*new_ies) + new_ie_len, GFP_ATOMIC);
- if (!new_ies)
- goto out_free;
-
- pos = new_ie;
-
- /* copy the nontransmitted SSID */
- cpy_len = nontrans_ssid->datalen + 2;
- memcpy(pos, nontrans_ssid, cpy_len);
- pos += cpy_len;
- /* copy the IEs between SSID and MBSSID */
- cpy_len = trans_ssid[1] + 2;
- memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
- pos += (mbssid - (trans_ssid + cpy_len));
- /* copy the IEs after MBSSID */
- cpy_len = mbssid[1] + 2;
- memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
-
- /* update ie */
- new_ies->len = new_ie_len;
- new_ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
- new_ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control);
- memcpy(new_ies->data, new_ie, new_ie_len);
- if (ieee80211_is_probe_resp(mgmt->frame_control)) {
- old = rcu_access_pointer(nontrans_bss->proberesp_ies);
- rcu_assign_pointer(nontrans_bss->proberesp_ies, new_ies);
- rcu_assign_pointer(nontrans_bss->ies, new_ies);
- if (old)
- kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
- } else {
- old = rcu_access_pointer(nontrans_bss->beacon_ies);
- rcu_assign_pointer(nontrans_bss->beacon_ies, new_ies);
- cfg80211_update_hidden_bsses(bss_from_pub(nontrans_bss),
- new_ies, old);
- rcu_assign_pointer(nontrans_bss->ies, new_ies);
- if (old)
- kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
- }
-
-out_free:
- kfree(new_ie);
-}
-
/* cfg80211_inform_bss_width_frame helper */
static struct cfg80211_bss *
cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
@@ -2565,51 +2453,31 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
struct ieee80211_mgmt *mgmt, size_t len,
gfp_t gfp)
{
- struct cfg80211_bss *res, *tmp_bss;
+ struct cfg80211_bss *res;
const u8 *ie = mgmt->u.probe_resp.variable;
- const struct cfg80211_bss_ies *ies1, *ies2;
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);
+ if (!res)
+ return NULL;

/* don't do any further MBSSID handling for S1G */
if (ieee80211_is_s1g_beacon(mgmt->frame_control))
return res;

- if (!res || !wiphy->support_mbssid ||
- !cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
- return res;
- if (wiphy->support_only_he_mbssid &&
- !cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, ie, ielen))
- return res;
-
+ ftype = ieee80211_is_beacon(mgmt->frame_control) ?
+ CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
non_tx_data.tx_bss = res;
- /* process each non-transmitting bss */
- cfg80211_parse_mbssid_frame_data(wiphy, data, mgmt, len,
- &non_tx_data, gfp);
-
- spin_lock_bh(&wiphy_to_rdev(wiphy)->bss_lock);

- /* check if the res has other nontransmitting bss which is not
- * in MBSSID IE
- */
- ies1 = rcu_access_pointer(res->ies);
-
- /* go through nontrans_list, if the timestamp of the BSS is
- * earlier than the timestamp of the transmitting BSS then
- * update it
- */
- list_for_each_entry(tmp_bss, &res->nontrans_list,
- nontrans_list) {
- ies2 = rcu_access_pointer(tmp_bss->ies);
- if (ies2->tsf < ies1->tsf)
- cfg80211_update_notlisted_nontrans(wiphy, tmp_bss,
- mgmt, len);
- }
- spin_unlock_bh(&wiphy_to_rdev(wiphy)->bss_lock);
+ /* 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);

return res;
}
--
2.38.1


2023-06-16 06:57:16

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 17/20] wifi: mac80211: use cfg80211 defragmentation helper

From: Benjamin Berg <[email protected]>

Use the shared functionality rather than copying it into mac80211.

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/util.c | 93 ++++++++++++++------------------------
2 files changed, 37 insertions(+), 58 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b05dfdcfff11..4ae9c58d6a12 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1752,6 +1752,8 @@ struct ieee802_11_elems {

/* mult-link element can be de-fragmented and thus u8 is not sufficient */
size_t multi_link_len;
+ /* The element in the original IEs */
+ const struct element *multi_link_elem;

/*
* store the per station profile pointer and length in case that the
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index b81089fc51e1..2e3270dc4d05 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -984,6 +984,7 @@ ieee80211_parse_extension_element(u32 *crc,
break;
case WLAN_EID_EXT_EHT_MULTI_LINK:
if (ieee80211_mle_size_ok(data, len)) {
+ elems->multi_link_elem = (void *)elem;
elems->multi_link = (void *)data;
elems->multi_link_len = len;
}
@@ -1458,56 +1459,11 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
return found ? profile_len : 0;
}

-static void ieee80211_defragment_element(struct ieee802_11_elems *elems,
- void **elem_ptr, size_t *len,
- size_t total_len, u8 frag_id)
-{
- u8 *data = *elem_ptr, *pos, *start;
- const struct element *elem;
-
- /*
- * Since 'data' points to the data of the element, not the element
- * itself, allow 254 in case it was an extended element where the
- * extended ID isn't part of the data we see here and thus not part of
- * 'len' either.
- */
- if (!data || (*len != 254 && *len != 255))
- return;
-
- start = elems->scratch_pos;
-
- if (WARN_ON(*len > (elems->scratch + elems->scratch_len -
- elems->scratch_pos)))
- return;
-
- memcpy(elems->scratch_pos, data, *len);
- elems->scratch_pos += *len;
-
- pos = data + *len;
- total_len -= *len;
- for_each_element(elem, pos, total_len) {
- if (elem->id != frag_id)
- break;
-
- if (WARN_ON(elem->datalen >
- (elems->scratch + elems->scratch_len -
- elems->scratch_pos)))
- return;
-
- memcpy(elems->scratch_pos, elem->data, elem->datalen);
- elems->scratch_pos += elem->datalen;
-
- *len += elem->datalen;
- }
-
- *elem_ptr = start;
-}
-
static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,
u8 link_id)
{
const struct ieee80211_multi_link_elem *ml = elems->multi_link;
- size_t ml_len = elems->multi_link_len;
+ ssize_t ml_len = elems->multi_link_len;
const struct element *sub;

if (!ml || !ml_len)
@@ -1519,6 +1475,7 @@ static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,

for_each_mle_subelement(sub, (u8 *)ml, ml_len) {
struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data;
+ ssize_t sta_prof_len;
u16 control;

if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
@@ -1536,14 +1493,23 @@ static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,
if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE))
return;

- elems->prof = prof;
- elems->sta_prof_len = sub->datalen;
-
/* the sub element can be fragmented */
- ieee80211_defragment_element(elems, (void **)&elems->prof,
- &elems->sta_prof_len,
- ml_len - (sub->data - (u8 *)ml),
- IEEE80211_MLE_SUBELEM_FRAGMENT);
+ sta_prof_len =
+ cfg80211_defragment_element(sub,
+ (u8 *)ml, ml_len,
+ elems->scratch_pos,
+ elems->scratch +
+ elems->scratch_len -
+ elems->scratch_pos,
+ IEEE80211_MLE_SUBELEM_FRAGMENT);
+
+ if (sta_prof_len < 0)
+ return;
+
+ elems->prof = (void *)elems->scratch_pos;
+ elems->sta_prof_len = sta_prof_len;
+ elems->scratch_pos += sta_prof_len;
+
return;
}
}
@@ -1557,17 +1523,28 @@ static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems,
.from_ap = params->from_ap,
.link_id = -1,
};
+ ssize_t ml_len = elems->multi_link_len;
const struct element *non_inherit = NULL;
const u8 *end;

if (params->link_id == -1)
return;

- ieee80211_defragment_element(elems, (void **)&elems->multi_link,
- &elems->multi_link_len,
- elems->total_len - ((u8 *)elems->multi_link -
- elems->ie_start),
- WLAN_EID_FRAGMENT);
+ ml_len = cfg80211_defragment_element(elems->multi_link_elem,
+ elems->ie_start,
+ elems->total_len,
+ elems->scratch_pos,
+ elems->scratch +
+ elems->scratch_len -
+ elems->scratch_pos,
+ WLAN_EID_FRAGMENT);
+
+ if (ml_len < 0)
+ return;
+
+ elems->multi_link = (const void *)elems->scratch_pos;
+ elems->multi_link_len = ml_len;
+ elems->scratch_pos += ml_len;

ieee80211_mle_get_sta_prof(elems, params->link_id);
prof = elems->prof;
--
2.38.1


2023-06-16 06:57:24

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 19/20] wifi: mac80211: Add support for parsing Reconfiguration Multi Link element

From: Ilan Peer <[email protected]>

Parse Reconfiguration Multi Link IE.

Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/ieee80211_i.h | 5 +++++
net/mac80211/util.c | 21 ++++++++++++++++++---
2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 554aeb2e20d9..be3294719cb4 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1727,6 +1727,7 @@ struct ieee802_11_elems {
const struct ieee80211_eht_cap_elem *eht_cap;
const struct ieee80211_eht_operation *eht_operation;
const struct ieee80211_multi_link_elem *ml_basic;
+ const struct ieee80211_multi_link_elem *ml_reconf;

/* length of them, respectively */
u8 ext_capab_len;
@@ -1752,10 +1753,14 @@ struct ieee802_11_elems {

/* mult-link element can be de-fragmented and thus u8 is not sufficient */
size_t ml_basic_len;
+ size_t ml_reconf_len;

/* The basic Multi-Link element in the original IEs */
const struct element *ml_basic_elem;

+ /* The reconfiguration Multi-Link element in the original IEs */
+ const struct element *ml_reconf_elem;
+
/*
* store the per station profile pointer and length in case that the
* parsing also handled Multi-Link element parsing for a specific link
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 473c77dd3137..1e104c8756b1 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -984,9 +984,24 @@ ieee80211_parse_extension_element(u32 *crc,
break;
case WLAN_EID_EXT_EHT_MULTI_LINK:
if (ieee80211_mle_size_ok(data, len)) {
- elems->ml_basic_elem = (void *)elem;
- elems->ml_basic = (void *)data;
- elems->ml_basic_len = len;
+ const struct ieee80211_multi_link_elem *mle =
+ (void *)data;
+
+ switch (le16_get_bits(mle->control,
+ IEEE80211_ML_CONTROL_TYPE)) {
+ case IEEE80211_ML_CONTROL_TYPE_BASIC:
+ elems->ml_basic_elem = (void *)elem;
+ elems->ml_basic = data;
+ elems->ml_basic_len = len;
+ break;
+ case IEEE80211_ML_CONTROL_TYPE_RECONF:
+ elems->ml_reconf_elem = (void *)elem;
+ elems->ml_reconf = data;
+ elems->ml_reconf_len = len;
+ break;
+ default:
+ break;
+ }
}
break;
}
--
2.38.1


2023-06-16 06:57:49

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 20/20] wifi: mac80211: Rename ieee80211_mle_sta_prof_size_ok()

From: Ilan Peer <[email protected]>

Rename it to ieee80211_mle_basic_sta_prof_size_ok() as it
validates the size of the station profile included in
Basic Multi-Link element.

Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/linux/ieee80211.h | 6 ++++--
net/mac80211/util.c | 3 ++-
2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 47ddc65b443b..aeedd49e5101 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4798,11 +4798,13 @@ struct ieee80211_mle_per_sta_profile {
} __packed;

/**
- * ieee80211_mle_sta_prof_size_ok - validate multi-link element sta profile size
+ * ieee80211_mle_basic_sta_prof_size_ok - validate basic 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_sta_prof_size_ok(const u8 *data, size_t len)
+static inline bool ieee80211_mle_basic_sta_prof_size_ok(const u8 *data,
+ size_t len)
{
const struct ieee80211_mle_per_sta_profile *prof = (const void *)data;
u16 control;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 1e104c8756b1..2c53f6e17cfe 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1496,7 +1496,8 @@ static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,
if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE)
continue;

- if (!ieee80211_mle_sta_prof_size_ok(sub->data, sub->datalen))
+ if (!ieee80211_mle_basic_sta_prof_size_ok(sub->data,
+ sub->datalen))
return;

control = le16_to_cpu(prof->control);
--
2.38.1


2023-06-16 06:58:35

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 18/20] wifi: mac80211: Rename multi_link

From: Ilan Peer <[email protected]>

As a preparation to support Reconfiguration Multi Link
element, rename 'multi_link' and 'multi_link_len' fields
in 'struct ieee802_11_elems' to 'ml_basic' and 'ml_basic_len'.

Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
net/mac80211/ieee80211_i.h | 9 +++++----
net/mac80211/mlme.c | 8 ++++----
net/mac80211/util.c | 19 +++++++++----------
3 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4ae9c58d6a12..554aeb2e20d9 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1726,7 +1726,7 @@ struct ieee802_11_elems {
const struct ieee80211_aid_response_ie *aid_resp;
const struct ieee80211_eht_cap_elem *eht_cap;
const struct ieee80211_eht_operation *eht_operation;
- const struct ieee80211_multi_link_elem *multi_link;
+ const struct ieee80211_multi_link_elem *ml_basic;

/* length of them, respectively */
u8 ext_capab_len;
@@ -1751,9 +1751,10 @@ struct ieee802_11_elems {
u8 eht_cap_len;

/* mult-link element can be de-fragmented and thus u8 is not sufficient */
- size_t multi_link_len;
- /* The element in the original IEs */
- const struct element *multi_link_elem;
+ size_t ml_basic_len;
+
+ /* The basic Multi-Link element in the original IEs */
+ const struct element *ml_basic_elem;

/*
* store the per station profile pointer and length in case that the
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 1fc66f09cbb8..b8f8220cd9ff 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5277,24 +5277,24 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
}

if (ieee80211_vif_is_mld(&sdata->vif)) {
- if (!elems->multi_link) {
+ if (!elems->ml_basic) {
sdata_info(sdata,
"MLO association with %pM but no multi-link element in response!\n",
assoc_data->ap_addr);
goto abandon_assoc;
}

- if (le16_get_bits(elems->multi_link->control,
+ if (le16_get_bits(elems->ml_basic->control,
IEEE80211_ML_CONTROL_TYPE) !=
IEEE80211_ML_CONTROL_TYPE_BASIC) {
sdata_info(sdata,
"bad multi-link element (control=0x%x)\n",
- le16_to_cpu(elems->multi_link->control));
+ le16_to_cpu(elems->ml_basic->control));
goto abandon_assoc;
} else {
struct ieee80211_mle_basic_common_info *common;

- common = (void *)elems->multi_link->variable;
+ common = (void *)elems->ml_basic->variable;

if (memcmp(assoc_data->ap_addr,
common->mld_mac_addr, ETH_ALEN)) {
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 2e3270dc4d05..473c77dd3137 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -984,9 +984,9 @@ ieee80211_parse_extension_element(u32 *crc,
break;
case WLAN_EID_EXT_EHT_MULTI_LINK:
if (ieee80211_mle_size_ok(data, len)) {
- elems->multi_link_elem = (void *)elem;
- elems->multi_link = (void *)data;
- elems->multi_link_len = len;
+ elems->ml_basic_elem = (void *)elem;
+ elems->ml_basic = (void *)data;
+ elems->ml_basic_len = len;
}
break;
}
@@ -1462,8 +1462,8 @@ static size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len,
static void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems,
u8 link_id)
{
- const struct ieee80211_multi_link_elem *ml = elems->multi_link;
- ssize_t ml_len = elems->multi_link_len;
+ const struct ieee80211_multi_link_elem *ml = elems->ml_basic;
+ ssize_t ml_len = elems->ml_basic_len;
const struct element *sub;

if (!ml || !ml_len)
@@ -1523,14 +1523,14 @@ static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems,
.from_ap = params->from_ap,
.link_id = -1,
};
- ssize_t ml_len = elems->multi_link_len;
+ ssize_t ml_len = elems->ml_basic_len;
const struct element *non_inherit = NULL;
const u8 *end;

if (params->link_id == -1)
return;

- ml_len = cfg80211_defragment_element(elems->multi_link_elem,
+ ml_len = cfg80211_defragment_element(elems->ml_basic_elem,
elems->ie_start,
elems->total_len,
elems->scratch_pos,
@@ -1542,9 +1542,8 @@ static void ieee80211_mle_parse_link(struct ieee802_11_elems *elems,
if (ml_len < 0)
return;

- elems->multi_link = (const void *)elems->scratch_pos;
- elems->multi_link_len = ml_len;
- elems->scratch_pos += ml_len;
+ elems->ml_basic = (const void *)elems->scratch_pos;
+ elems->ml_basic_len = ml_len;

ieee80211_mle_get_sta_prof(elems, params->link_id);
prof = elems->prof;
--
2.38.1


2023-06-16 06:58:35

by Greenman, Gregory

[permalink] [raw]
Subject: [PATCH 16/20] wifi: cfg80211: add element defragmentation helper

From: Benjamin Berg <[email protected]>

This is already needed within mac80211 and support is also needed by
cfg80211 to parse ML elements.

Signed-off-by: Benjamin Berg <[email protected]>
Signed-off-by: Gregory Greenman <[email protected]>
---
include/net/cfg80211.h | 22 ++++++++++++++++
net/wireless/scan.c | 60 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 82 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 94ca5cb340f6..cf14a93e61eb 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -6664,6 +6664,28 @@ cfg80211_find_vendor_ie(unsigned int oui, int oui_type,
return (const void *)cfg80211_find_vendor_elem(oui, oui_type, ies, len);
}

+/**
+ * cfg80211_defragment_element - Defrag the given element data into a buffer
+ *
+ * @elem: the element to defragment
+ * @ies: elements where @elem is contained
+ * @ieslen: length of @ies
+ * @data: buffer to store element data
+ * @data_len: length of @data
+ * @frag_id: the element ID of fragments
+ *
+ * Return: length of @data, or -EINVAL on error
+ *
+ * Copy out all data from an element that may be fragmented into @data, while
+ * skipping all headers.
+ *
+ * The function uses memmove() internally. It is acceptable to defragment an
+ * element in-place.
+ */
+ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
+ size_t ieslen, u8 *data, size_t data_len,
+ u8 frag_id);
+
/**
* cfg80211_send_layer2_update - send layer 2 update frame
*
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 75e6e032bb3a..dc71c6ac5bf5 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -2288,6 +2288,66 @@ static void cfg80211_parse_mbssid_data(struct wiphy *wiphy,
kfree(profile);
}

+ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
+ size_t ieslen, u8 *data, size_t data_len,
+ u8 frag_id)
+{
+ const struct element *next;
+ ssize_t copied;
+ u8 elem_datalen;
+
+ if (!elem)
+ return -EINVAL;
+
+ /* elem might be invalid after the memmove */
+ next = (void *)(elem->data + elem->datalen);
+
+ elem_datalen = elem->datalen;
+ if (elem->id == WLAN_EID_EXTENSION) {
+ copied = elem->datalen - 1;
+ if (copied > data_len)
+ return -ENOSPC;
+
+ memmove(data, elem->data + 1, copied);
+ } else {
+ copied = elem->datalen;
+ if (copied > data_len)
+ return -ENOSPC;
+
+ memmove(data, elem->data, copied);
+ }
+
+ /* Fragmented elements must have 255 bytes */
+ if (elem_datalen < 255)
+ return copied;
+
+ for (elem = next;
+ elem->data < ies + ieslen &&
+ elem->data + elem->datalen < ies + ieslen;
+ elem = next) {
+ /* elem might be invalid after the memmove */
+ next = (void *)(elem->data + elem->datalen);
+
+ if (elem->id != frag_id)
+ break;
+
+ elem_datalen = elem->datalen;
+
+ if (copied + elem_datalen > data_len)
+ return -ENOSPC;
+
+ memmove(data + copied, elem->data, elem_datalen);
+ copied += elem_datalen;
+
+ /* Only the last fragment may be short */
+ if (elem_datalen != 255)
+ break;
+ }
+
+ return copied;
+}
+EXPORT_SYMBOL(cfg80211_defragment_element);
+
struct cfg80211_bss *
cfg80211_inform_bss_data(struct wiphy *wiphy,
struct cfg80211_inform_bss *data,
--
2.38.1