Prerequisite patch:
https://patchwork.kernel.org/project/linux-wireless/patch/[email protected]/
As per IEEE Std 802.11ax-2021, 11.1.3.8.3 Discovery of a nontransmitted
BSSID profile, an EMA AP that transmits a Beacon frame carrying
a partial list of nontransmitted BSSID profiles should include in
the frame a Reduced Neighbor Report element carrying information for
at least the nontransmitted BSSIDs that are not present in
the Multiple BSSID element carried in that frame.
Add new attribute NL80211_ATTR_EMA_RNR_ELEMS to support the above.
Number of RNR elements must be more than or equal to the number of
MBSSID elements. This attribute can be used only when EMA is enabled.
Userspace is responsible for splitting the RNR into multiple elements
such that each element excludes the non-transmitting profiles already
included in the MBSSID element (%NL80211_ATTR_MBSSID_ELEMS) at
the same index.
MAC80211 will generate EMA beacons by adding MBSSID and RNR elements
at the same index. If the userspace provides more RNR elements than the
number of MBSSID elements then these will be added in every EMA beacon.
Aloka Dixit (2):
cfg80211: support RNR for EMA AP
mac80211: support RNR for EMA AP
include/net/cfg80211.h | 19 +++++++++++
include/uapi/linux/nl80211.h | 13 ++++++++
net/mac80211/cfg.c | 63 +++++++++++++++++++++++++++++++++---
net/mac80211/ieee80211_i.h | 21 ++++++++++--
net/mac80211/tx.c | 10 ++++++
net/wireless/nl80211.c | 50 ++++++++++++++++++++++++++++
6 files changed, 168 insertions(+), 8 deletions(-)
base-commit: 901c247f9687b5aecc950a931a3b0e1930d02bfd
prerequisite-patch-id: ee07532847cf9c473ca00649746ad1cf737c6bf6
--
2.17.1
As per IEEE Std 802.11ax-2021, 11.1.3.8.3 Discovery of a nontransmitted
BSSID profile, an EMA AP that transmits a Beacon frame carrying a partial
list of nontransmitted BSSID profiles should include in the frame
a Reduced Neighbor Report element carrying information for at least the
nontransmitted BSSIDs that are not present in the Multiple BSSID element
carried in that frame.
Add new nested attribute NL80211_ATTR_EMA_RNR_ELEMS to support the above.
Number of RNR elements must be more than or equal to the number of
MBSSID elements. This attribute can be used only when EMA is enabled.
Userspace is responsible for splitting the RNR into multiple elements such
that each element excludes the non-transmitting profiles already included
in the MBSSID element (%NL80211_ATTR_MBSSID_ELEMS) at the same index.
Each EMA beacon will be generated by adding MBSSID and RNR elements
at the same index. If the userspace provides more RNR elements than the
number of MBSSID elements then these will be added in every EMA beacon.
Signed-off-by: Aloka Dixit <[email protected]>
---
include/net/cfg80211.h | 19 ++++++++++++++
include/uapi/linux/nl80211.h | 13 ++++++++++
net/wireless/nl80211.c | 50 ++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 11a370e64143..bfde9e909c06 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1172,6 +1172,23 @@ struct cfg80211_mbssid_elems {
} elem[];
};
+/**
+ * struct cfg80211_rnr_elems - Reduced neighbor report (RNR) elements
+ *
+ * @cnt: Number of elements in array %elems.
+ *
+ * @elem: Array of RNR element(s) to be added into Beacon frames.
+ * @elem.data: Data for RNR elements.
+ * @elem.len: Length of data.
+ */
+struct cfg80211_rnr_elems {
+ u8 cnt;
+ struct {
+ const u8 *data;
+ size_t len;
+ } elem[];
+};
+
/**
* struct cfg80211_beacon_data - beacon data
* @link_id: the link ID for the AP MLD link sending this beacon
@@ -1192,6 +1209,7 @@ struct cfg80211_mbssid_elems {
* @probe_resp_len: length of probe response template (@probe_resp)
* @probe_resp: probe response template (AP mode only)
* @mbssid_ies: multiple BSSID elements
+ * @rnr_ies: reduced neighbor report 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
@@ -1215,6 +1233,7 @@ struct cfg80211_beacon_data {
const u8 *lci;
const u8 *civicloc;
struct cfg80211_mbssid_elems *mbssid_ies;
+ struct cfg80211_rnr_elems *rnr_ies;
s8 ftm_responder;
size_t head_len, tail_len;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index c14a91bbca7c..2623fb4542ec 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2751,6 +2751,17 @@ enum nl80211_commands {
* the incoming frame RX timestamp.
* @NL80211_ATTR_TD_BITMAP: Transition Disable bitmap, for subsequent
* (re)associations.
+ * @NL80211_ATTR_EMA_RNR_ELEMS: Optional nested attribute for
+ * reduced neighbor report (RNR) elements. This attribute can be used
+ * only when NL80211_MBSSID_CONFIG_ATTR_EMA is enabled.
+ * Userspace is responsible for splitting the RNR into multiple
+ * elements such that each element excludes the non-transmitting
+ * profiles already included in the MBSSID element
+ * (%NL80211_ATTR_MBSSID_ELEMS) at the same index. Each EMA beacon
+ * will be generated by adding MBSSID and RNR elements at the same
+ * index. If the userspace includes more RNR elements than number of
+ * MBSSID elements then these will be added in every EMA beacon.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3280,6 +3291,8 @@ enum nl80211_attrs {
NL80211_ATTR_RX_HW_TIMESTAMP,
NL80211_ATTR_TD_BITMAP,
+ NL80211_ATTR_EMA_RNR_ELEMS,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 148f66edb015..ee0f3bff989d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -805,6 +805,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN),
[NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT },
+ [NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED },
};
/* policy for the key attributes */
@@ -5391,6 +5392,30 @@ nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
return elems;
}
+static struct cfg80211_rnr_elems *nl80211_parse_rnr_elems(struct wiphy *wiphy,
+ struct nlattr *attrs)
+{
+ struct nlattr *nl_elems;
+ struct cfg80211_rnr_elems *elems;
+ int rem_elems;
+ u8 i = 0, num_elems = 0;
+
+ 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_he_bss_color(struct nlattr *attrs,
struct cfg80211_he_bss_color *he_bss_color)
{
@@ -5514,6 +5539,20 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
return PTR_ERR(mbssid);
bcn->mbssid_ies = mbssid;
+
+ if (bcn->mbssid_ies && attrs[NL80211_ATTR_EMA_RNR_ELEMS]) {
+ struct cfg80211_rnr_elems *rnr =
+ nl80211_parse_rnr_elems(&rdev->wiphy,
+ attrs[NL80211_ATTR_EMA_RNR_ELEMS]);
+
+ if (IS_ERR(rnr))
+ return PTR_ERR(rnr);
+
+ if (rnr && rnr->cnt < bcn->mbssid_ies->cnt)
+ return -EINVAL;
+
+ bcn->rnr_ies = rnr;
+ }
}
return 0;
@@ -6018,6 +6057,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
goto out_unlock;
}
+ if (!params->mbssid_config.ema && params->beacon.rnr_ies) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
err = nl80211_calculate_ap_params(params);
if (err)
goto out_unlock;
@@ -6057,6 +6101,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
params->mbssid_config.tx_wdev->netdev &&
params->mbssid_config.tx_wdev->netdev != dev)
dev_put(params->mbssid_config.tx_wdev->netdev);
+ kfree(params->beacon.rnr_ies);
kfree(params);
return err;
@@ -6091,6 +6136,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
out:
kfree(params.mbssid_ies);
+ kfree(params.rnr_ies);
return err;
}
@@ -10061,6 +10107,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
free:
kfree(params.beacon_after.mbssid_ies);
kfree(params.beacon_csa.mbssid_ies);
+ kfree(params.beacon_after.rnr_ies);
+ kfree(params.beacon_csa.rnr_ies);
kfree(csa_attrs);
return err;
}
@@ -15832,6 +15880,8 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
out:
kfree(params.beacon_next.mbssid_ies);
kfree(params.beacon_color_change.mbssid_ies);
+ kfree(params.beacon_next.rnr_ies);
+ kfree(params.beacon_color_change.rnr_ies);
kfree(tb);
return err;
}
base-commit: 901c247f9687b5aecc950a931a3b0e1930d02bfd
prerequisite-patch-id: ee07532847cf9c473ca00649746ad1cf737c6bf6
--
2.17.1
Generate EMA beacons, each including MBSSID and RNR elements at a given
index. If number of stored RNR elements is more than the number of
MBSSID elements then add those in every EMA beacon.
Signed-off-by: Aloka Dixit <[email protected]>
---
v1: Prerequisite patch:
https://patchwork.kernel.org/project/linux-wireless/patch/[email protected]/
net/mac80211/cfg.c | 63 +++++++++++++++++++++++++++++++++++---
net/mac80211/ieee80211_i.h | 21 +++++++++++--
net/mac80211/tx.c | 10 ++++++
3 files changed, 86 insertions(+), 8 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b60e8c835e46..7267e7d3c0df 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1083,6 +1083,23 @@ ieee80211_copy_mbssid_beacon(u8 *pos, struct cfg80211_mbssid_elems *dst,
return offset;
}
+static int
+ieee80211_copy_rnr_beacon(u8 *pos, struct cfg80211_rnr_elems *dst,
+ struct cfg80211_rnr_elems *src)
+{
+ int i, offset = 0;
+
+ for (i = 0; i < src->cnt; i++) {
+ memcpy(pos + offset, src->elem[i].data, src->elem[i].len);
+ dst->elem[i].len = src->elem[i].len;
+ dst->elem[i].data = pos + offset;
+ offset += dst->elem[i].len;
+ }
+ dst->cnt = src->cnt;
+
+ return offset;
+}
+
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_link_data *link,
struct cfg80211_beacon_data *params,
@@ -1090,6 +1107,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_color_change_settings *cca)
{
struct cfg80211_mbssid_elems *mbssid = NULL;
+ struct cfg80211_rnr_elems *rnr = NULL;
struct beacon_data *new, *old;
int new_head_len, new_tail_len;
int size, err;
@@ -1121,11 +1139,21 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
if (params->mbssid_ies) {
mbssid = params->mbssid_ies;
size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
- size += ieee80211_get_mbssid_beacon_len(mbssid, mbssid->cnt);
+ if (params->rnr_ies) {
+ rnr = params->rnr_ies;
+ size += struct_size(new->rnr_ies, elem, rnr->cnt);
+ }
+ size += ieee80211_get_mbssid_beacon_len(mbssid, rnr,
+ mbssid->cnt);
} else if (old && old->mbssid_ies) {
mbssid = old->mbssid_ies;
size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
- size += ieee80211_get_mbssid_beacon_len(mbssid, mbssid->cnt);
+ if (old && old->rnr_ies) {
+ rnr = old->rnr_ies;
+ size += struct_size(new->rnr_ies, elem, rnr->cnt);
+ }
+ size += ieee80211_get_mbssid_beacon_len(mbssid, rnr,
+ mbssid->cnt);
}
new = kzalloc(size, GFP_KERNEL);
@@ -1136,7 +1164,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
/*
* pointers go into the block we allocated,
- * memory is | beacon_data | head | tail | mbssid_ies
+ * memory is | beacon_data | head | tail | mbssid_ies | rnr_ies
*/
new->head = ((u8 *) new) + sizeof(*new);
new->tail = new->head + new_head_len;
@@ -1148,7 +1176,13 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
new->mbssid_ies = (void *)pos;
pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
- ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies, mbssid);
+ pos += ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies,
+ mbssid);
+ if (rnr) {
+ new->rnr_ies = (void *)pos;
+ pos += struct_size(new->rnr_ies, elem, rnr->cnt);
+ ieee80211_copy_rnr_beacon(pos, new->rnr_ies, rnr);
+ }
/* update bssid_indicator */
link_conf->bssid_indicator =
ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
@@ -1444,6 +1478,7 @@ static void ieee80211_free_next_beacon(struct ieee80211_link_data *link)
return;
kfree(link->u.ap.next_beacon->mbssid_ies);
+ kfree(link->u.ap.next_beacon->rnr_ies);
kfree(link->u.ap.next_beacon);
link->u.ap.next_beacon = NULL;
}
@@ -3339,6 +3374,7 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
beacon->proberesp_ies_len + beacon->assocresp_ies_len +
beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len +
ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies,
+ beacon->rnr_ies,
beacon->mbssid_ies->cnt);
new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
@@ -3354,6 +3390,18 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
kfree(new_beacon);
return NULL;
}
+
+ if (beacon->rnr_ies && beacon->rnr_ies->cnt) {
+ new_beacon->rnr_ies =
+ kzalloc(struct_size(new_beacon->rnr_ies,
+ elem, beacon->rnr_ies->cnt),
+ GFP_KERNEL);
+ if (!new_beacon->rnr_ies) {
+ kfree(new_beacon->mbssid_ies);
+ kfree(new_beacon);
+ return NULL;
+ }
+ }
}
pos = (u8 *)(new_beacon + 1);
@@ -3393,10 +3441,15 @@ 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)
+ if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
pos += ieee80211_copy_mbssid_beacon(pos,
new_beacon->mbssid_ies,
beacon->mbssid_ies);
+ if (beacon->rnr_ies && beacon->rnr_ies->cnt)
+ pos += ieee80211_copy_rnr_beacon(pos,
+ new_beacon->rnr_ies,
+ beacon->rnr_ies);
+ }
/* might copy -1, meaning no changes requested */
new_beacon->ftm_responder = beacon->ftm_responder;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e6ee40866dfa..bf1f30b172ca 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -269,6 +269,7 @@ struct beacon_data {
u16 cntdwn_counter_offsets[IEEE80211_MAX_CNTDWN_COUNTERS_NUM];
u8 cntdwn_current_counter;
struct cfg80211_mbssid_elems *mbssid_ies;
+ struct cfg80211_rnr_elems *rnr_ies;
struct rcu_head rcu_head;
};
@@ -1166,20 +1167,34 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
}
static inline int
-ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems, u8 i)
+ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems,
+ struct cfg80211_rnr_elems *rnr_elems,
+ u8 i)
{
int len = 0;
if (!elems || !elems->cnt || i > elems->cnt)
return 0;
- if (i < elems->cnt)
- return elems->elem[i].len;
+ if (i < elems->cnt) {
+ len = elems->elem[i].len;
+ if (rnr_elems) {
+ len += rnr_elems->elem[i].len;
+ for (i = elems->cnt; i < rnr_elems->cnt; i++)
+ len += rnr_elems->elem[i].len;
+ }
+ return len;
+ }
/* i == elems->cnt, calculate total length of all MBSSID elements */
for (i = 0; i < elems->cnt; i++)
len += elems->elem[i].len;
+ if (rnr_elems) {
+ for (i = 0; i < rnr_elems->cnt; i++)
+ len += rnr_elems->elem[i].len;
+ }
+
return len;
}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index ac4a916e7573..a8d618de649c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -5099,6 +5099,15 @@ ieee80211_beacon_add_mbssid(struct sk_buff *skb, struct beacon_data *beacon,
if (i < beacon->mbssid_ies->cnt) {
skb_put_data(skb, beacon->mbssid_ies->elem[i].data,
beacon->mbssid_ies->elem[i].len);
+
+ if (beacon->rnr_ies && beacon->rnr_ies->cnt) {
+ skb_put_data(skb, beacon->rnr_ies->elem[i].data,
+ beacon->rnr_ies->elem[i].len);
+
+ for (i = beacon->mbssid_ies->cnt; i < beacon->rnr_ies->cnt; i++)
+ skb_put_data(skb, beacon->rnr_ies->elem[i].data,
+ beacon->rnr_ies->elem[i].len);
+ }
return;
}
@@ -5136,6 +5145,7 @@ ieee80211_beacon_get_ap(struct ieee80211_hw *hw,
* tail length, maximum TIM length and multiple BSSID length
*/
mbssid_len = ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies,
+ beacon->rnr_ies,
ema_index);
skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
--
2.17.1
On Mon, 2022-11-14 at 15:04 -0800, Aloka Dixit wrote:
>
> if (params->mbssid_ies) {
> mbssid = params->mbssid_ies;
> size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
> - size += ieee80211_get_mbssid_beacon_len(mbssid, mbssid->cnt);
> + if (params->rnr_ies) {
> + rnr = params->rnr_ies;
> + size += struct_size(new->rnr_ies, elem, rnr->cnt);
> + }
Is this right? The struct_size() is only the size of the struct, but you
need the size of the elements contained in it here, i.e. the sum of the
.len fields?
johannes
On 2/14/2023 8:03 PM, Johannes Berg wrote:
> On Mon, 2022-11-14 at 15:04 -0800, Aloka Dixit wrote:
>>
>> if (params->mbssid_ies) {
>> mbssid = params->mbssid_ies;
>> size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
>> - size += ieee80211_get_mbssid_beacon_len(mbssid, mbssid->cnt);
>> + if (params->rnr_ies) {
>> + rnr = params->rnr_ies;
>> + size += struct_size(new->rnr_ies, elem, rnr->cnt);
>> + }
>
> Is this right? The struct_size() is only the size of the struct, but you
> need the size of the elements contained in it here, i.e. the sum of the
> .len fields?
>
> johannes
>
Yes, I modified ieee80211_get_mbssid_beacon_len() to include the 'len'
fields if rnr_ies is present. It is called right after the lines you
have copied here with the new input parameter.