2021-04-26 19:06:48

by Aloka Dixit

[permalink] [raw]
Subject: [PATCH v10 0/4] multiple bssid and EMA support in AP mode

This patchset adds support for multiple BSSID and
enhanced multi-BSSID advertisements for AP mode.

HWSIM support will be added in a separate patchset.

John Crispin (4):
nl80211: multiple bssid and EMA support in AP mode
mac80211: multiple bssid support in interface handling
mac80211: MBSSID and EMA support in beacon handling
mac80211: CSA on non-transmitting interfaces

include/net/cfg80211.h | 51 +++++++
include/net/mac80211.h | 123 ++++++++++++++-
include/uapi/linux/nl80211.h | 101 ++++++++++++-
net/mac80211/cfg.c | 198 +++++++++++++++++++++++--
net/mac80211/debugfs.c | 2 +
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/iface.c | 12 ++
net/mac80211/tx.c | 189 ++++++++++++++++++++---
net/wireless/nl80211.c | 280 ++++++++++++++++++++++++++++++-----
9 files changed, 883 insertions(+), 75 deletions(-)


base-commit: 5d869070569a23aa909c6e7e9d010fc438a492ef
--
2.31.1


2021-04-26 19:06:56

by Aloka Dixit

[permalink] [raw]
Subject: [PATCH v10 1/4] nl80211: multiple bssid and EMA support in AP mode

From: John Crispin <[email protected]>

This commit adds new attributes to configure support for multiple BSSID
and advanced multi-BSSID advertisements (EMA) in AP mode.

New nested attributes:
(1) NL80211_ATTR_MBSSID_CONFIG: Per interface settings.
(2) NL80211_ATTR_MBSSID_ELEMS: MBSSID elements to be added in beacons.
Both are required from the userspace for successful configuration.

Configuration options:
(1) NL80211_MBSSID_CONFIG_ATTR_PARENT: Index of the transmitting interface
as advertised by the kernel
(2) NL80211_MBSSID_CONFIG_ATTR_COUNT: Total number of interfaces in the set
(3) NL80211_MBSSID_CONFIG_ATTR_INDEX: Index of the interfaces getting set
up in MBSSID set. Range: 1 to NL80211_MBSSID_CONFIG_ATTR_COUNT-1.
(4) NL80211_MBSSID_CONFIG_ATTR_EMA: Set to 1 if enhanced multi-BSSID
advertisements should be enabled.

Elements data:
(1) NL80211_MBSSID_ELEMS_ATTR_COUNT: Number of MBSSID elements
(2) NL80211_MBSSID_ELEMS_ATTR_DATA: Elements data. If EMA is enabled, these
elements will be added in separate beacon templates.

Memory for the elements is allocated dynamically. While this change frees
the memory in existing functions which call nl80211_parse_beacon(),
a comment is added to indicate the new references to do the same.

Reported-by: kernel test robot <[email protected]>
Reported-by: Dan Carpenter <[email protected]>
Signed-off-by: John Crispin <[email protected]>
Co-developed-by: Aloka Dixit <[email protected]>
Signed-off-by: Aloka Dixit <[email protected]>
---
v10: Shorter variable names and more descriptive comments

include/net/cfg80211.h | 51 +++++++
include/uapi/linux/nl80211.h | 101 ++++++++++++-
net/wireless/nl80211.c | 280 ++++++++++++++++++++++++++++++-----
3 files changed, 392 insertions(+), 40 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5224f885a99a..8709a4b8d944 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1033,6 +1033,38 @@ struct cfg80211_crypto_settings {
enum nl80211_sae_pwe_mechanism sae_pwe;
};

+/**
+ * struct cfg80211_mbssid_config - AP settings for multi bssid
+ *
+ * @parent: interface index of the transmitted profile in the MBSSID set
+ * @index: index of this AP in the multi bssid group.
+ * @count: the total number of multi bssid peer APs.
+ * @ema: Shall the beacons be sent out in EMA mode.
+ */
+struct cfg80211_mbssid_config {
+ u32 parent;
+ u8 index;
+ u8 count;
+ bool ema;
+};
+
+/**
+ * struct cfg80211_mbssid_elems - Multiple BSSID elements
+ *
+ * @cnt: Number of elements in array %elems.
+ *
+ * @elem: Array of multiple BSSID element(s) to be added into Beacon frames.
+ * @elem.data: Data for multiple BSSID elements.
+ * @elem.len: Length of data.
+ */
+struct cfg80211_mbssid_elems {
+ u8 cnt;
+ struct {
+ u8 *data;
+ size_t len;
+ } elem[];
+};
+
/**
* struct cfg80211_beacon_data - beacon data
* @head: head portion of beacon (before TIM IE)
@@ -1051,6 +1083,7 @@ struct cfg80211_crypto_settings {
* @assocresp_ies_len: length of assocresp_ies in octets
* @probe_resp_len: length of probe response template (@probe_resp)
* @probe_resp: probe response template (AP mode only)
+ * @mbssid: multiple BSSID elements
* @ftm_responder: enable FTM responder functionality; -1 for no change
* (which also implies no change in LCI/civic location data)
* @lci: Measurement Report element content, starting with Measurement Token
@@ -1068,6 +1101,7 @@ struct cfg80211_beacon_data {
const u8 *probe_resp;
const u8 *lci;
const u8 *civicloc;
+ struct cfg80211_mbssid_elems *mbssid;
s8 ftm_responder;

size_t head_len, tail_len;
@@ -1182,6 +1216,7 @@ enum cfg80211_ap_settings_flags {
* @he_oper: HE operation IE (or %NULL if HE isn't enabled)
* @fils_discovery: FILS discovery transmission parameters
* @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
+ * @mbssid: AP settings for multiple bssid
*/
struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef;
@@ -1214,6 +1249,7 @@ struct cfg80211_ap_settings {
struct cfg80211_he_bss_color he_bss_color;
struct cfg80211_fils_discovery fils_discovery;
struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
+ struct cfg80211_mbssid_config mbssid;
};

/**
@@ -4945,6 +4981,16 @@ struct wiphy_iftype_akm_suites {
* configuration through the %NL80211_TID_CONFIG_ATTR_RETRY_SHORT and
* %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes
* @sar_capa: SAR control capabilities
+ *
+ * @mbssid: Describes driver's multiple BSSID configuration support
+ * @mbssid.max_interfaces: maximum number of interfaces supported by the driver
+ * in a multiple BSSID set that can ben configured using
+ * %NL80211_MBSSID_CONFIG_ATTR_COUNT.
+ * Kernel supports only one MBSSID per wiphy.
+ * @mbssid.max_periodicity: maximum profile periodicity supported by the driver
+ * that can be configured using %NL80211_MBSSID_ELEMS_ATTR_COUNT.
+ * This value is used only when enhanced multi-BSSID advertisements feature
+ * is enabled through %NL80211_MBSSID_CONFIG_ATTR_EMA flag.
*/
struct wiphy {
struct mutex mtx;
@@ -5087,6 +5133,11 @@ struct wiphy {

const struct cfg80211_sar_capa *sar_capa;

+ struct {
+ u8 max_interfaces;
+ u8 max_periodicity;
+ } mbssid;
+
char priv[] __aligned(NETDEV_ALIGN);
};

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index f962c06e9818..e047393edeb1 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -337,7 +337,10 @@
* @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
* %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
* userspace to request deletion of a virtual interface, then requires
- * attribute %NL80211_ATTR_IFINDEX.
+ * attribute %NL80211_ATTR_IFINDEX. If multiple BSSID advertisements are
+ * enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS,
+ * and if this command is used for the transmitting interface, then all
+ * the non-transmitting interfaces are deleted as well.
*
* @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
* by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
@@ -2560,6 +2563,21 @@ enum nl80211_commands {
* disassoc events to indicate that an immediate reconnect to the AP
* is desired.
*
+ * @NL80211_ATTR_MBSSID_CONFIG: Nested parameter to configure multiple BSSID
+ * advertisements. It is also used to advertise the maximum number of
+ * interfaces supported by the driver when multiple BSSID is enabled.
+ * This attribute and %NL80211_ATTR_MBSSID_ELEMS are both required for
+ * configuration. Current support is only for AP mode and drivers must
+ * set NL80211_EXT_FEATURE_MBSSID_AP for this attribute to be used.
+ * See &enum nl80211_mbssid_config_attributes for details.
+ *
+ * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements
+ * data. It is also used to advertise the maximum profile periodicity
+ * supported by the driver when enhanced multi-BSSID advertisements (EMA)
+ * are enabled. This attribute and %NL80211_ATTR_MBSSID_CONFIG are both
+ * required for configuration. Current support is only for AP mode.
+ * See &enum nl80211_mbssid_elems_attributes for details.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3057,6 +3075,9 @@ enum nl80211_attrs {

NL80211_ATTR_DISABLE_HE,

+ NL80211_ATTR_MBSSID_CONFIG,
+ NL80211_ATTR_MBSSID_ELEMS,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
@@ -5950,6 +5971,10 @@ enum nl80211_feature_flags {
* frame protection for all management frames exchanged during the
* negotiation and range measurement procedure.
*
+ * @NL80211_EXT_FEATURE_MBSSID_AP: Device supports multiple BSSID advertisement
+ * @NL80211_EXT_FEATURE_EMA_AP: Device supports enhanced multiple BSSID
+ * advertisements (EMA).
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -6014,6 +6039,8 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_SECURE_LTF,
NL80211_EXT_FEATURE_SECURE_RTT,
NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+ NL80211_EXT_FEATURE_MBSSID_AP,
+ NL80211_EXT_FEATURE_EMA_AP,

/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -7299,4 +7326,76 @@ enum nl80211_sar_specs_attrs {
NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
};

+/**
+ * enum nl80211_mbssid_config_attributes - Attributes to configure/advertise
+ * multiple BSSID and enhanced multi-BSSID advertisements in AP mode.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_PARENT: For a non-transmitted BSSID, this
+ * attribute provides the interface index (u32) of the transmitted profile.
+ * Must match one of the interface indices advertised by the kernel.
+ * Mandatory parameter for non-transmitting interfaces, ignored for the
+ * transmitting interface.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_COUNT: Total number of BSSs (u8) in the
+ * multiple BSSID set. Mandatory attribute with minimum value of 1.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_INDEX: The index of this BSS (u8) in the
+ * multiple BSSID set. Mandatory parameter for non-transmitting interfaces,
+ * ignored for the transmitting interface.
+ * Range: 1 to %NL80211_MBSSID_CONFIG_ATTR_COUNT-1.
+ *
+ * @NL80211_MBSSID_CONFIG_ATTR_EMA: Userspace and the drivers should use this
+ * flag to indicate if enhanced multi-BSSID advertisements (EMA AP) feature
+ * is supported/enabled. When set, beacons are generated as per EMA rules,
+ * see %nl80211_mbssid_elems_attributes for details. Drivers must set
+ * NL80211_EXT_FEATURE_EMA_AP for this flag to be used.
+ *
+ * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal
+ * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute
+ */
+enum nl80211_mbssid_config_attributes {
+ __NL80211_MBSSID_CONFIG_ATTR_INVALID,
+
+ NL80211_MBSSID_CONFIG_ATTR_PARENT,
+ NL80211_MBSSID_CONFIG_ATTR_COUNT,
+ NL80211_MBSSID_CONFIG_ATTR_INDEX,
+ NL80211_MBSSID_CONFIG_ATTR_EMA,
+
+ /* keep last */
+ __NL80211_MBSSID_CONFIG_ATTR_LAST,
+ NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
+};
+
+/**
+ * enum nl80211_mbssid_elems_attributes - Attributes used to pass multiple BSSID
+ * elements data. Current support is only for AP mode.
+ * If enhanced multi-BSSID advertisements (EMA) are enabled with flag
+ * %NL80211_MBSSID_CONFIG_ATTR_EMA, elements provided through this
+ * attribute are split into multiple beacons. Otherwise all elements will
+ * be included in every beacon for non-EMA AP.
+ *
+ * @__NL80211_MBSSID_ELEMS_ATTR_INVALID: Invalid
+ *
+ * @NL80211_MBSSID_ELEMS_ATTR_COUNT: Number of multiple BSSID elements (u8).
+ * This attribute is also used to advertise the maximum profile periodicity
+ * supported by the driver if EMA is enabled.
+ *
+ * @NL80211_MBSSID_ELEMS_ATTR_DATA: Array of multiple BSSID elements.
+ *
+ * @__NL80211_MBSSID_ELEMS_ATTR_LAST: Internal
+ * @NL80211_MBSSID_ELEMS_ATTR_MAX: highest attribute
+ */
+enum nl80211_mbssid_elems_attributes {
+ __NL80211_MBSSID_ELEMS_ATTR_INVALID,
+
+ NL80211_MBSSID_ELEMS_ATTR_COUNT,
+ NL80211_MBSSID_ELEMS_ATTR_DATA,
+
+ /* keep last */
+ __NL80211_MBSSID_ELEMS_ATTR_LAST,
+ NL80211_MBSSID_ELEMS_ATTR_MAX = __NL80211_MBSSID_ELEMS_ATTR_LAST - 1,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7e811a3b0987..68cd33033488 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -437,6 +437,20 @@ sar_policy[NL80211_SAR_ATTR_MAX + 1] = {
[NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy),
};

+static const struct nla_policy
+nl80211_mbssid_elems_policy[NL80211_MBSSID_ELEMS_ATTR_MAX + 1] = {
+ [NL80211_MBSSID_ELEMS_ATTR_COUNT] = { .type = NLA_U8 },
+ [NL80211_MBSSID_ELEMS_ATTR_DATA] = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
+ [NL80211_MBSSID_CONFIG_ATTR_PARENT] = { .type = NLA_U32 },
+ [NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 },
+ [NL80211_MBSSID_CONFIG_ATTR_COUNT] = { .type = NLA_U8 },
+ [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG },
+};
+
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
@@ -759,6 +773,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
[NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
[NL80211_ATTR_DISABLE_HE] = { .type = NLA_FLAG },
+ [NL80211_ATTR_MBSSID_CONFIG] =
+ NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
+ [NL80211_ATTR_MBSSID_ELEMS] =
+ NLA_POLICY_NESTED(nl80211_mbssid_elems_policy),
};

/* policy for the key attributes */
@@ -2198,6 +2216,48 @@ nl80211_put_sar_specs(struct cfg80211_registered_device *rdev,
return -ENOBUFS;
}

+static int nl80211_put_mbssid_support(struct wiphy *wiphy,
+ struct sk_buff *msg)
+{
+ struct nlattr *config = NULL, *elems = NULL;
+
+ if (!wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_MBSSID_AP))
+ return 0;
+
+ if (wiphy->mbssid.max_interfaces) {
+ config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
+ if (!config)
+ goto fail;
+
+ if (nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_COUNT,
+ wiphy->mbssid.max_interfaces))
+ goto fail;
+
+ nla_nest_end(msg, config);
+ }
+
+ if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_EMA_AP) &&
+ wiphy->mbssid.max_periodicity) {
+ elems = nla_nest_start(msg, NL80211_ATTR_MBSSID_ELEMS);
+ if (!elems)
+ goto fail;
+
+ if (nla_put_u8(msg, NL80211_MBSSID_ELEMS_ATTR_COUNT,
+ wiphy->mbssid.max_periodicity))
+ goto fail;
+
+ nla_nest_end(msg, elems);
+ }
+ return 0;
+
+fail:
+ if (elems)
+ nla_nest_cancel(msg, elems);
+ if (config)
+ nla_nest_cancel(msg, config);
+ return -ENOBUFS;
+}
+
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
@@ -2780,6 +2840,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
if (nl80211_put_sar_specs(rdev, msg))
goto nla_put_failure;

+ if (nl80211_put_mbssid_support(&rdev->wiphy, msg))
+ goto nla_put_failure;
+
/* done */
state->split_start = 0;
break;
@@ -4958,6 +5021,92 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
return 0;
}

+static int nl80211_parse_mbssid_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct nlattr *attrs, u8 num_elems,
+ struct cfg80211_mbssid_config *config)
+{
+ struct nlattr *tb[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
+ struct net_device *parent;
+
+ if (!wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_MBSSID_AP))
+ return -EOPNOTSUPP;
+
+ if (nla_parse_nested(tb, NL80211_MBSSID_CONFIG_ATTR_MAX, attrs, NULL,
+ NULL) || !tb[NL80211_MBSSID_CONFIG_ATTR_COUNT])
+ return -EINVAL;
+
+ config->ema = nla_get_flag(tb[NL80211_MBSSID_CONFIG_ATTR_EMA]);
+ if (config->ema) {
+ if (!wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_EMA_AP))
+ return -EOPNOTSUPP;
+
+ /* nl80211_parse_mbssid_elems() sets this value */
+ if (!num_elems || (wiphy->mbssid.max_periodicity &&
+ num_elems > wiphy->mbssid.max_periodicity))
+ return -EINVAL;
+ }
+
+ config->count = nla_get_u8(tb[NL80211_MBSSID_CONFIG_ATTR_COUNT]);
+ if (!config->count || (wiphy->mbssid.max_interfaces &&
+ config->count > wiphy->mbssid.max_interfaces))
+ return -EINVAL;
+
+ if (tb[NL80211_MBSSID_CONFIG_ATTR_PARENT]) {
+ config->parent = nla_get_u32(tb[NL80211_MBSSID_CONFIG_ATTR_PARENT]);
+ parent = __dev_get_by_index(wiphy_net(wiphy), config->parent);
+ if (!parent || !parent->ieee80211_ptr)
+ return -EINVAL;
+
+ if (parent == dev)
+ config->parent = 0;
+ }
+
+ if (tb[NL80211_MBSSID_CONFIG_ATTR_INDEX] && config->parent) {
+ config->index = nla_get_u8(tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]);
+ if (!config->index || config->index >= config->count)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct cfg80211_mbssid_elems *
+nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
+{
+ struct nlattr *nl_elems, *tb[NL80211_MBSSID_ELEMS_ATTR_MAX + 1];
+ struct cfg80211_mbssid_elems *elems = NULL;
+ int rem_elems;
+ u8 i = 0, num_elems;
+
+ if (!wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_MBSSID_AP))
+ return NULL;
+
+ if (nla_parse_nested(tb, NL80211_MBSSID_ELEMS_ATTR_MAX, attrs, NULL,
+ NULL) ||
+ !tb[NL80211_MBSSID_ELEMS_ATTR_COUNT] ||
+ !tb[NL80211_MBSSID_ELEMS_ATTR_DATA])
+ return NULL;
+
+ num_elems = nla_get_u8(tb[NL80211_MBSSID_ELEMS_ATTR_COUNT]);
+ if (!num_elems)
+ return NULL;
+
+ elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL);
+ if (!elems)
+ return NULL;
+
+ elems->cnt = num_elems;
+ nla_for_each_nested(nl_elems, tb[NL80211_MBSSID_ELEMS_ATTR_DATA],
+ rem_elems) {
+ elems->elem[i].data = nla_data(nl_elems);
+ elems->elem[i].len = nla_len(nl_elems);
+ i++;
+ }
+
+ return elems;
+}
+
static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
struct nlattr *attrs[],
struct cfg80211_beacon_data *bcn)
@@ -5038,6 +5187,15 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
bcn->ftm_responder = -1;
}

+ if (attrs[NL80211_ATTR_MBSSID_ELEMS]) {
+ bcn->mbssid = nl80211_parse_mbssid_elems(&rdev->wiphy,
+ attrs[NL80211_ATTR_MBSSID_ELEMS]);
+ if (!bcn->mbssid)
+ return -EINVAL;
+
+ /* Caller must free bcn->mbssid in case of error */
+ }
+
return 0;
}

@@ -5323,7 +5481,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)

err = nl80211_parse_beacon(rdev, info->attrs, &params.beacon);
if (err)
- return err;
+ goto out;

params.beacon_interval =
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
@@ -5333,7 +5491,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
err = cfg80211_validate_beacon_int(rdev, dev->ieee80211_ptr->iftype,
params.beacon_interval);
if (err)
- return err;
+ goto out;

/*
* In theory, some of these attributes should be required here
@@ -5346,8 +5504,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
params.ssid_len =
nla_len(info->attrs[NL80211_ATTR_SSID]);
- if (params.ssid_len == 0)
- return -EINVAL;
+ if (params.ssid_len == 0) {
+ err = -EINVAL;
+ goto out;
+ }
}

if (info->attrs[NL80211_ATTR_HIDDEN_SSID])
@@ -5360,57 +5520,74 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
params.auth_type = nla_get_u32(
info->attrs[NL80211_ATTR_AUTH_TYPE]);
if (!nl80211_valid_auth_type(rdev, params.auth_type,
- NL80211_CMD_START_AP))
- return -EINVAL;
+ NL80211_CMD_START_AP)) {
+ err = -EINVAL;
+ goto out;
+ }
} else
params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;

err = nl80211_crypto_settings(rdev, info, &params.crypto,
NL80211_MAX_NR_CIPHER_SUITES);
if (err)
- return err;
+ goto out;

if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
- if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
- return -EOPNOTSUPP;
+ if (!(rdev->wiphy.features &
+ NL80211_FEATURE_INACTIVITY_TIMER)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
params.inactivity_timeout = nla_get_u16(
info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
}

if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
- if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
- return -EINVAL;
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+ err = -EINVAL;
+ goto out;
+ }
params.p2p_ctwindow =
nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
if (params.p2p_ctwindow != 0 &&
- !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
- return -EINVAL;
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) {
+ err = -EINVAL;
+ goto out;
+ }
}

if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
u8 tmp;

- if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
- return -EINVAL;
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+ err = -EINVAL;
+ goto out;
+ }
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
params.p2p_opp_ps = tmp;
if (params.p2p_opp_ps != 0 &&
- !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
- return -EINVAL;
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) {
+ err = -EINVAL;
+ goto out;
+ }
}

if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info, &params.chandef);
if (err)
- return err;
+ goto out;
} else if (wdev->preset_chandef.chan) {
params.chandef = wdev->preset_chandef;
- } else if (!nl80211_get_ap_channel(rdev, &params))
- return -EINVAL;
+ } else if (!nl80211_get_ap_channel(rdev, &params)) {
+ err = -EINVAL;
+ goto out;
+ }

if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
- wdev->iftype))
- return -EINVAL;
+ wdev->iftype)) {
+ err = -EINVAL;
+ goto out;
+ }

if (info->attrs[NL80211_ATTR_TX_RATES]) {
err = nl80211_parse_tx_bitrate_mask(info, info->attrs,
@@ -5418,12 +5595,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
&params.beacon_rate,
dev, false);
if (err)
- return err;
+ goto out;

err = validate_beacon_tx_rate(rdev, params.chandef.chan->band,
&params.beacon_rate);
if (err)
- return err;
+ goto out;
}

if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
@@ -5434,29 +5611,38 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
break;
case NL80211_SMPS_STATIC:
if (!(rdev->wiphy.features &
- NL80211_FEATURE_STATIC_SMPS))
- return -EINVAL;
+ NL80211_FEATURE_STATIC_SMPS)) {
+ err = -EINVAL;
+ goto out;
+ }
break;
case NL80211_SMPS_DYNAMIC:
if (!(rdev->wiphy.features &
- NL80211_FEATURE_DYNAMIC_SMPS))
- return -EINVAL;
+ NL80211_FEATURE_DYNAMIC_SMPS)) {
+ err = -EINVAL;
+ goto out;
+ }
break;
default:
- return -EINVAL;
+ err = -EINVAL;
+ goto out;
}
} else {
params.smps_mode = NL80211_SMPS_OFF;
}

params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
- if (params.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ])
- return -EOPNOTSUPP;
+ if (params.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }

if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
params.acl = parse_acl_data(&rdev->wiphy, info);
- if (IS_ERR(params.acl))
- return PTR_ERR(params.acl);
+ if (IS_ERR(params.acl)) {
+ err = PTR_ERR(params.acl);
+ goto out;
+ }
}

params.twt_responder =
@@ -5494,6 +5680,15 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
goto out;
}

+ if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) {
+ err = nl80211_parse_mbssid_config(&rdev->wiphy, dev,
+ info->attrs[NL80211_ATTR_MBSSID_CONFIG],
+ params.beacon.mbssid->cnt,
+ &params.mbssid);
+ if (err)
+ goto out;
+ }
+
nl80211_calculate_ap_params(&params);

if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])
@@ -5514,8 +5709,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev_unlock(wdev);

out:
- kfree(params.acl);
-
+ if (!IS_ERR(params.acl))
+ kfree(params.acl);
+ kfree(params.beacon.mbssid);
return err;
}

@@ -5539,12 +5735,14 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)

err = nl80211_parse_beacon(rdev, info->attrs, &params);
if (err)
- return err;
+ goto out;

wdev_lock(wdev);
err = rdev_change_beacon(rdev, dev, &params);
wdev_unlock(wdev);

+out:
+ kfree(params.mbssid);
return err;
}

@@ -9224,12 +9422,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)

err = nl80211_parse_beacon(rdev, info->attrs, &params.beacon_after);
if (err)
- return err;
+ goto free;

csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs),
GFP_KERNEL);
- if (!csa_attrs)
- return -ENOMEM;
+ if (!csa_attrs) {
+ err = -ENOMEM;
+ goto free;
+ }

err = nla_parse_nested_deprecated(csa_attrs, NL80211_ATTR_MAX,
info->attrs[NL80211_ATTR_CSA_IES],
@@ -9347,6 +9547,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
wdev_unlock(wdev);

free:
+ kfree(params.beacon_after.mbssid);
+ kfree(params.beacon_csa.mbssid);
kfree(csa_attrs);
return err;
}

base-commit: 5d869070569a23aa909c6e7e9d010fc438a492ef
--
2.31.1

2021-04-26 19:06:56

by Aloka Dixit

[permalink] [raw]
Subject: [PATCH v10 3/4] mac80211: MBSSID and EMA support in beacon handling

From: John Crispin <[email protected]>

New fields added in struct beacon_data to store all MBSSID elements and
the index of the next element to be included in beacon template.

For a non-EMA AP, a single beacon template is generated with all elements.
For an EMA AP, multiple beacon templates are generated and each includes
a single MBSSID element. EMA profile periodicity equals the count of
elements.
This patch also generates and includes multiple BSSID configuration element
in each beacon template for both EMA/non-EMA AP when MBSSID is enabled.

New field added in struct ieee80211_mutable_offsets to store offset of the
first MBSSID element in beacons. Also the offset for channel switch
announcement (CSA) is modified to account for MBSSID length.

Depending on the hardware support and the usecase, drivers should use
following functions for beacon generation:
(1) ieee80211_beacon_get_template() - Existing function for legacy beacon
generation for non-MBSSID APs. No change in prototype.
(2) ieee80211_beacon_get_template_ema_next() - Generate the next EMA
beacon template using the 'ema_index' stored in beacon_data.
(3) ieee80211_beacon_get_template_ema_list() - Generate all EMA templates.
Drivers must call ieee80211_beacon_free_ema_list() to free the memory.

This commit also adds a missing free() for sdata->u.ap.next_beacon in
ieee80211_set_csa_beacon() and sets the pointer to NULL in case of error.

Signed-off-by: John Crispin <[email protected]>
Co-developed-by: Aloka Dixit <[email protected]>
Signed-off-by: Aloka Dixit <[email protected]>
---
v10: Added the lock missing in v9 for ieee80211_beacon_get_tim()

include/net/mac80211.h | 89 +++++++++++++++++
net/mac80211/cfg.c | 116 ++++++++++++++++++++---
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/tx.c | 189 ++++++++++++++++++++++++++++++++-----
4 files changed, 363 insertions(+), 33 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index ca83bfe14355..a5bc0d996cbe 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -4897,12 +4897,14 @@ void ieee80211_report_low_ack(struct ieee80211_sta *sta, u32 num_packets);
* @cntdwn_counter_offs: array of IEEE80211_MAX_CNTDWN_COUNTERS_NUM offsets
* to countdown counters. This array can contain zero values which
* should be ignored.
+ * @mbssid_offset: position of the multiple bssid element
*/
struct ieee80211_mutable_offsets {
u16 tim_offset;
u16 tim_length;

u16 cntdwn_counter_offs[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
+ u16 mbssid_offset;
};

/**
@@ -4929,6 +4931,93 @@ ieee80211_beacon_get_template(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_mutable_offsets *offs);

+/**
+ * enum ieee80211_bcn_tmpl_ema - EMA beacon generation type
+ *
+ * When enhanced multi-BSSID advertisements (EMA) mode is enabled, the
+ * non-transmitting profiles from the multiple BSSID set are split into more
+ * than one multiple BSSID elements if required. Each EMA beacon includes only
+ * one element to reduce the total size. The number of beacons required to
+ * cover all profiles is called as the profile periodicity of the set.
+ *
+ * In MAC80211, the multiple BSSID elements passed by the application are
+ * stored in a array and the index of the next element (starting from 0) to be
+ * included in the beacon template is tracked through the member ema_index of
+ * struct beacon_data.
+ *
+ * @IEEE80211_BCN_EMA_NONE: Used when EMA is disabled. Only one beacon
+ * template will be generated which includes all multiple BSSID elements.
+ * @IEEE80211_BCN_EMA_NEXT: Used when EMA is enabled. Includes the next
+ * multiple BSSID element while generating the beacon template.
+ * @IEEE80211_BCN_EMA_BASE: Used when EMA is enabled. Beacon template includes
+ * the multiple MBSSID element at a specified index which should be set
+ * to a value more than or equal to IEEE80211_BCN_EMA_BASE.
+ */
+enum ieee80211_bcn_tmpl_ema {
+ IEEE80211_BCN_EMA_NONE = -2,
+ IEEE80211_BCN_EMA_NEXT = -1,
+ IEEE80211_BCN_EMA_BASE = 0,
+};
+
+/**
+ * ieee80211_beacon_get_template_ema_next - EMA beacon template generation
+ * function for drivers using the sw offload path.
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @offs: &struct ieee80211_mutable_offsets pointer to struct that will
+ * receive the offsets that may be updated by the driver.
+ *
+ * This function follows the same rules as ieee80211_beacon_get_template()
+ * but returns a beacon template which includes the next multiple BSSID
+ * element.
+ *
+ * Return: The beacon template. %NULL on error.
+ */
+struct sk_buff *ieee80211_beacon_get_template_ema_next(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_mutable_offsets *offs);
+
+/**
+ * struct ieee80211_ema_bcn_list - list entry of an EMA beacon
+ * @list: the list pointer.
+ * @skb: the skb containing this specific beacon
+ * @offs: &struct ieee80211_mutable_offsets pointer to struct that will
+ * receive the offsets that may be updated by the driver.
+ */
+struct ieee80211_ema_bcn_list {
+ struct list_head list;
+ struct sk_buff *skb;
+ struct ieee80211_mutable_offsets offs;
+};
+
+/**
+ * ieee80211_beacon_get_template_ema_list - EMA beacon template generation
+ * function for drivers using the hw offload.
+ * @hw: pointer obtained from ieee80211_alloc_hw().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @head: linked list head that will get populated with
+ * &struct ieee80211_ema_bcn_list pointers.
+ *
+ * This function follows the same rules as ieee80211_beacon_get_template()
+ * but returns a linked list of all beacon templates required to cover all
+ * profiles in the multiple BSSID set. Each template includes only one multiple
+ * BSSID element.
+ *
+ * Return: The nuber of entries in the list or 0 on error.
+ */
+int ieee80211_beacon_get_template_ema_list(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct list_head *head);
+
+/**
+ * ieee80211_beacon_free_ema_list - free an EMA beacon template list
+ * @head: linked list head containing &struct ieee80211_ema_bcn_list pointers.
+ *
+ * This function will free a list previously acquired by calling
+ * ieee80211_beacon_get_template_ema_list()
+ */
+void ieee80211_beacon_free_ema_list(struct list_head *head);
+
/**
* ieee80211_beacon_get_tim - beacon generation function
* @hw: pointer obtained from ieee80211_alloc_hw().
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index d1a965897b9d..db21e1a621b1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1002,14 +1002,49 @@ static int ieee80211_set_ftm_responder_params(
return 0;
}

+static int ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems)
+{
+ int i, len = 0;
+
+ if (!elems)
+ return 0;
+
+ for (i = 0; i < elems->cnt; i++)
+ len += elems->elem[i].len;
+
+ return len;
+}
+
+static u8 *ieee80211_copy_mbssid_beacon(u8 *offset,
+ struct cfg80211_mbssid_elems *dest,
+ struct cfg80211_mbssid_elems *src)
+{
+ int i;
+
+ if (!dest || !src)
+ return offset;
+
+ dest->cnt = src->cnt;
+ for (i = 0; i < dest->cnt; i++) {
+ dest->elem[i].len = src->elem[i].len;
+ dest->elem[i].data = offset;
+ memcpy(dest->elem[i].data, src->elem[i].data,
+ dest->elem[i].len);
+ offset += dest->elem[i].len;
+ }
+
+ return offset;
+}
+
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
struct cfg80211_beacon_data *params,
const struct ieee80211_csa_settings *csa)
{
struct beacon_data *new, *old;
- int new_head_len, new_tail_len;
+ int new_head_len, new_tail_len, new_mbssid_len = 0;
int size, err;
u32 changed = BSS_CHANGED_BEACON;
+ u8 *new_mbssid_offset;

old = sdata_dereference(sdata->u.ap.beacon, sdata);

@@ -1031,12 +1066,26 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
else
new_tail_len = old->tail_len;

- size = sizeof(*new) + new_head_len + new_tail_len;
+ /* new or old mbssid? */
+ if (params->mbssid)
+ new_mbssid_len = ieee80211_get_mbssid_beacon_len(params->mbssid);
+ else if (old && old->mbssid)
+ new_mbssid_len = ieee80211_get_mbssid_beacon_len(old->mbssid);
+
+ size = sizeof(*new) + new_head_len + new_tail_len + new_mbssid_len;

new = kzalloc(size, GFP_KERNEL);
if (!new)
return -ENOMEM;

+ if (new_mbssid_len) {
+ new->mbssid = kzalloc(sizeof(*params->mbssid), GFP_KERNEL);
+ if (!new->mbssid) {
+ kfree(new);
+ return -ENOMEM;
+ }
+ }
+
/* start filling the new info now */

/*
@@ -1048,6 +1097,15 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
new->head_len = new_head_len;
new->tail_len = new_tail_len;

+ /* copy in optional mbssid_ies */
+ new_mbssid_offset = new->tail + new_tail_len;
+ if (params->mbssid)
+ ieee80211_copy_mbssid_beacon(new_mbssid_offset, new->mbssid,
+ params->mbssid);
+ else if (old && old->mbssid)
+ ieee80211_copy_mbssid_beacon(new_mbssid_offset, new->mbssid,
+ old->mbssid);
+
if (csa) {
new->cntdwn_current_counter = csa->count;
memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_beacon,
@@ -1071,6 +1129,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
err = ieee80211_set_probe_resp(sdata, params->probe_resp,
params->probe_resp_len, csa);
if (err < 0) {
+ kfree(new->mbssid);
kfree(new);
return err;
}
@@ -1086,6 +1145,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
params->civicloc_len);

if (err < 0) {
+ kfree(new->mbssid);
kfree(new);
return err;
}
@@ -1095,9 +1155,10 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,

rcu_assign_pointer(sdata->u.ap.beacon, new);

- if (old)
+ if (old) {
+ kfree(old->mbssid);
kfree_rcu(old, rcu_head);
-
+ }
return changed;
}

@@ -1255,8 +1316,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
if (err) {
old = sdata_dereference(sdata->u.ap.beacon, sdata);

- if (old)
+ if (old) {
+ kfree(old->mbssid);
kfree_rcu(old, rcu_head);
+ }
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
goto error;
}
@@ -1336,8 +1399,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)

mutex_unlock(&local->mtx);

- kfree(sdata->u.ap.next_beacon);
- sdata->u.ap.next_beacon = NULL;
+ if (sdata->u.ap.next_beacon) {
+ kfree(sdata->u.ap.next_beacon->mbssid);
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+ }

/* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
@@ -1349,6 +1415,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
RCU_INIT_POINTER(sdata->u.ap.fils_discovery, NULL);
RCU_INIT_POINTER(sdata->u.ap.unsol_bcast_probe_resp, NULL);
+ kfree(old_beacon->mbssid);
kfree_rcu(old_beacon, rcu_head);
if (old_probe_resp)
kfree_rcu(old_probe_resp, rcu_head);
@@ -3107,13 +3174,24 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)

len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
beacon->proberesp_ies_len + beacon->assocresp_ies_len +
- beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
+ beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len +
+ ieee80211_get_mbssid_beacon_len(beacon->mbssid);

new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
if (!new_beacon)
return NULL;

+ if (beacon->mbssid) {
+ new_beacon->mbssid = kzalloc(sizeof(*beacon->mbssid),
+ GFP_KERNEL);
+ if (!new_beacon->mbssid) {
+ kfree(new_beacon);
+ return NULL;
+ }
+ }
+
pos = (u8 *)(new_beacon + 1);
+
if (beacon->head_len) {
new_beacon->head_len = beacon->head_len;
new_beacon->head = pos;
@@ -3150,6 +3228,9 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
pos += beacon->probe_resp_len;
}
+ if (beacon->mbssid && beacon->mbssid->cnt)
+ pos = ieee80211_copy_mbssid_beacon(pos, new_beacon->mbssid,
+ beacon->mbssid);

/* might copy -1, meaning no changes requested */
new_beacon->ftm_responder = beacon->ftm_responder;
@@ -3187,8 +3268,11 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP:
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
NULL);
- kfree(sdata->u.ap.next_beacon);
- sdata->u.ap.next_beacon = NULL;
+ if (sdata->u.ap.next_beacon) {
+ kfree(sdata->u.ap.next_beacon->mbssid);
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+ }

if (err < 0)
return err;
@@ -3343,8 +3427,14 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
if ((params->n_counter_offsets_beacon >
IEEE80211_MAX_CNTDWN_COUNTERS_NUM) ||
(params->n_counter_offsets_presp >
- IEEE80211_MAX_CNTDWN_COUNTERS_NUM))
+ IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) {
+ if (sdata->u.ap.next_beacon) {
+ kfree(sdata->u.ap.next_beacon->mbssid);
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+ }
return -EINVAL;
+ }

csa.counter_offsets_beacon = params->counter_offsets_beacon;
csa.counter_offsets_presp = params->counter_offsets_presp;
@@ -3353,8 +3443,10 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
csa.count = params->count;

err = ieee80211_assign_beacon(sdata, &params->beacon_csa, &csa);
- if (err < 0) {
+ if (err < 0 && sdata->u.ap.next_beacon) {
+ kfree(sdata->u.ap.next_beacon->mbssid);
kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
return err;
}
*changed |= err;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8fcbaa1eedf3..cc9455434ddc 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -261,6 +261,8 @@ struct beacon_data {
struct ieee80211_meshconf_ie *meshconf;
u16 cntdwn_counter_offsets[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
u8 cntdwn_current_counter;
+ struct cfg80211_mbssid_elems *mbssid;
+ u16 ema_index;
struct rcu_head rcu_head;
};

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 0b719f3d2dec..4bba3fd5368c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4746,11 +4746,68 @@ static int ieee80211_beacon_protect(struct sk_buff *skb,
return 0;
}

+static int ieee80211_beacon_mbssid_len(struct beacon_data *beacon, int index)
+{
+ int len = 0, i;
+
+ if (index == IEEE80211_BCN_EMA_NEXT) {
+ index = beacon->ema_index;
+ beacon->ema_index++;
+ beacon->ema_index %= beacon->mbssid->cnt;
+ }
+
+ if (index != IEEE80211_BCN_EMA_NONE && index >= beacon->mbssid->cnt)
+ return -1;
+
+ if (beacon->mbssid->cnt) {
+ if (index >= IEEE80211_BCN_EMA_BASE) {
+ len = beacon->mbssid->elem[index].len;
+ } else {
+ for (i = 0; i < beacon->mbssid->cnt; i++)
+ len += beacon->mbssid->elem[i].len;
+ }
+ }
+ return len;
+}
+
+static void
+ieee80211_beacon_add_mbssid_config(struct ieee80211_vif *vif,
+ struct sk_buff *skb,
+ struct cfg80211_mbssid_elems *elems)
+{
+ u8 *pos = skb_put(skb, 5);
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 3;
+ *pos++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
+ *pos++ = vif->bss_conf.mbssid.count;
+ *pos++ = elems->cnt;
+}
+
+static void ieee80211_beacon_add_mbssid(struct ieee80211_vif *vif,
+ struct sk_buff *skb,
+ struct cfg80211_mbssid_elems *elems,
+ int index)
+{
+ if (index >= IEEE80211_BCN_EMA_BASE) {
+ ieee80211_beacon_add_mbssid_config(vif, skb, elems);
+ skb_put_data(skb, elems->elem[index].data,
+ elems->elem[index].len);
+ } else {
+ int i;
+
+ for (i = 0; i < elems->cnt; i++)
+ skb_put_data(skb, elems->elem[i].data,
+ elems->elem[i].len);
+ }
+}
+
static struct sk_buff *
__ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_mutable_offsets *offs,
- bool is_template)
+ bool is_template,
+ int ema_index)
{
struct ieee80211_local *local = hw_to_local(hw);
struct beacon_data *beacon = NULL;
@@ -4762,13 +4819,11 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *chanctx_conf;
int csa_off_base = 0;

- rcu_read_lock();
-
sdata = vif_to_sdata(vif);
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);

if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
- goto out;
+ return NULL;

if (offs)
memset(offs, 0, sizeof(*offs));
@@ -4778,6 +4833,9 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,

beacon = rcu_dereference(ap->beacon);
if (beacon) {
+ int mbssid_elems_len = 0;
+ int mbssid_config_len = 5;
+
if (beacon->cntdwn_counter_offsets[0]) {
if (!is_template)
ieee80211_beacon_update_cntdwn(vif);
@@ -4785,6 +4843,14 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
ieee80211_set_beacon_cntdwn(sdata, beacon);
}

+ if (beacon->mbssid) {
+ mbssid_elems_len =
+ ieee80211_beacon_mbssid_len(beacon,
+ ema_index);
+ if (mbssid_elems_len == -1)
+ return NULL;
+ }
+
/*
* headroom, head length,
* tail length and maximum TIM length
@@ -4792,9 +4858,11 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
skb = dev_alloc_skb(local->tx_headroom +
beacon->head_len +
beacon->tail_len + 256 +
- local->hw.extra_beacon_tailroom);
+ local->hw.extra_beacon_tailroom +
+ mbssid_config_len +
+ mbssid_elems_len);
if (!skb)
- goto out;
+ return NULL;

skb_reserve(skb, local->tx_headroom);
skb_put_data(skb, beacon->head, beacon->head_len);
@@ -4810,21 +4878,32 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
csa_off_base = skb->len;
}

+ if (mbssid_elems_len) {
+ ieee80211_beacon_add_mbssid(vif, skb,
+ beacon->mbssid,
+ ema_index);
+ if (offs) {
+ offs->mbssid_offset = skb->len -
+ mbssid_elems_len;
+ csa_off_base = skb->len;
+ }
+ }
+
if (beacon->tail)
skb_put_data(skb, beacon->tail,
beacon->tail_len);

if (ieee80211_beacon_protect(skb, local, sdata) < 0)
- goto out;
+ return NULL;
} else
- goto out;
+ return NULL;
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_hdr *hdr;

beacon = rcu_dereference(ifibss->presp);
if (!beacon)
- goto out;
+ return NULL;

if (beacon->cntdwn_counter_offsets[0]) {
if (!is_template)
@@ -4836,7 +4915,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
local->hw.extra_beacon_tailroom);
if (!skb)
- goto out;
+ return NULL;
skb_reserve(skb, local->tx_headroom);
skb_put_data(skb, beacon->head, beacon->head_len);

@@ -4848,7 +4927,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,

beacon = rcu_dereference(ifmsh->beacon);
if (!beacon)
- goto out;
+ return NULL;

if (beacon->cntdwn_counter_offsets[0]) {
if (!is_template)
@@ -4871,7 +4950,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
beacon->tail_len +
local->hw.extra_beacon_tailroom);
if (!skb)
- goto out;
+ return NULL;
skb_reserve(skb, local->tx_headroom);
skb_put_data(skb, beacon->head, beacon->head_len);
ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template);
@@ -4884,7 +4963,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
skb_put_data(skb, beacon->tail, beacon->tail_len);
} else {
WARN_ON(1);
- goto out;
+ return NULL;
}

/* CSA offsets */
@@ -4927,31 +5006,99 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_ASSIGN_SEQ |
IEEE80211_TX_CTL_FIRST_FRAGMENT;
- out:
- rcu_read_unlock();
return skb;

}

-struct sk_buff *
-ieee80211_beacon_get_template(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_mutable_offsets *offs)
+struct sk_buff *ieee80211_beacon_get_template(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_mutable_offsets *offs)
{
- return __ieee80211_beacon_get(hw, vif, offs, true);
+ struct sk_buff *bcn;
+
+ rcu_read_lock();
+ bcn = __ieee80211_beacon_get(hw, vif, offs, true,
+ IEEE80211_BCN_EMA_NONE);
+ rcu_read_unlock();
+
+ return bcn;
}
EXPORT_SYMBOL(ieee80211_beacon_get_template);

+struct sk_buff *ieee80211_beacon_get_template_ema_next(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_mutable_offsets *offs)
+{
+ struct sk_buff *bcn;
+
+ rcu_read_lock();
+ bcn = __ieee80211_beacon_get(hw, vif, offs, true,
+ IEEE80211_BCN_EMA_NEXT);
+ rcu_read_unlock();
+
+ return bcn;
+}
+EXPORT_SYMBOL(ieee80211_beacon_get_template_ema_next);
+
+void ieee80211_beacon_free_ema_list(struct list_head *head)
+{
+ struct ieee80211_ema_bcn_list *ema, *tmp;
+
+ list_for_each_entry_safe(ema, tmp, head, list) {
+ kfree_skb(ema->skb);
+ kfree(ema);
+ }
+}
+EXPORT_SYMBOL(ieee80211_beacon_free_ema_list);
+
+int
+ieee80211_beacon_get_template_ema_list(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct list_head *head)
+{
+ int cnt = 0;
+
+ rcu_read_lock();
+ while (true) {
+ struct ieee80211_ema_bcn_list *ema;
+
+ ema = kmalloc(sizeof(*ema), GFP_KERNEL);
+ if (!ema) {
+ ieee80211_beacon_free_ema_list(head);
+ cnt = 0;
+ goto out;
+ }
+
+ ema->skb = __ieee80211_beacon_get(hw, vif, &ema->offs, true,
+ cnt);
+ if (!ema->skb) {
+ kfree(ema);
+ break;
+ }
+ list_add_tail(&ema->list, head);
+ cnt++;
+ }
+out:
+ rcu_read_unlock();
+
+ return cnt;
+}
+EXPORT_SYMBOL(ieee80211_beacon_get_template_ema_list);
+
struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 *tim_offset, u16 *tim_length)
{
struct ieee80211_mutable_offsets offs = {};
- struct sk_buff *bcn = __ieee80211_beacon_get(hw, vif, &offs, false);
+ struct sk_buff *bcn = NULL;
struct sk_buff *copy;
struct ieee80211_supported_band *sband;
int shift;

+ rcu_read_lock();
+ bcn = __ieee80211_beacon_get(hw, vif, &offs, false,
+ IEEE80211_BCN_EMA_NONE);
+ rcu_read_unlock();
if (!bcn)
return bcn;

--
2.31.1

2021-04-26 19:07:02

by Aloka Dixit

[permalink] [raw]
Subject: [PATCH v10 4/4] mac80211: CSA on non-transmitting interfaces

From: John Crispin <[email protected]>

Trigger ieee80211_csa_finish() on the non-transmitting interfaces
when channel switch concludes on the transmitting interface.

The mutex wiphy->mtx is released before locking sdata->local->iflist_mtx
to avoid deadlock with any other code path which may be using the same
mutexes in opposite order.

Signed-off-by: John Crispin <[email protected]>
Co-developed-by: Aloka Dixit <[email protected]>
Signed-off-by: Aloka Dixit <[email protected]>
---
v10: Used iflist_mtx instead of rcu_read_lock() as most other accesses
to sdata->local->interfaces do so.

net/mac80211/cfg.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index db21e1a621b1..5ca75a23d54c 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3254,8 +3254,21 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);

- ieee80211_queue_work(&sdata->local->hw,
- &sdata->csa_finalize_work);
+ if (sdata->vif.mbssid.flags & IEEE80211_VIF_MBSSID_TX) {
+ struct ieee80211_sub_if_data *child;
+
+ wiphy_unlock(sdata->local->hw.wiphy);
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_for_each_entry(child, &sdata->local->interfaces, list)
+ if (child->vif.mbssid.parent == vif &&
+ ieee80211_sdata_running(child))
+ ieee80211_queue_work(&child->local->hw,
+ &child->csa_finalize_work);
+ mutex_unlock(&sdata->local->iflist_mtx);
+ wiphy_lock(sdata->local->hw.wiphy);
+ }
+
+ ieee80211_queue_work(&sdata->local->hw, &sdata->csa_finalize_work);
}
EXPORT_SYMBOL(ieee80211_csa_finish);

--
2.31.1

2021-04-26 19:07:02

by Aloka Dixit

[permalink] [raw]
Subject: [PATCH v10 2/4] mac80211: multiple bssid support in interface handling

From: John Crispin <[email protected]>

This change adds a new helper function ieee80211_set_mbssid_options()
to set multiple BSSID and enhanced multi-BSSID advertisement (EMA)
configuration in mac80211.

New flags are set per interface to indicate transmitting/non-transmitting
and EMA enabled/disabled configurations. Value 0 indicates that the AP does
not support multiple BSSID advertisements.

For non-transmitting profiles, a parent pointer points to the transmitting
interface from the global list of interfaces per wiphy.

When ieee80211_do_stop() is called for a non-transmitting interface,
it first brings down the transmitting interface.

When ieee80211_del_iface() is called for the transmitting interface,
it first brings down all the non-transmitting interfaces.

Signed-off-by: John Crispin <[email protected]>
Co-developed-by: Aloka Dixit <[email protected]>
Signed-off-by: Aloka Dixit <[email protected]>
---
v10: Released wiphy->mtx before dev_close().
New flag for IEEE80211_HW_SUPPORTS_EMA_AP.
ieee80211_set_mbssid_options() returns int instead of void.


include/net/mac80211.h | 34 ++++++++++++++++++-
net/mac80211/cfg.c | 74 ++++++++++++++++++++++++++++++++++++++++++
net/mac80211/debugfs.c | 2 ++
net/mac80211/iface.c | 12 +++++++
4 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 445b66c6eb7e..ca83bfe14355 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -631,6 +631,7 @@ struct ieee80211_fils_discovery {
* @s1g: BSS is S1G BSS (affects Association Request format).
* @beacon_tx_rate: The configured beacon transmit rate that needs to be passed
* to driver when rate control is offloaded to firmware.
+ * @mbssid: Multiple bssid settings for AP mode
*/
struct ieee80211_bss_conf {
const u8 *bssid;
@@ -700,6 +701,7 @@ struct ieee80211_bss_conf {
u32 unsol_bcast_probe_resp_interval;
bool s1g;
struct cfg80211_bitrate_mask beacon_tx_rate;
+ struct cfg80211_mbssid_config mbssid;
};

/**
@@ -1663,6 +1665,20 @@ enum ieee80211_offload_flags {
IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2),
};

+/**
+ * enum ieee80211_vif_mbssid_flags - virtual interface multiple bssid flags
+ *
+ * @IEEE80211_VIF_MBSSID_TX: Set for the transmitting profile.
+ * @IEEE80211_VIF_MBSSID_NON_TX: Set for non-transmitting profiles.
+ * @IEEE80211_VIF_MBSSID_EMA: Set when enhanced multi-BSS advertisements
+ * are enabled.
+ */
+enum ieee80211_vif_mbssid_flags {
+ IEEE80211_VIF_MBSSID_TX = BIT(1),
+ IEEE80211_VIF_MBSSID_NON_TX = BIT(2),
+ IEEE80211_VIF_MBSSID_EMA = BIT(3),
+};
+
/**
* struct ieee80211_vif - per-interface data
*
@@ -1709,6 +1725,10 @@ enum ieee80211_offload_flags {
* protected by fq->lock.
* @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
* &enum ieee80211_offload_flags.
+ *
+ * @mbssid: Multiple BSSID configurations.
+ * @mbssid.parent: Interface index of the transmitted BSS.
+ * @mbssid.flags: multiple bssid flags, see enum ieee80211_vif_mbssid_flags.
*/
struct ieee80211_vif {
enum nl80211_iftype type;
@@ -1737,6 +1757,11 @@ struct ieee80211_vif {

bool txqs_stopped[IEEE80211_NUM_ACS];

+ struct {
+ struct ieee80211_vif *parent;
+ u32 flags;
+ } mbssid;
+
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};
@@ -2381,7 +2406,7 @@ struct ieee80211_txq {
* @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU
* length in tx status information
*
- * @IEEE80211_HW_SUPPORTS_MULTI_BSSID: Hardware supports multi BSSID
+ * @IEEE80211_HW_SUPPORTS_MULTI_BSSID: Hardware supports multi BSSID in STA mode
*
* @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
* only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
@@ -2402,6 +2427,11 @@ struct ieee80211_txq {
* usage and 802.11 frames with %RX_FLAG_ONLY_MONITOR set for monitor to
* the stack.
*
+ * @IEEE80211_HW_SUPPORTS_MBSSID_AP: Hardware supports multiple BSSID
+ * advertisements in AP mode.
+ * @IEEE80211_HW_SUPPORTS_EMA_AP: Hardware supports enhanced multiple BSSID
+ * advertisements in AP mode.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -2457,6 +2487,8 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD,
IEEE80211_HW_SUPPORTS_CONC_MON_RX_DECAP,
+ IEEE80211_HW_SUPPORTS_MBSSID_AP,
+ IEEE80211_HW_SUPPORTS_EMA_AP,

/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 7a99892e5aba..d1a965897b9d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -111,6 +111,44 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
return 0;
}

+static int ieee80211_set_mbssid_options(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_mbssid_config params)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct wiphy *wiphy = local->hw.wiphy;
+ struct net_device *parent;
+ struct ieee80211_sub_if_data *psdata;
+
+ if (!params.count || sdata->vif.type != NL80211_IFTYPE_AP ||
+ !ieee80211_hw_check(&local->hw, SUPPORTS_MBSSID_AP))
+ return -EINVAL;
+
+ sdata->vif.mbssid.parent = NULL;
+ sdata->vif.mbssid.flags = IEEE80211_VIF_MBSSID_TX;
+ if (params.parent) {
+ parent = __dev_get_by_index(wiphy_net(wiphy), params.parent);
+ if (!parent || !parent->ieee80211_ptr)
+ return -EINVAL;
+
+ psdata = IEEE80211_WDEV_TO_SUB_IF(parent->ieee80211_ptr);
+ if (psdata != sdata) {
+ if (psdata->vif.mbssid.parent)
+ return -EINVAL;
+ sdata->vif.mbssid.parent = &psdata->vif;
+ sdata->vif.mbssid.flags = IEEE80211_VIF_MBSSID_NON_TX;
+ }
+ }
+
+ if (params.ema) {
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_EMA_AP))
+ return -EINVAL;
+ sdata->vif.mbssid.flags |= IEEE80211_VIF_MBSSID_EMA;
+ }
+
+ sdata->vif.bss_conf.mbssid = params;
+ return 0;
+}
+
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
@@ -141,6 +179,36 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,

static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_local *local;
+ struct ieee80211_vif *vif;
+
+ if (!sdata)
+ return 0;
+
+ local = sdata->local;
+ vif = &sdata->vif;
+ if (vif->type == NL80211_IFTYPE_AP &&
+ ieee80211_hw_check(&local->hw, SUPPORTS_MBSSID_AP)) {
+ if (vif->mbssid.flags & IEEE80211_VIF_MBSSID_TX) {
+ struct ieee80211_sub_if_data *child, *tmpsdata;
+
+ wiphy_unlock(local->hw.wiphy);
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry_safe(child, tmpsdata,
+ &local->interfaces, list) {
+ if (child->vif.mbssid.parent == vif &&
+ ieee80211_sdata_running(child))
+ dev_close(child->wdev.netdev);
+ }
+ mutex_unlock(&local->iflist_mtx);
+ wiphy_lock(local->hw.wiphy);
+ } else {
+ vif->mbssid.parent = NULL;
+ vif->mbssid.flags = 0;
+ }
+ }
+
ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));

return 0;
@@ -1078,6 +1146,12 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
changed |= BSS_CHANGED_HE_BSS_COLOR;
}

+ if (params->mbssid.count) {
+ err = ieee80211_set_mbssid_options(sdata, params->mbssid);
+ if (err)
+ return err;
+ }
+
mutex_lock(&local->mtx);
err = ieee80211_vif_use_channel(sdata, &params->chandef,
IEEE80211_CHANCTX_SHARED);
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 9245c0421bda..00e5089a4a6c 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -457,6 +457,8 @@ static const char *hw_flag_names[] = {
FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
FLAG(SUPPORTS_CONC_MON_RX_DECAP),
+ FLAG(SUPPORTS_MBSSID_AP),
+ FLAG(SUPPORTS_EMA_AP),
#undef FLAG
};

diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b1c170939e44..1ab75d464569 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -375,6 +375,18 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
struct cfg80211_chan_def chandef;
bool cancel_scan;
struct cfg80211_nan_func *func;
+ struct ieee80211_sub_if_data *parent;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ ieee80211_hw_check(&local->hw, SUPPORTS_MBSSID_AP) &&
+ sdata->vif.mbssid.flags & IEEE80211_VIF_MBSSID_NON_TX) {
+ parent = vif_to_sdata(sdata->vif.mbssid.parent);
+ if (parent && ieee80211_sdata_running(parent)) {
+ wiphy_unlock(local->hw.wiphy);
+ dev_close(parent->wdev.netdev);
+ wiphy_lock(local->hw.wiphy);
+ }
+ }

clear_bit(SDATA_STATE_RUNNING, &sdata->state);

--
2.31.1