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