wifi: mac80211: correctly identify S1G short beacon
authorLachlan Hodges <lachlan.hodges@morsemicro.com>
Tue, 1 Jul 2025 07:55:41 +0000 (17:55 +1000)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 7 Jul 2025 08:42:15 +0000 (10:42 +0200)
mac80211 identifies a short beacon by the presence of the next
TBTT field, however the standard actually doesn't explicitly state that
the next TBTT can't be in a long beacon or even that it is required in
a short beacon - and as a result this validation does not work for all
vendor implementations.

The standard explicitly states that an S1G long beacon shall contain
the S1G beacon compatibility element as the first element in a beacon
transmitted at a TBTT that is not a TSBTT (Target Short Beacon
Transmission Time) as per IEEE80211-2024 11.1.3.10.1. This is validated
by 9.3.4.3 Table 9-76 which states that the S1G beacon compatibility
element is only allowed in the full set and is not allowed in the
minimum set of elements permitted for use within short beacons.

Correctly identify short beacons by the lack of an S1G beacon
compatibility element as the first element in an S1G beacon frame.

Fixes: 9eaffe5078ca ("cfg80211: convert S1G beacon to scan results")
Signed-off-by: Simon Wadsworth <simon@morsemicro.com>
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
Link: https://patch.msgid.link/20250701075541.162619-1-lachlan.hodges@morsemicro.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/linux/ieee80211.h
net/mac80211/mlme.c

index 22f39e5e2ff1b62e37ff0c2c0d9e09ea03a304b1..996be3c2cff07d81f9c51ce6793e4dd764ee4117 100644 (file)
@@ -662,18 +662,6 @@ static inline bool ieee80211_s1g_has_cssid(__le16 fc)
                (fc & cpu_to_le16(IEEE80211_S1G_BCN_CSSID));
 }
 
-/**
- * ieee80211_is_s1g_short_beacon - check if frame is an S1G short beacon
- * @fc: frame control bytes in little-endian byteorder
- * Return: whether or not the frame is an S1G short beacon,
- *     i.e. it is an S1G beacon with 'next TBTT' flag set
- */
-static inline bool ieee80211_is_s1g_short_beacon(__le16 fc)
-{
-       return ieee80211_is_s1g_beacon(fc) &&
-               (fc & cpu_to_le16(IEEE80211_S1G_BCN_NEXT_TBTT));
-}
-
 /**
  * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM
  * @fc: frame control bytes in little-endian byteorder
@@ -4901,6 +4889,39 @@ static inline bool ieee80211_is_ftm(struct sk_buff *skb)
        return false;
 }
 
+/**
+ * ieee80211_is_s1g_short_beacon - check if frame is an S1G short beacon
+ * @fc: frame control bytes in little-endian byteorder
+ * @variable: pointer to the beacon frame elements
+ * @variable_len: length of the frame elements
+ * Return: whether or not the frame is an S1G short beacon. As per
+ *     IEEE80211-2024 11.1.3.10.1, The S1G beacon compatibility element shall
+ *     always be present as the first element in beacon frames generated at a
+ *     TBTT (Target Beacon Transmission Time), so any frame not containing
+ *     this element must have been generated at a TSBTT (Target Short Beacon
+ *     Transmission Time) that is not a TBTT. Additionally, short beacons are
+ *     prohibited from containing the S1G beacon compatibility element as per
+ *     IEEE80211-2024 9.3.4.3 Table 9-76, so if we have an S1G beacon with
+ *     either no elements or the first element is not the beacon compatibility
+ *     element, we have a short beacon.
+ */
+static inline bool ieee80211_is_s1g_short_beacon(__le16 fc, const u8 *variable,
+                                                size_t variable_len)
+{
+       if (!ieee80211_is_s1g_beacon(fc))
+               return false;
+
+       /*
+        * If the frame does not contain at least 1 element (this is perfectly
+        * valid in a short beacon) and is an S1G beacon, we have a short
+        * beacon.
+        */
+       if (variable_len < 2)
+               return true;
+
+       return variable[0] != WLAN_EID_S1G_BCN_COMPAT;
+}
+
 struct element {
        u8 id;
        u8 datalen;
index 2d46d4af60d7bdc96b25d6e92a68271802deedd3..7ddb8e77b4c73264bdd48422c231d8ed73f6d647 100644 (file)
@@ -7195,6 +7195,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
        struct ieee80211_bss_conf *bss_conf = link->conf;
        struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
        struct ieee80211_mgmt *mgmt = (void *) hdr;
+       struct ieee80211_ext *ext = NULL;
        size_t baselen;
        struct ieee802_11_elems *elems;
        struct ieee80211_local *local = sdata->local;
@@ -7220,7 +7221,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
        /* Process beacon from the current BSS */
        bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
        if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
-               struct ieee80211_ext *ext = (void *) mgmt;
+               ext = (void *)mgmt;
                variable = ext->u.s1g_beacon.variable +
                           ieee80211_s1g_optional_len(ext->frame_control);
        }
@@ -7407,7 +7408,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
        }
 
        if ((ncrc == link->u.mgd.beacon_crc && link->u.mgd.beacon_crc_valid) ||
-           ieee80211_is_s1g_short_beacon(mgmt->frame_control))
+           (ext && ieee80211_is_s1g_short_beacon(ext->frame_control,
+                                                 parse_params.start,
+                                                 parse_params.len)))
                goto free;
        link->u.mgd.beacon_crc = ncrc;
        link->u.mgd.beacon_crc_valid = true;