blob: d455dd0afc4b8ec266e4b8d9e65f6ba380acaf1c [file] [log] [blame]
developerd7066b02023-04-21 11:17:26 +08001From 67ce530e39e0372f11006c5695f682e71152f7b7 Mon Sep 17 00:00:00 2001
2From: Johannes Berg <johannes.berg@intel.com>
3Date: Fri, 27 Jan 2023 12:39:31 +0100
4Subject: [PATCH 4/9] wifi: mac80211: mlme: handle EHT channel puncturing
5
6Handle the Puncturing info received from the AP in the
7EHT Operation element in beacons.
8
9If the info is invalid:
10 - during association: disable EHT connection for the AP
11 - after association: disconnect
12
13This commit includes many (internal) bugfixes and spec
14updates various people.
15
16Co-developed-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
17Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
18Link: https://lore.kernel.org/r/20230127123930.4fbc74582331.I3547481d49f958389f59dfeba3fcc75e72b0aa6e@changeid
19Signed-off-by: Johannes Berg <johannes.berg@intel.com>
20---
21 include/net/mac80211.h | 5 +-
22 net/mac80211/cfg.c | 2 +-
23 net/mac80211/chan.c | 2 +-
24 net/mac80211/ieee80211_i.h | 2 +-
25 net/mac80211/mlme.c | 224 ++++++++++++++++++++++++++++++++++++-
26 5 files changed, 228 insertions(+), 7 deletions(-)
27
28diff --git a/include/net/mac80211.h b/include/net/mac80211.h
29index 3388cc7..8fb38c9 100644
30--- a/include/net/mac80211.h
31+++ b/include/net/mac80211.h
32@@ -340,7 +340,7 @@ struct ieee80211_vif_chanctx_switch {
33 * @BSS_CHANGED_FILS_DISCOVERY: FILS discovery status changed.
34 * @BSS_CHANGED_UNSOL_BCAST_PROBE_RESP: Unsolicited broadcast probe response
35 * status changed.
36- *
37+ * @BSS_CHANGED_EHT_PUNCTURING: The channel puncturing bitmap changed.
38 */
39 enum ieee80211_bss_change {
40 BSS_CHANGED_ASSOC = 1<<0,
41@@ -375,6 +375,7 @@ enum ieee80211_bss_change {
42 BSS_CHANGED_HE_BSS_COLOR = 1<<29,
43 BSS_CHANGED_FILS_DISCOVERY = 1<<30,
44 BSS_CHANGED_UNSOL_BCAST_PROBE_RESP = 1<<31,
45+ BSS_CHANGED_EHT_PUNCTURING = BIT_ULL(32),
46
47 /* when adding here, make sure to change ieee80211_reconfig */
48 };
49@@ -640,6 +641,7 @@ struct ieee80211_fils_discovery {
50 * @tx_pwr_env_num: number of @tx_pwr_env.
51 * @pwr_reduction: power constraint of BSS.
52 * @eht_support: does this BSS support EHT
53+ * @eht_puncturing: bitmap to indicate which channels are punctured in this BSS
54 * @csa_active: marks whether a channel switch is going on. Internally it is
55 * write-protected by sdata_lock and local->mtx so holding either is fine
56 * for read access.
57@@ -739,6 +741,7 @@ struct ieee80211_bss_conf {
58 u8 tx_pwr_env_num;
59 u8 pwr_reduction;
60 bool eht_support;
61+ u16 eht_puncturing;
62
63 bool csa_active;
64 bool mu_mimo_owner;
65diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
66index 7a5e459..5bb43de 100644
67--- a/net/mac80211/cfg.c
68+++ b/net/mac80211/cfg.c
69@@ -4181,7 +4181,7 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
70 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
71 struct ieee80211_link_data *link;
72 int ret;
73- u32 changed = 0;
74+ u64 changed = 0;
75
76 link = sdata_dereference(sdata->link[link_id], sdata);
77
78diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
79index e72cf07..dbc34fb 100644
80--- a/net/mac80211/chan.c
81+++ b/net/mac80211/chan.c
82@@ -1916,7 +1916,7 @@ int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link)
83
84 int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link,
85 const struct cfg80211_chan_def *chandef,
86- u32 *changed)
87+ u64 *changed)
88 {
89 struct ieee80211_sub_if_data *sdata = link->sdata;
90 struct ieee80211_bss_conf *link_conf = link->conf;
91diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
92index a4fab9a..04128d5 100644
93--- a/net/mac80211/ieee80211_i.h
94+++ b/net/mac80211/ieee80211_i.h
95@@ -2487,7 +2487,7 @@ int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link);
96 int __must_check
97 ieee80211_link_change_bandwidth(struct ieee80211_link_data *link,
98 const struct cfg80211_chan_def *chandef,
99- u32 *changed);
100+ u64 *changed);
101 void ieee80211_link_release_channel(struct ieee80211_link_data *link);
102 void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link);
103 void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link,
104diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
105index 0125b3e..8c69fd6 100644
106--- a/net/mac80211/mlme.c
107+++ b/net/mac80211/mlme.c
108@@ -8,7 +8,7 @@
109 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
110 * Copyright 2013-2014 Intel Mobile Communications GmbH
111 * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
112- * Copyright (C) 2018 - 2022 Intel Corporation
113+ * Copyright (C) 2018 - 2023 Intel Corporation
114 */
115
116 #include <linux/delay.h>
117@@ -88,6 +88,141 @@ MODULE_PARM_DESC(probe_wait_ms,
118 */
119 #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
120
121+struct ieee80211_per_bw_puncturing_values {
122+ u8 len;
123+ const u16 *valid_values;
124+};
125+
126+static const u16 puncturing_values_80mhz[] = {
127+ 0x8, 0x4, 0x2, 0x1
128+};
129+
130+static const u16 puncturing_values_160mhz[] = {
131+ 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1, 0xc0, 0x30, 0xc, 0x3
132+};
133+
134+static const u16 puncturing_values_320mhz[] = {
135+ 0xc000, 0x3000, 0xc00, 0x300, 0xc0, 0x30, 0xc, 0x3, 0xf000, 0xf00,
136+ 0xf0, 0xf, 0xfc00, 0xf300, 0xf0c0, 0xf030, 0xf00c, 0xf003, 0xc00f,
137+ 0x300f, 0xc0f, 0x30f, 0xcf, 0x3f
138+};
139+
140+#define IEEE80211_PER_BW_VALID_PUNCTURING_VALUES(_bw) \
141+ { \
142+ .len = ARRAY_SIZE(puncturing_values_ ## _bw ## mhz), \
143+ .valid_values = puncturing_values_ ## _bw ## mhz \
144+ }
145+
146+static const struct ieee80211_per_bw_puncturing_values per_bw_puncturing[] = {
147+ IEEE80211_PER_BW_VALID_PUNCTURING_VALUES(80),
148+ IEEE80211_PER_BW_VALID_PUNCTURING_VALUES(160),
149+ IEEE80211_PER_BW_VALID_PUNCTURING_VALUES(320)
150+};
151+
152+static bool ieee80211_valid_disable_subchannel_bitmap(u16 *bitmap,
153+ enum nl80211_chan_width bw)
154+{
155+ u32 idx, i;
156+
157+ switch (bw) {
158+ case NL80211_CHAN_WIDTH_80:
159+ idx = 0;
160+ break;
161+ case NL80211_CHAN_WIDTH_160:
162+ idx = 1;
163+ break;
164+ case NL80211_CHAN_WIDTH_320:
165+ idx = 2;
166+ break;
167+ default:
168+ *bitmap = 0;
169+ break;
170+ }
171+
172+ if (!*bitmap)
173+ return true;
174+
175+ for (i = 0; i < per_bw_puncturing[idx].len; i++)
176+ if (per_bw_puncturing[idx].valid_values[i] == *bitmap)
177+ return true;
178+
179+ return false;
180+}
181+
182+/*
183+ * Extract from the given disabled subchannel bitmap (raw format
184+ * from the EHT Operation Element) the bits for the subchannel
185+ * we're using right now.
186+ */
187+static u16
188+ieee80211_extract_dis_subch_bmap(const struct ieee80211_eht_operation *eht_oper,
189+ struct cfg80211_chan_def *chandef, u16 bitmap)
190+{
191+ struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional;
192+ struct cfg80211_chan_def ap_chandef = *chandef;
193+ u32 ap_center_freq, local_center_freq;
194+ u32 ap_bw, local_bw;
195+ int ap_start_freq, local_start_freq;
196+ u16 shift, mask;
197+
198+ if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) ||
199+ !(eht_oper->params &
200+ IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT))
201+ return 0;
202+
203+ /* set 160/320 supported to get the full AP definition */
204+ ieee80211_chandef_eht_oper(eht_oper, true, true, &ap_chandef);
205+ ap_center_freq = ap_chandef.center_freq1;
206+ ap_bw = 20 * BIT(u8_get_bits(info->control,
207+ IEEE80211_EHT_OPER_CHAN_WIDTH));
208+ ap_start_freq = ap_center_freq - ap_bw / 2;
209+ local_center_freq = chandef->center_freq1;
210+ local_bw = 20 * BIT(ieee80211_chan_width_to_rx_bw(chandef->width));
211+ local_start_freq = local_center_freq - local_bw / 2;
212+ shift = (local_start_freq - ap_start_freq) / 20;
213+ mask = BIT(local_bw / 20) - 1;
214+
215+ return (bitmap >> shift) & mask;
216+}
217+
218+/*
219+ * Handle the puncturing bitmap, possibly downgrading bandwidth to get a
220+ * valid bitmap.
221+ */
222+static void
223+ieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link,
224+ const struct ieee80211_eht_operation *eht_oper,
225+ u16 bitmap, u64 *changed)
226+{
227+ struct cfg80211_chan_def *chandef = &link->conf->chandef;
228+ u16 extracted;
229+ u64 _changed = 0;
230+
231+ if (!changed)
232+ changed = &_changed;
233+
234+ while (chandef->width > NL80211_CHAN_WIDTH_40) {
235+ extracted =
236+ ieee80211_extract_dis_subch_bmap(eht_oper, chandef,
237+ bitmap);
238+
239+ if (ieee80211_valid_disable_subchannel_bitmap(&bitmap,
240+ chandef->width))
241+ break;
242+ link->u.mgd.conn_flags |=
243+ ieee80211_chandef_downgrade(chandef);
244+ *changed |= BSS_CHANGED_BANDWIDTH;
245+ }
246+
247+ if (chandef->width <= NL80211_CHAN_WIDTH_40)
248+ extracted = 0;
249+
250+ if (link->conf->eht_puncturing != extracted) {
251+ link->conf->eht_puncturing = extracted;
252+ *changed |= BSS_CHANGED_EHT_PUNCTURING;
253+ }
254+}
255+
256 /*
257 * We can have multiple work items (and connection probing)
258 * scheduling this timer, but we need to take care to only
259@@ -413,7 +548,7 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
260 const struct ieee80211_he_operation *he_oper,
261 const struct ieee80211_eht_operation *eht_oper,
262 const struct ieee80211_s1g_oper_ie *s1g_oper,
263- const u8 *bssid, u32 *changed)
264+ const u8 *bssid, u64 *changed)
265 {
266 struct ieee80211_sub_if_data *sdata = link->sdata;
267 struct ieee80211_local *local = sdata->local;
268@@ -4111,6 +4246,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
269 link_sta);
270
271 bss_conf->eht_support = link_sta->pub->eht_cap.has_eht;
272+ *changed |= BSS_CHANGED_EHT_PUNCTURING;
273 } else {
274 bss_conf->eht_support = false;
275 }
276@@ -5423,6 +5559,45 @@ static bool ieee80211_rx_our_beacon(const u8 *tx_bssid,
277 return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid);
278 }
279
280+static bool ieee80211_config_puncturing(struct ieee80211_link_data *link,
281+ const struct ieee80211_eht_operation *eht_oper,
282+ u64 *changed)
283+{
284+ u16 bitmap = 0, extracted;
285+
286+ if ((eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) &&
287+ (eht_oper->params &
288+ IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) {
289+ const struct ieee80211_eht_operation_info *info =
290+ (void *)eht_oper->optional;
291+ const u8 *disable_subchannel_bitmap = info->optional;
292+
293+ bitmap = get_unaligned_le16(disable_subchannel_bitmap);
294+ }
295+
296+ extracted = ieee80211_extract_dis_subch_bmap(eht_oper,
297+ &link->conf->chandef,
298+ bitmap);
299+
300+ /* accept if there are no changes */
301+ if (!(*changed & BSS_CHANGED_BANDWIDTH) &&
302+ extracted == link->conf->eht_puncturing)
303+ return true;
304+
305+ if (!ieee80211_valid_disable_subchannel_bitmap(&bitmap,
306+ link->conf->chandef.width)) {
307+ link_info(link,
308+ "Got an invalid disable subchannel bitmap from AP %pM: bitmap = 0x%x, bw = 0x%x. disconnect\n",
309+ link->u.mgd.bssid,
310+ bitmap,
311+ link->conf->chandef.width);
312+ return false;
313+ }
314+
315+ ieee80211_handle_puncturing_bitmap(link, eht_oper, bitmap, changed);
316+ return true;
317+}
318+
319 static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
320 struct ieee80211_hdr *hdr, size_t len,
321 struct ieee80211_rx_status *rx_status)
322@@ -5439,7 +5614,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
323 struct ieee80211_channel *chan;
324 struct link_sta_info *link_sta;
325 struct sta_info *sta;
326- u32 changed = 0;
327+ u64 changed = 0;
328 bool erp_valid;
329 u8 erp_value = 0;
330 u32 ncrc = 0;
331@@ -5731,6 +5906,21 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
332 elems->pwr_constr_elem,
333 elems->cisco_dtpc_elem);
334
335+ if (elems->eht_operation &&
336+ !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) {
337+ if (!ieee80211_config_puncturing(link, elems->eht_operation,
338+ &changed)) {
339+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
340+ WLAN_REASON_DEAUTH_LEAVING,
341+ true, deauth_buf);
342+ ieee80211_report_disconnect(sdata, deauth_buf,
343+ sizeof(deauth_buf), true,
344+ WLAN_REASON_DEAUTH_LEAVING,
345+ false);
346+ goto free;
347+ }
348+ }
349+
350 ieee80211_link_info_change_notify(sdata, link, changed);
351 free:
352 kfree(elems);
353@@ -6832,9 +7022,12 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata,
354 ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
355 }
356
357+ link->conf->eht_puncturing = 0;
358+
359 rcu_read_lock();
360 beacon_ies = rcu_dereference(cbss->beacon_ies);
361 if (beacon_ies) {
362+ const struct ieee80211_eht_operation *eht_oper;
363 const struct element *elem;
364 u8 dtim_count = 0;
365
366@@ -6863,6 +7056,31 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata,
367 link->conf->ema_ap = true;
368 else
369 link->conf->ema_ap = false;
370+
371+ elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION,
372+ beacon_ies->data, beacon_ies->len);
373+ eht_oper = (const void *)(elem->data + 1);
374+
375+ if (elem &&
376+ ieee80211_eht_oper_size_ok((const void *)(elem->data + 1),
377+ elem->datalen - 1) &&
378+ (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) &&
379+ (eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) {
380+ const struct ieee80211_eht_operation_info *info =
381+ (void *)eht_oper->optional;
382+ const u8 *disable_subchannel_bitmap = info->optional;
383+ u16 bitmap;
384+
385+ bitmap = get_unaligned_le16(disable_subchannel_bitmap);
386+ if (ieee80211_valid_disable_subchannel_bitmap(&bitmap,
387+ link->conf->chandef.width))
388+ ieee80211_handle_puncturing_bitmap(link,
389+ eht_oper,
390+ bitmap,
391+ NULL);
392+ else
393+ conn_flags |= IEEE80211_CONN_DISABLE_EHT;
394+ }
395 }
396 rcu_read_unlock();
397
398--
3992.39.2
400