mac80211_generate_mac() {
local phy="$1"
+ local multiple_bssid="$2"
local id="${macidx:-0}"
local ref="$(cat /sys/class/ieee80211/${phy}/macaddress)"
local oIFS="$IFS"; IFS=":"; set -- $ref; IFS="$oIFS"
+ [ "$multiple_bssid" -eq 1 ] && {
+ printf "02:%s:%s:%s:%s:%02x" $b1 $2 $3 $4 $5 $macidx
+ return
+ }
+
macidx=$(($id + 1))
[ "$((0x$mask1))" -gt 0 ] && {
b1="0x$1"
}
mac80211_prepare_vif() {
+ local multiple_bssid=$1
+
json_select config
json_get_vars ifname mode ssid wds powersave macaddr enable wpa_psk_file vlan_file
json_select ..
[ -n "$macaddr" ] || {
- macaddr="$(mac80211_generate_mac $phy)"
+ macaddr="$(mac80211_generate_mac $phy $multiple_bssid)"
macidx="$(($macidx + 1))"
}
country chanbw distance \
txpower antenna_gain \
rxantenna txantenna \
- frag rts beacon_int:100 htmode
+ frag rts beacon_int:100 htmode \
+ multiple_bssid:0
json_get_values basic_rate_list basic_rate
json_get_values scan_list scan_list
json_select ..
mac80211_prepare_iw_htmode
for_each_interface "sta adhoc mesh monitor" mac80211_prepare_vif
NEWAPLIST=
- for_each_interface "ap" mac80211_prepare_vif
+ for_each_interface "ap" mac80211_prepare_vif $multiple_bssid
NEW_MD5=$(test -e "${hostapd_conf_file}" && md5sum ${hostapd_conf_file})
OLD_MD5=$(uci -q -P /var/state get wireless._${phy}.md5)
if [ "${NEWAPLIST}" != "${OLDAPLIST}" ]; then
--- /dev/null
+From f548ce607d01d9575ec1a64702f75f96b923d259 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 6 Oct 2020 15:43:48 +0200
+Subject: [PATCH 1/4] nl80211: add basic multiple bssid support
+
+This patch adds support for passing the multiple bssid config to the
+kernel when adding an interface. If the BSS is non-transmitting it needs
+to be indicated. A non-transmitting BSSID will have a parent interface,
+which needs to be transmitting. The multiple bssid elements are passed
+as an array.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ include/net/cfg80211.h | 33 +++++++++++++++++++++++++++++++++
+ include/uapi/linux/nl80211.h | 21 +++++++++++++++++++++
+ net/wireless/nl80211.c | 34 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 88 insertions(+)
+
+Index: backports-5.8-1/include/net/cfg80211.h
+===================================================================
+--- backports-5.8-1.orig/include/net/cfg80211.h
++++ backports-5.8-1/include/net/cfg80211.h
+@@ -455,6 +455,21 @@ struct ieee80211_supported_band {
+ };
+
+ /**
++ * struct ieee80211_multiple_bssid - AP settings for multi bssid
++ *
++ * @index: the index of this AP in the multi bssid group.
++ * @count: the total number of multi bssid peer APs.
++ * @parent: non-transmitted BSSs transmitted parents index
++ * @ema: Shall the beacons be sent out in EMA mode.
++ */
++struct ieee80211_multiple_bssid {
++ u8 index;
++ u8 count;
++ u32 parent;
++ bool ema;
++};
++
++/**
+ * ieee80211_get_sband_iftype_data - return sband data for a given iftype
+ * @sband: the sband to search for the STA on
+ * @iftype: enum nl80211_iftype
+@@ -983,6 +998,19 @@ struct cfg80211_crypto_settings {
+ };
+
+ /**
++ * struct cfg80211_multiple_bssid_data - multiple_bssid data
++ * @ies: array of extra information element(s) to add into Beacon frames for multiple
++ * bssid or %NULL
++ * @len: array of lengths of multiple_bssid.ies in octets
++ * @cnt: number of entries in multiple_bssid.ies
++ */
++struct cfg80211_multiple_bssid_data {
++ u8 *ies[NL80211_MULTIPLE_BSSID_IES_MAX];
++ size_t len[NL80211_MULTIPLE_BSSID_IES_MAX];
++ int cnt;
++};
++
++/**
+ * struct cfg80211_beacon_data - beacon data
+ * @head: head portion of beacon (before TIM IE)
+ * or %NULL if not changed
+@@ -1008,6 +1036,7 @@ struct cfg80211_crypto_settings {
+ * Token (measurement type 11)
+ * @lci_len: LCI data length
+ * @civicloc_len: Civic location data length
++ * @multiple_bssid: multiple_bssid data
+ */
+ struct cfg80211_beacon_data {
+ const u8 *head, *tail;
+@@ -1026,6 +1055,8 @@ struct cfg80211_beacon_data {
+ size_t probe_resp_len;
+ size_t lci_len;
+ size_t civicloc_len;
++
++ struct cfg80211_multiple_bssid_data multiple_bssid;
+ };
+
+ struct mac_address {
+@@ -1095,6 +1126,7 @@ enum cfg80211_ap_settings_flags {
+ * @he_obss_pd: OBSS Packet Detection settings
+ * @he_bss_color: BSS Color settings
+ * @he_oper: HE operation IE (or %NULL if HE isn't enabled)
++ * @multiple_bssid: AP settings for multiple bssid
+ */
+ struct cfg80211_ap_settings {
+ struct cfg80211_chan_def chandef;
+@@ -1125,6 +1157,7 @@ struct cfg80211_ap_settings {
+ u32 flags;
+ struct ieee80211_he_obss_pd he_obss_pd;
+ struct cfg80211_he_bss_color he_bss_color;
++ struct ieee80211_multiple_bssid multiple_bssid;
+ };
+
+ /**
+Index: backports-5.8-1/include/uapi/linux/nl80211.h
+===================================================================
+--- backports-5.8-1.orig/include/uapi/linux/nl80211.h
++++ backports-5.8-1/include/uapi/linux/nl80211.h
+@@ -2508,6 +2508,19 @@ enum nl80211_commands {
+ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
+ * transmit power to stay within regulatory limits. u32, dBi.
+ *
++ * @NL80211_ATTR_MULTIPLE_BSSID_PARENT: If this is a Non-Transmitted BSSID, define
++ * the parent (transmitting) interface.
++ *
++ * @NL80211_ATTR_MULTIPLE_BSSID_INDEX: The index of this BSS inside the multi bssid
++ * element.
++ *
++ * @NL80211_ATTR_MULTIPLE_BSSID_COUNT: The number of BSSs inside the multi bssid element.
++ *
++ * @NL80211_ATTR_MULTIPLE_BSSID_IES: The Elements that describe our multiple BSS group.
++ * these get passed separately as the kernel might need to split them up for EMA VAP.
++ *
++ * @NL80211_ATTR_MULTIPLE_BSSID_EMA: Shall the multiple BSS beacons be sent out in EMA mode.
++ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -2992,6 +3005,12 @@ enum nl80211_attrs {
+
+ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
+
++ NL80211_ATTR_MULTIPLE_BSSID_PARENT,
++ NL80211_ATTR_MULTIPLE_BSSID_INDEX,
++ NL80211_ATTR_MULTIPLE_BSSID_COUNT,
++ NL80211_ATTR_MULTIPLE_BSSID_IES,
++ NL80211_ATTR_MULTIPLE_BSSID_EMA,
++
+ /* add attributes here, update the policy in nl80211.c */
+
+ __NL80211_ATTR_AFTER_LAST,
+@@ -3053,6 +3072,8 @@ enum nl80211_attrs {
+
+ #define NL80211_CQM_TXE_MAX_INTVL 1800
+
++#define NL80211_MULTIPLE_BSSID_IES_MAX 8
++
+ /**
+ * enum nl80211_iftype - (virtual) interface types
+ *
+Index: backports-5.8-1/net/wireless/nl80211.c
+===================================================================
+--- backports-5.8-1.orig/net/wireless/nl80211.c
++++ backports-5.8-1/net/wireless/nl80211.c
+@@ -659,6 +659,11 @@ static const struct nla_policy nl80211_p
+ .len = sizeof(struct ieee80211_he_6ghz_capa),
+ },
+ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
++ [NL80211_ATTR_MULTIPLE_BSSID_PARENT] = { .type = NLA_U32 },
++ [NL80211_ATTR_MULTIPLE_BSSID_INDEX] = { .type = NLA_U8 },
++ [NL80211_ATTR_MULTIPLE_BSSID_COUNT] = NLA_POLICY_RANGE(NLA_U8, 1, 16),
++ [NL80211_ATTR_MULTIPLE_BSSID_IES] = { .type = NLA_NESTED },
++ [NL80211_ATTR_MULTIPLE_BSSID_EMA] = { .type = NLA_FLAG },
+ };
+
+ /* policy for the key attributes */
+@@ -4684,6 +4689,21 @@ static int nl80211_parse_beacon(struct c
+ bcn->ftm_responder = -1;
+ }
+
++ if (attrs[NL80211_ATTR_MULTIPLE_BSSID_IES]) {
++ struct nlattr *nl_ie;
++ int rem_ie;
++
++ nla_for_each_nested(nl_ie, attrs[NL80211_ATTR_MULTIPLE_BSSID_IES], rem_ie) {
++ if (bcn->multiple_bssid.cnt > NL80211_MULTIPLE_BSSID_IES_MAX)
++ return -EINVAL;
++ if (nla_type(nl_ie) != bcn->multiple_bssid.cnt + 1)
++ return -EINVAL;
++ bcn->multiple_bssid.ies[bcn->multiple_bssid.cnt] = nla_data(nl_ie);
++ bcn->multiple_bssid.len[bcn->multiple_bssid.cnt] = nla_len(nl_ie);
++ bcn->multiple_bssid.cnt++;
++ }
++ }
++
+ return 0;
+ }
+
+@@ -5046,6 +5066,20 @@ static int nl80211_start_ap(struct sk_bu
+ goto out;
+ }
+
++ if (info->attrs[NL80211_ATTR_MULTIPLE_BSSID_PARENT])
++ params.multiple_bssid.parent =
++ nla_get_u32(info->attrs[NL80211_ATTR_MULTIPLE_BSSID_PARENT]);
++
++ if (info->attrs[NL80211_ATTR_MULTIPLE_BSSID_INDEX])
++ params.multiple_bssid.index = nla_get_u8(
++ info->attrs[NL80211_ATTR_MULTIPLE_BSSID_INDEX]);
++
++ if (info->attrs[NL80211_ATTR_MULTIPLE_BSSID_COUNT])
++ params.multiple_bssid.count = nla_get_u8(
++ info->attrs[NL80211_ATTR_MULTIPLE_BSSID_COUNT]);
++ params.multiple_bssid.ema =
++ nla_get_flag(info->attrs[NL80211_ATTR_MULTIPLE_BSSID_EMA]);
++
+ nl80211_calculate_ap_params(¶ms);
+
+ if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])
--- /dev/null
+From 5ec6d96689c93093f51e233da5d28c6cd49f85ae Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 6 Oct 2020 15:45:37 +0200
+Subject: [PATCH 2/4] mac80211: add multiple bssid support to interface
+ handling
+
+When bringing up multi bssid APs we need to track the parent-child relation
+of non-transmitting and transmitting VAPs. This patch checks the above by
+using a linked list to track the relations. The patch also ensures that
+when a non-transmitting interface is closed the transmitting one is also
+closed.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ include/net/mac80211.h | 28 +++++++++++++++++++++-
+ net/mac80211/cfg.c | 53 ++++++++++++++++++++++++++++++++++++++++++
+ net/mac80211/debugfs.c | 1 +
+ net/mac80211/iface.c | 6 +++++
+ 4 files changed, 87 insertions(+), 1 deletion(-)
+
+Index: backports-5.8-1/include/net/mac80211.h
+===================================================================
+--- backports-5.8-1.orig/include/net/mac80211.h
++++ backports-5.8-1/include/net/mac80211.h
+@@ -674,6 +674,7 @@ struct ieee80211_bss_conf {
+ } he_oper;
+ struct ieee80211_he_obss_pd he_obss_pd;
+ struct cfg80211_he_bss_color he_bss_color;
++ struct ieee80211_multiple_bssid multiple_bssid;
+ };
+
+ /**
+@@ -1623,6 +1624,20 @@ enum ieee80211_offload_flags {
+ };
+
+ /**
++ * enum ieee80211_vif_multiple_bssid_flags - virtual interface multiple bssid flags
++ *
++ * @IEEE80211_VIF_MBSS_TRANSMITTING: this BSS is transmitting beacons
++ * @IEEE80211_VIF_MBSS_NON_TRANSMITTING: this BSS is not transmitting beacons
++ * @IEEE80211_VIF_MBSS_EMA_BEACON: beacons should be send out in EMA mode
++*/
++
++enum ieee80211_vif_multiple_bssid_flags {
++ IEEE80211_VIF_MBSS_TRANSMITTING = BIT(1),
++ IEEE80211_VIF_MBSS_NON_TRANSMITTING = BIT(2),
++ IEEE80211_VIF_MBSS_EMA_BEACON = BIT(3),
++};
++
++/**
+ * struct ieee80211_vif - per-interface data
+ *
+ * Data in this structure is continually present for driver
+@@ -1693,6 +1708,10 @@ struct ieee80211_vif {
+ bool rx_mcast_action_reg;
+
+ bool txqs_stopped[IEEE80211_NUM_ACS];
++ struct {
++ struct ieee80211_vif *parent;
++ u32 flags;
++ } multiple_bssid;
+
+ /* must be last */
+ u8 drv_priv[] __aligned(sizeof(void *));
+@@ -2341,7 +2360,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.
+@@ -2353,6 +2372,8 @@ struct ieee80211_txq {
+ * @IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD: Hardware supports tx encapsulation
+ * offload
+ *
++ * @IEEE80211_HW_SUPPORTS_MULTI_BSSID_AP: Hardware supports multi BSSID in AP mode
++ *
+ * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
+ */
+ enum ieee80211_hw_flags {
+@@ -2406,6 +2427,7 @@ enum ieee80211_hw_flags {
+ IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
+ IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT,
+ IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
++ IEEE80211_HW_SUPPORTS_MULTI_BSSID_AP,
+
+ /* keep last, obviously */
+ NUM_IEEE80211_HW_FLAGS
+Index: backports-5.8-1/net/mac80211/cfg.c
+===================================================================
+--- backports-5.8-1.orig/net/mac80211/cfg.c
++++ backports-5.8-1/net/mac80211/cfg.c
+@@ -111,6 +111,39 @@ static int ieee80211_set_mon_options(str
+ return 0;
+ }
+
++static void ieee80211_set_multiple_bssid_options(struct ieee80211_sub_if_data *sdata,
++ struct cfg80211_ap_settings *params)
++{
++ struct ieee80211_local *local = sdata->local;
++ struct wiphy *wiphy = local->hw.wiphy;
++ struct net_device *parent;
++ struct ieee80211_sub_if_data *psdata;
++
++ if (!ieee80211_hw_check(&local->hw, SUPPORTS_MULTI_BSSID_AP))
++ return;
++
++ if (!params->multiple_bssid.count)
++ return;
++
++ if (params->multiple_bssid.parent) {
++ parent = __dev_get_by_index(wiphy_net(wiphy),
++ params->multiple_bssid.parent);
++ if (!parent || !parent->ieee80211_ptr)
++ return;
++ psdata = IEEE80211_WDEV_TO_SUB_IF(parent->ieee80211_ptr);
++ if (psdata->vif.multiple_bssid.parent)
++ return;
++ sdata->vif.multiple_bssid.parent = &psdata->vif;
++ sdata->vif.multiple_bssid.flags |= IEEE80211_VIF_MBSS_NON_TRANSMITTING;
++ } else {
++ sdata->vif.multiple_bssid.flags |= IEEE80211_VIF_MBSS_TRANSMITTING;
++ }
++
++ if (params->multiple_bssid.ema)
++ sdata->vif.multiple_bssid.flags |= IEEE80211_VIF_MBSS_EMA_BEACON;
++ sdata->vif.bss_conf.multiple_bssid = params->multiple_bssid;
++}
++
+ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+@@ -141,6 +174,23 @@ static struct wireless_dev *ieee80211_ad
+
+ static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
+ {
++ struct ieee80211_sub_if_data *sdata;
++
++ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
++ if (sdata->vif.type == NL80211_IFTYPE_AP) {
++ if (sdata->vif.multiple_bssid.flags & IEEE80211_VIF_MBSS_TRANSMITTING) {
++ struct ieee80211_sub_if_data *child;
++
++ rcu_read_lock();
++ list_for_each_entry_rcu(child, &sdata->local->interfaces, list)
++ if (child->vif.multiple_bssid.parent == &sdata->vif)
++ dev_close(child->wdev.netdev);
++ rcu_read_unlock();
++ } else {
++ sdata->vif.multiple_bssid.parent = NULL;
++ }
++ }
++
+ ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));
+
+ return 0;
+@@ -1021,6 +1071,9 @@ static int ieee80211_start_ap(struct wip
+ IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
+ }
+
++ if (sdata->vif.type == NL80211_IFTYPE_AP)
++ ieee80211_set_multiple_bssid_options(sdata, params);
++
+ mutex_lock(&local->mtx);
+ err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,
+ IEEE80211_CHANCTX_SHARED);
+Index: backports-5.8-1/net/mac80211/debugfs.c
+===================================================================
+--- backports-5.8-1.orig/net/mac80211/debugfs.c
++++ backports-5.8-1/net/mac80211/debugfs.c
+@@ -409,6 +409,7 @@ static const char *hw_flag_names[] = {
+ FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
+ FLAG(AMPDU_KEYBORDER_SUPPORT),
+ FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
++ FLAG(SUPPORTS_MULTI_BSSID_AP),
+ #undef FLAG
+ };
+
+Index: backports-5.8-1/net/mac80211/iface.c
+===================================================================
+--- backports-5.8-1.orig/net/mac80211/iface.c
++++ backports-5.8-1/net/mac80211/iface.c
+@@ -377,6 +377,12 @@ static void ieee80211_do_stop(struct iee
+ bool cancel_scan;
+ struct cfg80211_nan_func *func;
+
++ /* make sure the parent is already down */
++ if (sdata->vif.type == NL80211_IFTYPE_AP &&
++ sdata->vif.multiple_bssid.parent &&
++ ieee80211_sdata_running(vif_to_sdata(sdata->vif.multiple_bssid.parent)))
++ dev_close(vif_to_sdata(sdata->vif.multiple_bssid.parent)->wdev.netdev);
++
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
--- /dev/null
+From fb4fc65312a19630f7726e2bd992931349daebee Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 6 Oct 2020 15:51:59 +0200
+Subject: [PATCH 3/4] mac80211: add multiple bssid/EMA support to beacon
+ handling
+
+With beacon_data now holding the additional information about the multiple
+bssid elements, we need to honour these in the various beacon handling
+code paths.
+
+Extend ieee80211_beacon_get_template() to allow generation of EMA beacons.
+The API provides support for HW that can offload the EMA beaconing aswell
+as HW that will require periodic updates of the beacon template.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ include/net/mac80211.h | 92 +++++++++++++++++++++-
+ net/mac80211/cfg.c | 59 +++++++++++++-
+ net/mac80211/ieee80211_i.h | 2 +
+ net/mac80211/tx.c | 156 +++++++++++++++++++++++++++++++++----
+ 4 files changed, 288 insertions(+), 21 deletions(-)
+
+Index: backports-5.8-1/include/net/mac80211.h
+===================================================================
+--- backports-5.8-1.orig/include/net/mac80211.h
++++ backports-5.8-1/include/net/mac80211.h
+@@ -4834,6 +4834,8 @@ struct ieee80211_mutable_offsets {
+ u16 tim_length;
+
+ u16 csa_counter_offs[IEEE80211_MAX_CSA_COUNTERS_NUM];
++ u16 multiple_bssid_offset;
++ u16 multiple_bssid_length;
+ };
+
+ /**
+@@ -4861,6 +4863,91 @@ ieee80211_beacon_get_template(struct iee
+ struct ieee80211_mutable_offsets *offs);
+
+ /**
++ * enum ieee80211_bcn_tmpl_ema - EMA beacon generation type
++ * @IEEE80211_BCN_EMA_NONE: don't generate an EMA beacon.
++ * @IEEE80211_BCN_EMA_NEXT: generate the next periodicity beacon.
++ * @IEEE80211_BCN_EMA_INDEX: generate beacon by periodicity index
++ * if the value is >= this enum value.
++ */
++enum ieee80211_bcn_tmpl_ema {
++ IEEE80211_BCN_EMA_NONE = -2,
++ IEEE80211_BCN_EMA_NEXT = -1,
++ IEEE80211_BCN_EMA_INDEX = 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 differs from ieee80211_beacon_get_template in the sense that
++ * it generates EMA VAP templates. When we use multiple_bssid, the beacons can
++ * get very large costing a lot of airtime. To work around this, we iterate
++ * over the multiple bssid elements and only send one inside the beacon for
++ * 1..n. Calling this function will auto-increment the periodicity counter.
++ *
++ * This function needs to follow the same rules as ieee80211_beacon_get_template
++ *
++ * 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 differs from ieee80211_beacon_get_template in the sense that
++ * it generates EMA VAP templates. When we use multiple_bssid, the beacons can
++ * get very large costing a lot of airtime. To work around this, we iterate
++ * over the multiple bssid elements and only send one inside the beacon for
++ * 1..n. This function will populate a linked list that the driver can pass
++ * to the HW.
++ *
++ * This function needs to follow the same rules as ieee80211_beacon_get_template
++ *
++ * 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().
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+Index: backports-5.8-1/net/mac80211/cfg.c
+===================================================================
+--- backports-5.8-1.orig/net/mac80211/cfg.c
++++ backports-5.8-1/net/mac80211/cfg.c
+@@ -929,12 +929,38 @@ static int ieee80211_set_ftm_responder_p
+ return 0;
+ }
+
++static int ieee80211_get_multiple_bssid_beacon_len(struct cfg80211_multiple_bssid_data *data)
++{
++ int i, len = 0;
++
++ for (i = 0; i < data->cnt; i++)
++ len += data->len[i];
++
++ return len;
++}
++
++static u8 *ieee80211_copy_multiple_bssid_beacon(u8 *offset,
++ struct cfg80211_multiple_bssid_data *new,
++ struct cfg80211_multiple_bssid_data *old)
++{
++ int i;
++
++ *new = *old;
++ for (i = 0; i < new->cnt; i++) {
++ new->ies[i] = offset;
++ memcpy(new->ies[i], old->ies[i], new->len[i]);
++ offset += new->len[i];
++ }
++ 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_multiple_bssid_len;
++ u8 *new_multiple_bssid_offset;
+ int size, err;
+ u32 changed = BSS_CHANGED_BEACON;
+
+@@ -958,7 +984,15 @@ static int ieee80211_assign_beacon(struc
+ else
+ new_tail_len = old->tail_len;
+
+- size = sizeof(*new) + new_head_len + new_tail_len;
++ /* new or old multiple_bssid? */
++ if (params->multiple_bssid.cnt || !old)
++ new_multiple_bssid_len =
++ ieee80211_get_multiple_bssid_beacon_len(¶ms->multiple_bssid);
++ else
++ new_multiple_bssid_len =
++ ieee80211_get_multiple_bssid_beacon_len(&old->multiple_bssid);
++
++ size = sizeof(*new) + new_head_len + new_tail_len + new_multiple_bssid_len;
+
+ new = kzalloc(size, GFP_KERNEL);
+ if (!new)
+@@ -975,6 +1009,18 @@ static int ieee80211_assign_beacon(struc
+ new->head_len = new_head_len;
+ new->tail_len = new_tail_len;
+
++ new_multiple_bssid_offset = new->tail + new_tail_len;
++
++ /* copy in optional multiple_bssid_ies */
++ if (params->multiple_bssid.cnt)
++ ieee80211_copy_multiple_bssid_beacon(new_multiple_bssid_offset,
++ &new->multiple_bssid,
++ ¶ms->multiple_bssid);
++ else if (old && old->multiple_bssid.cnt)
++ ieee80211_copy_multiple_bssid_beacon(new_multiple_bssid_offset,
++ &new->multiple_bssid,
++ &old->multiple_bssid);
++
+ if (csa) {
+ new->csa_current_counter = csa->count;
+ memcpy(new->csa_counter_offsets, csa->counter_offsets_beacon,
+@@ -3011,7 +3057,8 @@ cfg80211_beacon_dup(struct cfg80211_beac
+
+ 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_multiple_bssid_beacon_len(&beacon->multiple_bssid);
+
+ new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
+ if (!new_beacon)
+@@ -3054,6 +3101,10 @@ cfg80211_beacon_dup(struct cfg80211_beac
+ memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
+ pos += beacon->probe_resp_len;
+ }
++ if (beacon->multiple_bssid.cnt)
++ pos = ieee80211_copy_multiple_bssid_beacon(pos,
++ &new_beacon->multiple_bssid,
++ &beacon->multiple_bssid);
+
+ /* might copy -1, meaning no changes requested */
+ new_beacon->ftm_responder = beacon->ftm_responder;
+Index: backports-5.8-1/net/mac80211/ieee80211_i.h
+===================================================================
+--- backports-5.8-1.orig/net/mac80211/ieee80211_i.h
++++ backports-5.8-1/net/mac80211/ieee80211_i.h
+@@ -262,6 +262,8 @@ struct beacon_data {
+ struct ieee80211_meshconf_ie *meshconf;
+ u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
+ u8 csa_current_counter;
++ struct cfg80211_multiple_bssid_data multiple_bssid;
++ u16 ema_index;
+ struct rcu_head rcu_head;
+ };
+
+Index: backports-5.8-1/net/mac80211/tx.c
+===================================================================
+--- backports-5.8-1.orig/net/mac80211/tx.c
++++ backports-5.8-1/net/mac80211/tx.c
+@@ -4719,11 +4719,26 @@ static int ieee80211_beacon_protect(stru
+ return 0;
+ }
+
++static void
++ieee80211_beacon_add_multiple_bssid_config(struct ieee80211_vif *vif, struct sk_buff *skb,
++ struct cfg80211_multiple_bssid_data *config)
++{
++ u8 *pos = skb_put(skb, 6);
++
++ *pos++ = WLAN_EID_EXTENSION;
++ *pos++ = 4;
++ *pos++ = WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION;
++ *pos++ = 2;
++ *pos++ = vif->bss_conf.multiple_bssid.count;
++ *pos++ = config->cnt;
++}
++
+ 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;
+@@ -4735,13 +4750,11 @@ __ieee80211_beacon_get(struct ieee80211_
+ 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));
+@@ -4751,6 +4764,8 @@ __ieee80211_beacon_get(struct ieee80211_
+
+ beacon = rcu_dereference(ap->beacon);
+ if (beacon) {
++ int multiple_bssid_len = 0;
++
+ if (beacon->csa_counter_offsets[0]) {
+ if (!is_template)
+ __ieee80211_csa_update_counter(beacon);
+@@ -4758,6 +4773,27 @@ __ieee80211_beacon_get(struct ieee80211_
+ ieee80211_set_csa(sdata, beacon);
+ }
+
++ if (ema_index == IEEE80211_BCN_EMA_NEXT) {
++ ema_index = beacon->ema_index;
++ beacon->ema_index++;
++ beacon->ema_index %= beacon->multiple_bssid.cnt;
++ }
++
++ if (ema_index != IEEE80211_BCN_EMA_NONE &&
++ ema_index >= beacon->multiple_bssid.cnt)
++ return NULL;
++
++ if (beacon->multiple_bssid.cnt) {
++ if (ema_index >= IEEE80211_BCN_EMA_INDEX) {
++ multiple_bssid_len = beacon->multiple_bssid.len[ema_index];
++ } else {
++ int i;
++
++ for (i = 0; i < beacon->multiple_bssid.cnt; i++)
++ multiple_bssid_len = beacon->multiple_bssid.len[i];
++ }
++ }
++
+ /*
+ * headroom, head length,
+ * tail length and maximum TIM length
+@@ -4765,9 +4801,10 @@ __ieee80211_beacon_get(struct ieee80211_
+ skb = dev_alloc_skb(local->tx_headroom +
+ beacon->head_len +
+ beacon->tail_len + 256 +
+- local->hw.extra_beacon_tailroom);
++ local->hw.extra_beacon_tailroom +
++ multiple_bssid_len);
+ if (!skb)
+- goto out;
++ return NULL;
+
+ skb_reserve(skb, local->tx_headroom);
+ skb_put_data(skb, beacon->head, beacon->head_len);
+@@ -4783,21 +4820,38 @@ __ieee80211_beacon_get(struct ieee80211_
+ csa_off_base = skb->len;
+ }
+
++ if (multiple_bssid_len) {
++ if (ema_index >= IEEE80211_BCN_EMA_INDEX) {
++ ieee80211_beacon_add_multiple_bssid_config(vif, skb,
++ &beacon->multiple_bssid);
++ skb_put_data(skb, beacon->multiple_bssid.ies[beacon->ema_index],
++ beacon->multiple_bssid.len[beacon->ema_index]);
++ } else {
++ int i;
++
++ for (i = 0; i < beacon->multiple_bssid.cnt; i++)
++ skb_put_data(skb, beacon->multiple_bssid.ies[i],
++ beacon->multiple_bssid.len[i]);
++ }
++ if (offs)
++ offs->multiple_bssid_offset = skb->len - multiple_bssid_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->csa_counter_offsets[0]) {
+ if (!is_template)
+@@ -4809,7 +4863,7 @@ __ieee80211_beacon_get(struct ieee80211_
+ 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);
+
+@@ -4821,7 +4875,7 @@ __ieee80211_beacon_get(struct ieee80211_
+
+ beacon = rcu_dereference(ifmsh->beacon);
+ if (!beacon)
+- goto out;
++ return NULL;
+
+ if (beacon->csa_counter_offsets[0]) {
+ if (!is_template)
+@@ -4844,7 +4898,7 @@ __ieee80211_beacon_get(struct ieee80211_
+ 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);
+@@ -4857,7 +4911,7 @@ __ieee80211_beacon_get(struct ieee80211_
+ skb_put_data(skb, beacon->tail, beacon->tail_len);
+ } else {
+ WARN_ON(1);
+- goto out;
++ return NULL;
+ }
+
+ /* CSA offsets */
+@@ -4900,8 +4954,6 @@ __ieee80211_beacon_get(struct ieee80211_
+ info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT |
+ IEEE80211_TX_CTL_ASSIGN_SEQ |
+ IEEE80211_TX_CTL_FIRST_FRAGMENT;
+- out:
+- rcu_read_unlock();
+ return skb;
+
+ }
+@@ -4911,16 +4963,86 @@ ieee80211_beacon_get_template(struct iee
+ 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 = __ieee80211_beacon_get(hw, vif, &offs, false,
++ IEEE80211_BCN_EMA_NONE);
+ struct sk_buff *copy;
+ struct ieee80211_supported_band *sband;
+ int shift;
--- /dev/null
+From 73dbc6576a517c288395e7da1be98c47c85ab2f9 Mon Sep 17 00:00:00 2001
+From: John Crispin <john@phrozen.org>
+Date: Tue, 6 Oct 2020 17:22:39 +0200
+Subject: [PATCH 4/4] mac80211: dont allow CS on non-transmitting interfaces
+
+As a non-transmitting interface does not broadcast a beacon, we do not want
+to all channel switch announcements. They need to be triggerd on the
+transmitting interface.
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ net/mac80211/cfg.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
+index ee381557b1d8..1fc708b66b5e 100644
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -3506,6 +3506,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ if (sdata->vif.csa_active)
+ return -EBUSY;
+
++ if (sdata->vif.multiple_bssid.flags & IEEE80211_VIF_MBSS_NON_TRANSMITTING)
++ return -EINVAL;
++
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+--
+2.25.1
+
--- /dev/null
+Index: backports-5.8-1/net/mac80211/tx.c
+===================================================================
+--- backports-5.8-1.orig/net/mac80211/tx.c
++++ backports-5.8-1/net/mac80211/tx.c
+@@ -4833,8 +4833,25 @@ __ieee80211_beacon_get(struct ieee80211_
+ skb_put_data(skb, beacon->multiple_bssid.ies[i],
+ beacon->multiple_bssid.len[i]);
+ }
+- if (offs)
++ if (offs) {
++ const struct element *elem, *sub;
++ u8 *start;
++ int len;
++
+ offs->multiple_bssid_offset = skb->len - multiple_bssid_len;
++
++ start = &skb->data[offs->multiple_bssid_offset];
++ len = skb->len - offs->multiple_bssid_offset;
++ for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, skb->len - len) {
++ printk("%s:%s[%d]max_bssid_id %d\n", __FILE__, __func__, __LINE__, *elem->data);
++
++ for_each_element(sub, elem->data + 1, elem->datalen - 1) {
++ if (sub->id)
++ continue;
++ printk("%s:%s[%d]non transmitted offset %d\n", __FILE__, __func__, __LINE__, (u8 *)sub - skb->data);
++ }
++ }
++ }
+ }
+
+ if (beacon->tail)