Skip to content

Commit 1e1f706

Browse files
donnaskiezjmberg-intel
authored andcommitted
wifi: cfg80211/mac80211: correctly parse S1G beacon optional elements
S1G beacons are not traditional beacons but a type of extension frame. Extension frames contain the frame control and duration fields, followed by zero or more optional fields before the frame body. These optional fields are distinct from the variable length elements. The presence of optional fields is indicated in the frame control field. To correctly locate the elements offset, the frame control must be parsed to identify which optional fields are present. Currently, mac80211 parses S1G beacons based on fixed assumptions about the frame layout, without inspecting the frame control field. This can result in incorrect offsets to the "variable" portion of the frame. Properly parse S1G beacon frames by using the field lengths defined in IEEE 802.11-2024, section 9.3.4.3, ensuring that the elements offset is calculated accurately. Fixes: 9eaffe5 ("cfg80211: convert S1G beacon to scan results") Fixes: cd418ba ("mac80211: convert S1G beacon to scan results") Signed-off-by: Lachlan Hodges <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Johannes Berg <[email protected]>
1 parent 1b98f35 commit 1e1f706

File tree

4 files changed

+83
-32
lines changed

4 files changed

+83
-32
lines changed

include/linux/ieee80211.h

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@
111111

112112
/* bits unique to S1G beacon */
113113
#define IEEE80211_S1G_BCN_NEXT_TBTT 0x100
114+
#define IEEE80211_S1G_BCN_CSSID 0x200
115+
#define IEEE80211_S1G_BCN_ANO 0x400
114116

115117
/* see 802.11ah-2016 9.9 NDP CMAC frames */
116118
#define IEEE80211_S1G_1MHZ_NDP_BITS 25
@@ -153,9 +155,6 @@
153155

154156
#define IEEE80211_ANO_NETTYPE_WILD 15
155157

156-
/* bits unique to S1G beacon */
157-
#define IEEE80211_S1G_BCN_NEXT_TBTT 0x100
158-
159158
/* control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT */
160159
#define IEEE80211_CTL_EXT_POLL 0x2000
161160
#define IEEE80211_CTL_EXT_SPR 0x3000
@@ -627,6 +626,42 @@ static inline bool ieee80211_is_s1g_beacon(__le16 fc)
627626
cpu_to_le16(IEEE80211_FTYPE_EXT | IEEE80211_STYPE_S1G_BEACON);
628627
}
629628

629+
/**
630+
* ieee80211_s1g_has_next_tbtt - check if IEEE80211_S1G_BCN_NEXT_TBTT
631+
* @fc: frame control bytes in little-endian byteorder
632+
* Return: whether or not the frame contains the variable-length
633+
* next TBTT field
634+
*/
635+
static inline bool ieee80211_s1g_has_next_tbtt(__le16 fc)
636+
{
637+
return ieee80211_is_s1g_beacon(fc) &&
638+
(fc & cpu_to_le16(IEEE80211_S1G_BCN_NEXT_TBTT));
639+
}
640+
641+
/**
642+
* ieee80211_s1g_has_ano - check if IEEE80211_S1G_BCN_ANO
643+
* @fc: frame control bytes in little-endian byteorder
644+
* Return: whether or not the frame contains the variable-length
645+
* ANO field
646+
*/
647+
static inline bool ieee80211_s1g_has_ano(__le16 fc)
648+
{
649+
return ieee80211_is_s1g_beacon(fc) &&
650+
(fc & cpu_to_le16(IEEE80211_S1G_BCN_ANO));
651+
}
652+
653+
/**
654+
* ieee80211_s1g_has_cssid - check if IEEE80211_S1G_BCN_CSSID
655+
* @fc: frame control bytes in little-endian byteorder
656+
* Return: whether or not the frame contains the variable-length
657+
* compressed SSID field
658+
*/
659+
static inline bool ieee80211_s1g_has_cssid(__le16 fc)
660+
{
661+
return ieee80211_is_s1g_beacon(fc) &&
662+
(fc & cpu_to_le16(IEEE80211_S1G_BCN_CSSID));
663+
}
664+
630665
/**
631666
* ieee80211_is_s1g_short_beacon - check if frame is an S1G short beacon
632667
* @fc: frame control bytes in little-endian byteorder
@@ -1245,16 +1280,40 @@ struct ieee80211_ext {
12451280
u8 change_seq;
12461281
u8 variable[0];
12471282
} __packed s1g_beacon;
1248-
struct {
1249-
u8 sa[ETH_ALEN];
1250-
__le32 timestamp;
1251-
u8 change_seq;
1252-
u8 next_tbtt[3];
1253-
u8 variable[0];
1254-
} __packed s1g_short_beacon;
12551283
} u;
12561284
} __packed __aligned(2);
12571285

1286+
/**
1287+
* ieee80211_s1g_optional_len - determine length of optional S1G beacon fields
1288+
* @fc: frame control bytes in little-endian byteorder
1289+
* Return: total length in bytes of the optional fixed-length fields
1290+
*
1291+
* S1G beacons may contain up to three optional fixed-length fields that
1292+
* precede the variable-length elements. Whether these fields are present
1293+
* is indicated by flags in the frame control field.
1294+
*
1295+
* From IEEE 802.11-2024 section 9.3.4.3:
1296+
* - Next TBTT field may be 0 or 3 bytes
1297+
* - Short SSID field may be 0 or 4 bytes
1298+
* - Access Network Options (ANO) field may be 0 or 1 byte
1299+
*/
1300+
static inline size_t
1301+
ieee80211_s1g_optional_len(__le16 fc)
1302+
{
1303+
size_t len = 0;
1304+
1305+
if (ieee80211_s1g_has_next_tbtt(fc))
1306+
len += 3;
1307+
1308+
if (ieee80211_s1g_has_cssid(fc))
1309+
len += 4;
1310+
1311+
if (ieee80211_s1g_has_ano(fc))
1312+
len += 1;
1313+
1314+
return len;
1315+
}
1316+
12581317
#define IEEE80211_TWT_CONTROL_NDP BIT(0)
12591318
#define IEEE80211_TWT_CONTROL_RESP_MODE BIT(1)
12601319
#define IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST BIT(3)

net/mac80211/mlme.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7220,11 +7220,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
72207220
bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
72217221
if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
72227222
struct ieee80211_ext *ext = (void *) mgmt;
7223-
7224-
if (ieee80211_is_s1g_short_beacon(ext->frame_control))
7225-
variable = ext->u.s1g_short_beacon.variable;
7226-
else
7227-
variable = ext->u.s1g_beacon.variable;
7223+
variable = ext->u.s1g_beacon.variable +
7224+
ieee80211_s1g_optional_len(ext->frame_control);
72287225
}
72297226

72307227
baselen = (u8 *) variable - (u8 *) mgmt;

net/mac80211/scan.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
276276
struct ieee80211_mgmt *mgmt = (void *)skb->data;
277277
struct ieee80211_bss *bss;
278278
struct ieee80211_channel *channel;
279+
struct ieee80211_ext *ext;
279280
size_t min_hdr_len = offsetof(struct ieee80211_mgmt,
280281
u.probe_resp.variable);
281282

@@ -285,12 +286,10 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
285286
return;
286287

287288
if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
288-
if (ieee80211_is_s1g_short_beacon(mgmt->frame_control))
289-
min_hdr_len = offsetof(struct ieee80211_ext,
290-
u.s1g_short_beacon.variable);
291-
else
292-
min_hdr_len = offsetof(struct ieee80211_ext,
293-
u.s1g_beacon);
289+
ext = (struct ieee80211_ext *)mgmt;
290+
min_hdr_len =
291+
offsetof(struct ieee80211_ext, u.s1g_beacon.variable) +
292+
ieee80211_s1g_optional_len(ext->frame_control);
294293
}
295294

296295
if (skb->len < min_hdr_len)

net/wireless/scan.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3250,6 +3250,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
32503250
const u8 *ie;
32513251
size_t ielen;
32523252
u64 tsf;
3253+
size_t s1g_optional_len;
32533254

32543255
if (WARN_ON(!mgmt))
32553256
return NULL;
@@ -3264,12 +3265,11 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
32643265

32653266
if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
32663267
ext = (void *) mgmt;
3267-
if (ieee80211_is_s1g_short_beacon(mgmt->frame_control))
3268-
min_hdr_len = offsetof(struct ieee80211_ext,
3269-
u.s1g_short_beacon.variable);
3270-
else
3271-
min_hdr_len = offsetof(struct ieee80211_ext,
3272-
u.s1g_beacon.variable);
3268+
s1g_optional_len =
3269+
ieee80211_s1g_optional_len(ext->frame_control);
3270+
min_hdr_len =
3271+
offsetof(struct ieee80211_ext, u.s1g_beacon.variable) +
3272+
s1g_optional_len;
32733273
} else {
32743274
/* same for beacons */
32753275
min_hdr_len = offsetof(struct ieee80211_mgmt,
@@ -3285,11 +3285,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
32853285
const struct ieee80211_s1g_bcn_compat_ie *compat;
32863286
const struct element *elem;
32873287

3288-
if (ieee80211_is_s1g_short_beacon(mgmt->frame_control))
3289-
ie = ext->u.s1g_short_beacon.variable;
3290-
else
3291-
ie = ext->u.s1g_beacon.variable;
3292-
3288+
ie = ext->u.s1g_beacon.variable + s1g_optional_len;
32933289
elem = cfg80211_find_elem(WLAN_EID_S1G_BCN_COMPAT, ie, ielen);
32943290
if (!elem)
32953291
return NULL;

0 commit comments

Comments
 (0)