developer | d7066b0 | 2023-04-21 11:17:26 +0800 | [diff] [blame] | 1 | From 67ce530e39e0372f11006c5695f682e71152f7b7 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 4/9] 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 | diff --git a/include/net/mac80211.h b/include/net/mac80211.h |
| 29 | index 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; |
| 65 | diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c |
| 66 | index 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 | |
| 78 | diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c |
| 79 | index 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; |
| 91 | diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h |
| 92 | index 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, |
| 104 | diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c |
| 105 | index 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 | -- |
| 399 | 2.39.2 |
| 400 | |