Search Linux Wireless

[RFC 1/5] wifi: cfg80211: support configuring an S1G short beaconing BSS

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



S1G short beacons are an optional frame type used in an S1G BSS
that contain a limited set of elements. While they are optional,
they are a fundamental part of S1G that enables significant
power saving.

When an S1G BSS is configured with short beaconing enabled, the
BSS uses the TSBTT (Target Short Beacon Transmission Time) as the
base unit for the DTIM period and the beacon interval as per
IEEE80211-2024 11.1.2.1:

"In an S1G BSS, the S1G AP shall generate S1G Beacon frames
every dot11BeaconPeriod TUs; and if dot11ShortBeaconInterval is
true, it shall additionally generate S1G Beacon frames every
dot11ShortBeaconPeriod TUs"

Note: As the kernel uses the notion of beacon interval rather then
beacon period, we do the same with short beaconing - where
dot11ShortBeaconPeriod is known as the short beacon interval.

and per IEEE80211-2024 Annex C.3 MIB detail:

dot11ShortBeaconPeriod:

This attribute specifies the time period that a STA uses for
scheduling S1G Beacon transmissions in a TSBTT that is not a TBTT.

dot11ShortBeaconDTIMPeriod:

This attribute specifies the number of short beacon intervals that
elapse between transmission of S1G Beacon frames containing a TIM
element whose DTIM Count field is 0.

Expose 2 additional attributes alongside the short beacon frame data,
short_interval which is the time in TUs between each TSBTT and the
short_dtim_period which is the number of TSBTTs between each DTIM
beacon. Additionally, validate the elements present in the short
beacon as per IEEE80211-2024 9.3.4.3 Table 9-76 which describes
the elements permitted within a short beacon. Introduce these new
attributes as an optional netlink attribute to allow an S1G BSS
to be configured for short beaconing.

Signed-off-by: Lachlan Hodges <lachlan.hodges@xxxxxxxxxxxxxx>
---
 include/linux/ieee80211.h    |   4 +
 include/net/cfg80211.h       |  32 ++++++
 include/uapi/linux/nl80211.h |  32 ++++++
 net/wireless/nl80211.c       | 208 +++++++++++++++++++++++++++++++++++
 4 files changed, 276 insertions(+)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index e5a2096e022e..16e76cbf6bd6 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -3823,14 +3823,18 @@ enum ieee80211_eid {
 
 	WLAN_EID_REDUCED_NEIGHBOR_REPORT = 201,
 
+	WLAN_EID_RPS = 208,
+	WLAN_EID_PAGE_SLICE = 209,
 	WLAN_EID_AID_REQUEST = 210,
 	WLAN_EID_AID_RESPONSE = 211,
 	WLAN_EID_S1G_BCN_COMPAT = 213,
 	WLAN_EID_S1G_SHORT_BCN_INTERVAL = 214,
 	WLAN_EID_S1G_TWT = 216,
 	WLAN_EID_S1G_CAPABILITIES = 217,
+	WLAN_EID_SST = 220,
 	WLAN_EID_VENDOR_SPECIFIC = 221,
 	WLAN_EID_QOS_PARAMETER = 222,
+	WLAN_EID_S1G_RELAY = 224,
 	WLAN_EID_S1G_OPERATION = 232,
 	WLAN_EID_CAG_NUMBER = 237,
 	WLAN_EID_AP_CSN = 239,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 6ec9a8865b8b..c7c9a297753b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1423,6 +1423,34 @@ struct cfg80211_unsol_bcast_probe_resp {
 	const u8 *tmpl;
 };
 
+/**
+ * struct cfg80211_s1g_short_beacon - S1G short beacon data.
+ *
+ * @update: Set to true if the beacon head/tail has been updated. In this
+ *	case the BSS has already been configured to be short beaconing.
+ * @short_head: Short beacon head.
+ * @short_tail: Short beacon tail.
+ * @short_head_len: Short beacon head len.
+ * @short_tail_len: Short beacon tail len.
+ * @short_interval: Time in TU between each TSBTT (Target Short Beacon
+ *	Transmission Time). When short beaconing is enabled for an S1G
+ *	BSS, this value will be used to determine beacon transmission
+ *	times for both long and short beacons.
+ * @short_dtim_period: Specifies the number of TSBTTs that elapse between
+ *	transmission of S1G beacon frames containing a TIM element whose
+ *	DTIM count field is 0. When short beaconing is enabled for an S1G
+ *	BSS, this value is used to initialise the current DTIM count.
+ */
+struct cfg80211_s1g_short_beacon {
+	bool update;
+	const u8 *short_head;
+	const u8 *short_tail;
+	size_t short_head_len;
+	size_t short_tail_len;
+	u32 short_interval;
+	u8 short_dtim_period;
+};
+
 /**
  * struct cfg80211_ap_settings - AP configuration
  *
@@ -1463,6 +1491,7 @@ struct cfg80211_unsol_bcast_probe_resp {
  * @fils_discovery: FILS discovery transmission parameters
  * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
  * @mbssid_config: AP settings for multiple bssid
+ * @s1g_short_beacon: S1G short beacon data
  */
 struct cfg80211_ap_settings {
 	struct cfg80211_chan_def chandef;
@@ -1496,6 +1525,7 @@ struct cfg80211_ap_settings {
 	struct cfg80211_fils_discovery fils_discovery;
 	struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
 	struct cfg80211_mbssid_config mbssid_config;
+	struct cfg80211_s1g_short_beacon s1g_short_beacon;
 };
 
 
@@ -1507,11 +1537,13 @@ struct cfg80211_ap_settings {
  * @beacon: beacon data
  * @fils_discovery: FILS discovery transmission parameters
  * @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
+ * @s1g_short_beacon: S1G short beacon data
  */
 struct cfg80211_ap_update {
 	struct cfg80211_beacon_data beacon;
 	struct cfg80211_fils_discovery fils_discovery;
 	struct cfg80211_unsol_bcast_probe_resp unsol_bcast_probe_resp;
+	struct cfg80211_s1g_short_beacon s1g_short_beacon;
 };
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 39460334dafb..ccfc5ab1a42f 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2915,6 +2915,10 @@ enum nl80211_commands {
  *	applicable to that specific radio only. If the radio id is greater
  *	thank the number of radios, error denoting invalid value is returned.
  *
+ * @NL80211_ATTR_S1G_SHORT_BEACON: Optional parameter to configure an S1G BSS
+ *	with short beaconing support. It is a nested attribute, see
+ *	@enum nl80211_s1g_short_beacon_attrs.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3474,6 +3478,8 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_WIPHY_RADIO_INDEX,
 
+	NL80211_ATTR_S1G_SHORT_BEACON,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -8148,4 +8154,30 @@ enum nl80211_wiphy_radio_freq_range {
 	NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
 };
 
+/**
+ * enum nl80211_s1g_short_beacon_attrs - S1G short beacon data
+ *
+ * @__NL80211_S1G_SHORT_BEACON_ATTR_INVALID: Invalid
+ *
+ * @NL80211_S1G_SHORT_BEACON_HEAD: Short beacon head (binary).
+ * @NL80211_S1G_SHORT_BEACON_TAIL: Short beacon tail (binary).
+ * @NL80211_S1G_SHORT_BEACON_INTERVAL: Time in TUs between each short
+ *	beacon transmission (u32).
+ * @NL80211_S1G_SHORT_BEACON_DTIM_PERIOD: DTIM period for a short
+ *	beaconing BSS (u8).
+ */
+enum nl80211_s1g_short_beacon_attrs {
+	__NL80211_S1G_SHORT_BEACON_ATTR_INVALID,
+
+	NL80211_S1G_SHORT_BEACON_HEAD,
+	NL80211_S1G_SHORT_BEACON_TAIL,
+	NL80211_S1G_SHORT_BEACON_INTERVAL,
+	NL80211_S1G_SHORT_BEACON_DTIM_PERIOD,
+
+	/* keep last */
+	__NL80211_S1G_SHORT_BEACON_ATTR_LAST,
+	NL80211_S1G_SHORT_BEACON_ATTR_MAX =
+		__NL80211_S1G_SHORT_BEACON_ATTR_LAST - 1
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 4e6c0a4e2a82..519f8fe9f686 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -258,6 +258,10 @@ static int validate_beacon_head(const struct nlattr *attr,
 	data += fixedlen;
 	len -= fixedlen;
 
+	if (s1g_bcn &&
+	    ieee80211_is_s1g_short_beacon(ext->frame_control, data, len))
+		goto err;
+
 	for_each_element(elem, data, len) {
 		/* nothing */
 	}
@@ -288,6 +292,92 @@ static int validate_ie_attr(const struct nlattr *attr,
 	return -EINVAL;
 }
 
+/*
+ * Short beacons contain a limited set of allowed elements as per
+ * IEEE80211-2024 9.3.4.3 Table 9-76. The TIM element is allowed,
+ * but as it is inserted by mac80211, we do not check for it.
+ */
+static int is_valid_s1g_short_elem(const struct element *elem)
+{
+	switch (elem->id) {
+	case WLAN_EID_FMS_DESCRIPTOR:
+	case WLAN_EID_RPS:
+	case WLAN_EID_SST:
+	case WLAN_EID_S1G_RELAY:
+	case WLAN_EID_PAGE_SLICE:
+	case WLAN_EID_VENDOR_SPECIFIC:
+	case WLAN_EID_MMIE:
+	case WLAN_EID_MIC:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int validate_s1g_short_beacon_head(const struct nlattr *attr,
+					  struct netlink_ext_ack *extack)
+{
+	const u8 *data = nla_data(attr);
+	unsigned int len = nla_len(attr);
+	const struct element *elem;
+	unsigned int fixedlen, hdrlen;
+	const struct ieee80211_ext *ext = (void *)data;
+
+	if (len < offsetofend(typeof(*ext), frame_control))
+		goto err;
+
+	if (!ieee80211_is_s1g_beacon(ext->frame_control))
+		goto err;
+
+	fixedlen = offsetof(struct ieee80211_ext, u.s1g_beacon.variable) +
+		   ieee80211_s1g_optional_len(ext->frame_control);
+	hdrlen = offsetof(struct ieee80211_ext, u.s1g_beacon);
+
+	if (len < fixedlen)
+		goto err;
+
+	if (ieee80211_hdrlen(ext->frame_control) != hdrlen)
+		goto err;
+
+	data += fixedlen;
+	len -= fixedlen;
+
+	if (!ieee80211_is_s1g_short_beacon(ext->frame_control, data, len))
+		goto err;
+
+	for_each_element(elem, data, len) {
+		if (!is_valid_s1g_short_elem(elem))
+			goto err;
+	}
+
+	if (for_each_element_completed(elem, data, len))
+		return 0;
+
+err:
+	NL_SET_ERR_MSG_ATTR(extack, attr, "malformed short beacon head");
+	return -EINVAL;
+}
+
+static int validate_s1g_short_beacon_ie_attr(const struct nlattr *attr,
+					     struct netlink_ext_ack *extack)
+{
+	const u8 *data = nla_data(attr);
+	unsigned int len = nla_len(attr);
+	const struct element *elem;
+
+	for_each_element(elem, data, len) {
+		if (!is_valid_s1g_short_elem(elem))
+			goto err;
+	}
+
+	if (for_each_element_completed(elem, data, len))
+		return 0;
+
+err:
+	NL_SET_ERR_MSG_ATTR(extack, attr, "malformed short beacon elements");
+	return -EINVAL;
+}
+
 static int validate_he_capa(const struct nlattr *attr,
 			    struct netlink_ext_ack *extack)
 {
@@ -482,6 +572,20 @@ nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
 	[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
 };
 
+static const struct nla_policy
+nl80211_s1g_short_beacon[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1] = {
+	[NL80211_S1G_SHORT_BEACON_HEAD] =
+		NLA_POLICY_VALIDATE_FN(NLA_BINARY,
+				       validate_s1g_short_beacon_head,
+				       IEEE80211_MAX_DATA_LEN),
+	[NL80211_S1G_SHORT_BEACON_TAIL] =
+		NLA_POLICY_VALIDATE_FN(NLA_BINARY,
+				       validate_s1g_short_beacon_ie_attr,
+				       IEEE80211_MAX_DATA_LEN),
+	[NL80211_S1G_SHORT_BEACON_INTERVAL] = { .type = NLA_U32 },
+	[NL80211_S1G_SHORT_BEACON_DTIM_PERIOD] = { .type = NLA_U8 },
+};
+
 static const struct netlink_range_validation nl80211_punct_bitmap_range = {
 	.min = 0,
 	.max = 0xffff,
@@ -858,6 +962,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_EPCS] = { .type = NLA_FLAG },
 	[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 },
 	[NL80211_ATTR_WIPHY_RADIO_INDEX] = { .type = NLA_U8 },
+	[NL80211_ATTR_S1G_SHORT_BEACON] =
+		NLA_POLICY_NESTED(nl80211_s1g_short_beacon),
 };
 
 /* policy for the key attributes */
@@ -6202,6 +6308,73 @@ static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params
 	return 0;
 }
 
+static int nl80211_validate_s1g_short_conf(struct cfg80211_ap_settings *params)
+{
+	struct cfg80211_s1g_short_beacon *sb = &params->s1g_short_beacon;
+	u64 beacon_int = params->beacon_interval;
+	u32 short_int = sb->short_interval;
+
+	/*
+	 * As per IEEE80211 11.1.3.10.2, beacon_interval = n * short_interval
+	 * where n is a positive integer. Meaning a BSS can be configured such
+	 * that both the short beacon interval and long beacon interval are
+	 * equivalent. This effectively means we aren't short beaconing. In
+	 * this case, since the short beacon data is irellevent its probably a
+	 * misconfiguration and we should reject it.
+	 */
+	if (sb->short_interval >= params->beacon_interval)
+		return -EINVAL;
+
+	if (do_div(beacon_int, short_int))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int
+nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev,
+			       struct nlattr *attrs,
+			       struct cfg80211_s1g_short_beacon *sb)
+{
+	struct nlattr *tb[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1];
+	int ret;
+
+	if (!rdev->wiphy.bands[NL80211_BAND_S1GHZ])
+		return -EINVAL;
+
+	ret = nla_parse_nested(tb, NL80211_S1G_SHORT_BEACON_ATTR_MAX, attrs,
+			       NULL, NULL);
+	if (ret)
+		return ret;
+
+	/* Short beacon tail is optional (i.e might only include the TIM) */
+	if (!tb[NL80211_S1G_SHORT_BEACON_HEAD])
+		return -EINVAL;
+
+	sb->update = false;
+	sb->short_head = nla_data(tb[NL80211_S1G_SHORT_BEACON_HEAD]);
+	sb->short_head_len = nla_len(tb[NL80211_S1G_SHORT_BEACON_HEAD]);
+	sb->short_tail_len = 0;
+
+	if (tb[NL80211_S1G_SHORT_BEACON_TAIL]) {
+		sb->short_tail = nla_data(tb[NL80211_S1G_SHORT_BEACON_TAIL]);
+		sb->short_tail_len = nla_len(tb[NL80211_S1G_SHORT_BEACON_TAIL]);
+	}
+
+	if (!tb[NL80211_S1G_SHORT_BEACON_INTERVAL] ||
+	    !tb[NL80211_S1G_SHORT_BEACON_DTIM_PERIOD]) {
+		sb->update = true;
+		return 0;
+	}
+
+	sb->short_interval =
+		nla_get_u32(tb[NL80211_S1G_SHORT_BEACON_INTERVAL]);
+	sb->short_dtim_period =
+		nla_get_u8(tb[NL80211_S1G_SHORT_BEACON_DTIM_PERIOD]);
+
+	return 0;
+}
+
 static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -6442,6 +6615,28 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
 		goto out;
 	}
 
+	if (info->attrs[NL80211_ATTR_S1G_SHORT_BEACON]) {
+		err = nl80211_parse_s1g_short_beacon(
+			rdev, info->attrs[NL80211_ATTR_S1G_SHORT_BEACON],
+			&params->s1g_short_beacon);
+		if (err)
+			goto out;
+
+		/*
+		 * If we have only received the parameters to perform a
+		 * short beacon update, return an error to usermode as
+		 * the BSS has not yet been configured for short beaconing.
+		 */
+		if (params->s1g_short_beacon.update) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		err = nl80211_validate_s1g_short_conf(params);
+		if (err)
+			goto out;
+	}
+
 	err = nl80211_calculate_ap_params(params);
 	if (err)
 		goto out;
@@ -6550,6 +6745,19 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
 			goto out;
 	}
 
+	attr = info->attrs[NL80211_ATTR_S1G_SHORT_BEACON];
+	if (attr) {
+		err = nl80211_parse_s1g_short_beacon(rdev, attr,
+						     &params->s1g_short_beacon);
+		if (err)
+			goto out;
+
+		if (!params->s1g_short_beacon.update) {
+			err = -EINVAL;
+			goto out;
+		}
+	}
+
 	err = rdev_change_beacon(rdev, dev, params);
 
 out:
-- 
2.43.0





[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux