nl80211: MBSSID and EMA support in AP mode

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

- NL80211_ATTR_MBSSID_CONFIG used for per interface configuration.
- NL80211_ATTR_MBSSID_ELEMS used to MBSSID elements for beacons.

Memory for the elements is allocated dynamically. 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.

Signed-off-by: John Crispin <john@phrozen.org>
Co-developed-by: Aloka Dixit <alokad@codeaurora.org>
Signed-off-by: Aloka Dixit <alokad@codeaurora.org>
Link: https://lore.kernel.org/r/20210916025437.29138-2-alokad@codeaurora.org
[don't leave ERR_PTR hanging around]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
John Crispin 2021-09-15 19:54:34 -07:00 committed by Johannes Berg
parent 05075fe745
commit dc1e3cb8da
3 changed files with 293 additions and 5 deletions

View file

@ -1056,6 +1056,36 @@ struct cfg80211_crypto_settings {
enum nl80211_sae_pwe_mechanism sae_pwe;
};
/**
* struct cfg80211_mbssid_config - AP settings for multi bssid
*
* @tx_wdev: pointer to the transmitted interface in the MBSSID set
* @index: index of this AP in the multi bssid group.
* @ema: set to true if the beacons should be sent out in EMA mode.
*/
struct cfg80211_mbssid_config {
struct wireless_dev *tx_wdev;
u8 index;
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 {
const u8 *data;
size_t len;
} elem[];
};
/**
* struct cfg80211_beacon_data - beacon data
* @head: head portion of beacon (before TIM IE)
@ -1074,6 +1104,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_ies: 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
@ -1091,6 +1122,7 @@ struct cfg80211_beacon_data {
const u8 *probe_resp;
const u8 *lci;
const u8 *civicloc;
struct cfg80211_mbssid_elems *mbssid_ies;
s8 ftm_responder;
size_t head_len, tail_len;
@ -1205,6 +1237,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_config: AP settings for multiple bssid
*/
struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef;
@ -1237,6 +1270,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_config;
};
/**
@ -5003,6 +5037,13 @@ struct wiphy_iftype_akm_suites {
* %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes
* @sar_capa: SAR control capabilities
* @rfkill: a pointer to the rfkill structure
*
* @mbssid_max_interfaces: maximum number of interfaces supported by the driver
* in a multiple BSSID set. This field must be set to a non-zero value
* by the driver to advertise MBSSID support.
* @mbssid_max_ema_profile_periodicity: maximum profile periodicity supported by
* the driver. Setting this field to a non-zero value indicates that the
* driver supports enhanced multi-BSSID advertisements (EMA AP).
*/
struct wiphy {
struct mutex mtx;
@ -5147,6 +5188,9 @@ struct wiphy {
struct rfkill *rfkill;
u8 mbssid_max_interfaces;
u8 ema_max_profile_periodicity;
char priv[] __aligned(NETDEV_ALIGN);
};

View file

@ -360,7 +360,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.
@ -2624,6 +2627,18 @@ enum nl80211_commands {
* @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
* information for the time while performing a color switch.
*
* @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID
* advertisements (MBSSID) parameters in AP mode.
* Kernel uses this attribute to indicate the driver's support for MBSSID
* and enhanced multi-BSSID advertisements (EMA AP) to the userspace.
* Userspace should use this attribute to configure per interface MBSSID
* parameters.
* See &enum nl80211_mbssid_config_attributes for details.
*
* @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements.
* Mandatory parameter for the transmitting interface to enable MBSSID.
* Optional for the non-transmitting interfaces.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@ -3127,6 +3142,9 @@ enum nl80211_attrs {
NL80211_ATTR_COLOR_CHANGE_COLOR,
NL80211_ATTR_COLOR_CHANGE_ELEMS,
NL80211_ATTR_MBSSID_CONFIG,
NL80211_ATTR_MBSSID_ELEMS,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@ -7386,4 +7404,60 @@ enum nl80211_sar_specs_attrs {
NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1,
};
/**
* enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced
* multi-BSSID advertisements (EMA) in AP mode.
* Kernel uses some of these attributes to advertise driver's support for
* MBSSID and EMA.
* Remaining attributes should be used by the userspace to configure the
* features.
*
* @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid
*
* @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise
* the maximum number of MBSSID interfaces supported by the driver.
* Driver should indicate MBSSID support by setting
* wiphy->mbssid_max_interfaces to a value more than or equal to 2.
*
* @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel
* to advertise the maximum profile periodicity supported by the driver
* if EMA is enabled. Driver should indicate EMA support to the userspace
* by setting wiphy->mbssid_max_ema_profile_periodicity to
* a non-zero value.
*
* @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of
* this BSS (u8) in the multiple BSSID set.
* Value must be set to 0 for the transmitting interface and non-zero for
* all non-transmitting interfaces. The userspace will be responsible
* for using unique indices for the interfaces.
* Range: 0 to wiphy->mbssid_max_interfaces-1.
*
* @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for
* a non-transmitted profile which provides the interface index (u32) of
* the transmitted profile. The value must match one of the interface
* indices advertised by the kernel. Optional if the interface being set up
* is the transmitting one, however, if provided then the value must match
* the interface index of the same.
*
* @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature.
* Setting this flag is permitted only if the driver advertises EMA support
* by setting wiphy->mbssid_max_ema_profile_periodicity to non-zero.
*
* @__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_MAX_INTERFACES,
NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
NL80211_MBSSID_CONFIG_ATTR_INDEX,
NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX,
NL80211_MBSSID_CONFIG_ATTR_EMA,
/* keep last */
__NL80211_MBSSID_CONFIG_ATTR_LAST,
NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1,
};
#endif /* __LINUX_NL80211_H */

View file

@ -437,6 +437,16 @@ 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_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
[NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2),
[NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY] =
NLA_POLICY_MIN(NLA_U8, 1),
[NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 },
[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 },
[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 },
@ -763,6 +773,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 },
[NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
[NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
[NL80211_ATTR_MBSSID_CONFIG] =
NLA_POLICY_NESTED(nl80211_mbssid_config_policy),
[NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
};
/* policy for the key attributes */
@ -2207,6 +2220,35 @@ 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;
if (!wiphy->mbssid_max_interfaces)
return 0;
config = nla_nest_start(msg, NL80211_ATTR_MBSSID_CONFIG);
if (!config)
return -ENOBUFS;
if (nla_put_u8(msg, NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES,
wiphy->mbssid_max_interfaces))
goto fail;
if (wiphy->ema_max_profile_periodicity &&
nla_put_u8(msg,
NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY,
wiphy->ema_max_profile_periodicity))
goto fail;
nla_nest_end(msg, config);
return 0;
fail:
nla_nest_cancel(msg, config);
return -ENOBUFS;
}
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
@ -2792,6 +2834,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;
@ -4981,6 +5026,96 @@ 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,
struct cfg80211_mbssid_config *config,
u8 num_elems)
{
struct nlattr *tb[NL80211_MBSSID_CONFIG_ATTR_MAX + 1];
if (!wiphy->mbssid_max_interfaces)
return -EOPNOTSUPP;
if (nla_parse_nested(tb, NL80211_MBSSID_CONFIG_ATTR_MAX, attrs, NULL,
NULL) ||
!tb[NL80211_MBSSID_CONFIG_ATTR_INDEX])
return -EINVAL;
config->ema = nla_get_flag(tb[NL80211_MBSSID_CONFIG_ATTR_EMA]);
if (config->ema) {
if (!wiphy->ema_max_profile_periodicity)
return -EOPNOTSUPP;
if (num_elems > wiphy->ema_max_profile_periodicity)
return -EINVAL;
}
config->index = nla_get_u8(tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]);
if (config->index >= wiphy->mbssid_max_interfaces ||
(!config->index && !num_elems))
return -EINVAL;
if (tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]) {
u32 tx_ifindex =
nla_get_u32(tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]);
if ((!config->index && tx_ifindex != dev->ifindex) ||
(config->index && tx_ifindex == dev->ifindex))
return -EINVAL;
if (tx_ifindex != dev->ifindex) {
struct net_device *tx_netdev =
dev_get_by_index(wiphy_net(wiphy), tx_ifindex);
if (!tx_netdev || !tx_netdev->ieee80211_ptr ||
tx_netdev->ieee80211_ptr->wiphy != wiphy ||
tx_netdev->ieee80211_ptr->iftype !=
NL80211_IFTYPE_AP) {
dev_put(tx_netdev);
return -EINVAL;
}
config->tx_wdev = tx_netdev->ieee80211_ptr;
} else {
config->tx_wdev = dev->ieee80211_ptr;
}
} else if (!config->index) {
config->tx_wdev = dev->ieee80211_ptr;
} else {
return -EINVAL;
}
return 0;
}
static struct cfg80211_mbssid_elems *
nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
{
struct nlattr *nl_elems;
struct cfg80211_mbssid_elems *elems;
int rem_elems;
u8 i = 0, num_elems = 0;
if (!wiphy->mbssid_max_interfaces)
return ERR_PTR(-EINVAL);
nla_for_each_nested(nl_elems, attrs, rem_elems)
num_elems++;
elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL);
if (!elems)
return ERR_PTR(-ENOMEM);
nla_for_each_nested(nl_elems, attrs, rem_elems) {
elems->elem[i].data = nla_data(nl_elems);
elems->elem[i].len = nla_len(nl_elems);
i++;
}
elems->cnt = num_elems;
return elems;
}
static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
struct nlattr *attrs[],
struct cfg80211_beacon_data *bcn)
@ -5061,6 +5196,17 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
bcn->ftm_responder = -1;
}
if (attrs[NL80211_ATTR_MBSSID_ELEMS]) {
struct cfg80211_mbssid_elems *mbssid =
nl80211_parse_mbssid_elems(&rdev->wiphy,
attrs[NL80211_ATTR_MBSSID_ELEMS]);
if (IS_ERR(mbssid))
return PTR_ERR(mbssid);
bcn->mbssid_ies = mbssid;
}
return 0;
}
@ -5547,6 +5693,17 @@ 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->mbssid_config,
params->beacon.mbssid_ies ?
params->beacon.mbssid_ies->cnt :
0);
if (err)
goto out;
}
nl80211_calculate_ap_params(params);
if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])
@ -5568,6 +5725,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
out:
kfree(params->acl);
kfree(params->beacon.mbssid_ies);
if (params->mbssid_config.tx_wdev &&
params->mbssid_config.tx_wdev->netdev &&
params->mbssid_config.tx_wdev->netdev != dev)
dev_put(params->mbssid_config.tx_wdev->netdev);
kfree(params);
return err;
@ -5593,12 +5755,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_ies);
return err;
}
@ -9275,12 +9439,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],
@ -9398,6 +9564,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
wdev_unlock(wdev);
free:
kfree(params.beacon_after.mbssid_ies);
kfree(params.beacon_csa.mbssid_ies);
kfree(csa_attrs);
return err;
}
@ -14933,6 +15101,8 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
wdev_unlock(wdev);
out:
kfree(params.beacon_next.mbssid_ies);
kfree(params.beacon_color_change.mbssid_ies);
kfree(tb);
return err;
}