From patchwork Thu Sep 16 02:54:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aloka Dixit X-Patchwork-Id: 513477 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AF38DC433EF for ; Thu, 16 Sep 2021 02:55:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 90E7B6113E for ; Thu, 16 Sep 2021 02:55:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234008AbhIPC4Z (ORCPT ); Wed, 15 Sep 2021 22:56:25 -0400 Received: from m43-7.mailgun.net ([69.72.43.7]:16342 "EHLO m43-7.mailgun.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233847AbhIPC4Y (ORCPT ); Wed, 15 Sep 2021 22:56:24 -0400 DKIM-Signature: a=rsa-sha256; v=1; c=relaxed/relaxed; d=mg.codeaurora.org; q=dns/txt; s=smtp; t=1631760905; h=Content-Transfer-Encoding: MIME-Version: References: In-Reply-To: Message-Id: Date: Subject: To: From: Sender; bh=Fpi6vU+wGanDUgxmpwORFca3u2ACxgquL2v5HIryV2A=; b=WVelC1L4gu0nsaqaxYGdBypLHrW03gRgr+OeeIwJirFXk0feoYutzy9aI+yo8DKtX4nqOhDT 1T3FieHqs7VxMea9m5rHkHdtrpruVgvAVRljBUYg87U+d4eLoSvdJcsH7V6oQvmcAbwEactJ 5tksd90ZQBUhOYuNNP21hL+nowA= X-Mailgun-Sending-Ip: 69.72.43.7 X-Mailgun-Sid: WyI3YTAwOSIsICJsaW51eC13aXJlbGVzc0B2Z2VyLmtlcm5lbC5vcmciLCAiYmU5ZTRhIl0= Received: from smtp.codeaurora.org (ec2-35-166-182-171.us-west-2.compute.amazonaws.com [35.166.182.171]) by smtp-out-n02.prod.us-west-2.postgun.com with SMTP id 6142b1f7e0f78151d6408b46 (version=TLS1.2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); Thu, 16 Sep 2021 02:54:47 GMT Sender: alokad=codeaurora.org@mg.codeaurora.org Received: by smtp.codeaurora.org (Postfix, from userid 1001) id 9870CC4338F; Thu, 16 Sep 2021 02:54:47 +0000 (UTC) Received: from alokad-linux.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: alokad) by smtp.codeaurora.org (Postfix) with ESMTPSA id 4325FC43460; Thu, 16 Sep 2021 02:54:43 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 smtp.codeaurora.org 4325FC43460 Authentication-Results: aws-us-west-2-caf-mail-1.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: aws-us-west-2-caf-mail-1.web.codeaurora.org; spf=fail smtp.mailfrom=codeaurora.org From: Aloka Dixit To: johannes@sipsolutions.net, linux-wireless@vger.kernel.org Subject: [PATCH v12 1/4] nl80211: MBSSID and EMA support in AP mode Date: Wed, 15 Sep 2021 19:54:34 -0700 Message-Id: <20210916025437.29138-2-alokad@codeaurora.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210916025437.29138-1-alokad@codeaurora.org> References: <20210916025437.29138-1-alokad@codeaurora.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: John Crispin This commit adds 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 Co-developed-by: Aloka Dixit Signed-off-by: Aloka Dixit --- v12: Replaced __dev_get_by_index() with dev_get_by_index() and added dev_put(). nl80211_parse_mbssid_elems()returns ERR_PTR() insead of NULL in case of error and the caller nl80211_parse_beacon() uses PTR_ERR() to return the same. include/net/cfg80211.h | 44 ++++++ include/uapi/linux/nl80211.h | 76 +++++++++- net/wireless/nl80211.c | 277 ++++++++++++++++++++++++++++++----- 3 files changed, 358 insertions(+), 39 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 62dd8422e0dc..63b8c9890be8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1040,6 +1040,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 { + u8 *data; + size_t len; + } elem[]; +}; + /** * struct cfg80211_beacon_data - beacon data * @head: head portion of beacon (before TIM IE) @@ -1058,6 +1088,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 @@ -1075,6 +1106,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; @@ -1189,6 +1221,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; @@ -1221,6 +1254,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; }; /** @@ -4981,6 +5015,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; @@ -5125,6 +5166,9 @@ struct wiphy { struct rfkill *rfkill; + u8 mbssid_max_interfaces; + u8 ema_max_profile_periodicity; + char priv[] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c2efea98e060..f1a21314e0f0 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. @@ -2593,6 +2596,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 @@ -3096,6 +3111,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, @@ -7349,4 +7367,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 */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bf7cd4752547..1f6216509124 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -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,95 @@ 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 (!config->index) + config->tx_wdev = dev->ieee80211_ptr; + + 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 if (config->index) { + 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 +5195,14 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, bcn->ftm_responder = -1; } + if (attrs[NL80211_ATTR_MBSSID_ELEMS]) { + bcn->mbssid_ies = + nl80211_parse_mbssid_elems(&rdev->wiphy, + attrs[NL80211_ATTR_MBSSID_ELEMS]); + if (IS_ERR(bcn->mbssid_ies)) + return PTR_ERR(bcn->mbssid_ies); + } + return 0; } @@ -5346,7 +5488,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon); if (err) - return err; + goto out; params.beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); @@ -5356,7 +5498,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 @@ -5369,8 +5511,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]) @@ -5383,57 +5527,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, ¶ms.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, ¶ms.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, ¶ms)) - return -EINVAL; + } else if (!nl80211_get_ap_channel(rdev, ¶ms)) { + err = -EINVAL; + goto out; + } if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.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, @@ -5441,12 +5602,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) ¶ms.beacon_rate, dev, false); if (err) - return err; + goto out; err = validate_beacon_tx_rate(rdev, params.chandef.chan->band, ¶ms.beacon_rate); if (err) - return err; + goto out; } if (info->attrs[NL80211_ATTR_SMPS_MODE]) { @@ -5457,29 +5618,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 = @@ -5517,6 +5687,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], + ¶ms.mbssid_config, + (params.beacon.mbssid_ies ? + params.beacon.mbssid_ies->cnt : + 0)); + if (err) + goto out; + } + nl80211_calculate_ap_params(¶ms); if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT]) @@ -5537,7 +5718,14 @@ 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); + if (!IS_ERR(params.beacon.mbssid_ies)) + 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); return err; } @@ -5562,12 +5750,15 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) err = nl80211_parse_beacon(rdev, info->attrs, ¶ms); if (err) - return err; + goto out; wdev_lock(wdev); err = rdev_change_beacon(rdev, dev, ¶ms); wdev_unlock(wdev); +out: + if (!IS_ERR(params.mbssid_ies)) + kfree(params.mbssid_ies); return err; } @@ -9244,12 +9435,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.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], @@ -9367,6 +9560,10 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) wdev_unlock(wdev); free: + if (!IS_ERR(params.beacon_after.mbssid_ies)) + kfree(params.beacon_after.mbssid_ies); + if (!IS_ERR(params.beacon_csa.mbssid_ies)) + kfree(params.beacon_csa.mbssid_ies); kfree(csa_attrs); return err; } @@ -14900,6 +15097,10 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) wdev_unlock(wdev); out: + if (!IS_ERR(params.beacon_next.mbssid_ies)) + kfree(params.beacon_next.mbssid_ies); + if (!IS_ERR(params.beacon_color_change.mbssid_ies)) + kfree(params.beacon_color_change.mbssid_ies); kfree(tb); return err; } From patchwork Thu Sep 16 02:54:36 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aloka Dixit X-Patchwork-Id: 513478 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40D4FC433F5 for ; Thu, 16 Sep 2021 02:54:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 275A861178 for ; Thu, 16 Sep 2021 02:54:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233964AbhIPC4K (ORCPT ); Wed, 15 Sep 2021 22:56:10 -0400 Received: from m43-7.mailgun.net ([69.72.43.7]:16342 "EHLO m43-7.mailgun.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233847AbhIPC4K (ORCPT ); Wed, 15 Sep 2021 22:56:10 -0400 DKIM-Signature: a=rsa-sha256; v=1; c=relaxed/relaxed; d=mg.codeaurora.org; q=dns/txt; s=smtp; t=1631760890; h=Content-Transfer-Encoding: MIME-Version: References: In-Reply-To: Message-Id: Date: Subject: To: From: Sender; bh=o5Rd6g4WnGXZ9nSIdHMeccfXgbAWGndUYtN+NqSZSY0=; b=tuoLxx4MrQTjE7qMo0pX6RhiagZcw6i4b8f81jKa3Ednch9YOLZsHK/Shdj0/0QCia8vyG28 xeKF6JzWORcbd+IDbcC9PX/RWX73rok6XMRQKKKJaN6mI83ZEJZPR43Ceu8widNIuwdLWWnf O1j1EoopnzpHkltSDjm3VTZgbg8= X-Mailgun-Sending-Ip: 69.72.43.7 X-Mailgun-Sid: WyI3YTAwOSIsICJsaW51eC13aXJlbGVzc0B2Z2VyLmtlcm5lbC5vcmciLCAiYmU5ZTRhIl0= Received: from smtp.codeaurora.org (ec2-35-166-182-171.us-west-2.compute.amazonaws.com [35.166.182.171]) by smtp-out-n02.prod.us-west-2.postgun.com with SMTP id 6142b1f8e0f78151d6408d6d (version=TLS1.2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); Thu, 16 Sep 2021 02:54:48 GMT Sender: alokad=codeaurora.org@mg.codeaurora.org Received: by smtp.codeaurora.org (Postfix, from userid 1001) id D0851C43460; Thu, 16 Sep 2021 02:54:48 +0000 (UTC) Received: from alokad-linux.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: alokad) by smtp.codeaurora.org (Postfix) with ESMTPSA id 53AA4C43618; Thu, 16 Sep 2021 02:54:44 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 smtp.codeaurora.org 53AA4C43618 Authentication-Results: aws-us-west-2-caf-mail-1.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: aws-us-west-2-caf-mail-1.web.codeaurora.org; spf=fail smtp.mailfrom=codeaurora.org From: Aloka Dixit To: johannes@sipsolutions.net, linux-wireless@vger.kernel.org Subject: [PATCH v12 3/4] mac80211: MBSSID and EMA support in beacon handling Date: Wed, 15 Sep 2021 19:54:36 -0700 Message-Id: <20210916025437.29138-4-alokad@codeaurora.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210916025437.29138-1-alokad@codeaurora.org> References: <20210916025437.29138-1-alokad@codeaurora.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org From: John Crispin 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: - ieee80211_beacon_get_template() - Existing function for legacy beacon generation for non-MBSSID APs. No change in prototype. - ieee80211_beacon_get_template_ema_next() - Generate the next EMA beacon template using the 'ema_index' stored in beacon_data. - 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 Co-developed-by: Aloka Dixit Signed-off-by: Aloka Dixit --- v12: No changes. include/net/mac80211.h | 89 +++++++++++++++++++ net/mac80211/cfg.c | 139 +++++++++++++++++++++++++---- net/mac80211/ieee80211_i.h | 2 + net/mac80211/tx.c | 173 ++++++++++++++++++++++++++++++++----- 4 files changed, 365 insertions(+), 38 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 0d405e36c213..f09ba60b1b23 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4911,12 +4911,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; }; /** @@ -4943,6 +4945,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 e2b791c37591..23fa0bb49be2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -987,15 +987,50 @@ 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, const struct ieee80211_color_change_settings *cca) { 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); @@ -1017,12 +1052,27 @@ 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 multiple BSSID elements? */ + if (params->mbssid_ies) + new_mbssid_len = ieee80211_get_mbssid_beacon_len(params->mbssid_ies); + else if (old && old->mbssid_ies) + new_mbssid_len = ieee80211_get_mbssid_beacon_len(old->mbssid_ies); + + 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_ies = kzalloc(sizeof(*params->mbssid_ies), + GFP_KERNEL); + if (!new->mbssid_ies) { + kfree(new); + return -ENOMEM; + } + } + /* start filling the new info now */ /* @@ -1034,6 +1084,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_ies) + ieee80211_copy_mbssid_beacon(new_mbssid_offset, new->mbssid_ies, + params->mbssid_ies); + else if (old && old->mbssid_ies) + ieee80211_copy_mbssid_beacon(new_mbssid_offset, new->mbssid_ies, + old->mbssid_ies); + if (csa) { new->cntdwn_current_counter = csa->count; memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_beacon, @@ -1060,6 +1119,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, cca); if (err < 0) { + kfree(new->mbssid_ies); kfree(new); return err; } @@ -1075,6 +1135,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, params->civicloc_len); if (err < 0) { + kfree(new->mbssid_ies); kfree(new); return err; } @@ -1084,9 +1145,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_ies); kfree_rcu(old, rcu_head); - + } return changed; } @@ -1246,8 +1308,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_ies); kfree_rcu(old, rcu_head); + } RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); goto error; } @@ -1327,8 +1391,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_ies); + 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) @@ -1340,6 +1407,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_ies); kfree_rcu(old_beacon, rcu_head); if (old_probe_resp) kfree_rcu(old_probe_resp, rcu_head); @@ -3123,13 +3191,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_ies); new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL); if (!new_beacon) return NULL; + if (beacon->mbssid_ies) { + new_beacon->mbssid_ies = kzalloc(sizeof(*beacon->mbssid_ies), + GFP_KERNEL); + if (!new_beacon->mbssid_ies) { + 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; @@ -3166,6 +3245,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_ies && beacon->mbssid_ies->cnt) + pos = ieee80211_copy_mbssid_beacon(pos, new_beacon->mbssid_ies, + beacon->mbssid_ies); /* might copy -1, meaning no changes requested */ new_beacon->ftm_responder = beacon->ftm_responder; @@ -3203,8 +3285,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, 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_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } if (err < 0) return err; @@ -3359,8 +3444,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_ies); + 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; @@ -3370,7 +3461,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa, &csa, NULL); if (err < 0) { - kfree(sdata->u.ap.next_beacon); + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } return err; } *changed |= err; @@ -3460,8 +3555,11 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, static void ieee80211_color_change_abort(struct ieee80211_sub_if_data *sdata) { sdata->vif.color_change_active = false; - 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_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } cfg80211_color_change_aborted_notify(sdata->dev); } @@ -4199,8 +4297,11 @@ ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata, ret = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon, NULL, 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_ies); + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + } if (ret < 0) return ret; @@ -4243,7 +4344,11 @@ ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata, err = ieee80211_assign_beacon(sdata, ¶ms->beacon_color_change, NULL, &color_change); if (err < 0) { - kfree(sdata->u.ap.next_beacon); + if (sdata->u.ap.next_beacon) { + kfree(sdata->u.ap.next_beacon->mbssid_ies); + 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 159af6c3ffb0..8ba86f72577c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -257,6 +257,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_ies; + u16 ema_index; struct rcu_head rcu_head; }; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 2d1193ed3eb5..9406e5f3cf6d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4979,11 +4979,54 @@ 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_ies->cnt; + } + + if (index != IEEE80211_BCN_EMA_NONE && + index >= beacon->mbssid_ies->cnt) + return -1; + + if (beacon->mbssid_ies->cnt) { + if (index >= IEEE80211_BCN_EMA_BASE) { + len = beacon->mbssid_ies->elem[index].len; + } else { + for (i = 0; i < beacon->mbssid_ies->cnt; i++) + len += beacon->mbssid_ies->elem[i].len; + } + } + return len; +} + +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) { + 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; @@ -4995,13 +5038,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)); @@ -5011,6 +5052,8 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, beacon = rcu_dereference(ap->beacon); if (beacon) { + size_t mbssid_elems_len = 0; + if (beacon->cntdwn_counter_offsets[0]) { if (!is_template) ieee80211_beacon_update_cntdwn(vif); @@ -5018,6 +5061,14 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, ieee80211_set_beacon_cntdwn(sdata, beacon); } + if (beacon->mbssid_ies) { + 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 @@ -5025,9 +5076,10 @@ __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_elems_len); if (!skb) - goto out; + return NULL; skb_reserve(skb, local->tx_headroom); skb_put_data(skb, beacon->head, beacon->head_len); @@ -5044,21 +5096,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_ies, + 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) @@ -5070,7 +5133,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); @@ -5082,7 +5145,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) @@ -5105,7 +5168,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); @@ -5118,7 +5181,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 */ @@ -5161,31 +5224,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;