1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * BSS client mode implementation |
4 | * Copyright 2003-2008, Jouni Malinen <j@w1.fi> |
5 | * Copyright 2004, Instant802 Networks, Inc. |
6 | * Copyright 2005, Devicescape Software, Inc. |
7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
9 | * Copyright 2013-2014 Intel Mobile Communications GmbH |
10 | * Copyright (C) 2015 - 2017 Intel Deutschland GmbH |
11 | * Copyright (C) 2018 - 2023 Intel Corporation |
12 | */ |
13 | |
14 | #include <linux/delay.h> |
15 | #include <linux/fips.h> |
16 | #include <linux/if_ether.h> |
17 | #include <linux/skbuff.h> |
18 | #include <linux/if_arp.h> |
19 | #include <linux/etherdevice.h> |
20 | #include <linux/moduleparam.h> |
21 | #include <linux/rtnetlink.h> |
22 | #include <linux/crc32.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/export.h> |
25 | #include <net/mac80211.h> |
26 | #include <asm/unaligned.h> |
27 | |
28 | #include "ieee80211_i.h" |
29 | #include "driver-ops.h" |
30 | #include "rate.h" |
31 | #include "led.h" |
32 | #include "fils_aead.h" |
33 | |
34 | #define IEEE80211_AUTH_TIMEOUT (HZ / 5) |
35 | #define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) |
36 | #define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10) |
37 | #define IEEE80211_AUTH_TIMEOUT_SAE (HZ * 2) |
38 | #define IEEE80211_AUTH_MAX_TRIES 3 |
39 | #define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) |
40 | #define IEEE80211_AUTH_WAIT_SAE_RETRY (HZ * 2) |
41 | #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) |
42 | #define IEEE80211_ASSOC_TIMEOUT_LONG (HZ / 2) |
43 | #define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10) |
44 | #define IEEE80211_ASSOC_MAX_TRIES 3 |
45 | |
46 | static int max_nullfunc_tries = 2; |
47 | module_param(max_nullfunc_tries, int, 0644); |
48 | MODULE_PARM_DESC(max_nullfunc_tries, |
49 | "Maximum nullfunc tx tries before disconnecting (reason 4)." ); |
50 | |
51 | static int max_probe_tries = 5; |
52 | module_param(max_probe_tries, int, 0644); |
53 | MODULE_PARM_DESC(max_probe_tries, |
54 | "Maximum probe tries before disconnecting (reason 4)." ); |
55 | |
56 | /* |
57 | * Beacon loss timeout is calculated as N frames times the |
58 | * advertised beacon interval. This may need to be somewhat |
59 | * higher than what hardware might detect to account for |
60 | * delays in the host processing frames. But since we also |
61 | * probe on beacon miss before declaring the connection lost |
62 | * default to what we want. |
63 | */ |
64 | static int beacon_loss_count = 7; |
65 | module_param(beacon_loss_count, int, 0644); |
66 | MODULE_PARM_DESC(beacon_loss_count, |
67 | "Number of beacon intervals before we decide beacon was lost." ); |
68 | |
69 | /* |
70 | * Time the connection can be idle before we probe |
71 | * it to see if we can still talk to the AP. |
72 | */ |
73 | #define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ) |
74 | /* |
75 | * Time we wait for a probe response after sending |
76 | * a probe request because of beacon loss or for |
77 | * checking the connection still works. |
78 | */ |
79 | static int probe_wait_ms = 500; |
80 | module_param(probe_wait_ms, int, 0644); |
81 | MODULE_PARM_DESC(probe_wait_ms, |
82 | "Maximum time(ms) to wait for probe response" |
83 | " before disconnecting (reason 4)." ); |
84 | |
85 | /* |
86 | * How many Beacon frames need to have been used in average signal strength |
87 | * before starting to indicate signal change events. |
88 | */ |
89 | #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4 |
90 | |
91 | /* |
92 | * Extract from the given disabled subchannel bitmap (raw format |
93 | * from the EHT Operation Element) the bits for the subchannel |
94 | * we're using right now. |
95 | */ |
96 | static u16 |
97 | (const struct ieee80211_eht_operation *eht_oper, |
98 | struct cfg80211_chan_def *chandef, u16 bitmap) |
99 | { |
100 | struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional; |
101 | struct cfg80211_chan_def ap_chandef = *chandef; |
102 | u32 ap_center_freq, local_center_freq; |
103 | u32 ap_bw, local_bw; |
104 | int ap_start_freq, local_start_freq; |
105 | u16 shift, mask; |
106 | |
107 | if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) || |
108 | !(eht_oper->params & |
109 | IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) |
110 | return 0; |
111 | |
112 | /* set 160/320 supported to get the full AP definition */ |
113 | ieee80211_chandef_eht_oper(info: (const void *)eht_oper->optional, |
114 | support_160: true, support_320: true, chandef: &ap_chandef); |
115 | ap_center_freq = ap_chandef.center_freq1; |
116 | ap_bw = 20 * BIT(u8_get_bits(info->control, |
117 | IEEE80211_EHT_OPER_CHAN_WIDTH)); |
118 | ap_start_freq = ap_center_freq - ap_bw / 2; |
119 | local_center_freq = chandef->center_freq1; |
120 | local_bw = 20 * BIT(ieee80211_chan_width_to_rx_bw(chandef->width)); |
121 | local_start_freq = local_center_freq - local_bw / 2; |
122 | shift = (local_start_freq - ap_start_freq) / 20; |
123 | mask = BIT(local_bw / 20) - 1; |
124 | |
125 | return (bitmap >> shift) & mask; |
126 | } |
127 | |
128 | /* |
129 | * Handle the puncturing bitmap, possibly downgrading bandwidth to get a |
130 | * valid bitmap. |
131 | */ |
132 | static void |
133 | ieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link, |
134 | const struct ieee80211_eht_operation *eht_oper, |
135 | u16 bitmap, u64 *changed) |
136 | { |
137 | struct cfg80211_chan_def *chandef = &link->conf->chandef; |
138 | u16 ; |
139 | u64 _changed = 0; |
140 | |
141 | if (!changed) |
142 | changed = &_changed; |
143 | |
144 | while (chandef->width > NL80211_CHAN_WIDTH_40) { |
145 | extracted = |
146 | ieee80211_extract_dis_subch_bmap(eht_oper, chandef, |
147 | bitmap); |
148 | |
149 | if (cfg80211_valid_disable_subchannel_bitmap(bitmap: &bitmap, |
150 | chandef)) |
151 | break; |
152 | link->u.mgd.conn_flags |= |
153 | ieee80211_chandef_downgrade(c: chandef); |
154 | *changed |= BSS_CHANGED_BANDWIDTH; |
155 | } |
156 | |
157 | if (chandef->width <= NL80211_CHAN_WIDTH_40) |
158 | extracted = 0; |
159 | |
160 | if (link->conf->eht_puncturing != extracted) { |
161 | link->conf->eht_puncturing = extracted; |
162 | *changed |= BSS_CHANGED_EHT_PUNCTURING; |
163 | } |
164 | } |
165 | |
166 | /* |
167 | * We can have multiple work items (and connection probing) |
168 | * scheduling this timer, but we need to take care to only |
169 | * reschedule it when it should fire _earlier_ than it was |
170 | * asked for before, or if it's not pending right now. This |
171 | * function ensures that. Note that it then is required to |
172 | * run this function for all timeouts after the first one |
173 | * has happened -- the work that runs from this timer will |
174 | * do that. |
175 | */ |
176 | static void run_again(struct ieee80211_sub_if_data *sdata, |
177 | unsigned long timeout) |
178 | { |
179 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
180 | |
181 | if (!timer_pending(timer: &sdata->u.mgd.timer) || |
182 | time_before(timeout, sdata->u.mgd.timer.expires)) |
183 | mod_timer(timer: &sdata->u.mgd.timer, expires: timeout); |
184 | } |
185 | |
186 | void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) |
187 | { |
188 | if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) |
189 | return; |
190 | |
191 | if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) |
192 | return; |
193 | |
194 | mod_timer(timer: &sdata->u.mgd.bcn_mon_timer, |
195 | expires: round_jiffies_up(j: jiffies + sdata->u.mgd.beacon_timeout)); |
196 | } |
197 | |
198 | void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata) |
199 | { |
200 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
201 | |
202 | if (unlikely(!ifmgd->associated)) |
203 | return; |
204 | |
205 | if (ifmgd->probe_send_count) |
206 | ifmgd->probe_send_count = 0; |
207 | |
208 | if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) |
209 | return; |
210 | |
211 | mod_timer(timer: &ifmgd->conn_mon_timer, |
212 | expires: round_jiffies_up(j: jiffies + IEEE80211_CONNECTION_IDLE_TIME)); |
213 | } |
214 | |
215 | static int ecw2cw(int ecw) |
216 | { |
217 | return (1 << ecw) - 1; |
218 | } |
219 | |
220 | static ieee80211_conn_flags_t |
221 | ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, |
222 | struct ieee80211_link_data *link, |
223 | ieee80211_conn_flags_t conn_flags, |
224 | struct ieee80211_supported_band *sband, |
225 | struct ieee80211_channel *channel, |
226 | u32 vht_cap_info, |
227 | const struct ieee80211_ht_operation *ht_oper, |
228 | const struct ieee80211_vht_operation *vht_oper, |
229 | const struct ieee80211_he_operation *he_oper, |
230 | const struct ieee80211_eht_operation *eht_oper, |
231 | const struct ieee80211_s1g_oper_ie *s1g_oper, |
232 | struct cfg80211_chan_def *chandef, bool tracking) |
233 | { |
234 | struct cfg80211_chan_def vht_chandef; |
235 | struct ieee80211_sta_ht_cap sta_ht_cap; |
236 | ieee80211_conn_flags_t ret; |
237 | u32 ht_cfreq; |
238 | |
239 | memset(chandef, 0, sizeof(struct cfg80211_chan_def)); |
240 | chandef->chan = channel; |
241 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; |
242 | chandef->center_freq1 = channel->center_freq; |
243 | chandef->freq1_offset = channel->freq_offset; |
244 | |
245 | if (channel->band == NL80211_BAND_6GHZ) { |
246 | if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, eht_oper, |
247 | chandef)) { |
248 | mlme_dbg(sdata, |
249 | "bad 6 GHz operation, disabling HT/VHT/HE/EHT\n" ); |
250 | ret = IEEE80211_CONN_DISABLE_HT | |
251 | IEEE80211_CONN_DISABLE_VHT | |
252 | IEEE80211_CONN_DISABLE_HE | |
253 | IEEE80211_CONN_DISABLE_EHT; |
254 | } else { |
255 | ret = 0; |
256 | } |
257 | vht_chandef = *chandef; |
258 | goto out; |
259 | } else if (sband->band == NL80211_BAND_S1GHZ) { |
260 | if (!ieee80211_chandef_s1g_oper(oper: s1g_oper, chandef)) { |
261 | sdata_info(sdata, |
262 | "Missing S1G Operation Element? Trying operating == primary\n" ); |
263 | chandef->width = ieee80211_s1g_channel_width(chan: channel); |
264 | } |
265 | |
266 | ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_40MHZ | |
267 | IEEE80211_CONN_DISABLE_VHT | |
268 | IEEE80211_CONN_DISABLE_80P80MHZ | |
269 | IEEE80211_CONN_DISABLE_160MHZ; |
270 | goto out; |
271 | } |
272 | |
273 | memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); |
274 | ieee80211_apply_htcap_overrides(sdata, ht_cap: &sta_ht_cap); |
275 | |
276 | if (!ht_oper || !sta_ht_cap.ht_supported) { |
277 | mlme_dbg(sdata, "HT operation missing / HT not supported\n" ); |
278 | ret = IEEE80211_CONN_DISABLE_HT | |
279 | IEEE80211_CONN_DISABLE_VHT | |
280 | IEEE80211_CONN_DISABLE_HE | |
281 | IEEE80211_CONN_DISABLE_EHT; |
282 | goto out; |
283 | } |
284 | |
285 | chandef->width = NL80211_CHAN_WIDTH_20; |
286 | |
287 | ht_cfreq = ieee80211_channel_to_frequency(chan: ht_oper->primary_chan, |
288 | band: channel->band); |
289 | /* check that channel matches the right operating channel */ |
290 | if (!tracking && channel->center_freq != ht_cfreq) { |
291 | /* |
292 | * It's possible that some APs are confused here; |
293 | * Netgear WNDR3700 sometimes reports 4 higher than |
294 | * the actual channel in association responses, but |
295 | * since we look at probe response/beacon data here |
296 | * it should be OK. |
297 | */ |
298 | sdata_info(sdata, |
299 | "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n" , |
300 | channel->center_freq, ht_cfreq, |
301 | ht_oper->primary_chan, channel->band); |
302 | ret = IEEE80211_CONN_DISABLE_HT | |
303 | IEEE80211_CONN_DISABLE_VHT | |
304 | IEEE80211_CONN_DISABLE_HE | |
305 | IEEE80211_CONN_DISABLE_EHT; |
306 | goto out; |
307 | } |
308 | |
309 | /* check 40 MHz support, if we have it */ |
310 | if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { |
311 | ieee80211_chandef_ht_oper(ht_oper, chandef); |
312 | } else { |
313 | mlme_dbg(sdata, "40 MHz not supported\n" ); |
314 | /* 40 MHz (and 80 MHz) must be supported for VHT */ |
315 | ret = IEEE80211_CONN_DISABLE_VHT; |
316 | /* also mark 40 MHz disabled */ |
317 | ret |= IEEE80211_CONN_DISABLE_40MHZ; |
318 | goto out; |
319 | } |
320 | |
321 | if (!vht_oper || !sband->vht_cap.vht_supported) { |
322 | mlme_dbg(sdata, "VHT operation missing / VHT not supported\n" ); |
323 | ret = IEEE80211_CONN_DISABLE_VHT; |
324 | goto out; |
325 | } |
326 | |
327 | vht_chandef = *chandef; |
328 | if (!(conn_flags & IEEE80211_CONN_DISABLE_HE) && |
329 | he_oper && |
330 | (le32_to_cpu(he_oper->he_oper_params) & |
331 | IEEE80211_HE_OPERATION_VHT_OPER_INFO)) { |
332 | struct ieee80211_vht_operation he_oper_vht_cap; |
333 | |
334 | /* |
335 | * Set only first 3 bytes (other 2 aren't used in |
336 | * ieee80211_chandef_vht_oper() anyway) |
337 | */ |
338 | memcpy(&he_oper_vht_cap, he_oper->optional, 3); |
339 | he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0); |
340 | |
341 | if (!ieee80211_chandef_vht_oper(hw: &sdata->local->hw, vht_cap_info, |
342 | oper: &he_oper_vht_cap, htop: ht_oper, |
343 | chandef: &vht_chandef)) { |
344 | if (!(conn_flags & IEEE80211_CONN_DISABLE_HE)) |
345 | sdata_info(sdata, |
346 | "HE AP VHT information is invalid, disabling HE\n" ); |
347 | ret = IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT; |
348 | goto out; |
349 | } |
350 | } else if (!ieee80211_chandef_vht_oper(hw: &sdata->local->hw, |
351 | vht_cap_info, |
352 | oper: vht_oper, htop: ht_oper, |
353 | chandef: &vht_chandef)) { |
354 | if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) |
355 | sdata_info(sdata, |
356 | "AP VHT information is invalid, disabling VHT\n" ); |
357 | ret = IEEE80211_CONN_DISABLE_VHT; |
358 | goto out; |
359 | } |
360 | |
361 | if (!cfg80211_chandef_valid(chandef: &vht_chandef)) { |
362 | if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) |
363 | sdata_info(sdata, |
364 | "AP VHT information is invalid, disabling VHT\n" ); |
365 | ret = IEEE80211_CONN_DISABLE_VHT; |
366 | goto out; |
367 | } |
368 | |
369 | if (cfg80211_chandef_identical(chandef1: chandef, chandef2: &vht_chandef)) { |
370 | ret = 0; |
371 | goto out; |
372 | } |
373 | |
374 | if (!cfg80211_chandef_compatible(chandef1: chandef, chandef2: &vht_chandef)) { |
375 | if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) |
376 | sdata_info(sdata, |
377 | "AP VHT information doesn't match HT, disabling VHT\n" ); |
378 | ret = IEEE80211_CONN_DISABLE_VHT; |
379 | goto out; |
380 | } |
381 | |
382 | *chandef = vht_chandef; |
383 | |
384 | /* |
385 | * handle the case that the EHT operation indicates that it holds EHT |
386 | * operation information (in case that the channel width differs from |
387 | * the channel width reported in HT/VHT/HE). |
388 | */ |
389 | if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { |
390 | struct cfg80211_chan_def eht_chandef = *chandef; |
391 | |
392 | ieee80211_chandef_eht_oper(info: (const void *)eht_oper->optional, |
393 | support_160: eht_chandef.width == |
394 | NL80211_CHAN_WIDTH_160, |
395 | support_320: false, chandef: &eht_chandef); |
396 | |
397 | if (!cfg80211_chandef_valid(chandef: &eht_chandef)) { |
398 | if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT)) |
399 | sdata_info(sdata, |
400 | "AP EHT information is invalid, disabling EHT\n" ); |
401 | ret = IEEE80211_CONN_DISABLE_EHT; |
402 | goto out; |
403 | } |
404 | |
405 | if (!cfg80211_chandef_compatible(chandef1: chandef, chandef2: &eht_chandef)) { |
406 | if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT)) |
407 | sdata_info(sdata, |
408 | "AP EHT information is incompatible, disabling EHT\n" ); |
409 | ret = IEEE80211_CONN_DISABLE_EHT; |
410 | goto out; |
411 | } |
412 | |
413 | *chandef = eht_chandef; |
414 | } |
415 | |
416 | ret = 0; |
417 | |
418 | out: |
419 | /* |
420 | * When tracking the current AP, don't do any further checks if the |
421 | * new chandef is identical to the one we're currently using for the |
422 | * connection. This keeps us from playing ping-pong with regulatory, |
423 | * without it the following can happen (for example): |
424 | * - connect to an AP with 80 MHz, world regdom allows 80 MHz |
425 | * - AP advertises regdom US |
426 | * - CRDA loads regdom US with 80 MHz prohibited (old database) |
427 | * - the code below detects an unsupported channel, downgrades, and |
428 | * we disconnect from the AP in the caller |
429 | * - disconnect causes CRDA to reload world regdomain and the game |
430 | * starts anew. |
431 | * (see https://bugzilla.kernel.org/show_bug.cgi?id=70881) |
432 | * |
433 | * It seems possible that there are still scenarios with CSA or real |
434 | * bandwidth changes where a this could happen, but those cases are |
435 | * less common and wouldn't completely prevent using the AP. |
436 | */ |
437 | if (tracking && |
438 | cfg80211_chandef_identical(chandef1: chandef, chandef2: &link->conf->chandef)) |
439 | return ret; |
440 | |
441 | /* don't print the message below for VHT mismatch if VHT is disabled */ |
442 | if (ret & IEEE80211_CONN_DISABLE_VHT) |
443 | vht_chandef = *chandef; |
444 | |
445 | /* |
446 | * Ignore the DISABLED flag when we're already connected and only |
447 | * tracking the APs beacon for bandwidth changes - otherwise we |
448 | * might get disconnected here if we connect to an AP, update our |
449 | * regulatory information based on the AP's country IE and the |
450 | * information we have is wrong/outdated and disables the channel |
451 | * that we're actually using for the connection to the AP. |
452 | */ |
453 | while (!cfg80211_chandef_usable(wiphy: sdata->local->hw.wiphy, chandef, |
454 | prohibited_flags: tracking ? 0 : |
455 | IEEE80211_CHAN_DISABLED)) { |
456 | if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { |
457 | ret = IEEE80211_CONN_DISABLE_HT | |
458 | IEEE80211_CONN_DISABLE_VHT | |
459 | IEEE80211_CONN_DISABLE_HE | |
460 | IEEE80211_CONN_DISABLE_EHT; |
461 | break; |
462 | } |
463 | |
464 | ret |= ieee80211_chandef_downgrade(c: chandef); |
465 | } |
466 | |
467 | if (!he_oper || !cfg80211_chandef_usable(wiphy: sdata->wdev.wiphy, chandef, |
468 | prohibited_flags: IEEE80211_CHAN_NO_HE)) |
469 | ret |= IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT; |
470 | |
471 | if (!eht_oper || !cfg80211_chandef_usable(wiphy: sdata->wdev.wiphy, chandef, |
472 | prohibited_flags: IEEE80211_CHAN_NO_EHT)) |
473 | ret |= IEEE80211_CONN_DISABLE_EHT; |
474 | |
475 | if (chandef->width != vht_chandef.width && !tracking) |
476 | sdata_info(sdata, |
477 | "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n" ); |
478 | |
479 | WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); |
480 | return ret; |
481 | } |
482 | |
483 | static int ieee80211_config_bw(struct ieee80211_link_data *link, |
484 | const struct ieee80211_ht_cap *ht_cap, |
485 | const struct ieee80211_vht_cap *vht_cap, |
486 | const struct ieee80211_ht_operation *ht_oper, |
487 | const struct ieee80211_vht_operation *vht_oper, |
488 | const struct ieee80211_he_operation *he_oper, |
489 | const struct ieee80211_eht_operation *eht_oper, |
490 | const struct ieee80211_s1g_oper_ie *s1g_oper, |
491 | const u8 *bssid, u64 *changed) |
492 | { |
493 | struct ieee80211_sub_if_data *sdata = link->sdata; |
494 | struct ieee80211_local *local = sdata->local; |
495 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
496 | struct ieee80211_channel *chan = link->conf->chandef.chan; |
497 | struct ieee80211_supported_band *sband = |
498 | local->hw.wiphy->bands[chan->band]; |
499 | struct cfg80211_chan_def chandef; |
500 | u16 ht_opmode; |
501 | ieee80211_conn_flags_t flags; |
502 | u32 vht_cap_info = 0; |
503 | int ret; |
504 | |
505 | /* if HT was/is disabled, don't track any bandwidth changes */ |
506 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT || !ht_oper) |
507 | return 0; |
508 | |
509 | /* don't check VHT if we associated as non-VHT station */ |
510 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) |
511 | vht_oper = NULL; |
512 | |
513 | /* don't check HE if we associated as non-HE station */ |
514 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE || |
515 | !ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif)) { |
516 | he_oper = NULL; |
517 | eht_oper = NULL; |
518 | } |
519 | |
520 | /* don't check EHT if we associated as non-EHT station */ |
521 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT || |
522 | !ieee80211_get_eht_iftype_cap_vif(sband, vif: &sdata->vif)) |
523 | eht_oper = NULL; |
524 | |
525 | /* |
526 | * if bss configuration changed store the new one - |
527 | * this may be applicable even if channel is identical |
528 | */ |
529 | ht_opmode = le16_to_cpu(ht_oper->operation_mode); |
530 | if (link->conf->ht_operation_mode != ht_opmode) { |
531 | *changed |= BSS_CHANGED_HT; |
532 | link->conf->ht_operation_mode = ht_opmode; |
533 | } |
534 | |
535 | if (vht_cap) |
536 | vht_cap_info = le32_to_cpu(vht_cap->vht_cap_info); |
537 | |
538 | /* calculate new channel (type) based on HT/VHT/HE operation IEs */ |
539 | flags = ieee80211_determine_chantype(sdata, link, |
540 | conn_flags: link->u.mgd.conn_flags, |
541 | sband, channel: chan, vht_cap_info, |
542 | ht_oper, vht_oper, |
543 | he_oper, eht_oper, |
544 | s1g_oper, chandef: &chandef, tracking: true); |
545 | |
546 | /* |
547 | * Downgrade the new channel if we associated with restricted |
548 | * capabilities. For example, if we associated as a 20 MHz STA |
549 | * to a 40 MHz AP (due to regulatory, capabilities or config |
550 | * reasons) then switching to a 40 MHz channel now won't do us |
551 | * any good -- we couldn't use it with the AP. |
552 | */ |
553 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ && |
554 | chandef.width == NL80211_CHAN_WIDTH_80P80) |
555 | flags |= ieee80211_chandef_downgrade(c: &chandef); |
556 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_160MHZ && |
557 | chandef.width == NL80211_CHAN_WIDTH_160) |
558 | flags |= ieee80211_chandef_downgrade(c: &chandef); |
559 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_40MHZ && |
560 | chandef.width > NL80211_CHAN_WIDTH_20) |
561 | flags |= ieee80211_chandef_downgrade(c: &chandef); |
562 | |
563 | if (cfg80211_chandef_identical(chandef1: &chandef, chandef2: &link->conf->chandef)) |
564 | return 0; |
565 | |
566 | link_info(link, |
567 | "AP %pM changed bandwidth, new config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n" , |
568 | link->u.mgd.bssid, chandef.chan->center_freq, |
569 | chandef.chan->freq_offset, chandef.width, |
570 | chandef.center_freq1, chandef.freq1_offset, |
571 | chandef.center_freq2); |
572 | |
573 | if (flags != (link->u.mgd.conn_flags & |
574 | (IEEE80211_CONN_DISABLE_HT | |
575 | IEEE80211_CONN_DISABLE_VHT | |
576 | IEEE80211_CONN_DISABLE_HE | |
577 | IEEE80211_CONN_DISABLE_EHT | |
578 | IEEE80211_CONN_DISABLE_40MHZ | |
579 | IEEE80211_CONN_DISABLE_80P80MHZ | |
580 | IEEE80211_CONN_DISABLE_160MHZ | |
581 | IEEE80211_CONN_DISABLE_320MHZ)) || |
582 | !cfg80211_chandef_valid(chandef: &chandef)) { |
583 | sdata_info(sdata, |
584 | "AP %pM changed caps/bw in a way we can't support (0x%x/0x%x) - disconnect\n" , |
585 | link->u.mgd.bssid, flags, ifmgd->flags); |
586 | return -EINVAL; |
587 | } |
588 | |
589 | ret = ieee80211_link_change_bandwidth(link, chandef: &chandef, changed); |
590 | |
591 | if (ret) { |
592 | sdata_info(sdata, |
593 | "AP %pM changed bandwidth to incompatible one - disconnect\n" , |
594 | link->u.mgd.bssid); |
595 | return ret; |
596 | } |
597 | |
598 | return 0; |
599 | } |
600 | |
601 | /* frame sending functions */ |
602 | |
603 | static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, |
604 | struct sk_buff *skb, u8 ap_ht_param, |
605 | struct ieee80211_supported_band *sband, |
606 | struct ieee80211_channel *channel, |
607 | enum ieee80211_smps_mode smps, |
608 | ieee80211_conn_flags_t conn_flags) |
609 | { |
610 | u8 *pos; |
611 | u32 flags = channel->flags; |
612 | u16 cap; |
613 | struct ieee80211_sta_ht_cap ht_cap; |
614 | |
615 | BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap)); |
616 | |
617 | memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); |
618 | ieee80211_apply_htcap_overrides(sdata, ht_cap: &ht_cap); |
619 | |
620 | /* determine capability flags */ |
621 | cap = ht_cap.cap; |
622 | |
623 | switch (ap_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { |
624 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: |
625 | if (flags & IEEE80211_CHAN_NO_HT40PLUS) { |
626 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; |
627 | cap &= ~IEEE80211_HT_CAP_SGI_40; |
628 | } |
629 | break; |
630 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: |
631 | if (flags & IEEE80211_CHAN_NO_HT40MINUS) { |
632 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; |
633 | cap &= ~IEEE80211_HT_CAP_SGI_40; |
634 | } |
635 | break; |
636 | } |
637 | |
638 | /* |
639 | * If 40 MHz was disabled associate as though we weren't |
640 | * capable of 40 MHz -- some broken APs will never fall |
641 | * back to trying to transmit in 20 MHz. |
642 | */ |
643 | if (conn_flags & IEEE80211_CONN_DISABLE_40MHZ) { |
644 | cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; |
645 | cap &= ~IEEE80211_HT_CAP_SGI_40; |
646 | } |
647 | |
648 | /* set SM PS mode properly */ |
649 | cap &= ~IEEE80211_HT_CAP_SM_PS; |
650 | switch (smps) { |
651 | case IEEE80211_SMPS_AUTOMATIC: |
652 | case IEEE80211_SMPS_NUM_MODES: |
653 | WARN_ON(1); |
654 | fallthrough; |
655 | case IEEE80211_SMPS_OFF: |
656 | cap |= WLAN_HT_CAP_SM_PS_DISABLED << |
657 | IEEE80211_HT_CAP_SM_PS_SHIFT; |
658 | break; |
659 | case IEEE80211_SMPS_STATIC: |
660 | cap |= WLAN_HT_CAP_SM_PS_STATIC << |
661 | IEEE80211_HT_CAP_SM_PS_SHIFT; |
662 | break; |
663 | case IEEE80211_SMPS_DYNAMIC: |
664 | cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << |
665 | IEEE80211_HT_CAP_SM_PS_SHIFT; |
666 | break; |
667 | } |
668 | |
669 | /* reserve and fill IE */ |
670 | pos = skb_put(skb, len: sizeof(struct ieee80211_ht_cap) + 2); |
671 | ieee80211_ie_build_ht_cap(pos, ht_cap: &ht_cap, cap); |
672 | } |
673 | |
674 | /* This function determines vht capability flags for the association |
675 | * and builds the IE. |
676 | * Note - the function returns true to own the MU-MIMO capability |
677 | */ |
678 | static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, |
679 | struct sk_buff *skb, |
680 | struct ieee80211_supported_band *sband, |
681 | struct ieee80211_vht_cap *ap_vht_cap, |
682 | ieee80211_conn_flags_t conn_flags) |
683 | { |
684 | struct ieee80211_local *local = sdata->local; |
685 | u8 *pos; |
686 | u32 cap; |
687 | struct ieee80211_sta_vht_cap vht_cap; |
688 | u32 mask, ap_bf_sts, our_bf_sts; |
689 | bool mu_mimo_owner = false; |
690 | |
691 | BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); |
692 | |
693 | memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); |
694 | ieee80211_apply_vhtcap_overrides(sdata, vht_cap: &vht_cap); |
695 | |
696 | /* determine capability flags */ |
697 | cap = vht_cap.cap; |
698 | |
699 | if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ) { |
700 | u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; |
701 | |
702 | cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; |
703 | if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || |
704 | bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) |
705 | cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; |
706 | } |
707 | |
708 | if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ) { |
709 | cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; |
710 | cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; |
711 | } |
712 | |
713 | /* |
714 | * Some APs apparently get confused if our capabilities are better |
715 | * than theirs, so restrict what we advertise in the assoc request. |
716 | */ |
717 | if (!(ap_vht_cap->vht_cap_info & |
718 | cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) |
719 | cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | |
720 | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); |
721 | else if (!(ap_vht_cap->vht_cap_info & |
722 | cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE))) |
723 | cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; |
724 | |
725 | /* |
726 | * If some other vif is using the MU-MIMO capability we cannot associate |
727 | * using MU-MIMO - this will lead to contradictions in the group-id |
728 | * mechanism. |
729 | * Ownership is defined since association request, in order to avoid |
730 | * simultaneous associations with MU-MIMO. |
731 | */ |
732 | if (cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) { |
733 | bool disable_mu_mimo = false; |
734 | struct ieee80211_sub_if_data *other; |
735 | |
736 | list_for_each_entry_rcu(other, &local->interfaces, list) { |
737 | if (other->vif.bss_conf.mu_mimo_owner) { |
738 | disable_mu_mimo = true; |
739 | break; |
740 | } |
741 | } |
742 | if (disable_mu_mimo) |
743 | cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; |
744 | else |
745 | mu_mimo_owner = true; |
746 | } |
747 | |
748 | mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; |
749 | |
750 | ap_bf_sts = le32_to_cpu(ap_vht_cap->vht_cap_info) & mask; |
751 | our_bf_sts = cap & mask; |
752 | |
753 | if (ap_bf_sts < our_bf_sts) { |
754 | cap &= ~mask; |
755 | cap |= ap_bf_sts; |
756 | } |
757 | |
758 | /* reserve and fill IE */ |
759 | pos = skb_put(skb, len: sizeof(struct ieee80211_vht_cap) + 2); |
760 | ieee80211_ie_build_vht_cap(pos, vht_cap: &vht_cap, cap); |
761 | |
762 | return mu_mimo_owner; |
763 | } |
764 | |
765 | /* This function determines HE capability flags for the association |
766 | * and builds the IE. |
767 | */ |
768 | static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, |
769 | struct sk_buff *skb, |
770 | struct ieee80211_supported_band *sband, |
771 | enum ieee80211_smps_mode smps_mode, |
772 | ieee80211_conn_flags_t conn_flags) |
773 | { |
774 | u8 *pos, *pre_he_pos; |
775 | const struct ieee80211_sta_he_cap *he_cap; |
776 | u8 he_cap_size; |
777 | |
778 | he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif); |
779 | if (WARN_ON(!he_cap)) |
780 | return; |
781 | |
782 | /* get a max size estimate */ |
783 | he_cap_size = |
784 | 2 + 1 + sizeof(he_cap->he_cap_elem) + |
785 | ieee80211_he_mcs_nss_size(he_cap: &he_cap->he_cap_elem) + |
786 | ieee80211_he_ppe_size(ppe_thres_hdr: he_cap->ppe_thres[0], |
787 | phy_cap_info: he_cap->he_cap_elem.phy_cap_info); |
788 | pos = skb_put(skb, len: he_cap_size); |
789 | pre_he_pos = pos; |
790 | pos = ieee80211_ie_build_he_cap(disable_flags: conn_flags, |
791 | pos, he_cap, end: pos + he_cap_size); |
792 | /* trim excess if any */ |
793 | skb_trim(skb, len: skb->len - (pre_he_pos + he_cap_size - pos)); |
794 | |
795 | ieee80211_ie_build_he_6ghz_cap(sdata, smps_mode, skb); |
796 | } |
797 | |
798 | static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, |
799 | struct sk_buff *skb, |
800 | struct ieee80211_supported_band *sband) |
801 | { |
802 | u8 *pos; |
803 | const struct ieee80211_sta_he_cap *he_cap; |
804 | const struct ieee80211_sta_eht_cap *eht_cap; |
805 | u8 eht_cap_size; |
806 | |
807 | he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif); |
808 | eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, vif: &sdata->vif); |
809 | |
810 | /* |
811 | * EHT capabilities element is only added if the HE capabilities element |
812 | * was added so assume that 'he_cap' is valid and don't check it. |
813 | */ |
814 | if (WARN_ON(!he_cap || !eht_cap)) |
815 | return; |
816 | |
817 | eht_cap_size = |
818 | 2 + 1 + sizeof(eht_cap->eht_cap_elem) + |
819 | ieee80211_eht_mcs_nss_size(he_cap: &he_cap->he_cap_elem, |
820 | eht_cap: &eht_cap->eht_cap_elem, |
821 | from_ap: false) + |
822 | ieee80211_eht_ppe_size(ppe_thres_hdr: eht_cap->eht_ppe_thres[0], |
823 | phy_cap_info: eht_cap->eht_cap_elem.phy_cap_info); |
824 | pos = skb_put(skb, len: eht_cap_size); |
825 | ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, end: pos + eht_cap_size, |
826 | for_ap: false); |
827 | } |
828 | |
829 | static void ieee80211_assoc_add_rates(struct sk_buff *skb, |
830 | enum nl80211_chan_width width, |
831 | struct ieee80211_supported_band *sband, |
832 | struct ieee80211_mgd_assoc_data *assoc_data) |
833 | { |
834 | unsigned int rates_len, supp_rates_len; |
835 | u32 rates = 0; |
836 | int i, count; |
837 | u8 *pos; |
838 | |
839 | if (assoc_data->supp_rates_len) { |
840 | /* |
841 | * Get all rates supported by the device and the AP as |
842 | * some APs don't like getting a superset of their rates |
843 | * in the association request (e.g. D-Link DAP 1353 in |
844 | * b-only mode)... |
845 | */ |
846 | rates_len = ieee80211_parse_bitrates(width, sband, |
847 | srates: assoc_data->supp_rates, |
848 | srates_len: assoc_data->supp_rates_len, |
849 | rates: &rates); |
850 | } else { |
851 | /* |
852 | * In case AP not provide any supported rates information |
853 | * before association, we send information element(s) with |
854 | * all rates that we support. |
855 | */ |
856 | rates_len = sband->n_bitrates; |
857 | for (i = 0; i < sband->n_bitrates; i++) |
858 | rates |= BIT(i); |
859 | } |
860 | |
861 | supp_rates_len = rates_len; |
862 | if (supp_rates_len > 8) |
863 | supp_rates_len = 8; |
864 | |
865 | pos = skb_put(skb, len: supp_rates_len + 2); |
866 | *pos++ = WLAN_EID_SUPP_RATES; |
867 | *pos++ = supp_rates_len; |
868 | |
869 | count = 0; |
870 | for (i = 0; i < sband->n_bitrates; i++) { |
871 | if (BIT(i) & rates) { |
872 | int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); |
873 | *pos++ = (u8)rate; |
874 | if (++count == 8) |
875 | break; |
876 | } |
877 | } |
878 | |
879 | if (rates_len > count) { |
880 | pos = skb_put(skb, len: rates_len - count + 2); |
881 | *pos++ = WLAN_EID_EXT_SUPP_RATES; |
882 | *pos++ = rates_len - count; |
883 | |
884 | for (i++; i < sband->n_bitrates; i++) { |
885 | if (BIT(i) & rates) { |
886 | int rate; |
887 | |
888 | rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); |
889 | *pos++ = (u8)rate; |
890 | } |
891 | } |
892 | } |
893 | } |
894 | |
895 | static size_t ieee80211_add_before_ht_elems(struct sk_buff *skb, |
896 | const u8 *elems, |
897 | size_t elems_len, |
898 | size_t offset) |
899 | { |
900 | size_t noffset; |
901 | |
902 | static const u8 before_ht[] = { |
903 | WLAN_EID_SSID, |
904 | WLAN_EID_SUPP_RATES, |
905 | WLAN_EID_EXT_SUPP_RATES, |
906 | WLAN_EID_PWR_CAPABILITY, |
907 | WLAN_EID_SUPPORTED_CHANNELS, |
908 | WLAN_EID_RSN, |
909 | WLAN_EID_QOS_CAPA, |
910 | WLAN_EID_RRM_ENABLED_CAPABILITIES, |
911 | WLAN_EID_MOBILITY_DOMAIN, |
912 | WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */ |
913 | WLAN_EID_RIC_DATA, /* reassoc only */ |
914 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, |
915 | }; |
916 | static const u8 after_ric[] = { |
917 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, |
918 | WLAN_EID_HT_CAPABILITY, |
919 | WLAN_EID_BSS_COEX_2040, |
920 | /* luckily this is almost always there */ |
921 | WLAN_EID_EXT_CAPABILITY, |
922 | WLAN_EID_QOS_TRAFFIC_CAPA, |
923 | WLAN_EID_TIM_BCAST_REQ, |
924 | WLAN_EID_INTERWORKING, |
925 | /* 60 GHz (Multi-band, DMG, MMS) can't happen */ |
926 | WLAN_EID_VHT_CAPABILITY, |
927 | WLAN_EID_OPMODE_NOTIF, |
928 | }; |
929 | |
930 | if (!elems_len) |
931 | return offset; |
932 | |
933 | noffset = ieee80211_ie_split_ric(ies: elems, ielen: elems_len, |
934 | ids: before_ht, |
935 | ARRAY_SIZE(before_ht), |
936 | after_ric, |
937 | ARRAY_SIZE(after_ric), |
938 | offset); |
939 | skb_put_data(skb, data: elems + offset, len: noffset - offset); |
940 | |
941 | return noffset; |
942 | } |
943 | |
944 | static size_t ieee80211_add_before_vht_elems(struct sk_buff *skb, |
945 | const u8 *elems, |
946 | size_t elems_len, |
947 | size_t offset) |
948 | { |
949 | static const u8 before_vht[] = { |
950 | /* |
951 | * no need to list the ones split off before HT |
952 | * or generated here |
953 | */ |
954 | WLAN_EID_BSS_COEX_2040, |
955 | WLAN_EID_EXT_CAPABILITY, |
956 | WLAN_EID_QOS_TRAFFIC_CAPA, |
957 | WLAN_EID_TIM_BCAST_REQ, |
958 | WLAN_EID_INTERWORKING, |
959 | /* 60 GHz (Multi-band, DMG, MMS) can't happen */ |
960 | }; |
961 | size_t noffset; |
962 | |
963 | if (!elems_len) |
964 | return offset; |
965 | |
966 | /* RIC already taken care of in ieee80211_add_before_ht_elems() */ |
967 | noffset = ieee80211_ie_split(ies: elems, ielen: elems_len, |
968 | ids: before_vht, ARRAY_SIZE(before_vht), |
969 | offset); |
970 | skb_put_data(skb, data: elems + offset, len: noffset - offset); |
971 | |
972 | return noffset; |
973 | } |
974 | |
975 | static size_t ieee80211_add_before_he_elems(struct sk_buff *skb, |
976 | const u8 *elems, |
977 | size_t elems_len, |
978 | size_t offset) |
979 | { |
980 | static const u8 before_he[] = { |
981 | /* |
982 | * no need to list the ones split off before VHT |
983 | * or generated here |
984 | */ |
985 | WLAN_EID_OPMODE_NOTIF, |
986 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE, |
987 | /* 11ai elements */ |
988 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_SESSION, |
989 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_PUBLIC_KEY, |
990 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_KEY_CONFIRM, |
991 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_HLP_CONTAINER, |
992 | WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN, |
993 | /* TODO: add 11ah/11aj/11ak elements */ |
994 | }; |
995 | size_t noffset; |
996 | |
997 | if (!elems_len) |
998 | return offset; |
999 | |
1000 | /* RIC already taken care of in ieee80211_add_before_ht_elems() */ |
1001 | noffset = ieee80211_ie_split(ies: elems, ielen: elems_len, |
1002 | ids: before_he, ARRAY_SIZE(before_he), |
1003 | offset); |
1004 | skb_put_data(skb, data: elems + offset, len: noffset - offset); |
1005 | |
1006 | return noffset; |
1007 | } |
1008 | |
1009 | #define PRESENT_ELEMS_MAX 8 |
1010 | #define PRESENT_ELEM_EXT_OFFS 0x100 |
1011 | |
1012 | static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata, |
1013 | struct sk_buff *skb, u16 capab, |
1014 | const struct element *ext_capa, |
1015 | const u16 *present_elems); |
1016 | |
1017 | static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, |
1018 | struct sk_buff *skb, u16 *capab, |
1019 | const struct element *ext_capa, |
1020 | const u8 *, |
1021 | size_t , |
1022 | unsigned int link_id, |
1023 | struct ieee80211_link_data *link, |
1024 | u16 *present_elems) |
1025 | { |
1026 | enum nl80211_iftype iftype = ieee80211_vif_type_p2p(vif: &sdata->vif); |
1027 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1028 | struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; |
1029 | struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; |
1030 | struct ieee80211_channel *chan = cbss->channel; |
1031 | const struct ieee80211_sband_iftype_data *iftd; |
1032 | struct ieee80211_local *local = sdata->local; |
1033 | struct ieee80211_supported_band *sband; |
1034 | enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20; |
1035 | struct ieee80211_chanctx_conf *chanctx_conf; |
1036 | enum ieee80211_smps_mode smps_mode; |
1037 | u16 orig_capab = *capab; |
1038 | size_t offset = 0; |
1039 | int present_elems_len = 0; |
1040 | u8 *pos; |
1041 | int i; |
1042 | |
1043 | #define ADD_PRESENT_ELEM(id) do { \ |
1044 | /* need a last for termination - we use 0 == SSID */ \ |
1045 | if (!WARN_ON(present_elems_len >= PRESENT_ELEMS_MAX - 1)) \ |
1046 | present_elems[present_elems_len++] = (id); \ |
1047 | } while (0) |
1048 | #define ADD_PRESENT_EXT_ELEM(id) ADD_PRESENT_ELEM(PRESENT_ELEM_EXT_OFFS | (id)) |
1049 | |
1050 | if (link) |
1051 | smps_mode = link->smps_mode; |
1052 | else if (sdata->u.mgd.powersave) |
1053 | smps_mode = IEEE80211_SMPS_DYNAMIC; |
1054 | else |
1055 | smps_mode = IEEE80211_SMPS_OFF; |
1056 | |
1057 | if (link) { |
1058 | /* |
1059 | * 5/10 MHz scenarios are only viable without MLO, in which |
1060 | * case this pointer should be used ... All of this is a bit |
1061 | * unclear though, not sure this even works at all. |
1062 | */ |
1063 | rcu_read_lock(); |
1064 | chanctx_conf = rcu_dereference(link->conf->chanctx_conf); |
1065 | if (chanctx_conf) |
1066 | width = chanctx_conf->def.width; |
1067 | rcu_read_unlock(); |
1068 | } |
1069 | |
1070 | sband = local->hw.wiphy->bands[chan->band]; |
1071 | iftd = ieee80211_get_sband_iftype_data(sband, iftype); |
1072 | |
1073 | if (sband->band == NL80211_BAND_2GHZ) { |
1074 | *capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; |
1075 | *capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; |
1076 | } |
1077 | |
1078 | if ((cbss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && |
1079 | ieee80211_hw_check(&local->hw, SPECTRUM_MGMT)) |
1080 | *capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; |
1081 | |
1082 | if (sband->band != NL80211_BAND_S1GHZ) |
1083 | ieee80211_assoc_add_rates(skb, width, sband, assoc_data); |
1084 | |
1085 | if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT || |
1086 | *capab & WLAN_CAPABILITY_RADIO_MEASURE) { |
1087 | struct cfg80211_chan_def chandef = { |
1088 | .width = width, |
1089 | .chan = chan, |
1090 | }; |
1091 | |
1092 | pos = skb_put(skb, len: 4); |
1093 | *pos++ = WLAN_EID_PWR_CAPABILITY; |
1094 | *pos++ = 2; |
1095 | *pos++ = 0; /* min tx power */ |
1096 | /* max tx power */ |
1097 | *pos++ = ieee80211_chandef_max_power(chandef: &chandef); |
1098 | ADD_PRESENT_ELEM(WLAN_EID_PWR_CAPABILITY); |
1099 | } |
1100 | |
1101 | /* |
1102 | * Per spec, we shouldn't include the list of channels if we advertise |
1103 | * support for extended channel switching, but we've always done that; |
1104 | * (for now?) apply this restriction only on the (new) 6 GHz band. |
1105 | */ |
1106 | if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT && |
1107 | (sband->band != NL80211_BAND_6GHZ || |
1108 | !ext_capa || ext_capa->datalen < 1 || |
1109 | !(ext_capa->data[0] & WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING))) { |
1110 | /* TODO: get this in reg domain format */ |
1111 | pos = skb_put(skb, len: 2 * sband->n_channels + 2); |
1112 | *pos++ = WLAN_EID_SUPPORTED_CHANNELS; |
1113 | *pos++ = 2 * sband->n_channels; |
1114 | for (i = 0; i < sband->n_channels; i++) { |
1115 | int cf = sband->channels[i].center_freq; |
1116 | |
1117 | *pos++ = ieee80211_frequency_to_channel(freq: cf); |
1118 | *pos++ = 1; /* one channel in the subband*/ |
1119 | } |
1120 | ADD_PRESENT_ELEM(WLAN_EID_SUPPORTED_CHANNELS); |
1121 | } |
1122 | |
1123 | /* if present, add any custom IEs that go before HT */ |
1124 | offset = ieee80211_add_before_ht_elems(skb, elems: extra_elems, |
1125 | elems_len: extra_elems_len, |
1126 | offset); |
1127 | |
1128 | if (sband->band != NL80211_BAND_6GHZ && |
1129 | !(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT)) { |
1130 | ieee80211_add_ht_ie(sdata, skb, |
1131 | ap_ht_param: assoc_data->link[link_id].ap_ht_param, |
1132 | sband, channel: chan, smps: smps_mode, |
1133 | conn_flags: assoc_data->link[link_id].conn_flags); |
1134 | ADD_PRESENT_ELEM(WLAN_EID_HT_CAPABILITY); |
1135 | } |
1136 | |
1137 | /* if present, add any custom IEs that go before VHT */ |
1138 | offset = ieee80211_add_before_vht_elems(skb, elems: extra_elems, |
1139 | elems_len: extra_elems_len, |
1140 | offset); |
1141 | |
1142 | if (sband->band != NL80211_BAND_6GHZ && |
1143 | !(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT)) { |
1144 | bool mu_mimo_owner = |
1145 | ieee80211_add_vht_ie(sdata, skb, sband, |
1146 | ap_vht_cap: &assoc_data->link[link_id].ap_vht_cap, |
1147 | conn_flags: assoc_data->link[link_id].conn_flags); |
1148 | |
1149 | if (link) |
1150 | link->conf->mu_mimo_owner = mu_mimo_owner; |
1151 | ADD_PRESENT_ELEM(WLAN_EID_VHT_CAPABILITY); |
1152 | } |
1153 | |
1154 | /* |
1155 | * If AP doesn't support HT, mark HE and EHT as disabled. |
1156 | * If on the 5GHz band, make sure it supports VHT. |
1157 | */ |
1158 | if (assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT || |
1159 | (sband->band == NL80211_BAND_5GHZ && |
1160 | assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT)) |
1161 | assoc_data->link[link_id].conn_flags |= |
1162 | IEEE80211_CONN_DISABLE_HE | |
1163 | IEEE80211_CONN_DISABLE_EHT; |
1164 | |
1165 | /* if present, add any custom IEs that go before HE */ |
1166 | offset = ieee80211_add_before_he_elems(skb, elems: extra_elems, |
1167 | elems_len: extra_elems_len, |
1168 | offset); |
1169 | |
1170 | if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HE)) { |
1171 | ieee80211_add_he_ie(sdata, skb, sband, smps_mode, |
1172 | conn_flags: assoc_data->link[link_id].conn_flags); |
1173 | ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY); |
1174 | } |
1175 | |
1176 | /* |
1177 | * careful - need to know about all the present elems before |
1178 | * calling ieee80211_assoc_add_ml_elem(), so add this one if |
1179 | * we're going to put it after the ML element |
1180 | */ |
1181 | if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT)) |
1182 | ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY); |
1183 | |
1184 | if (link_id == assoc_data->assoc_link_id) |
1185 | ieee80211_assoc_add_ml_elem(sdata, skb, capab: orig_capab, ext_capa, |
1186 | present_elems); |
1187 | |
1188 | /* crash if somebody gets it wrong */ |
1189 | present_elems = NULL; |
1190 | |
1191 | if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT)) |
1192 | ieee80211_add_eht_ie(sdata, skb, sband); |
1193 | |
1194 | if (sband->band == NL80211_BAND_S1GHZ) { |
1195 | ieee80211_add_aid_request_ie(sdata, skb); |
1196 | ieee80211_add_s1g_capab_ie(sdata, caps: &sband->s1g_cap, skb); |
1197 | } |
1198 | |
1199 | if (iftd && iftd->vendor_elems.data && iftd->vendor_elems.len) |
1200 | skb_put_data(skb, data: iftd->vendor_elems.data, len: iftd->vendor_elems.len); |
1201 | |
1202 | if (link) |
1203 | link->u.mgd.conn_flags = assoc_data->link[link_id].conn_flags; |
1204 | |
1205 | return offset; |
1206 | } |
1207 | |
1208 | static void ieee80211_add_non_inheritance_elem(struct sk_buff *skb, |
1209 | const u16 *outer, |
1210 | const u16 *inner) |
1211 | { |
1212 | unsigned int skb_len = skb->len; |
1213 | bool at_extension = false; |
1214 | bool added = false; |
1215 | int i, j; |
1216 | u8 *len, *list_len = NULL; |
1217 | |
1218 | skb_put_u8(skb, val: WLAN_EID_EXTENSION); |
1219 | len = skb_put(skb, len: 1); |
1220 | skb_put_u8(skb, val: WLAN_EID_EXT_NON_INHERITANCE); |
1221 | |
1222 | for (i = 0; i < PRESENT_ELEMS_MAX && outer[i]; i++) { |
1223 | u16 elem = outer[i]; |
1224 | bool have_inner = false; |
1225 | |
1226 | /* should at least be sorted in the sense of normal -> ext */ |
1227 | WARN_ON(at_extension && elem < PRESENT_ELEM_EXT_OFFS); |
1228 | |
1229 | /* switch to extension list */ |
1230 | if (!at_extension && elem >= PRESENT_ELEM_EXT_OFFS) { |
1231 | at_extension = true; |
1232 | if (!list_len) |
1233 | skb_put_u8(skb, val: 0); |
1234 | list_len = NULL; |
1235 | } |
1236 | |
1237 | for (j = 0; j < PRESENT_ELEMS_MAX && inner[j]; j++) { |
1238 | if (elem == inner[j]) { |
1239 | have_inner = true; |
1240 | break; |
1241 | } |
1242 | } |
1243 | |
1244 | if (have_inner) |
1245 | continue; |
1246 | |
1247 | if (!list_len) { |
1248 | list_len = skb_put(skb, len: 1); |
1249 | *list_len = 0; |
1250 | } |
1251 | *list_len += 1; |
1252 | skb_put_u8(skb, val: (u8)elem); |
1253 | added = true; |
1254 | } |
1255 | |
1256 | /* if we added a list but no extension list, make a zero-len one */ |
1257 | if (added && (!at_extension || !list_len)) |
1258 | skb_put_u8(skb, val: 0); |
1259 | |
1260 | /* if nothing added remove extension element completely */ |
1261 | if (!added) |
1262 | skb_trim(skb, len: skb_len); |
1263 | else |
1264 | *len = skb->len - skb_len - 2; |
1265 | } |
1266 | |
1267 | static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata, |
1268 | struct sk_buff *skb, u16 capab, |
1269 | const struct element *ext_capa, |
1270 | const u16 *outer_present_elems) |
1271 | { |
1272 | struct ieee80211_local *local = sdata->local; |
1273 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1274 | struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; |
1275 | struct ieee80211_multi_link_elem *ml_elem; |
1276 | struct ieee80211_mle_basic_common_info *common; |
1277 | const struct wiphy_iftype_ext_capab *ift_ext_capa; |
1278 | __le16 eml_capa = 0, mld_capa_ops = 0; |
1279 | unsigned int link_id; |
1280 | u8 *ml_elem_len; |
1281 | void *capab_pos; |
1282 | |
1283 | if (!ieee80211_vif_is_mld(vif: &sdata->vif)) |
1284 | return; |
1285 | |
1286 | ift_ext_capa = cfg80211_get_iftype_ext_capa(wiphy: local->hw.wiphy, |
1287 | type: ieee80211_vif_type_p2p(vif: &sdata->vif)); |
1288 | if (ift_ext_capa) { |
1289 | eml_capa = cpu_to_le16(ift_ext_capa->eml_capabilities); |
1290 | mld_capa_ops = cpu_to_le16(ift_ext_capa->mld_capa_and_ops); |
1291 | } |
1292 | |
1293 | skb_put_u8(skb, val: WLAN_EID_EXTENSION); |
1294 | ml_elem_len = skb_put(skb, len: 1); |
1295 | skb_put_u8(skb, val: WLAN_EID_EXT_EHT_MULTI_LINK); |
1296 | ml_elem = skb_put(skb, len: sizeof(*ml_elem)); |
1297 | ml_elem->control = |
1298 | cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC | |
1299 | IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP); |
1300 | common = skb_put(skb, len: sizeof(*common)); |
1301 | common->len = sizeof(*common) + |
1302 | 2; /* MLD capa/ops */ |
1303 | memcpy(common->mld_mac_addr, sdata->vif.addr, ETH_ALEN); |
1304 | |
1305 | /* add EML_CAPA only if needed, see Draft P802.11be_D2.1, 35.3.17 */ |
1306 | if (eml_capa & |
1307 | cpu_to_le16((IEEE80211_EML_CAP_EMLSR_SUPP | |
1308 | IEEE80211_EML_CAP_EMLMR_SUPPORT))) { |
1309 | common->len += 2; /* EML capabilities */ |
1310 | ml_elem->control |= |
1311 | cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA); |
1312 | skb_put_data(skb, data: &eml_capa, len: sizeof(eml_capa)); |
1313 | } |
1314 | /* need indication from userspace to support this */ |
1315 | mld_capa_ops &= ~cpu_to_le16(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP); |
1316 | skb_put_data(skb, data: &mld_capa_ops, len: sizeof(mld_capa_ops)); |
1317 | |
1318 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { |
1319 | u16 link_present_elems[PRESENT_ELEMS_MAX] = {}; |
1320 | const u8 *; |
1321 | size_t ; |
1322 | size_t ; |
1323 | u8 *subelem_len = NULL; |
1324 | __le16 ctrl; |
1325 | |
1326 | if (!assoc_data->link[link_id].bss || |
1327 | link_id == assoc_data->assoc_link_id) |
1328 | continue; |
1329 | |
1330 | extra_elems = assoc_data->link[link_id].elems; |
1331 | extra_elems_len = assoc_data->link[link_id].elems_len; |
1332 | |
1333 | skb_put_u8(skb, val: IEEE80211_MLE_SUBELEM_PER_STA_PROFILE); |
1334 | subelem_len = skb_put(skb, len: 1); |
1335 | |
1336 | ctrl = cpu_to_le16(link_id | |
1337 | IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE | |
1338 | IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT); |
1339 | skb_put_data(skb, data: &ctrl, len: sizeof(ctrl)); |
1340 | skb_put_u8(skb, val: 1 + ETH_ALEN); /* STA Info Length */ |
1341 | skb_put_data(skb, data: assoc_data->link[link_id].addr, |
1342 | ETH_ALEN); |
1343 | /* |
1344 | * Now add the contents of the (re)association request, |
1345 | * but the "listen interval" and "current AP address" |
1346 | * (if applicable) are skipped. So we only have |
1347 | * the capability field (remember the position and fill |
1348 | * later), followed by the elements added below by |
1349 | * calling ieee80211_assoc_link_elems(). |
1350 | */ |
1351 | capab_pos = skb_put(skb, len: 2); |
1352 | |
1353 | extra_used = ieee80211_assoc_link_elems(sdata, skb, capab: &capab, |
1354 | ext_capa, |
1355 | extra_elems, |
1356 | extra_elems_len, |
1357 | link_id, NULL, |
1358 | present_elems: link_present_elems); |
1359 | if (extra_elems) |
1360 | skb_put_data(skb, data: extra_elems + extra_used, |
1361 | len: extra_elems_len - extra_used); |
1362 | |
1363 | put_unaligned_le16(val: capab, p: capab_pos); |
1364 | |
1365 | ieee80211_add_non_inheritance_elem(skb, outer: outer_present_elems, |
1366 | inner: link_present_elems); |
1367 | |
1368 | ieee80211_fragment_element(skb, len_pos: subelem_len, |
1369 | frag_id: IEEE80211_MLE_SUBELEM_FRAGMENT); |
1370 | } |
1371 | |
1372 | ieee80211_fragment_element(skb, len_pos: ml_elem_len, frag_id: WLAN_EID_FRAGMENT); |
1373 | } |
1374 | |
1375 | static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) |
1376 | { |
1377 | struct ieee80211_local *local = sdata->local; |
1378 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1379 | struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; |
1380 | struct ieee80211_link_data *link; |
1381 | struct sk_buff *skb; |
1382 | struct ieee80211_mgmt *mgmt; |
1383 | u8 *pos, qos_info, *ie_start; |
1384 | size_t offset, noffset; |
1385 | u16 capab = WLAN_CAPABILITY_ESS, link_capab; |
1386 | __le16 listen_int; |
1387 | struct element *ext_capa = NULL; |
1388 | enum nl80211_iftype iftype = ieee80211_vif_type_p2p(vif: &sdata->vif); |
1389 | struct ieee80211_prep_tx_info info = {}; |
1390 | unsigned int link_id, n_links = 0; |
1391 | u16 present_elems[PRESENT_ELEMS_MAX] = {}; |
1392 | void *capab_pos; |
1393 | size_t size; |
1394 | int ret; |
1395 | |
1396 | /* we know it's writable, cast away the const */ |
1397 | if (assoc_data->ie_len) |
1398 | ext_capa = (void *)cfg80211_find_elem(eid: WLAN_EID_EXT_CAPABILITY, |
1399 | ies: assoc_data->ie, |
1400 | len: assoc_data->ie_len); |
1401 | |
1402 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
1403 | |
1404 | size = local->hw.extra_tx_headroom + |
1405 | sizeof(*mgmt) + /* bit too much but doesn't matter */ |
1406 | 2 + assoc_data->ssid_len + /* SSID */ |
1407 | assoc_data->ie_len + /* extra IEs */ |
1408 | (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) + |
1409 | 9; /* WMM */ |
1410 | |
1411 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { |
1412 | struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; |
1413 | const struct ieee80211_sband_iftype_data *iftd; |
1414 | struct ieee80211_supported_band *sband; |
1415 | |
1416 | if (!cbss) |
1417 | continue; |
1418 | |
1419 | sband = local->hw.wiphy->bands[cbss->channel->band]; |
1420 | |
1421 | n_links++; |
1422 | /* add STA profile elements length */ |
1423 | size += assoc_data->link[link_id].elems_len; |
1424 | /* and supported rates length */ |
1425 | size += 4 + sband->n_bitrates; |
1426 | /* supported channels */ |
1427 | size += 2 + 2 * sband->n_channels; |
1428 | |
1429 | iftd = ieee80211_get_sband_iftype_data(sband, iftype); |
1430 | if (iftd) |
1431 | size += iftd->vendor_elems.len; |
1432 | |
1433 | /* power capability */ |
1434 | size += 4; |
1435 | |
1436 | /* HT, VHT, HE, EHT */ |
1437 | size += 2 + sizeof(struct ieee80211_ht_cap); |
1438 | size += 2 + sizeof(struct ieee80211_vht_cap); |
1439 | size += 2 + 1 + sizeof(struct ieee80211_he_cap_elem) + |
1440 | sizeof(struct ieee80211_he_mcs_nss_supp) + |
1441 | IEEE80211_HE_PPE_THRES_MAX_LEN; |
1442 | |
1443 | if (sband->band == NL80211_BAND_6GHZ) |
1444 | size += 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa); |
1445 | |
1446 | size += 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) + |
1447 | sizeof(struct ieee80211_eht_mcs_nss_supp) + |
1448 | IEEE80211_EHT_PPE_THRES_MAX_LEN; |
1449 | |
1450 | /* non-inheritance element */ |
1451 | size += 2 + 2 + PRESENT_ELEMS_MAX; |
1452 | |
1453 | /* should be the same across all BSSes */ |
1454 | if (cbss->capability & WLAN_CAPABILITY_PRIVACY) |
1455 | capab |= WLAN_CAPABILITY_PRIVACY; |
1456 | } |
1457 | |
1458 | if (ieee80211_vif_is_mld(vif: &sdata->vif)) { |
1459 | /* consider the multi-link element with STA profile */ |
1460 | size += sizeof(struct ieee80211_multi_link_elem); |
1461 | /* max common info field in basic multi-link element */ |
1462 | size += sizeof(struct ieee80211_mle_basic_common_info) + |
1463 | 2 + /* capa & op */ |
1464 | 2; /* EML capa */ |
1465 | |
1466 | /* |
1467 | * The capability elements were already considered above; |
1468 | * note this over-estimates a bit because there's no |
1469 | * STA profile for the assoc link. |
1470 | */ |
1471 | size += (n_links - 1) * |
1472 | (1 + 1 + /* subelement ID/length */ |
1473 | 2 + /* STA control */ |
1474 | 1 + ETH_ALEN + 2 /* STA Info field */); |
1475 | } |
1476 | |
1477 | link = sdata_dereference(sdata->link[assoc_data->assoc_link_id], sdata); |
1478 | if (WARN_ON(!link)) |
1479 | return -EINVAL; |
1480 | |
1481 | if (WARN_ON(!assoc_data->link[assoc_data->assoc_link_id].bss)) |
1482 | return -EINVAL; |
1483 | |
1484 | skb = alloc_skb(size, GFP_KERNEL); |
1485 | if (!skb) |
1486 | return -ENOMEM; |
1487 | |
1488 | skb_reserve(skb, len: local->hw.extra_tx_headroom); |
1489 | |
1490 | if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM) |
1491 | capab |= WLAN_CAPABILITY_RADIO_MEASURE; |
1492 | |
1493 | /* Set MBSSID support for HE AP if needed */ |
1494 | if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) && |
1495 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && |
1496 | ext_capa && ext_capa->datalen >= 3) |
1497 | ext_capa->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT; |
1498 | |
1499 | mgmt = skb_put_zero(skb, len: 24); |
1500 | memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN); |
1501 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
1502 | memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); |
1503 | |
1504 | listen_int = cpu_to_le16(assoc_data->s1g ? |
1505 | ieee80211_encode_usf(local->hw.conf.listen_interval) : |
1506 | local->hw.conf.listen_interval); |
1507 | if (!is_zero_ether_addr(addr: assoc_data->prev_ap_addr)) { |
1508 | skb_put(skb, len: 10); |
1509 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | |
1510 | IEEE80211_STYPE_REASSOC_REQ); |
1511 | capab_pos = &mgmt->u.reassoc_req.capab_info; |
1512 | mgmt->u.reassoc_req.listen_interval = listen_int; |
1513 | memcpy(mgmt->u.reassoc_req.current_ap, |
1514 | assoc_data->prev_ap_addr, ETH_ALEN); |
1515 | info.subtype = IEEE80211_STYPE_REASSOC_REQ; |
1516 | } else { |
1517 | skb_put(skb, len: 4); |
1518 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | |
1519 | IEEE80211_STYPE_ASSOC_REQ); |
1520 | capab_pos = &mgmt->u.assoc_req.capab_info; |
1521 | mgmt->u.assoc_req.listen_interval = listen_int; |
1522 | info.subtype = IEEE80211_STYPE_ASSOC_REQ; |
1523 | } |
1524 | |
1525 | /* SSID */ |
1526 | pos = skb_put(skb, len: 2 + assoc_data->ssid_len); |
1527 | ie_start = pos; |
1528 | *pos++ = WLAN_EID_SSID; |
1529 | *pos++ = assoc_data->ssid_len; |
1530 | memcpy(pos, assoc_data->ssid, assoc_data->ssid_len); |
1531 | |
1532 | /* add the elements for the assoc (main) link */ |
1533 | link_capab = capab; |
1534 | offset = ieee80211_assoc_link_elems(sdata, skb, capab: &link_capab, |
1535 | ext_capa, |
1536 | extra_elems: assoc_data->ie, |
1537 | extra_elems_len: assoc_data->ie_len, |
1538 | link_id: assoc_data->assoc_link_id, link, |
1539 | present_elems); |
1540 | put_unaligned_le16(val: link_capab, p: capab_pos); |
1541 | |
1542 | /* if present, add any custom non-vendor IEs */ |
1543 | if (assoc_data->ie_len) { |
1544 | noffset = ieee80211_ie_split_vendor(ies: assoc_data->ie, |
1545 | ielen: assoc_data->ie_len, |
1546 | offset); |
1547 | skb_put_data(skb, data: assoc_data->ie + offset, len: noffset - offset); |
1548 | offset = noffset; |
1549 | } |
1550 | |
1551 | if (assoc_data->wmm) { |
1552 | if (assoc_data->uapsd) { |
1553 | qos_info = ifmgd->uapsd_queues; |
1554 | qos_info |= (ifmgd->uapsd_max_sp_len << |
1555 | IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); |
1556 | } else { |
1557 | qos_info = 0; |
1558 | } |
1559 | |
1560 | pos = ieee80211_add_wmm_info_ie(buf: skb_put(skb, len: 9), qosinfo: qos_info); |
1561 | } |
1562 | |
1563 | /* add any remaining custom (i.e. vendor specific here) IEs */ |
1564 | if (assoc_data->ie_len) { |
1565 | noffset = assoc_data->ie_len; |
1566 | skb_put_data(skb, data: assoc_data->ie + offset, len: noffset - offset); |
1567 | } |
1568 | |
1569 | if (assoc_data->fils_kek_len) { |
1570 | ret = fils_encrypt_assoc_req(skb, assoc_data); |
1571 | if (ret < 0) { |
1572 | dev_kfree_skb(skb); |
1573 | return ret; |
1574 | } |
1575 | } |
1576 | |
1577 | pos = skb_tail_pointer(skb); |
1578 | kfree(objp: ifmgd->assoc_req_ies); |
1579 | ifmgd->assoc_req_ies = kmemdup(p: ie_start, size: pos - ie_start, GFP_ATOMIC); |
1580 | if (!ifmgd->assoc_req_ies) { |
1581 | dev_kfree_skb(skb); |
1582 | return -ENOMEM; |
1583 | } |
1584 | |
1585 | ifmgd->assoc_req_ies_len = pos - ie_start; |
1586 | |
1587 | info.link_id = assoc_data->assoc_link_id; |
1588 | drv_mgd_prepare_tx(local, sdata, info: &info); |
1589 | |
1590 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
1591 | if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) |
1592 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | |
1593 | IEEE80211_TX_INTFL_MLME_CONN_TX; |
1594 | ieee80211_tx_skb(sdata, skb); |
1595 | |
1596 | return 0; |
1597 | } |
1598 | |
1599 | void ieee80211_send_pspoll(struct ieee80211_local *local, |
1600 | struct ieee80211_sub_if_data *sdata) |
1601 | { |
1602 | struct ieee80211_pspoll *pspoll; |
1603 | struct sk_buff *skb; |
1604 | |
1605 | skb = ieee80211_pspoll_get(hw: &local->hw, vif: &sdata->vif); |
1606 | if (!skb) |
1607 | return; |
1608 | |
1609 | pspoll = (struct ieee80211_pspoll *) skb->data; |
1610 | pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); |
1611 | |
1612 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
1613 | ieee80211_tx_skb(sdata, skb); |
1614 | } |
1615 | |
1616 | void ieee80211_send_nullfunc(struct ieee80211_local *local, |
1617 | struct ieee80211_sub_if_data *sdata, |
1618 | bool powersave) |
1619 | { |
1620 | struct sk_buff *skb; |
1621 | struct ieee80211_hdr_3addr *nullfunc; |
1622 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1623 | |
1624 | skb = ieee80211_nullfunc_get(hw: &local->hw, vif: &sdata->vif, link_id: -1, |
1625 | qos_ok: !ieee80211_hw_check(&local->hw, |
1626 | DOESNT_SUPPORT_QOS_NDP)); |
1627 | if (!skb) |
1628 | return; |
1629 | |
1630 | nullfunc = (struct ieee80211_hdr_3addr *) skb->data; |
1631 | if (powersave) |
1632 | nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); |
1633 | |
1634 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | |
1635 | IEEE80211_TX_INTFL_OFFCHAN_TX_OK; |
1636 | |
1637 | if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) |
1638 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; |
1639 | |
1640 | if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) |
1641 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; |
1642 | |
1643 | ieee80211_tx_skb(sdata, skb); |
1644 | } |
1645 | |
1646 | void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, |
1647 | struct ieee80211_sub_if_data *sdata) |
1648 | { |
1649 | struct sk_buff *skb; |
1650 | struct ieee80211_hdr *nullfunc; |
1651 | __le16 fc; |
1652 | |
1653 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) |
1654 | return; |
1655 | |
1656 | skb = dev_alloc_skb(length: local->hw.extra_tx_headroom + 30); |
1657 | if (!skb) |
1658 | return; |
1659 | |
1660 | skb_reserve(skb, len: local->hw.extra_tx_headroom); |
1661 | |
1662 | nullfunc = skb_put_zero(skb, len: 30); |
1663 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | |
1664 | IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); |
1665 | nullfunc->frame_control = fc; |
1666 | memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN); |
1667 | memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); |
1668 | memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN); |
1669 | memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN); |
1670 | |
1671 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
1672 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; |
1673 | ieee80211_tx_skb(sdata, skb); |
1674 | } |
1675 | |
1676 | /* spectrum management related things */ |
1677 | static void ieee80211_chswitch_work(struct wiphy *wiphy, |
1678 | struct wiphy_work *work) |
1679 | { |
1680 | struct ieee80211_link_data *link = |
1681 | container_of(work, struct ieee80211_link_data, |
1682 | u.mgd.chswitch_work.work); |
1683 | struct ieee80211_sub_if_data *sdata = link->sdata; |
1684 | struct ieee80211_local *local = sdata->local; |
1685 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1686 | int ret; |
1687 | |
1688 | if (!ieee80211_sdata_running(sdata)) |
1689 | return; |
1690 | |
1691 | lockdep_assert_wiphy(local->hw.wiphy); |
1692 | |
1693 | if (!ifmgd->associated) |
1694 | return; |
1695 | |
1696 | if (!link->conf->csa_active) |
1697 | return; |
1698 | |
1699 | /* |
1700 | * using reservation isn't immediate as it may be deferred until later |
1701 | * with multi-vif. once reservation is complete it will re-schedule the |
1702 | * work with no reserved_chanctx so verify chandef to check if it |
1703 | * completed successfully |
1704 | */ |
1705 | |
1706 | if (link->reserved_chanctx) { |
1707 | /* |
1708 | * with multi-vif csa driver may call ieee80211_csa_finish() |
1709 | * many times while waiting for other interfaces to use their |
1710 | * reservations |
1711 | */ |
1712 | if (link->reserved_ready) |
1713 | return; |
1714 | |
1715 | ret = ieee80211_link_use_reserved_context(link); |
1716 | if (ret) { |
1717 | sdata_info(sdata, |
1718 | "failed to use reserved channel context, disconnecting (err=%d)\n" , |
1719 | ret); |
1720 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
1721 | work: &ifmgd->csa_connection_drop_work); |
1722 | } |
1723 | return; |
1724 | } |
1725 | |
1726 | if (!cfg80211_chandef_identical(chandef1: &link->conf->chandef, |
1727 | chandef2: &link->csa_chandef)) { |
1728 | sdata_info(sdata, |
1729 | "failed to finalize channel switch, disconnecting\n" ); |
1730 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
1731 | work: &ifmgd->csa_connection_drop_work); |
1732 | return; |
1733 | } |
1734 | |
1735 | link->u.mgd.csa_waiting_bcn = true; |
1736 | |
1737 | ieee80211_sta_reset_beacon_monitor(sdata); |
1738 | ieee80211_sta_reset_conn_monitor(sdata); |
1739 | } |
1740 | |
1741 | static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) |
1742 | { |
1743 | struct ieee80211_sub_if_data *sdata = link->sdata; |
1744 | struct ieee80211_local *local = sdata->local; |
1745 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1746 | int ret; |
1747 | |
1748 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
1749 | |
1750 | WARN_ON(!link->conf->csa_active); |
1751 | |
1752 | if (link->csa_block_tx) { |
1753 | ieee80211_wake_vif_queues(local, sdata, |
1754 | reason: IEEE80211_QUEUE_STOP_REASON_CSA); |
1755 | link->csa_block_tx = false; |
1756 | } |
1757 | |
1758 | link->conf->csa_active = false; |
1759 | link->u.mgd.csa_waiting_bcn = false; |
1760 | /* |
1761 | * If the CSA IE is still present on the beacon after the switch, |
1762 | * we need to consider it as a new CSA (possibly to self). |
1763 | */ |
1764 | link->u.mgd.beacon_crc_valid = false; |
1765 | |
1766 | ret = drv_post_channel_switch(link); |
1767 | if (ret) { |
1768 | sdata_info(sdata, |
1769 | "driver post channel switch failed, disconnecting\n" ); |
1770 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
1771 | work: &ifmgd->csa_connection_drop_work); |
1772 | return; |
1773 | } |
1774 | |
1775 | cfg80211_ch_switch_notify(dev: sdata->dev, chandef: &link->reserved_chandef, |
1776 | link_id: link->link_id, punct_bitmap: 0); |
1777 | } |
1778 | |
1779 | void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success, |
1780 | unsigned int link_id) |
1781 | { |
1782 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
1783 | |
1784 | trace_api_chswitch_done(sdata, success, link_id); |
1785 | |
1786 | rcu_read_lock(); |
1787 | |
1788 | if (!success) { |
1789 | sdata_info(sdata, |
1790 | "driver channel switch failed, disconnecting\n" ); |
1791 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
1792 | work: &sdata->u.mgd.csa_connection_drop_work); |
1793 | } else { |
1794 | struct ieee80211_link_data *link = |
1795 | rcu_dereference(sdata->link[link_id]); |
1796 | |
1797 | if (WARN_ON(!link)) { |
1798 | rcu_read_unlock(); |
1799 | return; |
1800 | } |
1801 | |
1802 | wiphy_delayed_work_queue(wiphy: sdata->local->hw.wiphy, |
1803 | dwork: &link->u.mgd.chswitch_work, delay: 0); |
1804 | } |
1805 | |
1806 | rcu_read_unlock(); |
1807 | } |
1808 | EXPORT_SYMBOL(ieee80211_chswitch_done); |
1809 | |
1810 | static void |
1811 | ieee80211_sta_abort_chanswitch(struct ieee80211_link_data *link) |
1812 | { |
1813 | struct ieee80211_sub_if_data *sdata = link->sdata; |
1814 | struct ieee80211_local *local = sdata->local; |
1815 | |
1816 | lockdep_assert_wiphy(local->hw.wiphy); |
1817 | |
1818 | if (!local->ops->abort_channel_switch) |
1819 | return; |
1820 | |
1821 | ieee80211_link_unreserve_chanctx(link); |
1822 | |
1823 | if (link->csa_block_tx) |
1824 | ieee80211_wake_vif_queues(local, sdata, |
1825 | reason: IEEE80211_QUEUE_STOP_REASON_CSA); |
1826 | |
1827 | link->csa_block_tx = false; |
1828 | link->conf->csa_active = false; |
1829 | |
1830 | drv_abort_channel_switch(sdata); |
1831 | } |
1832 | |
1833 | static void |
1834 | ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, |
1835 | u64 timestamp, u32 device_timestamp, |
1836 | struct ieee802_11_elems *elems, |
1837 | bool beacon) |
1838 | { |
1839 | struct ieee80211_sub_if_data *sdata = link->sdata; |
1840 | struct ieee80211_local *local = sdata->local; |
1841 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1842 | struct cfg80211_bss *cbss = link->u.mgd.bss; |
1843 | struct ieee80211_chanctx_conf *conf; |
1844 | struct ieee80211_chanctx *chanctx; |
1845 | enum nl80211_band current_band; |
1846 | struct ieee80211_csa_ie csa_ie; |
1847 | struct ieee80211_channel_switch ch_switch; |
1848 | struct ieee80211_bss *bss; |
1849 | unsigned long timeout; |
1850 | int res; |
1851 | |
1852 | lockdep_assert_wiphy(local->hw.wiphy); |
1853 | |
1854 | if (!cbss) |
1855 | return; |
1856 | |
1857 | current_band = cbss->channel->band; |
1858 | bss = (void *)cbss->priv; |
1859 | res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band, |
1860 | vht_cap_info: bss->vht_cap_info, |
1861 | conn_flags: link->u.mgd.conn_flags, |
1862 | bssid: link->u.mgd.bssid, csa_ie: &csa_ie); |
1863 | |
1864 | if (!res) { |
1865 | ch_switch.timestamp = timestamp; |
1866 | ch_switch.device_timestamp = device_timestamp; |
1867 | ch_switch.block_tx = csa_ie.mode; |
1868 | ch_switch.chandef = csa_ie.chandef; |
1869 | ch_switch.count = csa_ie.count; |
1870 | ch_switch.delay = csa_ie.max_switch_time; |
1871 | } |
1872 | |
1873 | if (res < 0) |
1874 | goto drop_connection; |
1875 | |
1876 | if (beacon && link->conf->csa_active && |
1877 | !link->u.mgd.csa_waiting_bcn) { |
1878 | if (res) |
1879 | ieee80211_sta_abort_chanswitch(link); |
1880 | else |
1881 | drv_channel_switch_rx_beacon(sdata, ch_switch: &ch_switch); |
1882 | return; |
1883 | } else if (link->conf->csa_active || res) { |
1884 | /* disregard subsequent announcements if already processing */ |
1885 | return; |
1886 | } |
1887 | |
1888 | if (link->conf->chandef.chan->band != |
1889 | csa_ie.chandef.chan->band) { |
1890 | sdata_info(sdata, |
1891 | "AP %pM switches to different band (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n" , |
1892 | link->u.mgd.bssid, |
1893 | csa_ie.chandef.chan->center_freq, |
1894 | csa_ie.chandef.width, csa_ie.chandef.center_freq1, |
1895 | csa_ie.chandef.center_freq2); |
1896 | goto drop_connection; |
1897 | } |
1898 | |
1899 | if (!cfg80211_chandef_usable(wiphy: local->hw.wiphy, chandef: &csa_ie.chandef, |
1900 | prohibited_flags: IEEE80211_CHAN_DISABLED)) { |
1901 | sdata_info(sdata, |
1902 | "AP %pM switches to unsupported channel " |
1903 | "(%d.%03d MHz, width:%d, CF1/2: %d.%03d/%d MHz), " |
1904 | "disconnecting\n" , |
1905 | link->u.mgd.bssid, |
1906 | csa_ie.chandef.chan->center_freq, |
1907 | csa_ie.chandef.chan->freq_offset, |
1908 | csa_ie.chandef.width, csa_ie.chandef.center_freq1, |
1909 | csa_ie.chandef.freq1_offset, |
1910 | csa_ie.chandef.center_freq2); |
1911 | goto drop_connection; |
1912 | } |
1913 | |
1914 | if (cfg80211_chandef_identical(chandef1: &csa_ie.chandef, |
1915 | chandef2: &link->conf->chandef) && |
1916 | (!csa_ie.mode || !beacon)) { |
1917 | if (link->u.mgd.csa_ignored_same_chan) |
1918 | return; |
1919 | sdata_info(sdata, |
1920 | "AP %pM tries to chanswitch to same channel, ignore\n" , |
1921 | link->u.mgd.bssid); |
1922 | link->u.mgd.csa_ignored_same_chan = true; |
1923 | return; |
1924 | } |
1925 | |
1926 | /* |
1927 | * Drop all TDLS peers - either we disconnect or move to a different |
1928 | * channel from this point on. There's no telling what our peer will do. |
1929 | * The TDLS WIDER_BW scenario is also problematic, as peers might now |
1930 | * have an incompatible wider chandef. |
1931 | */ |
1932 | ieee80211_teardown_tdls_peers(sdata); |
1933 | |
1934 | conf = rcu_dereference_protected(link->conf->chanctx_conf, |
1935 | lockdep_is_held(&local->hw.wiphy->mtx)); |
1936 | if (!conf) { |
1937 | sdata_info(sdata, |
1938 | "no channel context assigned to vif?, disconnecting\n" ); |
1939 | goto drop_connection; |
1940 | } |
1941 | |
1942 | chanctx = container_of(conf, struct ieee80211_chanctx, conf); |
1943 | |
1944 | if (local->use_chanctx && |
1945 | !ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) { |
1946 | sdata_info(sdata, |
1947 | "driver doesn't support chan-switch with channel contexts\n" ); |
1948 | goto drop_connection; |
1949 | } |
1950 | |
1951 | if (drv_pre_channel_switch(sdata, ch_switch: &ch_switch)) { |
1952 | sdata_info(sdata, |
1953 | "preparing for channel switch failed, disconnecting\n" ); |
1954 | goto drop_connection; |
1955 | } |
1956 | |
1957 | res = ieee80211_link_reserve_chanctx(link, chandef: &csa_ie.chandef, |
1958 | mode: chanctx->mode, radar_required: false); |
1959 | if (res) { |
1960 | sdata_info(sdata, |
1961 | "failed to reserve channel context for channel switch, disconnecting (err=%d)\n" , |
1962 | res); |
1963 | goto drop_connection; |
1964 | } |
1965 | |
1966 | link->conf->csa_active = true; |
1967 | link->csa_chandef = csa_ie.chandef; |
1968 | link->csa_block_tx = csa_ie.mode; |
1969 | link->u.mgd.csa_ignored_same_chan = false; |
1970 | link->u.mgd.beacon_crc_valid = false; |
1971 | |
1972 | if (link->csa_block_tx) |
1973 | ieee80211_stop_vif_queues(local, sdata, |
1974 | reason: IEEE80211_QUEUE_STOP_REASON_CSA); |
1975 | |
1976 | cfg80211_ch_switch_started_notify(dev: sdata->dev, chandef: &csa_ie.chandef, |
1977 | link_id: link->link_id, count: csa_ie.count, |
1978 | quiet: csa_ie.mode, punct_bitmap: 0); |
1979 | |
1980 | if (local->ops->channel_switch) { |
1981 | /* use driver's channel switch callback */ |
1982 | drv_channel_switch(local, sdata, ch_switch: &ch_switch); |
1983 | return; |
1984 | } |
1985 | |
1986 | /* channel switch handled in software */ |
1987 | timeout = TU_TO_JIFFIES((max_t(int, csa_ie.count, 1) - 1) * |
1988 | cbss->beacon_interval); |
1989 | wiphy_delayed_work_queue(wiphy: local->hw.wiphy, |
1990 | dwork: &link->u.mgd.chswitch_work, |
1991 | delay: timeout); |
1992 | return; |
1993 | drop_connection: |
1994 | /* |
1995 | * This is just so that the disconnect flow will know that |
1996 | * we were trying to switch channel and failed. In case the |
1997 | * mode is 1 (we are not allowed to Tx), we will know not to |
1998 | * send a deauthentication frame. Those two fields will be |
1999 | * reset when the disconnection worker runs. |
2000 | */ |
2001 | link->conf->csa_active = true; |
2002 | link->csa_block_tx = csa_ie.mode; |
2003 | |
2004 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
2005 | work: &ifmgd->csa_connection_drop_work); |
2006 | } |
2007 | |
2008 | static bool |
2009 | ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata, |
2010 | struct ieee80211_channel *channel, |
2011 | const u8 *country_ie, u8 country_ie_len, |
2012 | const u8 *pwr_constr_elem, |
2013 | int *chan_pwr, int *pwr_reduction) |
2014 | { |
2015 | struct ieee80211_country_ie_triplet *triplet; |
2016 | int chan = ieee80211_frequency_to_channel(freq: channel->center_freq); |
2017 | int i, chan_increment; |
2018 | bool have_chan_pwr = false; |
2019 | |
2020 | /* Invalid IE */ |
2021 | if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) |
2022 | return false; |
2023 | |
2024 | triplet = (void *)(country_ie + 3); |
2025 | country_ie_len -= 3; |
2026 | |
2027 | switch (channel->band) { |
2028 | default: |
2029 | WARN_ON_ONCE(1); |
2030 | fallthrough; |
2031 | case NL80211_BAND_2GHZ: |
2032 | case NL80211_BAND_60GHZ: |
2033 | case NL80211_BAND_LC: |
2034 | chan_increment = 1; |
2035 | break; |
2036 | case NL80211_BAND_5GHZ: |
2037 | chan_increment = 4; |
2038 | break; |
2039 | case NL80211_BAND_6GHZ: |
2040 | /* |
2041 | * In the 6 GHz band, the "maximum transmit power level" |
2042 | * field in the triplets is reserved, and thus will be |
2043 | * zero and we shouldn't use it to control TX power. |
2044 | * The actual TX power will be given in the transmit |
2045 | * power envelope element instead. |
2046 | */ |
2047 | return false; |
2048 | } |
2049 | |
2050 | /* find channel */ |
2051 | while (country_ie_len >= 3) { |
2052 | u8 first_channel = triplet->chans.first_channel; |
2053 | |
2054 | if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID) |
2055 | goto next; |
2056 | |
2057 | for (i = 0; i < triplet->chans.num_channels; i++) { |
2058 | if (first_channel + i * chan_increment == chan) { |
2059 | have_chan_pwr = true; |
2060 | *chan_pwr = triplet->chans.max_power; |
2061 | break; |
2062 | } |
2063 | } |
2064 | if (have_chan_pwr) |
2065 | break; |
2066 | |
2067 | next: |
2068 | triplet++; |
2069 | country_ie_len -= 3; |
2070 | } |
2071 | |
2072 | if (have_chan_pwr && pwr_constr_elem) |
2073 | *pwr_reduction = *pwr_constr_elem; |
2074 | else |
2075 | *pwr_reduction = 0; |
2076 | |
2077 | return have_chan_pwr; |
2078 | } |
2079 | |
2080 | static void ieee80211_find_cisco_dtpc(struct ieee80211_sub_if_data *sdata, |
2081 | struct ieee80211_channel *channel, |
2082 | const u8 *cisco_dtpc_ie, |
2083 | int *pwr_level) |
2084 | { |
2085 | /* From practical testing, the first data byte of the DTPC element |
2086 | * seems to contain the requested dBm level, and the CLI on Cisco |
2087 | * APs clearly state the range is -127 to 127 dBm, which indicates |
2088 | * a signed byte, although it seemingly never actually goes negative. |
2089 | * The other byte seems to always be zero. |
2090 | */ |
2091 | *pwr_level = (__s8)cisco_dtpc_ie[4]; |
2092 | } |
2093 | |
2094 | static u64 ieee80211_handle_pwr_constr(struct ieee80211_link_data *link, |
2095 | struct ieee80211_channel *channel, |
2096 | struct ieee80211_mgmt *mgmt, |
2097 | const u8 *country_ie, u8 country_ie_len, |
2098 | const u8 *pwr_constr_ie, |
2099 | const u8 *cisco_dtpc_ie) |
2100 | { |
2101 | struct ieee80211_sub_if_data *sdata = link->sdata; |
2102 | bool has_80211h_pwr = false, has_cisco_pwr = false; |
2103 | int chan_pwr = 0, pwr_reduction_80211h = 0; |
2104 | int pwr_level_cisco, pwr_level_80211h; |
2105 | int new_ap_level; |
2106 | __le16 capab = mgmt->u.probe_resp.capab_info; |
2107 | |
2108 | if (ieee80211_is_s1g_beacon(fc: mgmt->frame_control)) |
2109 | return 0; /* TODO */ |
2110 | |
2111 | if (country_ie && |
2112 | (capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) || |
2113 | capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) { |
2114 | has_80211h_pwr = ieee80211_find_80211h_pwr_constr( |
2115 | sdata, channel, country_ie, country_ie_len, |
2116 | pwr_constr_elem: pwr_constr_ie, chan_pwr: &chan_pwr, pwr_reduction: &pwr_reduction_80211h); |
2117 | pwr_level_80211h = |
2118 | max_t(int, 0, chan_pwr - pwr_reduction_80211h); |
2119 | } |
2120 | |
2121 | if (cisco_dtpc_ie) { |
2122 | ieee80211_find_cisco_dtpc( |
2123 | sdata, channel, cisco_dtpc_ie, pwr_level: &pwr_level_cisco); |
2124 | has_cisco_pwr = true; |
2125 | } |
2126 | |
2127 | if (!has_80211h_pwr && !has_cisco_pwr) |
2128 | return 0; |
2129 | |
2130 | /* If we have both 802.11h and Cisco DTPC, apply both limits |
2131 | * by picking the smallest of the two power levels advertised. |
2132 | */ |
2133 | if (has_80211h_pwr && |
2134 | (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) { |
2135 | new_ap_level = pwr_level_80211h; |
2136 | |
2137 | if (link->ap_power_level == new_ap_level) |
2138 | return 0; |
2139 | |
2140 | sdata_dbg(sdata, |
2141 | "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n" , |
2142 | pwr_level_80211h, chan_pwr, pwr_reduction_80211h, |
2143 | link->u.mgd.bssid); |
2144 | } else { /* has_cisco_pwr is always true here. */ |
2145 | new_ap_level = pwr_level_cisco; |
2146 | |
2147 | if (link->ap_power_level == new_ap_level) |
2148 | return 0; |
2149 | |
2150 | sdata_dbg(sdata, |
2151 | "Limiting TX power to %d dBm as advertised by %pM\n" , |
2152 | pwr_level_cisco, link->u.mgd.bssid); |
2153 | } |
2154 | |
2155 | link->ap_power_level = new_ap_level; |
2156 | if (__ieee80211_recalc_txpower(sdata)) |
2157 | return BSS_CHANGED_TXPOWER; |
2158 | return 0; |
2159 | } |
2160 | |
2161 | /* powersave */ |
2162 | static void ieee80211_enable_ps(struct ieee80211_local *local, |
2163 | struct ieee80211_sub_if_data *sdata) |
2164 | { |
2165 | struct ieee80211_conf *conf = &local->hw.conf; |
2166 | |
2167 | /* |
2168 | * If we are scanning right now then the parameters will |
2169 | * take effect when scan finishes. |
2170 | */ |
2171 | if (local->scanning) |
2172 | return; |
2173 | |
2174 | if (conf->dynamic_ps_timeout > 0 && |
2175 | !ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) { |
2176 | mod_timer(timer: &local->dynamic_ps_timer, expires: jiffies + |
2177 | msecs_to_jiffies(m: conf->dynamic_ps_timeout)); |
2178 | } else { |
2179 | if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) |
2180 | ieee80211_send_nullfunc(local, sdata, powersave: true); |
2181 | |
2182 | if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && |
2183 | ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) |
2184 | return; |
2185 | |
2186 | conf->flags |= IEEE80211_CONF_PS; |
2187 | ieee80211_hw_config(local, changed: IEEE80211_CONF_CHANGE_PS); |
2188 | } |
2189 | } |
2190 | |
2191 | static void ieee80211_change_ps(struct ieee80211_local *local) |
2192 | { |
2193 | struct ieee80211_conf *conf = &local->hw.conf; |
2194 | |
2195 | if (local->ps_sdata) { |
2196 | ieee80211_enable_ps(local, sdata: local->ps_sdata); |
2197 | } else if (conf->flags & IEEE80211_CONF_PS) { |
2198 | conf->flags &= ~IEEE80211_CONF_PS; |
2199 | ieee80211_hw_config(local, changed: IEEE80211_CONF_CHANGE_PS); |
2200 | del_timer_sync(timer: &local->dynamic_ps_timer); |
2201 | wiphy_work_cancel(wiphy: local->hw.wiphy, |
2202 | work: &local->dynamic_ps_enable_work); |
2203 | } |
2204 | } |
2205 | |
2206 | static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) |
2207 | { |
2208 | struct ieee80211_local *local = sdata->local; |
2209 | struct ieee80211_if_managed *mgd = &sdata->u.mgd; |
2210 | struct sta_info *sta = NULL; |
2211 | bool authorized = false; |
2212 | |
2213 | if (!mgd->powersave) |
2214 | return false; |
2215 | |
2216 | if (mgd->broken_ap) |
2217 | return false; |
2218 | |
2219 | if (!mgd->associated) |
2220 | return false; |
2221 | |
2222 | if (mgd->flags & IEEE80211_STA_CONNECTION_POLL) |
2223 | return false; |
2224 | |
2225 | if (!(local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) && |
2226 | !sdata->deflink.u.mgd.have_beacon) |
2227 | return false; |
2228 | |
2229 | rcu_read_lock(); |
2230 | sta = sta_info_get(sdata, addr: sdata->vif.cfg.ap_addr); |
2231 | if (sta) |
2232 | authorized = test_sta_flag(sta, flag: WLAN_STA_AUTHORIZED); |
2233 | rcu_read_unlock(); |
2234 | |
2235 | return authorized; |
2236 | } |
2237 | |
2238 | /* need to hold RTNL or interface lock */ |
2239 | void ieee80211_recalc_ps(struct ieee80211_local *local) |
2240 | { |
2241 | struct ieee80211_sub_if_data *sdata, *found = NULL; |
2242 | int count = 0; |
2243 | int timeout; |
2244 | |
2245 | if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS) || |
2246 | ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) { |
2247 | local->ps_sdata = NULL; |
2248 | return; |
2249 | } |
2250 | |
2251 | list_for_each_entry(sdata, &local->interfaces, list) { |
2252 | if (!ieee80211_sdata_running(sdata)) |
2253 | continue; |
2254 | if (sdata->vif.type == NL80211_IFTYPE_AP) { |
2255 | /* If an AP vif is found, then disable PS |
2256 | * by setting the count to zero thereby setting |
2257 | * ps_sdata to NULL. |
2258 | */ |
2259 | count = 0; |
2260 | break; |
2261 | } |
2262 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
2263 | continue; |
2264 | found = sdata; |
2265 | count++; |
2266 | } |
2267 | |
2268 | if (count == 1 && ieee80211_powersave_allowed(sdata: found)) { |
2269 | u8 dtimper = found->deflink.u.mgd.dtim_period; |
2270 | |
2271 | timeout = local->dynamic_ps_forced_timeout; |
2272 | if (timeout < 0) |
2273 | timeout = 100; |
2274 | local->hw.conf.dynamic_ps_timeout = timeout; |
2275 | |
2276 | /* If the TIM IE is invalid, pretend the value is 1 */ |
2277 | if (!dtimper) |
2278 | dtimper = 1; |
2279 | |
2280 | local->hw.conf.ps_dtim_period = dtimper; |
2281 | local->ps_sdata = found; |
2282 | } else { |
2283 | local->ps_sdata = NULL; |
2284 | } |
2285 | |
2286 | ieee80211_change_ps(local); |
2287 | } |
2288 | |
2289 | void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata) |
2290 | { |
2291 | bool ps_allowed = ieee80211_powersave_allowed(sdata); |
2292 | |
2293 | if (sdata->vif.cfg.ps != ps_allowed) { |
2294 | sdata->vif.cfg.ps = ps_allowed; |
2295 | ieee80211_vif_cfg_change_notify(sdata, changed: BSS_CHANGED_PS); |
2296 | } |
2297 | } |
2298 | |
2299 | void ieee80211_dynamic_ps_disable_work(struct wiphy *wiphy, |
2300 | struct wiphy_work *work) |
2301 | { |
2302 | struct ieee80211_local *local = |
2303 | container_of(work, struct ieee80211_local, |
2304 | dynamic_ps_disable_work); |
2305 | |
2306 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { |
2307 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; |
2308 | ieee80211_hw_config(local, changed: IEEE80211_CONF_CHANGE_PS); |
2309 | } |
2310 | |
2311 | ieee80211_wake_queues_by_reason(hw: &local->hw, |
2312 | queues: IEEE80211_MAX_QUEUE_MAP, |
2313 | reason: IEEE80211_QUEUE_STOP_REASON_PS, |
2314 | refcounted: false); |
2315 | } |
2316 | |
2317 | void ieee80211_dynamic_ps_enable_work(struct wiphy *wiphy, |
2318 | struct wiphy_work *work) |
2319 | { |
2320 | struct ieee80211_local *local = |
2321 | container_of(work, struct ieee80211_local, |
2322 | dynamic_ps_enable_work); |
2323 | struct ieee80211_sub_if_data *sdata = local->ps_sdata; |
2324 | struct ieee80211_if_managed *ifmgd; |
2325 | unsigned long flags; |
2326 | int q; |
2327 | |
2328 | /* can only happen when PS was just disabled anyway */ |
2329 | if (!sdata) |
2330 | return; |
2331 | |
2332 | ifmgd = &sdata->u.mgd; |
2333 | |
2334 | if (local->hw.conf.flags & IEEE80211_CONF_PS) |
2335 | return; |
2336 | |
2337 | if (local->hw.conf.dynamic_ps_timeout > 0) { |
2338 | /* don't enter PS if TX frames are pending */ |
2339 | if (drv_tx_frames_pending(local)) { |
2340 | mod_timer(timer: &local->dynamic_ps_timer, expires: jiffies + |
2341 | msecs_to_jiffies( |
2342 | m: local->hw.conf.dynamic_ps_timeout)); |
2343 | return; |
2344 | } |
2345 | |
2346 | /* |
2347 | * transmission can be stopped by others which leads to |
2348 | * dynamic_ps_timer expiry. Postpone the ps timer if it |
2349 | * is not the actual idle state. |
2350 | */ |
2351 | spin_lock_irqsave(&local->queue_stop_reason_lock, flags); |
2352 | for (q = 0; q < local->hw.queues; q++) { |
2353 | if (local->queue_stop_reasons[q]) { |
2354 | spin_unlock_irqrestore(lock: &local->queue_stop_reason_lock, |
2355 | flags); |
2356 | mod_timer(timer: &local->dynamic_ps_timer, expires: jiffies + |
2357 | msecs_to_jiffies( |
2358 | m: local->hw.conf.dynamic_ps_timeout)); |
2359 | return; |
2360 | } |
2361 | } |
2362 | spin_unlock_irqrestore(lock: &local->queue_stop_reason_lock, flags); |
2363 | } |
2364 | |
2365 | if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && |
2366 | !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { |
2367 | if (drv_tx_frames_pending(local)) { |
2368 | mod_timer(timer: &local->dynamic_ps_timer, expires: jiffies + |
2369 | msecs_to_jiffies( |
2370 | m: local->hw.conf.dynamic_ps_timeout)); |
2371 | } else { |
2372 | ieee80211_send_nullfunc(local, sdata, powersave: true); |
2373 | /* Flush to get the tx status of nullfunc frame */ |
2374 | ieee80211_flush_queues(local, sdata, drop: false); |
2375 | } |
2376 | } |
2377 | |
2378 | if (!(ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) && |
2379 | ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) || |
2380 | (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { |
2381 | ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; |
2382 | local->hw.conf.flags |= IEEE80211_CONF_PS; |
2383 | ieee80211_hw_config(local, changed: IEEE80211_CONF_CHANGE_PS); |
2384 | } |
2385 | } |
2386 | |
2387 | void ieee80211_dynamic_ps_timer(struct timer_list *t) |
2388 | { |
2389 | struct ieee80211_local *local = from_timer(local, t, dynamic_ps_timer); |
2390 | |
2391 | wiphy_work_queue(wiphy: local->hw.wiphy, work: &local->dynamic_ps_enable_work); |
2392 | } |
2393 | |
2394 | void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work) |
2395 | { |
2396 | struct ieee80211_link_data *link = |
2397 | container_of(work, struct ieee80211_link_data, |
2398 | dfs_cac_timer_work.work); |
2399 | struct cfg80211_chan_def chandef = link->conf->chandef; |
2400 | struct ieee80211_sub_if_data *sdata = link->sdata; |
2401 | |
2402 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
2403 | |
2404 | if (sdata->wdev.cac_started) { |
2405 | ieee80211_link_release_channel(link); |
2406 | cfg80211_cac_event(netdev: sdata->dev, chandef: &chandef, |
2407 | event: NL80211_RADAR_CAC_FINISHED, |
2408 | GFP_KERNEL); |
2409 | } |
2410 | } |
2411 | |
2412 | static bool |
2413 | __ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) |
2414 | { |
2415 | struct ieee80211_local *local = sdata->local; |
2416 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2417 | bool ret = false; |
2418 | int ac; |
2419 | |
2420 | if (local->hw.queues < IEEE80211_NUM_ACS) |
2421 | return false; |
2422 | |
2423 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { |
2424 | struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; |
2425 | int non_acm_ac; |
2426 | unsigned long now = jiffies; |
2427 | |
2428 | if (tx_tspec->action == TX_TSPEC_ACTION_NONE && |
2429 | tx_tspec->admitted_time && |
2430 | time_after(now, tx_tspec->time_slice_start + HZ)) { |
2431 | tx_tspec->consumed_tx_time = 0; |
2432 | tx_tspec->time_slice_start = now; |
2433 | |
2434 | if (tx_tspec->downgraded) |
2435 | tx_tspec->action = |
2436 | TX_TSPEC_ACTION_STOP_DOWNGRADE; |
2437 | } |
2438 | |
2439 | switch (tx_tspec->action) { |
2440 | case TX_TSPEC_ACTION_STOP_DOWNGRADE: |
2441 | /* take the original parameters */ |
2442 | if (drv_conf_tx(local, link: &sdata->deflink, ac, |
2443 | params: &sdata->deflink.tx_conf[ac])) |
2444 | link_err(&sdata->deflink, |
2445 | "failed to set TX queue parameters for queue %d\n" , |
2446 | ac); |
2447 | tx_tspec->action = TX_TSPEC_ACTION_NONE; |
2448 | tx_tspec->downgraded = false; |
2449 | ret = true; |
2450 | break; |
2451 | case TX_TSPEC_ACTION_DOWNGRADE: |
2452 | if (time_after(now, tx_tspec->time_slice_start + HZ)) { |
2453 | tx_tspec->action = TX_TSPEC_ACTION_NONE; |
2454 | ret = true; |
2455 | break; |
2456 | } |
2457 | /* downgrade next lower non-ACM AC */ |
2458 | for (non_acm_ac = ac + 1; |
2459 | non_acm_ac < IEEE80211_NUM_ACS; |
2460 | non_acm_ac++) |
2461 | if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac))) |
2462 | break; |
2463 | /* Usually the loop will result in using BK even if it |
2464 | * requires admission control, but such a configuration |
2465 | * makes no sense and we have to transmit somehow - the |
2466 | * AC selection does the same thing. |
2467 | * If we started out trying to downgrade from BK, then |
2468 | * the extra condition here might be needed. |
2469 | */ |
2470 | if (non_acm_ac >= IEEE80211_NUM_ACS) |
2471 | non_acm_ac = IEEE80211_AC_BK; |
2472 | if (drv_conf_tx(local, link: &sdata->deflink, ac, |
2473 | params: &sdata->deflink.tx_conf[non_acm_ac])) |
2474 | link_err(&sdata->deflink, |
2475 | "failed to set TX queue parameters for queue %d\n" , |
2476 | ac); |
2477 | tx_tspec->action = TX_TSPEC_ACTION_NONE; |
2478 | ret = true; |
2479 | wiphy_delayed_work_queue(wiphy: local->hw.wiphy, |
2480 | dwork: &ifmgd->tx_tspec_wk, |
2481 | delay: tx_tspec->time_slice_start + |
2482 | HZ - now + 1); |
2483 | break; |
2484 | case TX_TSPEC_ACTION_NONE: |
2485 | /* nothing now */ |
2486 | break; |
2487 | } |
2488 | } |
2489 | |
2490 | return ret; |
2491 | } |
2492 | |
2493 | void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) |
2494 | { |
2495 | if (__ieee80211_sta_handle_tspec_ac_params(sdata)) |
2496 | ieee80211_link_info_change_notify(sdata, link: &sdata->deflink, |
2497 | changed: BSS_CHANGED_QOS); |
2498 | } |
2499 | |
2500 | static void ieee80211_sta_handle_tspec_ac_params_wk(struct wiphy *wiphy, |
2501 | struct wiphy_work *work) |
2502 | { |
2503 | struct ieee80211_sub_if_data *sdata; |
2504 | |
2505 | sdata = container_of(work, struct ieee80211_sub_if_data, |
2506 | u.mgd.tx_tspec_wk.work); |
2507 | ieee80211_sta_handle_tspec_ac_params(sdata); |
2508 | } |
2509 | |
2510 | void ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link) |
2511 | { |
2512 | struct ieee80211_sub_if_data *sdata = link->sdata; |
2513 | struct ieee80211_local *local = sdata->local; |
2514 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2515 | struct ieee80211_tx_queue_params *params = link->tx_conf; |
2516 | u8 ac; |
2517 | |
2518 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { |
2519 | mlme_dbg(sdata, |
2520 | "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n" , |
2521 | ac, params[ac].acm, |
2522 | params[ac].aifs, params[ac].cw_min, params[ac].cw_max, |
2523 | params[ac].txop, params[ac].uapsd, |
2524 | ifmgd->tx_tspec[ac].downgraded); |
2525 | if (!ifmgd->tx_tspec[ac].downgraded && |
2526 | drv_conf_tx(local, link, ac, params: ¶ms[ac])) |
2527 | link_err(link, |
2528 | "failed to set TX queue parameters for AC %d\n" , |
2529 | ac); |
2530 | } |
2531 | } |
2532 | |
2533 | /* MLME */ |
2534 | static bool |
2535 | ieee80211_sta_wmm_params(struct ieee80211_local *local, |
2536 | struct ieee80211_link_data *link, |
2537 | const u8 *wmm_param, size_t wmm_param_len, |
2538 | const struct ieee80211_mu_edca_param_set *mu_edca) |
2539 | { |
2540 | struct ieee80211_sub_if_data *sdata = link->sdata; |
2541 | struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS]; |
2542 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2543 | size_t left; |
2544 | int count, mu_edca_count, ac; |
2545 | const u8 *pos; |
2546 | u8 uapsd_queues = 0; |
2547 | |
2548 | if (!local->ops->conf_tx) |
2549 | return false; |
2550 | |
2551 | if (local->hw.queues < IEEE80211_NUM_ACS) |
2552 | return false; |
2553 | |
2554 | if (!wmm_param) |
2555 | return false; |
2556 | |
2557 | if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) |
2558 | return false; |
2559 | |
2560 | if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) |
2561 | uapsd_queues = ifmgd->uapsd_queues; |
2562 | |
2563 | count = wmm_param[6] & 0x0f; |
2564 | /* -1 is the initial value of ifmgd->mu_edca_last_param_set. |
2565 | * if mu_edca was preset before and now it disappeared tell |
2566 | * the driver about it. |
2567 | */ |
2568 | mu_edca_count = mu_edca ? mu_edca->mu_qos_info & 0x0f : -1; |
2569 | if (count == link->u.mgd.wmm_last_param_set && |
2570 | mu_edca_count == link->u.mgd.mu_edca_last_param_set) |
2571 | return false; |
2572 | link->u.mgd.wmm_last_param_set = count; |
2573 | link->u.mgd.mu_edca_last_param_set = mu_edca_count; |
2574 | |
2575 | pos = wmm_param + 8; |
2576 | left = wmm_param_len - 8; |
2577 | |
2578 | memset(¶ms, 0, sizeof(params)); |
2579 | |
2580 | sdata->wmm_acm = 0; |
2581 | for (; left >= 4; left -= 4, pos += 4) { |
2582 | int aci = (pos[0] >> 5) & 0x03; |
2583 | int acm = (pos[0] >> 4) & 0x01; |
2584 | bool uapsd = false; |
2585 | |
2586 | switch (aci) { |
2587 | case 1: /* AC_BK */ |
2588 | ac = IEEE80211_AC_BK; |
2589 | if (acm) |
2590 | sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ |
2591 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) |
2592 | uapsd = true; |
2593 | params[ac].mu_edca = !!mu_edca; |
2594 | if (mu_edca) |
2595 | params[ac].mu_edca_param_rec = mu_edca->ac_bk; |
2596 | break; |
2597 | case 2: /* AC_VI */ |
2598 | ac = IEEE80211_AC_VI; |
2599 | if (acm) |
2600 | sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ |
2601 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) |
2602 | uapsd = true; |
2603 | params[ac].mu_edca = !!mu_edca; |
2604 | if (mu_edca) |
2605 | params[ac].mu_edca_param_rec = mu_edca->ac_vi; |
2606 | break; |
2607 | case 3: /* AC_VO */ |
2608 | ac = IEEE80211_AC_VO; |
2609 | if (acm) |
2610 | sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ |
2611 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) |
2612 | uapsd = true; |
2613 | params[ac].mu_edca = !!mu_edca; |
2614 | if (mu_edca) |
2615 | params[ac].mu_edca_param_rec = mu_edca->ac_vo; |
2616 | break; |
2617 | case 0: /* AC_BE */ |
2618 | default: |
2619 | ac = IEEE80211_AC_BE; |
2620 | if (acm) |
2621 | sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ |
2622 | if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) |
2623 | uapsd = true; |
2624 | params[ac].mu_edca = !!mu_edca; |
2625 | if (mu_edca) |
2626 | params[ac].mu_edca_param_rec = mu_edca->ac_be; |
2627 | break; |
2628 | } |
2629 | |
2630 | params[ac].aifs = pos[0] & 0x0f; |
2631 | |
2632 | if (params[ac].aifs < 2) { |
2633 | link_info(link, |
2634 | "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n" , |
2635 | params[ac].aifs, aci); |
2636 | params[ac].aifs = 2; |
2637 | } |
2638 | params[ac].cw_max = ecw2cw(ecw: (pos[1] & 0xf0) >> 4); |
2639 | params[ac].cw_min = ecw2cw(ecw: pos[1] & 0x0f); |
2640 | params[ac].txop = get_unaligned_le16(p: pos + 2); |
2641 | params[ac].acm = acm; |
2642 | params[ac].uapsd = uapsd; |
2643 | |
2644 | if (params[ac].cw_min == 0 || |
2645 | params[ac].cw_min > params[ac].cw_max) { |
2646 | link_info(link, |
2647 | "AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n" , |
2648 | params[ac].cw_min, params[ac].cw_max, aci); |
2649 | return false; |
2650 | } |
2651 | ieee80211_regulatory_limit_wmm_params(sdata, qparam: ¶ms[ac], ac); |
2652 | } |
2653 | |
2654 | /* WMM specification requires all 4 ACIs. */ |
2655 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { |
2656 | if (params[ac].cw_min == 0) { |
2657 | link_info(link, |
2658 | "AP has invalid WMM params (missing AC %d), using defaults\n" , |
2659 | ac); |
2660 | return false; |
2661 | } |
2662 | } |
2663 | |
2664 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) |
2665 | link->tx_conf[ac] = params[ac]; |
2666 | |
2667 | ieee80211_mgd_set_link_qos_params(link); |
2668 | |
2669 | /* enable WMM or activate new settings */ |
2670 | link->conf->qos = true; |
2671 | return true; |
2672 | } |
2673 | |
2674 | static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) |
2675 | { |
2676 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
2677 | |
2678 | sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL; |
2679 | ieee80211_run_deferred_scan(local: sdata->local); |
2680 | } |
2681 | |
2682 | static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) |
2683 | { |
2684 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
2685 | |
2686 | __ieee80211_stop_poll(sdata); |
2687 | } |
2688 | |
2689 | static u64 ieee80211_handle_bss_capability(struct ieee80211_link_data *link, |
2690 | u16 capab, bool erp_valid, u8 erp) |
2691 | { |
2692 | struct ieee80211_bss_conf *bss_conf = link->conf; |
2693 | struct ieee80211_supported_band *sband; |
2694 | u64 changed = 0; |
2695 | bool use_protection; |
2696 | bool use_short_preamble; |
2697 | bool use_short_slot; |
2698 | |
2699 | sband = ieee80211_get_link_sband(link); |
2700 | if (!sband) |
2701 | return changed; |
2702 | |
2703 | if (erp_valid) { |
2704 | use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0; |
2705 | use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0; |
2706 | } else { |
2707 | use_protection = false; |
2708 | use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE); |
2709 | } |
2710 | |
2711 | use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); |
2712 | if (sband->band == NL80211_BAND_5GHZ || |
2713 | sband->band == NL80211_BAND_6GHZ) |
2714 | use_short_slot = true; |
2715 | |
2716 | if (use_protection != bss_conf->use_cts_prot) { |
2717 | bss_conf->use_cts_prot = use_protection; |
2718 | changed |= BSS_CHANGED_ERP_CTS_PROT; |
2719 | } |
2720 | |
2721 | if (use_short_preamble != bss_conf->use_short_preamble) { |
2722 | bss_conf->use_short_preamble = use_short_preamble; |
2723 | changed |= BSS_CHANGED_ERP_PREAMBLE; |
2724 | } |
2725 | |
2726 | if (use_short_slot != bss_conf->use_short_slot) { |
2727 | bss_conf->use_short_slot = use_short_slot; |
2728 | changed |= BSS_CHANGED_ERP_SLOT; |
2729 | } |
2730 | |
2731 | return changed; |
2732 | } |
2733 | |
2734 | static u64 ieee80211_link_set_associated(struct ieee80211_link_data *link, |
2735 | struct cfg80211_bss *cbss) |
2736 | { |
2737 | struct ieee80211_sub_if_data *sdata = link->sdata; |
2738 | struct ieee80211_bss_conf *bss_conf = link->conf; |
2739 | struct ieee80211_bss *bss = (void *)cbss->priv; |
2740 | u64 changed = BSS_CHANGED_QOS; |
2741 | |
2742 | /* not really used in MLO */ |
2743 | sdata->u.mgd.beacon_timeout = |
2744 | usecs_to_jiffies(u: ieee80211_tu_to_usec(tu: beacon_loss_count * |
2745 | bss_conf->beacon_int)); |
2746 | |
2747 | changed |= ieee80211_handle_bss_capability(link, |
2748 | capab: bss_conf->assoc_capability, |
2749 | erp_valid: bss->has_erp_value, |
2750 | erp: bss->erp_value); |
2751 | |
2752 | ieee80211_check_rate_mask(link); |
2753 | |
2754 | link->u.mgd.bss = cbss; |
2755 | memcpy(link->u.mgd.bssid, cbss->bssid, ETH_ALEN); |
2756 | |
2757 | if (sdata->vif.p2p || |
2758 | sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) { |
2759 | const struct cfg80211_bss_ies *ies; |
2760 | |
2761 | rcu_read_lock(); |
2762 | ies = rcu_dereference(cbss->ies); |
2763 | if (ies) { |
2764 | int ret; |
2765 | |
2766 | ret = cfg80211_get_p2p_attr( |
2767 | ies: ies->data, len: ies->len, |
2768 | attr: IEEE80211_P2P_ATTR_ABSENCE_NOTICE, |
2769 | buf: (u8 *) &bss_conf->p2p_noa_attr, |
2770 | bufsize: sizeof(bss_conf->p2p_noa_attr)); |
2771 | if (ret >= 2) { |
2772 | link->u.mgd.p2p_noa_index = |
2773 | bss_conf->p2p_noa_attr.index; |
2774 | changed |= BSS_CHANGED_P2P_PS; |
2775 | } |
2776 | } |
2777 | rcu_read_unlock(); |
2778 | } |
2779 | |
2780 | if (link->u.mgd.have_beacon) { |
2781 | bss_conf->beacon_rate = bss->beacon_rate; |
2782 | changed |= BSS_CHANGED_BEACON_INFO; |
2783 | } else { |
2784 | bss_conf->beacon_rate = NULL; |
2785 | } |
2786 | |
2787 | /* Tell the driver to monitor connection quality (if supported) */ |
2788 | if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI && |
2789 | bss_conf->cqm_rssi_thold) |
2790 | changed |= BSS_CHANGED_CQM; |
2791 | |
2792 | return changed; |
2793 | } |
2794 | |
2795 | static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, |
2796 | struct ieee80211_mgd_assoc_data *assoc_data, |
2797 | u64 changed[IEEE80211_MLD_MAX_NUM_LINKS]) |
2798 | { |
2799 | struct ieee80211_local *local = sdata->local; |
2800 | struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; |
2801 | u64 vif_changed = BSS_CHANGED_ASSOC; |
2802 | unsigned int link_id; |
2803 | |
2804 | lockdep_assert_wiphy(local->hw.wiphy); |
2805 | |
2806 | sdata->u.mgd.associated = true; |
2807 | |
2808 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { |
2809 | struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; |
2810 | struct ieee80211_link_data *link; |
2811 | |
2812 | if (!cbss || |
2813 | assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) |
2814 | continue; |
2815 | |
2816 | if (ieee80211_vif_is_mld(vif: &sdata->vif) && |
2817 | !(ieee80211_vif_usable_links(vif: &sdata->vif) & BIT(link_id))) |
2818 | continue; |
2819 | |
2820 | link = sdata_dereference(sdata->link[link_id], sdata); |
2821 | if (WARN_ON(!link)) |
2822 | return; |
2823 | |
2824 | changed[link_id] |= ieee80211_link_set_associated(link, cbss); |
2825 | } |
2826 | |
2827 | /* just to be sure */ |
2828 | ieee80211_stop_poll(sdata); |
2829 | |
2830 | ieee80211_led_assoc(local, associated: 1); |
2831 | |
2832 | vif_cfg->assoc = 1; |
2833 | |
2834 | /* Enable ARP filtering */ |
2835 | if (vif_cfg->arp_addr_cnt) |
2836 | vif_changed |= BSS_CHANGED_ARP_FILTER; |
2837 | |
2838 | if (ieee80211_vif_is_mld(vif: &sdata->vif)) { |
2839 | for (link_id = 0; |
2840 | link_id < IEEE80211_MLD_MAX_NUM_LINKS; |
2841 | link_id++) { |
2842 | struct ieee80211_link_data *link; |
2843 | struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; |
2844 | |
2845 | if (!cbss || |
2846 | !(BIT(link_id) & |
2847 | ieee80211_vif_usable_links(vif: &sdata->vif)) || |
2848 | assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) |
2849 | continue; |
2850 | |
2851 | link = sdata_dereference(sdata->link[link_id], sdata); |
2852 | if (WARN_ON(!link)) |
2853 | return; |
2854 | |
2855 | ieee80211_link_info_change_notify(sdata, link, |
2856 | changed: changed[link_id]); |
2857 | |
2858 | ieee80211_recalc_smps(sdata, link); |
2859 | } |
2860 | |
2861 | ieee80211_vif_cfg_change_notify(sdata, changed: vif_changed); |
2862 | } else { |
2863 | ieee80211_bss_info_change_notify(sdata, |
2864 | changed: vif_changed | changed[0]); |
2865 | } |
2866 | |
2867 | ieee80211_recalc_ps(local); |
2868 | |
2869 | /* leave this here to not change ordering in non-MLO cases */ |
2870 | if (!ieee80211_vif_is_mld(vif: &sdata->vif)) |
2871 | ieee80211_recalc_smps(sdata, link: &sdata->deflink); |
2872 | ieee80211_recalc_ps_vif(sdata); |
2873 | |
2874 | netif_carrier_on(dev: sdata->dev); |
2875 | } |
2876 | |
2877 | static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, |
2878 | u16 stype, u16 reason, bool tx, |
2879 | u8 *frame_buf) |
2880 | { |
2881 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2882 | struct ieee80211_local *local = sdata->local; |
2883 | unsigned int link_id; |
2884 | u64 changed = 0; |
2885 | struct ieee80211_prep_tx_info info = { |
2886 | .subtype = stype, |
2887 | }; |
2888 | |
2889 | lockdep_assert_wiphy(local->hw.wiphy); |
2890 | |
2891 | if (WARN_ON_ONCE(tx && !frame_buf)) |
2892 | return; |
2893 | |
2894 | if (WARN_ON(!ifmgd->associated)) |
2895 | return; |
2896 | |
2897 | ieee80211_stop_poll(sdata); |
2898 | |
2899 | ifmgd->associated = false; |
2900 | |
2901 | /* other links will be destroyed */ |
2902 | sdata->deflink.u.mgd.bss = NULL; |
2903 | |
2904 | netif_carrier_off(dev: sdata->dev); |
2905 | |
2906 | /* |
2907 | * if we want to get out of ps before disassoc (why?) we have |
2908 | * to do it before sending disassoc, as otherwise the null-packet |
2909 | * won't be valid. |
2910 | */ |
2911 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { |
2912 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; |
2913 | ieee80211_hw_config(local, changed: IEEE80211_CONF_CHANGE_PS); |
2914 | } |
2915 | local->ps_sdata = NULL; |
2916 | |
2917 | /* disable per-vif ps */ |
2918 | ieee80211_recalc_ps_vif(sdata); |
2919 | |
2920 | /* make sure ongoing transmission finishes */ |
2921 | synchronize_net(); |
2922 | |
2923 | /* |
2924 | * drop any frame before deauth/disassoc, this can be data or |
2925 | * management frame. Since we are disconnecting, we should not |
2926 | * insist sending these frames which can take time and delay |
2927 | * the disconnection and possible the roaming. |
2928 | */ |
2929 | if (tx) |
2930 | ieee80211_flush_queues(local, sdata, drop: true); |
2931 | |
2932 | /* deauthenticate/disassociate now */ |
2933 | if (tx || frame_buf) { |
2934 | /* |
2935 | * In multi channel scenarios guarantee that the virtual |
2936 | * interface is granted immediate airtime to transmit the |
2937 | * deauthentication frame by calling mgd_prepare_tx, if the |
2938 | * driver requested so. |
2939 | */ |
2940 | if (ieee80211_hw_check(&local->hw, DEAUTH_NEED_MGD_TX_PREP)) { |
2941 | for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); |
2942 | link_id++) { |
2943 | struct ieee80211_link_data *link; |
2944 | |
2945 | link = sdata_dereference(sdata->link[link_id], |
2946 | sdata); |
2947 | if (!link) |
2948 | continue; |
2949 | if (link->u.mgd.have_beacon) |
2950 | break; |
2951 | } |
2952 | if (link_id == IEEE80211_MLD_MAX_NUM_LINKS) { |
2953 | info.link_id = ffs(sdata->vif.active_links) - 1; |
2954 | drv_mgd_prepare_tx(local: sdata->local, sdata, info: &info); |
2955 | } |
2956 | } |
2957 | |
2958 | ieee80211_send_deauth_disassoc(sdata, da: sdata->vif.cfg.ap_addr, |
2959 | bssid: sdata->vif.cfg.ap_addr, stype, |
2960 | reason, send_frame: tx, frame_buf); |
2961 | } |
2962 | |
2963 | /* flush out frame - make sure the deauth was actually sent */ |
2964 | if (tx) |
2965 | ieee80211_flush_queues(local, sdata, drop: false); |
2966 | |
2967 | drv_mgd_complete_tx(local: sdata->local, sdata, info: &info); |
2968 | |
2969 | /* clear AP addr only after building the needed mgmt frames */ |
2970 | eth_zero_addr(addr: sdata->deflink.u.mgd.bssid); |
2971 | eth_zero_addr(addr: sdata->vif.cfg.ap_addr); |
2972 | |
2973 | sdata->vif.cfg.ssid_len = 0; |
2974 | |
2975 | /* remove AP and TDLS peers */ |
2976 | sta_info_flush(sdata); |
2977 | |
2978 | /* finally reset all BSS / config parameters */ |
2979 | if (!ieee80211_vif_is_mld(vif: &sdata->vif)) |
2980 | changed |= ieee80211_reset_erp_info(sdata); |
2981 | |
2982 | ieee80211_led_assoc(local, associated: 0); |
2983 | changed |= BSS_CHANGED_ASSOC; |
2984 | sdata->vif.cfg.assoc = false; |
2985 | |
2986 | sdata->deflink.u.mgd.p2p_noa_index = -1; |
2987 | memset(&sdata->vif.bss_conf.p2p_noa_attr, 0, |
2988 | sizeof(sdata->vif.bss_conf.p2p_noa_attr)); |
2989 | |
2990 | /* on the next assoc, re-program HT/VHT parameters */ |
2991 | memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); |
2992 | memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); |
2993 | memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa)); |
2994 | memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask)); |
2995 | |
2996 | /* |
2997 | * reset MU-MIMO ownership and group data in default link, |
2998 | * if used, other links are destroyed |
2999 | */ |
3000 | memset(sdata->vif.bss_conf.mu_group.membership, 0, |
3001 | sizeof(sdata->vif.bss_conf.mu_group.membership)); |
3002 | memset(sdata->vif.bss_conf.mu_group.position, 0, |
3003 | sizeof(sdata->vif.bss_conf.mu_group.position)); |
3004 | if (!ieee80211_vif_is_mld(vif: &sdata->vif)) |
3005 | changed |= BSS_CHANGED_MU_GROUPS; |
3006 | sdata->vif.bss_conf.mu_mimo_owner = false; |
3007 | |
3008 | sdata->deflink.ap_power_level = IEEE80211_UNSET_POWER_LEVEL; |
3009 | |
3010 | del_timer_sync(timer: &local->dynamic_ps_timer); |
3011 | wiphy_work_cancel(wiphy: local->hw.wiphy, work: &local->dynamic_ps_enable_work); |
3012 | |
3013 | /* Disable ARP filtering */ |
3014 | if (sdata->vif.cfg.arp_addr_cnt) |
3015 | changed |= BSS_CHANGED_ARP_FILTER; |
3016 | |
3017 | sdata->vif.bss_conf.qos = false; |
3018 | if (!ieee80211_vif_is_mld(vif: &sdata->vif)) { |
3019 | changed |= BSS_CHANGED_QOS; |
3020 | /* The BSSID (not really interesting) and HT changed */ |
3021 | changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; |
3022 | ieee80211_bss_info_change_notify(sdata, changed); |
3023 | } else { |
3024 | ieee80211_vif_cfg_change_notify(sdata, changed); |
3025 | } |
3026 | |
3027 | /* disassociated - set to defaults now */ |
3028 | ieee80211_set_wmm_default(link: &sdata->deflink, bss_notify: false, enable_qos: false); |
3029 | |
3030 | del_timer_sync(timer: &sdata->u.mgd.conn_mon_timer); |
3031 | del_timer_sync(timer: &sdata->u.mgd.bcn_mon_timer); |
3032 | del_timer_sync(timer: &sdata->u.mgd.timer); |
3033 | |
3034 | sdata->vif.bss_conf.dtim_period = 0; |
3035 | sdata->vif.bss_conf.beacon_rate = NULL; |
3036 | |
3037 | sdata->deflink.u.mgd.have_beacon = false; |
3038 | sdata->deflink.u.mgd.tracking_signal_avg = false; |
3039 | sdata->deflink.u.mgd.disable_wmm_tracking = false; |
3040 | |
3041 | ifmgd->flags = 0; |
3042 | sdata->deflink.u.mgd.conn_flags = 0; |
3043 | |
3044 | for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { |
3045 | struct ieee80211_link_data *link; |
3046 | |
3047 | link = sdata_dereference(sdata->link[link_id], sdata); |
3048 | if (!link) |
3049 | continue; |
3050 | ieee80211_link_release_channel(link); |
3051 | } |
3052 | |
3053 | sdata->vif.bss_conf.csa_active = false; |
3054 | sdata->deflink.u.mgd.csa_waiting_bcn = false; |
3055 | sdata->deflink.u.mgd.csa_ignored_same_chan = false; |
3056 | if (sdata->deflink.csa_block_tx) { |
3057 | ieee80211_wake_vif_queues(local, sdata, |
3058 | reason: IEEE80211_QUEUE_STOP_REASON_CSA); |
3059 | sdata->deflink.csa_block_tx = false; |
3060 | } |
3061 | |
3062 | /* existing TX TSPEC sessions no longer exist */ |
3063 | memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec)); |
3064 | wiphy_delayed_work_cancel(wiphy: local->hw.wiphy, dwork: &ifmgd->tx_tspec_wk); |
3065 | |
3066 | sdata->vif.bss_conf.pwr_reduction = 0; |
3067 | sdata->vif.bss_conf.tx_pwr_env_num = 0; |
3068 | memset(sdata->vif.bss_conf.tx_pwr_env, 0, |
3069 | sizeof(sdata->vif.bss_conf.tx_pwr_env)); |
3070 | |
3071 | memset(&sdata->u.mgd.ttlm_info, 0, |
3072 | sizeof(sdata->u.mgd.ttlm_info)); |
3073 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, dwork: &ifmgd->ttlm_work); |
3074 | ieee80211_vif_set_links(sdata, new_links: 0, dormant_links: 0); |
3075 | } |
3076 | |
3077 | static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) |
3078 | { |
3079 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3080 | struct ieee80211_local *local = sdata->local; |
3081 | |
3082 | lockdep_assert_wiphy(local->hw.wiphy); |
3083 | |
3084 | if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)) |
3085 | return; |
3086 | |
3087 | __ieee80211_stop_poll(sdata); |
3088 | |
3089 | ieee80211_recalc_ps(local); |
3090 | |
3091 | if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) |
3092 | return; |
3093 | |
3094 | /* |
3095 | * We've received a probe response, but are not sure whether |
3096 | * we have or will be receiving any beacons or data, so let's |
3097 | * schedule the timers again, just in case. |
3098 | */ |
3099 | ieee80211_sta_reset_beacon_monitor(sdata); |
3100 | |
3101 | mod_timer(timer: &ifmgd->conn_mon_timer, |
3102 | expires: round_jiffies_up(j: jiffies + |
3103 | IEEE80211_CONNECTION_IDLE_TIME)); |
3104 | } |
3105 | |
3106 | static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, |
3107 | struct ieee80211_hdr *hdr, |
3108 | u16 tx_time) |
3109 | { |
3110 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3111 | u16 tid; |
3112 | int ac; |
3113 | struct ieee80211_sta_tx_tspec *tx_tspec; |
3114 | unsigned long now = jiffies; |
3115 | |
3116 | if (!ieee80211_is_data_qos(fc: hdr->frame_control)) |
3117 | return; |
3118 | |
3119 | tid = ieee80211_get_tid(hdr); |
3120 | ac = ieee80211_ac_from_tid(tid); |
3121 | tx_tspec = &ifmgd->tx_tspec[ac]; |
3122 | |
3123 | if (likely(!tx_tspec->admitted_time)) |
3124 | return; |
3125 | |
3126 | if (time_after(now, tx_tspec->time_slice_start + HZ)) { |
3127 | tx_tspec->consumed_tx_time = 0; |
3128 | tx_tspec->time_slice_start = now; |
3129 | |
3130 | if (tx_tspec->downgraded) { |
3131 | tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE; |
3132 | wiphy_delayed_work_queue(wiphy: sdata->local->hw.wiphy, |
3133 | dwork: &ifmgd->tx_tspec_wk, delay: 0); |
3134 | } |
3135 | } |
3136 | |
3137 | if (tx_tspec->downgraded) |
3138 | return; |
3139 | |
3140 | tx_tspec->consumed_tx_time += tx_time; |
3141 | |
3142 | if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) { |
3143 | tx_tspec->downgraded = true; |
3144 | tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE; |
3145 | wiphy_delayed_work_queue(wiphy: sdata->local->hw.wiphy, |
3146 | dwork: &ifmgd->tx_tspec_wk, delay: 0); |
3147 | } |
3148 | } |
3149 | |
3150 | void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, |
3151 | struct ieee80211_hdr *hdr, bool ack, u16 tx_time) |
3152 | { |
3153 | ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time); |
3154 | |
3155 | if (!ieee80211_is_any_nullfunc(fc: hdr->frame_control) || |
3156 | !sdata->u.mgd.probe_send_count) |
3157 | return; |
3158 | |
3159 | if (ack) |
3160 | sdata->u.mgd.probe_send_count = 0; |
3161 | else |
3162 | sdata->u.mgd.nullfunc_failed = true; |
3163 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, work: &sdata->work); |
3164 | } |
3165 | |
3166 | static void ieee80211_mlme_send_probe_req(struct ieee80211_sub_if_data *sdata, |
3167 | const u8 *src, const u8 *dst, |
3168 | const u8 *ssid, size_t ssid_len, |
3169 | struct ieee80211_channel *channel) |
3170 | { |
3171 | struct sk_buff *skb; |
3172 | |
3173 | skb = ieee80211_build_probe_req(sdata, src, dst, ratemask: (u32)-1, chan: channel, |
3174 | ssid, ssid_len, NULL, ie_len: 0, |
3175 | flags: IEEE80211_PROBE_FLAG_DIRECTED); |
3176 | if (skb) |
3177 | ieee80211_tx_skb(sdata, skb); |
3178 | } |
3179 | |
3180 | static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) |
3181 | { |
3182 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3183 | u8 *dst = sdata->vif.cfg.ap_addr; |
3184 | u8 unicast_limit = max(1, max_probe_tries - 3); |
3185 | struct sta_info *sta; |
3186 | |
3187 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3188 | |
3189 | if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) |
3190 | return; |
3191 | |
3192 | /* |
3193 | * Try sending broadcast probe requests for the last three |
3194 | * probe requests after the first ones failed since some |
3195 | * buggy APs only support broadcast probe requests. |
3196 | */ |
3197 | if (ifmgd->probe_send_count >= unicast_limit) |
3198 | dst = NULL; |
3199 | |
3200 | /* |
3201 | * When the hardware reports an accurate Tx ACK status, it's |
3202 | * better to send a nullfunc frame instead of a probe request, |
3203 | * as it will kick us off the AP quickly if we aren't associated |
3204 | * anymore. The timeout will be reset if the frame is ACKed by |
3205 | * the AP. |
3206 | */ |
3207 | ifmgd->probe_send_count++; |
3208 | |
3209 | if (dst) { |
3210 | sta = sta_info_get(sdata, addr: dst); |
3211 | if (!WARN_ON(!sta)) |
3212 | ieee80211_check_fast_rx(sta); |
3213 | } |
3214 | |
3215 | if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) { |
3216 | ifmgd->nullfunc_failed = false; |
3217 | ieee80211_send_nullfunc(local: sdata->local, sdata, powersave: false); |
3218 | } else { |
3219 | ieee80211_mlme_send_probe_req(sdata, src: sdata->vif.addr, dst, |
3220 | ssid: sdata->vif.cfg.ssid, |
3221 | ssid_len: sdata->vif.cfg.ssid_len, |
3222 | channel: sdata->deflink.u.mgd.bss->channel); |
3223 | } |
3224 | |
3225 | ifmgd->probe_timeout = jiffies + msecs_to_jiffies(m: probe_wait_ms); |
3226 | run_again(sdata, timeout: ifmgd->probe_timeout); |
3227 | } |
3228 | |
3229 | static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, |
3230 | bool beacon) |
3231 | { |
3232 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3233 | bool already = false; |
3234 | |
3235 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3236 | |
3237 | if (WARN_ON_ONCE(ieee80211_vif_is_mld(&sdata->vif))) |
3238 | return; |
3239 | |
3240 | if (!ieee80211_sdata_running(sdata)) |
3241 | return; |
3242 | |
3243 | if (!ifmgd->associated) |
3244 | return; |
3245 | |
3246 | if (sdata->local->tmp_channel || sdata->local->scanning) |
3247 | return; |
3248 | |
3249 | if (sdata->local->suspending) { |
3250 | /* reschedule after resume */ |
3251 | ieee80211_reset_ap_probe(sdata); |
3252 | return; |
3253 | } |
3254 | |
3255 | if (beacon) { |
3256 | mlme_dbg_ratelimited(sdata, |
3257 | "detected beacon loss from AP (missed %d beacons) - probing\n" , |
3258 | beacon_loss_count); |
3259 | |
3260 | ieee80211_cqm_beacon_loss_notify(vif: &sdata->vif, GFP_KERNEL); |
3261 | } |
3262 | |
3263 | /* |
3264 | * The driver/our work has already reported this event or the |
3265 | * connection monitoring has kicked in and we have already sent |
3266 | * a probe request. Or maybe the AP died and the driver keeps |
3267 | * reporting until we disassociate... |
3268 | * |
3269 | * In either case we have to ignore the current call to this |
3270 | * function (except for setting the correct probe reason bit) |
3271 | * because otherwise we would reset the timer every time and |
3272 | * never check whether we received a probe response! |
3273 | */ |
3274 | if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) |
3275 | already = true; |
3276 | |
3277 | ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; |
3278 | |
3279 | if (already) |
3280 | return; |
3281 | |
3282 | ieee80211_recalc_ps(local: sdata->local); |
3283 | |
3284 | ifmgd->probe_send_count = 0; |
3285 | ieee80211_mgd_probe_ap_send(sdata); |
3286 | } |
3287 | |
3288 | struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, |
3289 | struct ieee80211_vif *vif) |
3290 | { |
3291 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
3292 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3293 | struct cfg80211_bss *cbss; |
3294 | struct sk_buff *skb; |
3295 | const struct element *ssid; |
3296 | int ssid_len; |
3297 | |
3298 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3299 | |
3300 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || |
3301 | ieee80211_vif_is_mld(&sdata->vif))) |
3302 | return NULL; |
3303 | |
3304 | if (ifmgd->associated) |
3305 | cbss = sdata->deflink.u.mgd.bss; |
3306 | else if (ifmgd->auth_data) |
3307 | cbss = ifmgd->auth_data->bss; |
3308 | else if (ifmgd->assoc_data && ifmgd->assoc_data->link[0].bss) |
3309 | cbss = ifmgd->assoc_data->link[0].bss; |
3310 | else |
3311 | return NULL; |
3312 | |
3313 | rcu_read_lock(); |
3314 | ssid = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_SSID); |
3315 | if (WARN_ONCE(!ssid || ssid->datalen > IEEE80211_MAX_SSID_LEN, |
3316 | "invalid SSID element (len=%d)" , |
3317 | ssid ? ssid->datalen : -1)) |
3318 | ssid_len = 0; |
3319 | else |
3320 | ssid_len = ssid->datalen; |
3321 | |
3322 | skb = ieee80211_build_probe_req(sdata, src: sdata->vif.addr, dst: cbss->bssid, |
3323 | ratemask: (u32) -1, chan: cbss->channel, |
3324 | ssid: ssid->data, ssid_len, |
3325 | NULL, ie_len: 0, flags: IEEE80211_PROBE_FLAG_DIRECTED); |
3326 | rcu_read_unlock(); |
3327 | |
3328 | return skb; |
3329 | } |
3330 | EXPORT_SYMBOL(ieee80211_ap_probereq_get); |
3331 | |
3332 | static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, |
3333 | const u8 *buf, size_t len, bool tx, |
3334 | u16 reason, bool reconnect) |
3335 | { |
3336 | struct ieee80211_event event = { |
3337 | .type = MLME_EVENT, |
3338 | .u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT, |
3339 | .u.mlme.reason = reason, |
3340 | }; |
3341 | |
3342 | if (tx) |
3343 | cfg80211_tx_mlme_mgmt(dev: sdata->dev, buf, len, reconnect); |
3344 | else |
3345 | cfg80211_rx_mlme_mgmt(dev: sdata->dev, buf, len); |
3346 | |
3347 | drv_event_callback(local: sdata->local, sdata, event: &event); |
3348 | } |
3349 | |
3350 | static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) |
3351 | { |
3352 | struct ieee80211_local *local = sdata->local; |
3353 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3354 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
3355 | bool tx; |
3356 | |
3357 | lockdep_assert_wiphy(local->hw.wiphy); |
3358 | |
3359 | if (!ifmgd->associated) |
3360 | return; |
3361 | |
3362 | /* in MLO assume we have a link where we can TX the frame */ |
3363 | tx = ieee80211_vif_is_mld(vif: &sdata->vif) || |
3364 | !sdata->deflink.csa_block_tx; |
3365 | |
3366 | if (!ifmgd->driver_disconnect) { |
3367 | unsigned int link_id; |
3368 | |
3369 | /* |
3370 | * AP is probably out of range (or not reachable for another |
3371 | * reason) so remove the bss structs for that AP. In the case |
3372 | * of multi-link, it's not clear that all of them really are |
3373 | * out of range, but if they weren't the driver likely would |
3374 | * have switched to just have a single link active? |
3375 | */ |
3376 | for (link_id = 0; |
3377 | link_id < ARRAY_SIZE(sdata->link); |
3378 | link_id++) { |
3379 | struct ieee80211_link_data *link; |
3380 | |
3381 | link = sdata_dereference(sdata->link[link_id], sdata); |
3382 | if (!link) |
3383 | continue; |
3384 | cfg80211_unlink_bss(wiphy: local->hw.wiphy, bss: link->u.mgd.bss); |
3385 | link->u.mgd.bss = NULL; |
3386 | } |
3387 | } |
3388 | |
3389 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
3390 | reason: ifmgd->driver_disconnect ? |
3391 | WLAN_REASON_DEAUTH_LEAVING : |
3392 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, |
3393 | tx, frame_buf); |
3394 | /* the other links will be destroyed */ |
3395 | sdata->vif.bss_conf.csa_active = false; |
3396 | sdata->deflink.u.mgd.csa_waiting_bcn = false; |
3397 | if (sdata->deflink.csa_block_tx) { |
3398 | ieee80211_wake_vif_queues(local, sdata, |
3399 | reason: IEEE80211_QUEUE_STOP_REASON_CSA); |
3400 | sdata->deflink.csa_block_tx = false; |
3401 | } |
3402 | |
3403 | ieee80211_report_disconnect(sdata, buf: frame_buf, len: sizeof(frame_buf), tx, |
3404 | reason: WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, |
3405 | reconnect: ifmgd->reconnect); |
3406 | ifmgd->reconnect = false; |
3407 | } |
3408 | |
3409 | static void ieee80211_beacon_connection_loss_work(struct wiphy *wiphy, |
3410 | struct wiphy_work *work) |
3411 | { |
3412 | struct ieee80211_sub_if_data *sdata = |
3413 | container_of(work, struct ieee80211_sub_if_data, |
3414 | u.mgd.beacon_connection_loss_work); |
3415 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3416 | |
3417 | if (ifmgd->connection_loss) { |
3418 | sdata_info(sdata, "Connection to AP %pM lost\n" , |
3419 | sdata->vif.cfg.ap_addr); |
3420 | __ieee80211_disconnect(sdata); |
3421 | ifmgd->connection_loss = false; |
3422 | } else if (ifmgd->driver_disconnect) { |
3423 | sdata_info(sdata, |
3424 | "Driver requested disconnection from AP %pM\n" , |
3425 | sdata->vif.cfg.ap_addr); |
3426 | __ieee80211_disconnect(sdata); |
3427 | ifmgd->driver_disconnect = false; |
3428 | } else { |
3429 | if (ifmgd->associated) |
3430 | sdata->deflink.u.mgd.beacon_loss_count++; |
3431 | ieee80211_mgd_probe_ap(sdata, beacon: true); |
3432 | } |
3433 | } |
3434 | |
3435 | static void ieee80211_csa_connection_drop_work(struct wiphy *wiphy, |
3436 | struct wiphy_work *work) |
3437 | { |
3438 | struct ieee80211_sub_if_data *sdata = |
3439 | container_of(work, struct ieee80211_sub_if_data, |
3440 | u.mgd.csa_connection_drop_work); |
3441 | |
3442 | __ieee80211_disconnect(sdata); |
3443 | } |
3444 | |
3445 | void ieee80211_beacon_loss(struct ieee80211_vif *vif) |
3446 | { |
3447 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
3448 | struct ieee80211_hw *hw = &sdata->local->hw; |
3449 | |
3450 | trace_api_beacon_loss(sdata); |
3451 | |
3452 | sdata->u.mgd.connection_loss = false; |
3453 | wiphy_work_queue(wiphy: hw->wiphy, work: &sdata->u.mgd.beacon_connection_loss_work); |
3454 | } |
3455 | EXPORT_SYMBOL(ieee80211_beacon_loss); |
3456 | |
3457 | void ieee80211_connection_loss(struct ieee80211_vif *vif) |
3458 | { |
3459 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
3460 | struct ieee80211_hw *hw = &sdata->local->hw; |
3461 | |
3462 | trace_api_connection_loss(sdata); |
3463 | |
3464 | sdata->u.mgd.connection_loss = true; |
3465 | wiphy_work_queue(wiphy: hw->wiphy, work: &sdata->u.mgd.beacon_connection_loss_work); |
3466 | } |
3467 | EXPORT_SYMBOL(ieee80211_connection_loss); |
3468 | |
3469 | void ieee80211_disconnect(struct ieee80211_vif *vif, bool reconnect) |
3470 | { |
3471 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
3472 | struct ieee80211_hw *hw = &sdata->local->hw; |
3473 | |
3474 | trace_api_disconnect(sdata, reconnect); |
3475 | |
3476 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) |
3477 | return; |
3478 | |
3479 | sdata->u.mgd.driver_disconnect = true; |
3480 | sdata->u.mgd.reconnect = reconnect; |
3481 | wiphy_work_queue(wiphy: hw->wiphy, work: &sdata->u.mgd.beacon_connection_loss_work); |
3482 | } |
3483 | EXPORT_SYMBOL(ieee80211_disconnect); |
3484 | |
3485 | static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, |
3486 | bool assoc) |
3487 | { |
3488 | struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; |
3489 | |
3490 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3491 | |
3492 | if (!assoc) { |
3493 | /* |
3494 | * we are not authenticated yet, the only timer that could be |
3495 | * running is the timeout for the authentication response which |
3496 | * which is not relevant anymore. |
3497 | */ |
3498 | del_timer_sync(timer: &sdata->u.mgd.timer); |
3499 | sta_info_destroy_addr(sdata, addr: auth_data->ap_addr); |
3500 | |
3501 | /* other links are destroyed */ |
3502 | sdata->deflink.u.mgd.conn_flags = 0; |
3503 | eth_zero_addr(addr: sdata->deflink.u.mgd.bssid); |
3504 | ieee80211_link_info_change_notify(sdata, link: &sdata->deflink, |
3505 | changed: BSS_CHANGED_BSSID); |
3506 | sdata->u.mgd.flags = 0; |
3507 | |
3508 | ieee80211_link_release_channel(link: &sdata->deflink); |
3509 | ieee80211_vif_set_links(sdata, new_links: 0, dormant_links: 0); |
3510 | } |
3511 | |
3512 | cfg80211_put_bss(wiphy: sdata->local->hw.wiphy, bss: auth_data->bss); |
3513 | kfree(objp: auth_data); |
3514 | sdata->u.mgd.auth_data = NULL; |
3515 | } |
3516 | |
3517 | enum assoc_status { |
3518 | ASSOC_SUCCESS, |
3519 | ASSOC_REJECTED, |
3520 | ASSOC_TIMEOUT, |
3521 | ASSOC_ABANDON, |
3522 | }; |
3523 | |
3524 | static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, |
3525 | enum assoc_status status) |
3526 | { |
3527 | struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; |
3528 | |
3529 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3530 | |
3531 | if (status != ASSOC_SUCCESS) { |
3532 | /* |
3533 | * we are not associated yet, the only timer that could be |
3534 | * running is the timeout for the association response which |
3535 | * which is not relevant anymore. |
3536 | */ |
3537 | del_timer_sync(timer: &sdata->u.mgd.timer); |
3538 | sta_info_destroy_addr(sdata, addr: assoc_data->ap_addr); |
3539 | |
3540 | sdata->deflink.u.mgd.conn_flags = 0; |
3541 | eth_zero_addr(addr: sdata->deflink.u.mgd.bssid); |
3542 | ieee80211_link_info_change_notify(sdata, link: &sdata->deflink, |
3543 | changed: BSS_CHANGED_BSSID); |
3544 | sdata->u.mgd.flags = 0; |
3545 | sdata->vif.bss_conf.mu_mimo_owner = false; |
3546 | |
3547 | if (status != ASSOC_REJECTED) { |
3548 | struct cfg80211_assoc_failure data = { |
3549 | .timeout = status == ASSOC_TIMEOUT, |
3550 | }; |
3551 | int i; |
3552 | |
3553 | BUILD_BUG_ON(ARRAY_SIZE(data.bss) != |
3554 | ARRAY_SIZE(assoc_data->link)); |
3555 | |
3556 | for (i = 0; i < ARRAY_SIZE(data.bss); i++) |
3557 | data.bss[i] = assoc_data->link[i].bss; |
3558 | |
3559 | if (ieee80211_vif_is_mld(vif: &sdata->vif)) |
3560 | data.ap_mld_addr = assoc_data->ap_addr; |
3561 | |
3562 | cfg80211_assoc_failure(dev: sdata->dev, data: &data); |
3563 | } |
3564 | |
3565 | ieee80211_link_release_channel(link: &sdata->deflink); |
3566 | ieee80211_vif_set_links(sdata, new_links: 0, dormant_links: 0); |
3567 | } |
3568 | |
3569 | kfree(objp: assoc_data); |
3570 | sdata->u.mgd.assoc_data = NULL; |
3571 | } |
3572 | |
3573 | static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, |
3574 | struct ieee80211_mgmt *mgmt, size_t len) |
3575 | { |
3576 | struct ieee80211_local *local = sdata->local; |
3577 | struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; |
3578 | const struct element *challenge; |
3579 | u8 *pos; |
3580 | u32 tx_flags = 0; |
3581 | struct ieee80211_prep_tx_info info = { |
3582 | .subtype = IEEE80211_STYPE_AUTH, |
3583 | .link_id = auth_data->link_id, |
3584 | }; |
3585 | |
3586 | pos = mgmt->u.auth.variable; |
3587 | challenge = cfg80211_find_elem(eid: WLAN_EID_CHALLENGE, ies: pos, |
3588 | len: len - (pos - (u8 *)mgmt)); |
3589 | if (!challenge) |
3590 | return; |
3591 | auth_data->expected_transaction = 4; |
3592 | drv_mgd_prepare_tx(local: sdata->local, sdata, info: &info); |
3593 | if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) |
3594 | tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | |
3595 | IEEE80211_TX_INTFL_MLME_CONN_TX; |
3596 | ieee80211_send_auth(sdata, transaction: 3, auth_alg: auth_data->algorithm, status: 0, |
3597 | extra: (void *)challenge, |
3598 | extra_len: challenge->datalen + sizeof(*challenge), |
3599 | bssid: auth_data->ap_addr, da: auth_data->ap_addr, |
3600 | key: auth_data->key, key_len: auth_data->key_len, |
3601 | key_idx: auth_data->key_idx, tx_flags); |
3602 | } |
3603 | |
3604 | static bool ieee80211_mark_sta_auth(struct ieee80211_sub_if_data *sdata) |
3605 | { |
3606 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3607 | const u8 *ap_addr = ifmgd->auth_data->ap_addr; |
3608 | struct sta_info *sta; |
3609 | |
3610 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3611 | |
3612 | sdata_info(sdata, "authenticated\n" ); |
3613 | ifmgd->auth_data->done = true; |
3614 | ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; |
3615 | ifmgd->auth_data->timeout_started = true; |
3616 | run_again(sdata, timeout: ifmgd->auth_data->timeout); |
3617 | |
3618 | /* move station state to auth */ |
3619 | sta = sta_info_get(sdata, addr: ap_addr); |
3620 | if (!sta) { |
3621 | WARN_ONCE(1, "%s: STA %pM not found" , sdata->name, ap_addr); |
3622 | return false; |
3623 | } |
3624 | if (sta_info_move_state(sta, new_state: IEEE80211_STA_AUTH)) { |
3625 | sdata_info(sdata, "failed moving %pM to auth\n" , ap_addr); |
3626 | return false; |
3627 | } |
3628 | |
3629 | return true; |
3630 | } |
3631 | |
3632 | static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, |
3633 | struct ieee80211_mgmt *mgmt, size_t len) |
3634 | { |
3635 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3636 | u16 auth_alg, auth_transaction, status_code; |
3637 | struct ieee80211_event event = { |
3638 | .type = MLME_EVENT, |
3639 | .u.mlme.data = AUTH_EVENT, |
3640 | }; |
3641 | struct ieee80211_prep_tx_info info = { |
3642 | .subtype = IEEE80211_STYPE_AUTH, |
3643 | }; |
3644 | |
3645 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3646 | |
3647 | if (len < 24 + 6) |
3648 | return; |
3649 | |
3650 | if (!ifmgd->auth_data || ifmgd->auth_data->done) |
3651 | return; |
3652 | |
3653 | if (!ether_addr_equal(addr1: ifmgd->auth_data->ap_addr, addr2: mgmt->bssid)) |
3654 | return; |
3655 | |
3656 | auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); |
3657 | auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); |
3658 | status_code = le16_to_cpu(mgmt->u.auth.status_code); |
3659 | |
3660 | if (auth_alg != ifmgd->auth_data->algorithm || |
3661 | (auth_alg != WLAN_AUTH_SAE && |
3662 | auth_transaction != ifmgd->auth_data->expected_transaction) || |
3663 | (auth_alg == WLAN_AUTH_SAE && |
3664 | (auth_transaction < ifmgd->auth_data->expected_transaction || |
3665 | auth_transaction > 2))) { |
3666 | sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n" , |
3667 | mgmt->sa, auth_alg, ifmgd->auth_data->algorithm, |
3668 | auth_transaction, |
3669 | ifmgd->auth_data->expected_transaction); |
3670 | goto notify_driver; |
3671 | } |
3672 | |
3673 | if (status_code != WLAN_STATUS_SUCCESS) { |
3674 | cfg80211_rx_mlme_mgmt(dev: sdata->dev, buf: (u8 *)mgmt, len); |
3675 | |
3676 | if (auth_alg == WLAN_AUTH_SAE && |
3677 | (status_code == WLAN_STATUS_ANTI_CLOG_REQUIRED || |
3678 | (auth_transaction == 1 && |
3679 | (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || |
3680 | status_code == WLAN_STATUS_SAE_PK)))) { |
3681 | /* waiting for userspace now */ |
3682 | ifmgd->auth_data->waiting = true; |
3683 | ifmgd->auth_data->timeout = |
3684 | jiffies + IEEE80211_AUTH_WAIT_SAE_RETRY; |
3685 | ifmgd->auth_data->timeout_started = true; |
3686 | run_again(sdata, timeout: ifmgd->auth_data->timeout); |
3687 | goto notify_driver; |
3688 | } |
3689 | |
3690 | sdata_info(sdata, "%pM denied authentication (status %d)\n" , |
3691 | mgmt->sa, status_code); |
3692 | ieee80211_destroy_auth_data(sdata, assoc: false); |
3693 | event.u.mlme.status = MLME_DENIED; |
3694 | event.u.mlme.reason = status_code; |
3695 | drv_event_callback(local: sdata->local, sdata, event: &event); |
3696 | goto notify_driver; |
3697 | } |
3698 | |
3699 | switch (ifmgd->auth_data->algorithm) { |
3700 | case WLAN_AUTH_OPEN: |
3701 | case WLAN_AUTH_LEAP: |
3702 | case WLAN_AUTH_FT: |
3703 | case WLAN_AUTH_SAE: |
3704 | case WLAN_AUTH_FILS_SK: |
3705 | case WLAN_AUTH_FILS_SK_PFS: |
3706 | case WLAN_AUTH_FILS_PK: |
3707 | break; |
3708 | case WLAN_AUTH_SHARED_KEY: |
3709 | if (ifmgd->auth_data->expected_transaction != 4) { |
3710 | ieee80211_auth_challenge(sdata, mgmt, len); |
3711 | /* need another frame */ |
3712 | return; |
3713 | } |
3714 | break; |
3715 | default: |
3716 | WARN_ONCE(1, "invalid auth alg %d" , |
3717 | ifmgd->auth_data->algorithm); |
3718 | goto notify_driver; |
3719 | } |
3720 | |
3721 | event.u.mlme.status = MLME_SUCCESS; |
3722 | info.success = 1; |
3723 | drv_event_callback(local: sdata->local, sdata, event: &event); |
3724 | if (ifmgd->auth_data->algorithm != WLAN_AUTH_SAE || |
3725 | (auth_transaction == 2 && |
3726 | ifmgd->auth_data->expected_transaction == 2)) { |
3727 | if (!ieee80211_mark_sta_auth(sdata)) |
3728 | return; /* ignore frame -- wait for timeout */ |
3729 | } else if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE && |
3730 | auth_transaction == 2) { |
3731 | sdata_info(sdata, "SAE peer confirmed\n" ); |
3732 | ifmgd->auth_data->peer_confirmed = true; |
3733 | } |
3734 | |
3735 | cfg80211_rx_mlme_mgmt(dev: sdata->dev, buf: (u8 *)mgmt, len); |
3736 | notify_driver: |
3737 | drv_mgd_complete_tx(local: sdata->local, sdata, info: &info); |
3738 | } |
3739 | |
3740 | #define case_WLAN(type) \ |
3741 | case WLAN_REASON_##type: return #type |
3742 | |
3743 | const char *ieee80211_get_reason_code_string(u16 reason_code) |
3744 | { |
3745 | switch (reason_code) { |
3746 | case_WLAN(UNSPECIFIED); |
3747 | case_WLAN(PREV_AUTH_NOT_VALID); |
3748 | case_WLAN(DEAUTH_LEAVING); |
3749 | case_WLAN(DISASSOC_DUE_TO_INACTIVITY); |
3750 | case_WLAN(DISASSOC_AP_BUSY); |
3751 | case_WLAN(CLASS2_FRAME_FROM_NONAUTH_STA); |
3752 | case_WLAN(CLASS3_FRAME_FROM_NONASSOC_STA); |
3753 | case_WLAN(DISASSOC_STA_HAS_LEFT); |
3754 | case_WLAN(STA_REQ_ASSOC_WITHOUT_AUTH); |
3755 | case_WLAN(DISASSOC_BAD_POWER); |
3756 | case_WLAN(DISASSOC_BAD_SUPP_CHAN); |
3757 | case_WLAN(INVALID_IE); |
3758 | case_WLAN(MIC_FAILURE); |
3759 | case_WLAN(4WAY_HANDSHAKE_TIMEOUT); |
3760 | case_WLAN(GROUP_KEY_HANDSHAKE_TIMEOUT); |
3761 | case_WLAN(IE_DIFFERENT); |
3762 | case_WLAN(INVALID_GROUP_CIPHER); |
3763 | case_WLAN(INVALID_PAIRWISE_CIPHER); |
3764 | case_WLAN(INVALID_AKMP); |
3765 | case_WLAN(UNSUPP_RSN_VERSION); |
3766 | case_WLAN(INVALID_RSN_IE_CAP); |
3767 | case_WLAN(IEEE8021X_FAILED); |
3768 | case_WLAN(CIPHER_SUITE_REJECTED); |
3769 | case_WLAN(DISASSOC_UNSPECIFIED_QOS); |
3770 | case_WLAN(DISASSOC_QAP_NO_BANDWIDTH); |
3771 | case_WLAN(DISASSOC_LOW_ACK); |
3772 | case_WLAN(DISASSOC_QAP_EXCEED_TXOP); |
3773 | case_WLAN(QSTA_LEAVE_QBSS); |
3774 | case_WLAN(QSTA_NOT_USE); |
3775 | case_WLAN(QSTA_REQUIRE_SETUP); |
3776 | case_WLAN(QSTA_TIMEOUT); |
3777 | case_WLAN(QSTA_CIPHER_NOT_SUPP); |
3778 | case_WLAN(MESH_PEER_CANCELED); |
3779 | case_WLAN(MESH_MAX_PEERS); |
3780 | case_WLAN(MESH_CONFIG); |
3781 | case_WLAN(MESH_CLOSE); |
3782 | case_WLAN(MESH_MAX_RETRIES); |
3783 | case_WLAN(MESH_CONFIRM_TIMEOUT); |
3784 | case_WLAN(MESH_INVALID_GTK); |
3785 | case_WLAN(MESH_INCONSISTENT_PARAM); |
3786 | case_WLAN(MESH_INVALID_SECURITY); |
3787 | case_WLAN(MESH_PATH_ERROR); |
3788 | case_WLAN(MESH_PATH_NOFORWARD); |
3789 | case_WLAN(MESH_PATH_DEST_UNREACHABLE); |
3790 | case_WLAN(MAC_EXISTS_IN_MBSS); |
3791 | case_WLAN(MESH_CHAN_REGULATORY); |
3792 | case_WLAN(MESH_CHAN); |
3793 | default: return "<unknown>" ; |
3794 | } |
3795 | } |
3796 | |
3797 | static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, |
3798 | struct ieee80211_mgmt *mgmt, size_t len) |
3799 | { |
3800 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3801 | u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); |
3802 | |
3803 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3804 | |
3805 | if (len < 24 + 2) |
3806 | return; |
3807 | |
3808 | if (!ether_addr_equal(addr1: mgmt->bssid, addr2: mgmt->sa)) { |
3809 | ieee80211_tdls_handle_disconnect(sdata, peer: mgmt->sa, reason: reason_code); |
3810 | return; |
3811 | } |
3812 | |
3813 | if (ifmgd->associated && |
3814 | ether_addr_equal(addr1: mgmt->bssid, addr2: sdata->vif.cfg.ap_addr)) { |
3815 | sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n" , |
3816 | sdata->vif.cfg.ap_addr, reason_code, |
3817 | ieee80211_get_reason_code_string(reason_code)); |
3818 | |
3819 | ieee80211_set_disassoc(sdata, stype: 0, reason: 0, tx: false, NULL); |
3820 | |
3821 | ieee80211_report_disconnect(sdata, buf: (u8 *)mgmt, len, tx: false, |
3822 | reason: reason_code, reconnect: false); |
3823 | return; |
3824 | } |
3825 | |
3826 | if (ifmgd->assoc_data && |
3827 | ether_addr_equal(addr1: mgmt->bssid, addr2: ifmgd->assoc_data->ap_addr)) { |
3828 | sdata_info(sdata, |
3829 | "deauthenticated from %pM while associating (Reason: %u=%s)\n" , |
3830 | ifmgd->assoc_data->ap_addr, reason_code, |
3831 | ieee80211_get_reason_code_string(reason_code)); |
3832 | |
3833 | ieee80211_destroy_assoc_data(sdata, status: ASSOC_ABANDON); |
3834 | |
3835 | cfg80211_rx_mlme_mgmt(dev: sdata->dev, buf: (u8 *)mgmt, len); |
3836 | return; |
3837 | } |
3838 | } |
3839 | |
3840 | |
3841 | static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, |
3842 | struct ieee80211_mgmt *mgmt, size_t len) |
3843 | { |
3844 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
3845 | u16 reason_code; |
3846 | |
3847 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
3848 | |
3849 | if (len < 24 + 2) |
3850 | return; |
3851 | |
3852 | if (!ifmgd->associated || |
3853 | !ether_addr_equal(addr1: mgmt->bssid, addr2: sdata->vif.cfg.ap_addr)) |
3854 | return; |
3855 | |
3856 | reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); |
3857 | |
3858 | if (!ether_addr_equal(addr1: mgmt->bssid, addr2: mgmt->sa)) { |
3859 | ieee80211_tdls_handle_disconnect(sdata, peer: mgmt->sa, reason: reason_code); |
3860 | return; |
3861 | } |
3862 | |
3863 | sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n" , |
3864 | sdata->vif.cfg.ap_addr, reason_code, |
3865 | ieee80211_get_reason_code_string(reason_code)); |
3866 | |
3867 | ieee80211_set_disassoc(sdata, stype: 0, reason: 0, tx: false, NULL); |
3868 | |
3869 | ieee80211_report_disconnect(sdata, buf: (u8 *)mgmt, len, tx: false, reason: reason_code, |
3870 | reconnect: false); |
3871 | } |
3872 | |
3873 | static void ieee80211_get_rates(struct ieee80211_supported_band *sband, |
3874 | u8 *supp_rates, unsigned int supp_rates_len, |
3875 | u32 *rates, u32 *basic_rates, |
3876 | bool *have_higher_than_11mbit, |
3877 | int *min_rate, int *min_rate_index) |
3878 | { |
3879 | int i, j; |
3880 | |
3881 | for (i = 0; i < supp_rates_len; i++) { |
3882 | int rate = supp_rates[i] & 0x7f; |
3883 | bool is_basic = !!(supp_rates[i] & 0x80); |
3884 | |
3885 | if ((rate * 5) > 110) |
3886 | *have_higher_than_11mbit = true; |
3887 | |
3888 | /* |
3889 | * Skip HT, VHT, HE, EHT and SAE H2E only BSS membership |
3890 | * selectors since they're not rates. |
3891 | * |
3892 | * Note: Even though the membership selector and the basic |
3893 | * rate flag share the same bit, they are not exactly |
3894 | * the same. |
3895 | */ |
3896 | if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) || |
3897 | supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY) || |
3898 | supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY) || |
3899 | supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY) || |
3900 | supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E)) |
3901 | continue; |
3902 | |
3903 | for (j = 0; j < sband->n_bitrates; j++) { |
3904 | struct ieee80211_rate *br; |
3905 | int brate; |
3906 | |
3907 | br = &sband->bitrates[j]; |
3908 | |
3909 | brate = DIV_ROUND_UP(br->bitrate, 5); |
3910 | if (brate == rate) { |
3911 | *rates |= BIT(j); |
3912 | if (is_basic) |
3913 | *basic_rates |= BIT(j); |
3914 | if ((rate * 5) < *min_rate) { |
3915 | *min_rate = rate * 5; |
3916 | *min_rate_index = j; |
3917 | } |
3918 | break; |
3919 | } |
3920 | } |
3921 | } |
3922 | } |
3923 | |
3924 | static bool ieee80211_twt_req_supported(struct ieee80211_sub_if_data *sdata, |
3925 | struct ieee80211_supported_band *sband, |
3926 | const struct link_sta_info *link_sta, |
3927 | const struct ieee802_11_elems *elems) |
3928 | { |
3929 | const struct ieee80211_sta_he_cap *own_he_cap = |
3930 | ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif); |
3931 | |
3932 | if (elems->ext_capab_len < 10) |
3933 | return false; |
3934 | |
3935 | if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT)) |
3936 | return false; |
3937 | |
3938 | return link_sta->pub->he_cap.he_cap_elem.mac_cap_info[0] & |
3939 | IEEE80211_HE_MAC_CAP0_TWT_RES && |
3940 | own_he_cap && |
3941 | (own_he_cap->he_cap_elem.mac_cap_info[0] & |
3942 | IEEE80211_HE_MAC_CAP0_TWT_REQ); |
3943 | } |
3944 | |
3945 | static u64 ieee80211_recalc_twt_req(struct ieee80211_sub_if_data *sdata, |
3946 | struct ieee80211_supported_band *sband, |
3947 | struct ieee80211_link_data *link, |
3948 | struct link_sta_info *link_sta, |
3949 | struct ieee802_11_elems *elems) |
3950 | { |
3951 | bool twt = ieee80211_twt_req_supported(sdata, sband, link_sta, elems); |
3952 | |
3953 | if (link->conf->twt_requester != twt) { |
3954 | link->conf->twt_requester = twt; |
3955 | return BSS_CHANGED_TWT; |
3956 | } |
3957 | return 0; |
3958 | } |
3959 | |
3960 | static bool ieee80211_twt_bcast_support(struct ieee80211_sub_if_data *sdata, |
3961 | struct ieee80211_bss_conf *bss_conf, |
3962 | struct ieee80211_supported_band *sband, |
3963 | struct link_sta_info *link_sta) |
3964 | { |
3965 | const struct ieee80211_sta_he_cap *own_he_cap = |
3966 | ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif); |
3967 | |
3968 | return bss_conf->he_support && |
3969 | (link_sta->pub->he_cap.he_cap_elem.mac_cap_info[2] & |
3970 | IEEE80211_HE_MAC_CAP2_BCAST_TWT) && |
3971 | own_he_cap && |
3972 | (own_he_cap->he_cap_elem.mac_cap_info[2] & |
3973 | IEEE80211_HE_MAC_CAP2_BCAST_TWT); |
3974 | } |
3975 | |
3976 | static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, |
3977 | struct link_sta_info *link_sta, |
3978 | struct cfg80211_bss *cbss, |
3979 | struct ieee80211_mgmt *mgmt, |
3980 | const u8 *elem_start, |
3981 | unsigned int elem_len, |
3982 | u64 *changed) |
3983 | { |
3984 | struct ieee80211_sub_if_data *sdata = link->sdata; |
3985 | struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; |
3986 | struct ieee80211_bss_conf *bss_conf = link->conf; |
3987 | struct ieee80211_local *local = sdata->local; |
3988 | unsigned int link_id = link->link_id; |
3989 | struct ieee80211_elems_parse_params parse_params = { |
3990 | .start = elem_start, |
3991 | .len = elem_len, |
3992 | .link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id, |
3993 | .from_ap = true, |
3994 | }; |
3995 | bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; |
3996 | bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ; |
3997 | const struct cfg80211_bss_ies *bss_ies = NULL; |
3998 | struct ieee80211_supported_band *sband; |
3999 | struct ieee802_11_elems *elems; |
4000 | const __le16 prof_bss_param_ch_present = |
4001 | cpu_to_le16(IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT); |
4002 | u16 capab_info; |
4003 | bool ret; |
4004 | |
4005 | elems = ieee802_11_parse_elems_full(params: &parse_params); |
4006 | if (!elems) |
4007 | return false; |
4008 | |
4009 | if (link_id == assoc_data->assoc_link_id) { |
4010 | capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); |
4011 | |
4012 | /* |
4013 | * we should not get to this flow unless the association was |
4014 | * successful, so set the status directly to success |
4015 | */ |
4016 | assoc_data->link[link_id].status = WLAN_STATUS_SUCCESS; |
4017 | if (elems->ml_basic) { |
4018 | if (!(elems->ml_basic->control & |
4019 | cpu_to_le16(IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT))) { |
4020 | ret = false; |
4021 | goto out; |
4022 | } |
4023 | link->u.mgd.bss_param_ch_cnt = |
4024 | ieee80211_mle_get_bss_param_ch_cnt(mle: elems->ml_basic); |
4025 | } |
4026 | } else if (!elems->prof || |
4027 | !(elems->prof->control & prof_bss_param_ch_present)) { |
4028 | ret = false; |
4029 | goto out; |
4030 | } else { |
4031 | const u8 *ptr = elems->prof->variable + |
4032 | elems->prof->sta_info_len - 1; |
4033 | |
4034 | /* |
4035 | * During parsing, we validated that these fields exist, |
4036 | * otherwise elems->prof would have been set to NULL. |
4037 | */ |
4038 | capab_info = get_unaligned_le16(p: ptr); |
4039 | assoc_data->link[link_id].status = get_unaligned_le16(p: ptr + 2); |
4040 | link->u.mgd.bss_param_ch_cnt = |
4041 | ieee80211_mle_basic_sta_prof_bss_param_ch_cnt(prof: elems->prof); |
4042 | |
4043 | if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) { |
4044 | link_info(link, "association response status code=%u\n" , |
4045 | assoc_data->link[link_id].status); |
4046 | ret = true; |
4047 | goto out; |
4048 | } |
4049 | } |
4050 | |
4051 | if (!is_s1g && !elems->supp_rates) { |
4052 | sdata_info(sdata, "no SuppRates element in AssocResp\n" ); |
4053 | ret = false; |
4054 | goto out; |
4055 | } |
4056 | |
4057 | link->u.mgd.tdls_chan_switch_prohibited = |
4058 | elems->ext_capab && elems->ext_capab_len >= 5 && |
4059 | (elems->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED); |
4060 | |
4061 | /* |
4062 | * Some APs are erroneously not including some information in their |
4063 | * (re)association response frames. Try to recover by using the data |
4064 | * from the beacon or probe response. This seems to afflict mobile |
4065 | * 2G/3G/4G wifi routers, reported models include the "Onda PN51T", |
4066 | * "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device. |
4067 | */ |
4068 | if (!is_6ghz && |
4069 | ((assoc_data->wmm && !elems->wmm_param) || |
4070 | (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && |
4071 | (!elems->ht_cap_elem || !elems->ht_operation)) || |
4072 | (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && |
4073 | (!elems->vht_cap_elem || !elems->vht_operation)))) { |
4074 | const struct cfg80211_bss_ies *ies; |
4075 | struct ieee802_11_elems *bss_elems; |
4076 | |
4077 | rcu_read_lock(); |
4078 | ies = rcu_dereference(cbss->ies); |
4079 | if (ies) |
4080 | bss_ies = kmemdup(p: ies, size: sizeof(*ies) + ies->len, |
4081 | GFP_ATOMIC); |
4082 | rcu_read_unlock(); |
4083 | if (!bss_ies) { |
4084 | ret = false; |
4085 | goto out; |
4086 | } |
4087 | |
4088 | parse_params.start = bss_ies->data; |
4089 | parse_params.len = bss_ies->len; |
4090 | parse_params.bss = cbss; |
4091 | bss_elems = ieee802_11_parse_elems_full(params: &parse_params); |
4092 | if (!bss_elems) { |
4093 | ret = false; |
4094 | goto out; |
4095 | } |
4096 | |
4097 | if (assoc_data->wmm && |
4098 | !elems->wmm_param && bss_elems->wmm_param) { |
4099 | elems->wmm_param = bss_elems->wmm_param; |
4100 | sdata_info(sdata, |
4101 | "AP bug: WMM param missing from AssocResp\n" ); |
4102 | } |
4103 | |
4104 | /* |
4105 | * Also check if we requested HT/VHT, otherwise the AP doesn't |
4106 | * have to include the IEs in the (re)association response. |
4107 | */ |
4108 | if (!elems->ht_cap_elem && bss_elems->ht_cap_elem && |
4109 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) { |
4110 | elems->ht_cap_elem = bss_elems->ht_cap_elem; |
4111 | sdata_info(sdata, |
4112 | "AP bug: HT capability missing from AssocResp\n" ); |
4113 | } |
4114 | if (!elems->ht_operation && bss_elems->ht_operation && |
4115 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) { |
4116 | elems->ht_operation = bss_elems->ht_operation; |
4117 | sdata_info(sdata, |
4118 | "AP bug: HT operation missing from AssocResp\n" ); |
4119 | } |
4120 | if (!elems->vht_cap_elem && bss_elems->vht_cap_elem && |
4121 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { |
4122 | elems->vht_cap_elem = bss_elems->vht_cap_elem; |
4123 | sdata_info(sdata, |
4124 | "AP bug: VHT capa missing from AssocResp\n" ); |
4125 | } |
4126 | if (!elems->vht_operation && bss_elems->vht_operation && |
4127 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { |
4128 | elems->vht_operation = bss_elems->vht_operation; |
4129 | sdata_info(sdata, |
4130 | "AP bug: VHT operation missing from AssocResp\n" ); |
4131 | } |
4132 | |
4133 | kfree(objp: bss_elems); |
4134 | } |
4135 | |
4136 | /* |
4137 | * We previously checked these in the beacon/probe response, so |
4138 | * they should be present here. This is just a safety net. |
4139 | */ |
4140 | if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && |
4141 | (!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) { |
4142 | sdata_info(sdata, |
4143 | "HT AP is missing WMM params or HT capability/operation\n" ); |
4144 | ret = false; |
4145 | goto out; |
4146 | } |
4147 | |
4148 | if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && |
4149 | (!elems->vht_cap_elem || !elems->vht_operation)) { |
4150 | sdata_info(sdata, |
4151 | "VHT AP is missing VHT capability/operation\n" ); |
4152 | ret = false; |
4153 | goto out; |
4154 | } |
4155 | |
4156 | if (is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && |
4157 | !elems->he_6ghz_capa) { |
4158 | sdata_info(sdata, |
4159 | "HE 6 GHz AP is missing HE 6 GHz band capability\n" ); |
4160 | ret = false; |
4161 | goto out; |
4162 | } |
4163 | |
4164 | if (WARN_ON(!link->conf->chandef.chan)) { |
4165 | ret = false; |
4166 | goto out; |
4167 | } |
4168 | sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; |
4169 | |
4170 | if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && |
4171 | (!elems->he_cap || !elems->he_operation)) { |
4172 | sdata_info(sdata, |
4173 | "HE AP is missing HE capability/operation\n" ); |
4174 | ret = false; |
4175 | goto out; |
4176 | } |
4177 | |
4178 | /* Set up internal HT/VHT capabilities */ |
4179 | if (elems->ht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) |
4180 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, |
4181 | ht_cap_ie: elems->ht_cap_elem, |
4182 | link_sta); |
4183 | |
4184 | if (elems->vht_cap_elem && |
4185 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { |
4186 | const struct ieee80211_vht_cap *bss_vht_cap = NULL; |
4187 | const struct cfg80211_bss_ies *ies; |
4188 | |
4189 | /* |
4190 | * Cisco AP module 9115 with FW 17.3 has a bug and sends a |
4191 | * too large maximum MPDU length in the association response |
4192 | * (indicating 12k) that it cannot actually process ... |
4193 | * Work around that. |
4194 | */ |
4195 | rcu_read_lock(); |
4196 | ies = rcu_dereference(cbss->ies); |
4197 | if (ies) { |
4198 | const struct element *elem; |
4199 | |
4200 | elem = cfg80211_find_elem(eid: WLAN_EID_VHT_CAPABILITY, |
4201 | ies: ies->data, len: ies->len); |
4202 | if (elem && elem->datalen >= sizeof(*bss_vht_cap)) |
4203 | bss_vht_cap = (const void *)elem->data; |
4204 | } |
4205 | |
4206 | ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, |
4207 | vht_cap_ie: elems->vht_cap_elem, |
4208 | vht_cap_ie2: bss_vht_cap, link_sta); |
4209 | rcu_read_unlock(); |
4210 | } |
4211 | |
4212 | if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && |
4213 | elems->he_cap) { |
4214 | ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, |
4215 | he_cap_ie: elems->he_cap, |
4216 | he_cap_len: elems->he_cap_len, |
4217 | he_6ghz_capa: elems->he_6ghz_capa, |
4218 | link_sta); |
4219 | |
4220 | bss_conf->he_support = link_sta->pub->he_cap.has_he; |
4221 | if (elems->rsnx && elems->rsnx_len && |
4222 | (elems->rsnx[0] & WLAN_RSNX_CAPA_PROTECTED_TWT) && |
4223 | wiphy_ext_feature_isset(wiphy: local->hw.wiphy, |
4224 | ftidx: NL80211_EXT_FEATURE_PROTECTED_TWT)) |
4225 | bss_conf->twt_protected = true; |
4226 | else |
4227 | bss_conf->twt_protected = false; |
4228 | |
4229 | *changed |= ieee80211_recalc_twt_req(sdata, sband, link, |
4230 | link_sta, elems); |
4231 | |
4232 | if (elems->eht_operation && elems->eht_cap && |
4233 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) { |
4234 | ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, |
4235 | he_cap_ie: elems->he_cap, |
4236 | he_cap_len: elems->he_cap_len, |
4237 | eht_cap_ie_elem: elems->eht_cap, |
4238 | eht_cap_len: elems->eht_cap_len, |
4239 | link_sta); |
4240 | |
4241 | bss_conf->eht_support = link_sta->pub->eht_cap.has_eht; |
4242 | *changed |= BSS_CHANGED_EHT_PUNCTURING; |
4243 | } else { |
4244 | bss_conf->eht_support = false; |
4245 | } |
4246 | } else { |
4247 | bss_conf->he_support = false; |
4248 | bss_conf->twt_requester = false; |
4249 | bss_conf->twt_protected = false; |
4250 | bss_conf->eht_support = false; |
4251 | } |
4252 | |
4253 | bss_conf->twt_broadcast = |
4254 | ieee80211_twt_bcast_support(sdata, bss_conf, sband, link_sta); |
4255 | |
4256 | if (bss_conf->he_support) { |
4257 | bss_conf->he_bss_color.color = |
4258 | le32_get_bits(v: elems->he_operation->he_oper_params, |
4259 | IEEE80211_HE_OPERATION_BSS_COLOR_MASK); |
4260 | bss_conf->he_bss_color.partial = |
4261 | le32_get_bits(v: elems->he_operation->he_oper_params, |
4262 | IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR); |
4263 | bss_conf->he_bss_color.enabled = |
4264 | !le32_get_bits(v: elems->he_operation->he_oper_params, |
4265 | IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED); |
4266 | |
4267 | if (bss_conf->he_bss_color.enabled) |
4268 | *changed |= BSS_CHANGED_HE_BSS_COLOR; |
4269 | |
4270 | bss_conf->htc_trig_based_pkt_ext = |
4271 | le32_get_bits(v: elems->he_operation->he_oper_params, |
4272 | IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK); |
4273 | bss_conf->frame_time_rts_th = |
4274 | le32_get_bits(v: elems->he_operation->he_oper_params, |
4275 | IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK); |
4276 | |
4277 | bss_conf->uora_exists = !!elems->uora_element; |
4278 | if (elems->uora_element) |
4279 | bss_conf->uora_ocw_range = elems->uora_element[0]; |
4280 | |
4281 | ieee80211_he_op_ie_to_bss_conf(vif: &sdata->vif, he_op_ie_elem: elems->he_operation); |
4282 | ieee80211_he_spr_ie_to_bss_conf(vif: &sdata->vif, he_spr_ie_elem: elems->he_spr); |
4283 | /* TODO: OPEN: what happens if BSS color disable is set? */ |
4284 | } |
4285 | |
4286 | if (cbss->transmitted_bss) { |
4287 | bss_conf->nontransmitted = true; |
4288 | ether_addr_copy(dst: bss_conf->transmitter_bssid, |
4289 | src: cbss->transmitted_bss->bssid); |
4290 | bss_conf->bssid_indicator = cbss->max_bssid_indicator; |
4291 | bss_conf->bssid_index = cbss->bssid_index; |
4292 | } |
4293 | |
4294 | /* |
4295 | * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data |
4296 | * in their association response, so ignore that data for our own |
4297 | * configuration. If it changed since the last beacon, we'll get the |
4298 | * next beacon and update then. |
4299 | */ |
4300 | |
4301 | /* |
4302 | * If an operating mode notification IE is present, override the |
4303 | * NSS calculation (that would be done in rate_control_rate_init()) |
4304 | * and use the # of streams from that element. |
4305 | */ |
4306 | if (elems->opmode_notif && |
4307 | !(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) { |
4308 | u8 nss; |
4309 | |
4310 | nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; |
4311 | nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; |
4312 | nss += 1; |
4313 | link_sta->pub->rx_nss = nss; |
4314 | } |
4315 | |
4316 | /* |
4317 | * Always handle WMM once after association regardless |
4318 | * of the first value the AP uses. Setting -1 here has |
4319 | * that effect because the AP values is an unsigned |
4320 | * 4-bit value. |
4321 | */ |
4322 | link->u.mgd.wmm_last_param_set = -1; |
4323 | link->u.mgd.mu_edca_last_param_set = -1; |
4324 | |
4325 | if (link->u.mgd.disable_wmm_tracking) { |
4326 | ieee80211_set_wmm_default(link, bss_notify: false, enable_qos: false); |
4327 | } else if (!ieee80211_sta_wmm_params(local, link, wmm_param: elems->wmm_param, |
4328 | wmm_param_len: elems->wmm_param_len, |
4329 | mu_edca: elems->mu_edca_param_set)) { |
4330 | /* still enable QoS since we might have HT/VHT */ |
4331 | ieee80211_set_wmm_default(link, bss_notify: false, enable_qos: true); |
4332 | /* disable WMM tracking in this case to disable |
4333 | * tracking WMM parameter changes in the beacon if |
4334 | * the parameters weren't actually valid. Doing so |
4335 | * avoids changing parameters very strangely when |
4336 | * the AP is going back and forth between valid and |
4337 | * invalid parameters. |
4338 | */ |
4339 | link->u.mgd.disable_wmm_tracking = true; |
4340 | } |
4341 | |
4342 | if (elems->max_idle_period_ie) { |
4343 | bss_conf->max_idle_period = |
4344 | le16_to_cpu(elems->max_idle_period_ie->max_idle_period); |
4345 | bss_conf->protected_keep_alive = |
4346 | !!(elems->max_idle_period_ie->idle_options & |
4347 | WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE); |
4348 | *changed |= BSS_CHANGED_KEEP_ALIVE; |
4349 | } else { |
4350 | bss_conf->max_idle_period = 0; |
4351 | bss_conf->protected_keep_alive = false; |
4352 | } |
4353 | |
4354 | /* set assoc capability (AID was already set earlier), |
4355 | * ieee80211_set_associated() will tell the driver */ |
4356 | bss_conf->assoc_capability = capab_info; |
4357 | |
4358 | ret = true; |
4359 | out: |
4360 | kfree(objp: elems); |
4361 | kfree(objp: bss_ies); |
4362 | return ret; |
4363 | } |
4364 | |
4365 | static int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link, |
4366 | struct sta_info *sta, |
4367 | struct link_sta_info *link_sta, |
4368 | struct cfg80211_bss *cbss) |
4369 | { |
4370 | struct ieee80211_sub_if_data *sdata = link->sdata; |
4371 | struct ieee80211_local *local = sdata->local; |
4372 | struct ieee80211_bss *bss = (void *)cbss->priv; |
4373 | u32 rates = 0, basic_rates = 0; |
4374 | bool have_higher_than_11mbit = false; |
4375 | int min_rate = INT_MAX, min_rate_index = -1; |
4376 | struct ieee80211_supported_band *sband; |
4377 | |
4378 | memcpy(link_sta->addr, cbss->bssid, ETH_ALEN); |
4379 | memcpy(link_sta->pub->addr, cbss->bssid, ETH_ALEN); |
4380 | |
4381 | /* TODO: S1G Basic Rate Set is expressed elsewhere */ |
4382 | if (cbss->channel->band == NL80211_BAND_S1GHZ) { |
4383 | ieee80211_s1g_sta_rate_init(sta); |
4384 | return 0; |
4385 | } |
4386 | |
4387 | sband = local->hw.wiphy->bands[cbss->channel->band]; |
4388 | |
4389 | ieee80211_get_rates(sband, supp_rates: bss->supp_rates, supp_rates_len: bss->supp_rates_len, |
4390 | rates: &rates, basic_rates: &basic_rates, have_higher_than_11mbit: &have_higher_than_11mbit, |
4391 | min_rate: &min_rate, min_rate_index: &min_rate_index); |
4392 | |
4393 | /* |
4394 | * This used to be a workaround for basic rates missing |
4395 | * in the association response frame. Now that we no |
4396 | * longer use the basic rates from there, it probably |
4397 | * doesn't happen any more, but keep the workaround so |
4398 | * in case some *other* APs are buggy in different ways |
4399 | * we can connect -- with a warning. |
4400 | * Allow this workaround only in case the AP provided at least |
4401 | * one rate. |
4402 | */ |
4403 | if (min_rate_index < 0) { |
4404 | link_info(link, "No legacy rates in association response\n" ); |
4405 | return -EINVAL; |
4406 | } else if (!basic_rates) { |
4407 | link_info(link, "No basic rates, using min rate instead\n" ); |
4408 | basic_rates = BIT(min_rate_index); |
4409 | } |
4410 | |
4411 | if (rates) |
4412 | link_sta->pub->supp_rates[cbss->channel->band] = rates; |
4413 | else |
4414 | link_info(link, "No rates found, keeping mandatory only\n" ); |
4415 | |
4416 | link->conf->basic_rates = basic_rates; |
4417 | |
4418 | /* cf. IEEE 802.11 9.2.12 */ |
4419 | link->operating_11g_mode = sband->band == NL80211_BAND_2GHZ && |
4420 | have_higher_than_11mbit; |
4421 | |
4422 | return 0; |
4423 | } |
4424 | |
4425 | static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, |
4426 | struct cfg80211_bss *cbss) |
4427 | { |
4428 | struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; |
4429 | const struct element *ht_cap_elem, *vht_cap_elem; |
4430 | const struct cfg80211_bss_ies *ies; |
4431 | const struct ieee80211_ht_cap *ht_cap; |
4432 | const struct ieee80211_vht_cap *vht_cap; |
4433 | const struct ieee80211_he_cap_elem *he_cap; |
4434 | const struct element *he_cap_elem; |
4435 | u16 mcs_80_map, mcs_160_map; |
4436 | int i, mcs_nss_size; |
4437 | bool support_160; |
4438 | u8 chains = 1; |
4439 | |
4440 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) |
4441 | return chains; |
4442 | |
4443 | ht_cap_elem = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_HT_CAPABILITY); |
4444 | if (ht_cap_elem && ht_cap_elem->datalen >= sizeof(*ht_cap)) { |
4445 | ht_cap = (void *)ht_cap_elem->data; |
4446 | chains = ieee80211_mcs_to_chains(mcs: &ht_cap->mcs); |
4447 | /* |
4448 | * TODO: use "Tx Maximum Number Spatial Streams Supported" and |
4449 | * "Tx Unequal Modulation Supported" fields. |
4450 | */ |
4451 | } |
4452 | |
4453 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) |
4454 | return chains; |
4455 | |
4456 | vht_cap_elem = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_VHT_CAPABILITY); |
4457 | if (vht_cap_elem && vht_cap_elem->datalen >= sizeof(*vht_cap)) { |
4458 | u8 nss; |
4459 | u16 tx_mcs_map; |
4460 | |
4461 | vht_cap = (void *)vht_cap_elem->data; |
4462 | tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); |
4463 | for (nss = 8; nss > 0; nss--) { |
4464 | if (((tx_mcs_map >> (2 * (nss - 1))) & 3) != |
4465 | IEEE80211_VHT_MCS_NOT_SUPPORTED) |
4466 | break; |
4467 | } |
4468 | /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */ |
4469 | chains = max(chains, nss); |
4470 | } |
4471 | |
4472 | if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) |
4473 | return chains; |
4474 | |
4475 | ies = rcu_dereference(cbss->ies); |
4476 | he_cap_elem = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_HE_CAPABILITY, |
4477 | ies: ies->data, len: ies->len); |
4478 | |
4479 | if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap)) |
4480 | return chains; |
4481 | |
4482 | /* skip one byte ext_tag_id */ |
4483 | he_cap = (void *)(he_cap_elem->data + 1); |
4484 | mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); |
4485 | |
4486 | /* invalid HE IE */ |
4487 | if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap)) |
4488 | return chains; |
4489 | |
4490 | /* mcs_nss is right after he_cap info */ |
4491 | he_mcs_nss_supp = (void *)(he_cap + 1); |
4492 | |
4493 | mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); |
4494 | |
4495 | for (i = 7; i >= 0; i--) { |
4496 | u8 mcs_80 = mcs_80_map >> (2 * i) & 3; |
4497 | |
4498 | if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { |
4499 | chains = max_t(u8, chains, i + 1); |
4500 | break; |
4501 | } |
4502 | } |
4503 | |
4504 | support_160 = he_cap->phy_cap_info[0] & |
4505 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; |
4506 | |
4507 | if (!support_160) |
4508 | return chains; |
4509 | |
4510 | mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160); |
4511 | for (i = 7; i >= 0; i--) { |
4512 | u8 mcs_160 = mcs_160_map >> (2 * i) & 3; |
4513 | |
4514 | if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { |
4515 | chains = max_t(u8, chains, i + 1); |
4516 | break; |
4517 | } |
4518 | } |
4519 | |
4520 | return chains; |
4521 | } |
4522 | |
4523 | static bool |
4524 | ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, |
4525 | const struct cfg80211_bss_ies *ies, |
4526 | const struct ieee80211_he_operation *he_op) |
4527 | { |
4528 | const struct element *he_cap_elem; |
4529 | const struct ieee80211_he_cap_elem *he_cap; |
4530 | struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; |
4531 | u16 mcs_80_map_tx, mcs_80_map_rx; |
4532 | u16 ap_min_req_set; |
4533 | int mcs_nss_size; |
4534 | int nss; |
4535 | |
4536 | he_cap_elem = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_HE_CAPABILITY, |
4537 | ies: ies->data, len: ies->len); |
4538 | |
4539 | if (!he_cap_elem) |
4540 | return false; |
4541 | |
4542 | /* invalid HE IE */ |
4543 | if (he_cap_elem->datalen < 1 + sizeof(*he_cap)) { |
4544 | sdata_info(sdata, |
4545 | "Invalid HE elem, Disable HE\n" ); |
4546 | return false; |
4547 | } |
4548 | |
4549 | /* skip one byte ext_tag_id */ |
4550 | he_cap = (void *)(he_cap_elem->data + 1); |
4551 | mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); |
4552 | |
4553 | /* invalid HE IE */ |
4554 | if (he_cap_elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size) { |
4555 | sdata_info(sdata, |
4556 | "Invalid HE elem with nss size, Disable HE\n" ); |
4557 | return false; |
4558 | } |
4559 | |
4560 | /* mcs_nss is right after he_cap info */ |
4561 | he_mcs_nss_supp = (void *)(he_cap + 1); |
4562 | |
4563 | mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); |
4564 | mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80); |
4565 | |
4566 | /* P802.11-REVme/D0.3 |
4567 | * 27.1.1 Introduction to the HE PHY |
4568 | * ... |
4569 | * An HE STA shall support the following features: |
4570 | * ... |
4571 | * Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all |
4572 | * supported channel widths for HE SU PPDUs |
4573 | */ |
4574 | if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED || |
4575 | (mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) { |
4576 | sdata_info(sdata, |
4577 | "Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n" , |
4578 | mcs_80_map_tx, mcs_80_map_rx); |
4579 | return false; |
4580 | } |
4581 | |
4582 | if (!he_op) |
4583 | return true; |
4584 | |
4585 | ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); |
4586 | |
4587 | /* |
4588 | * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all |
4589 | * zeroes, which is nonsense, and completely inconsistent with itself |
4590 | * (it doesn't have 8 streams). Accept the settings in this case anyway. |
4591 | */ |
4592 | if (!ap_min_req_set) |
4593 | return true; |
4594 | |
4595 | /* make sure the AP is consistent with itself |
4596 | * |
4597 | * P802.11-REVme/D0.3 |
4598 | * 26.17.1 Basic HE BSS operation |
4599 | * |
4600 | * A STA that is operating in an HE BSS shall be able to receive and |
4601 | * transmit at each of the <HE-MCS, NSS> tuple values indicated by the |
4602 | * Basic HE-MCS And NSS Set field of the HE Operation parameter of the |
4603 | * MLME-START.request primitive and shall be able to receive at each of |
4604 | * the <HE-MCS, NSS> tuple values indicated by the Supported HE-MCS and |
4605 | * NSS Set field in the HE Capabilities parameter of the MLMESTART.request |
4606 | * primitive |
4607 | */ |
4608 | for (nss = 8; nss > 0; nss--) { |
4609 | u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; |
4610 | u8 ap_rx_val; |
4611 | u8 ap_tx_val; |
4612 | |
4613 | if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED) |
4614 | continue; |
4615 | |
4616 | ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3; |
4617 | ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3; |
4618 | |
4619 | if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || |
4620 | ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || |
4621 | ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) { |
4622 | sdata_info(sdata, |
4623 | "Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n" , |
4624 | nss, ap_rx_val, ap_rx_val, ap_op_val); |
4625 | return false; |
4626 | } |
4627 | } |
4628 | |
4629 | return true; |
4630 | } |
4631 | |
4632 | static bool |
4633 | ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata, |
4634 | struct ieee80211_supported_band *sband, |
4635 | const struct ieee80211_he_operation *he_op) |
4636 | { |
4637 | const struct ieee80211_sta_he_cap *sta_he_cap = |
4638 | ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif); |
4639 | u16 ap_min_req_set; |
4640 | int i; |
4641 | |
4642 | if (!sta_he_cap || !he_op) |
4643 | return false; |
4644 | |
4645 | ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); |
4646 | |
4647 | /* |
4648 | * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all |
4649 | * zeroes, which is nonsense, and completely inconsistent with itself |
4650 | * (it doesn't have 8 streams). Accept the settings in this case anyway. |
4651 | */ |
4652 | if (!ap_min_req_set) |
4653 | return true; |
4654 | |
4655 | /* Need to go over for 80MHz, 160MHz and for 80+80 */ |
4656 | for (i = 0; i < 3; i++) { |
4657 | const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp = |
4658 | &sta_he_cap->he_mcs_nss_supp; |
4659 | u16 sta_mcs_map_rx = |
4660 | le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]); |
4661 | u16 sta_mcs_map_tx = |
4662 | le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]); |
4663 | u8 nss; |
4664 | bool verified = true; |
4665 | |
4666 | /* |
4667 | * For each band there is a maximum of 8 spatial streams |
4668 | * possible. Each of the sta_mcs_map_* is a 16-bit struct built |
4669 | * of 2 bits per NSS (1-8), with the values defined in enum |
4670 | * ieee80211_he_mcs_support. Need to make sure STA TX and RX |
4671 | * capabilities aren't less than the AP's minimum requirements |
4672 | * for this HE BSS per SS. |
4673 | * It is enough to find one such band that meets the reqs. |
4674 | */ |
4675 | for (nss = 8; nss > 0; nss--) { |
4676 | u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3; |
4677 | u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3; |
4678 | u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; |
4679 | |
4680 | if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED) |
4681 | continue; |
4682 | |
4683 | /* |
4684 | * Make sure the HE AP doesn't require MCSs that aren't |
4685 | * supported by the client as required by spec |
4686 | * |
4687 | * P802.11-REVme/D0.3 |
4688 | * 26.17.1 Basic HE BSS operation |
4689 | * |
4690 | * An HE STA shall not attempt to join * (MLME-JOIN.request primitive) |
4691 | * a BSS, unless it supports (i.e., is able to both transmit and |
4692 | * receive using) all of the <HE-MCS, NSS> tuples in the basic |
4693 | * HE-MCS and NSS set. |
4694 | */ |
4695 | if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || |
4696 | sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || |
4697 | (ap_val > sta_rx_val) || (ap_val > sta_tx_val)) { |
4698 | verified = false; |
4699 | break; |
4700 | } |
4701 | } |
4702 | |
4703 | if (verified) |
4704 | return true; |
4705 | } |
4706 | |
4707 | /* If here, STA doesn't meet AP's HE min requirements */ |
4708 | return false; |
4709 | } |
4710 | |
4711 | static u8 |
4712 | ieee80211_get_eht_cap_mcs_nss(const struct ieee80211_sta_he_cap *sta_he_cap, |
4713 | const struct ieee80211_sta_eht_cap *sta_eht_cap, |
4714 | unsigned int idx, int bw) |
4715 | { |
4716 | u8 he_phy_cap0 = sta_he_cap->he_cap_elem.phy_cap_info[0]; |
4717 | u8 eht_phy_cap0 = sta_eht_cap->eht_cap_elem.phy_cap_info[0]; |
4718 | |
4719 | /* handle us being a 20 MHz-only EHT STA - with four values |
4720 | * for MCS 0-7, 8-9, 10-11, 12-13. |
4721 | */ |
4722 | if (!(he_phy_cap0 & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) |
4723 | return sta_eht_cap->eht_mcs_nss_supp.only_20mhz.rx_tx_max_nss[idx]; |
4724 | |
4725 | /* the others have MCS 0-9 together, rather than separately from 0-7 */ |
4726 | if (idx > 0) |
4727 | idx--; |
4728 | |
4729 | switch (bw) { |
4730 | case 0: |
4731 | return sta_eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_max_nss[idx]; |
4732 | case 1: |
4733 | if (!(he_phy_cap0 & |
4734 | (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | |
4735 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G))) |
4736 | return 0xff; /* pass check */ |
4737 | return sta_eht_cap->eht_mcs_nss_supp.bw._160.rx_tx_max_nss[idx]; |
4738 | case 2: |
4739 | if (!(eht_phy_cap0 & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)) |
4740 | return 0xff; /* pass check */ |
4741 | return sta_eht_cap->eht_mcs_nss_supp.bw._320.rx_tx_max_nss[idx]; |
4742 | } |
4743 | |
4744 | WARN_ON(1); |
4745 | return 0; |
4746 | } |
4747 | |
4748 | static bool |
4749 | ieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata, |
4750 | struct ieee80211_supported_band *sband, |
4751 | const struct ieee80211_eht_operation *eht_op) |
4752 | { |
4753 | const struct ieee80211_sta_he_cap *sta_he_cap = |
4754 | ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif); |
4755 | const struct ieee80211_sta_eht_cap *sta_eht_cap = |
4756 | ieee80211_get_eht_iftype_cap_vif(sband, vif: &sdata->vif); |
4757 | const struct ieee80211_eht_mcs_nss_supp_20mhz_only *req; |
4758 | unsigned int i; |
4759 | |
4760 | if (!sta_he_cap || !sta_eht_cap || !eht_op) |
4761 | return false; |
4762 | |
4763 | req = &eht_op->basic_mcs_nss; |
4764 | |
4765 | for (i = 0; i < ARRAY_SIZE(req->rx_tx_max_nss); i++) { |
4766 | u8 req_rx_nss, req_tx_nss; |
4767 | unsigned int bw; |
4768 | |
4769 | req_rx_nss = u8_get_bits(v: req->rx_tx_max_nss[i], |
4770 | IEEE80211_EHT_MCS_NSS_RX); |
4771 | req_tx_nss = u8_get_bits(v: req->rx_tx_max_nss[i], |
4772 | IEEE80211_EHT_MCS_NSS_TX); |
4773 | |
4774 | for (bw = 0; bw < 3; bw++) { |
4775 | u8 have, have_rx_nss, have_tx_nss; |
4776 | |
4777 | have = ieee80211_get_eht_cap_mcs_nss(sta_he_cap, |
4778 | sta_eht_cap, |
4779 | idx: i, bw); |
4780 | have_rx_nss = u8_get_bits(v: have, |
4781 | IEEE80211_EHT_MCS_NSS_RX); |
4782 | have_tx_nss = u8_get_bits(v: have, |
4783 | IEEE80211_EHT_MCS_NSS_TX); |
4784 | |
4785 | if (req_rx_nss > have_rx_nss || |
4786 | req_tx_nss > have_tx_nss) |
4787 | return false; |
4788 | } |
4789 | } |
4790 | |
4791 | return true; |
4792 | } |
4793 | |
4794 | static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, |
4795 | struct ieee80211_link_data *link, |
4796 | struct cfg80211_bss *cbss, |
4797 | bool mlo, |
4798 | ieee80211_conn_flags_t *conn_flags) |
4799 | { |
4800 | struct ieee80211_local *local = sdata->local; |
4801 | const struct ieee80211_ht_cap *ht_cap = NULL; |
4802 | const struct ieee80211_ht_operation *ht_oper = NULL; |
4803 | const struct ieee80211_vht_operation *vht_oper = NULL; |
4804 | const struct ieee80211_he_operation *he_oper = NULL; |
4805 | const struct ieee80211_eht_operation *eht_oper = NULL; |
4806 | const struct ieee80211_s1g_oper_ie *s1g_oper = NULL; |
4807 | struct ieee80211_supported_band *sband; |
4808 | struct cfg80211_chan_def chandef; |
4809 | bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; |
4810 | bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; |
4811 | bool supports_mlo = false; |
4812 | struct ieee80211_bss *bss = (void *)cbss->priv; |
4813 | struct ieee80211_elems_parse_params parse_params = { |
4814 | .link_id = -1, |
4815 | .from_ap = true, |
4816 | }; |
4817 | struct ieee802_11_elems *elems; |
4818 | const struct cfg80211_bss_ies *ies; |
4819 | int ret; |
4820 | u32 i; |
4821 | bool have_80mhz; |
4822 | |
4823 | lockdep_assert_wiphy(local->hw.wiphy); |
4824 | |
4825 | rcu_read_lock(); |
4826 | |
4827 | ies = rcu_dereference(cbss->ies); |
4828 | parse_params.start = ies->data; |
4829 | parse_params.len = ies->len; |
4830 | elems = ieee802_11_parse_elems_full(params: &parse_params); |
4831 | if (!elems) { |
4832 | rcu_read_unlock(); |
4833 | return -ENOMEM; |
4834 | } |
4835 | |
4836 | sband = local->hw.wiphy->bands[cbss->channel->band]; |
4837 | |
4838 | *conn_flags &= ~(IEEE80211_CONN_DISABLE_40MHZ | |
4839 | IEEE80211_CONN_DISABLE_80P80MHZ | |
4840 | IEEE80211_CONN_DISABLE_160MHZ); |
4841 | |
4842 | /* disable HT/VHT/HE if we don't support them */ |
4843 | if (!sband->ht_cap.ht_supported && !is_6ghz) { |
4844 | mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n" ); |
4845 | *conn_flags |= IEEE80211_CONN_DISABLE_HT; |
4846 | *conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
4847 | *conn_flags |= IEEE80211_CONN_DISABLE_HE; |
4848 | *conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
4849 | } |
4850 | |
4851 | if (!sband->vht_cap.vht_supported && is_5ghz) { |
4852 | mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n" ); |
4853 | *conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
4854 | *conn_flags |= IEEE80211_CONN_DISABLE_HE; |
4855 | *conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
4856 | } |
4857 | |
4858 | if (!ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif)) { |
4859 | mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n" ); |
4860 | *conn_flags |= IEEE80211_CONN_DISABLE_HE; |
4861 | *conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
4862 | } |
4863 | |
4864 | if (!ieee80211_get_eht_iftype_cap_vif(sband, vif: &sdata->vif)) { |
4865 | mlme_dbg(sdata, "EHT not supported, disabling EHT\n" ); |
4866 | *conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
4867 | } |
4868 | |
4869 | if (!(*conn_flags & IEEE80211_CONN_DISABLE_HT) && !is_6ghz) { |
4870 | ht_oper = elems->ht_operation; |
4871 | ht_cap = elems->ht_cap_elem; |
4872 | |
4873 | if (!ht_cap) { |
4874 | *conn_flags |= IEEE80211_CONN_DISABLE_HT; |
4875 | ht_oper = NULL; |
4876 | } |
4877 | } |
4878 | |
4879 | if (!(*conn_flags & IEEE80211_CONN_DISABLE_VHT) && !is_6ghz) { |
4880 | vht_oper = elems->vht_operation; |
4881 | if (vht_oper && !ht_oper) { |
4882 | vht_oper = NULL; |
4883 | sdata_info(sdata, |
4884 | "AP advertised VHT without HT, disabling HT/VHT/HE\n" ); |
4885 | *conn_flags |= IEEE80211_CONN_DISABLE_HT; |
4886 | *conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
4887 | *conn_flags |= IEEE80211_CONN_DISABLE_HE; |
4888 | *conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
4889 | } |
4890 | |
4891 | if (!elems->vht_cap_elem) { |
4892 | *conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
4893 | vht_oper = NULL; |
4894 | } |
4895 | } |
4896 | |
4897 | if (!(*conn_flags & IEEE80211_CONN_DISABLE_HE)) { |
4898 | he_oper = elems->he_operation; |
4899 | |
4900 | if (link && is_6ghz) { |
4901 | struct ieee80211_bss_conf *bss_conf; |
4902 | u8 j = 0; |
4903 | |
4904 | bss_conf = link->conf; |
4905 | |
4906 | if (elems->pwr_constr_elem) |
4907 | bss_conf->pwr_reduction = *elems->pwr_constr_elem; |
4908 | |
4909 | BUILD_BUG_ON(ARRAY_SIZE(bss_conf->tx_pwr_env) != |
4910 | ARRAY_SIZE(elems->tx_pwr_env)); |
4911 | |
4912 | for (i = 0; i < elems->tx_pwr_env_num; i++) { |
4913 | if (elems->tx_pwr_env_len[i] > |
4914 | sizeof(bss_conf->tx_pwr_env[j])) |
4915 | continue; |
4916 | |
4917 | bss_conf->tx_pwr_env_num++; |
4918 | memcpy(&bss_conf->tx_pwr_env[j], elems->tx_pwr_env[i], |
4919 | elems->tx_pwr_env_len[i]); |
4920 | j++; |
4921 | } |
4922 | } |
4923 | |
4924 | if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_op: he_oper) || |
4925 | !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_op: he_oper)) |
4926 | *conn_flags |= IEEE80211_CONN_DISABLE_HE | |
4927 | IEEE80211_CONN_DISABLE_EHT; |
4928 | } |
4929 | |
4930 | /* |
4931 | * EHT requires HE to be supported as well. Specifically for 6 GHz |
4932 | * channels, the operation channel information can only be deduced from |
4933 | * both the 6 GHz operation information (from the HE operation IE) and |
4934 | * EHT operation. |
4935 | */ |
4936 | if (!(*conn_flags & |
4937 | (IEEE80211_CONN_DISABLE_HE | |
4938 | IEEE80211_CONN_DISABLE_EHT)) && |
4939 | he_oper) { |
4940 | const struct cfg80211_bss_ies *cbss_ies; |
4941 | const struct element *eht_ml_elem; |
4942 | const u8 *eht_oper_ie; |
4943 | |
4944 | cbss_ies = rcu_dereference(cbss->ies); |
4945 | eht_oper_ie = cfg80211_find_ext_ie(ext_eid: WLAN_EID_EXT_EHT_OPERATION, |
4946 | ies: cbss_ies->data, len: cbss_ies->len); |
4947 | if (eht_oper_ie && eht_oper_ie[1] >= |
4948 | 1 + sizeof(struct ieee80211_eht_operation)) |
4949 | eht_oper = (void *)(eht_oper_ie + 3); |
4950 | else |
4951 | eht_oper = NULL; |
4952 | |
4953 | if (!ieee80211_verify_sta_eht_mcs_support(sdata, sband, eht_op: eht_oper)) |
4954 | *conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
4955 | |
4956 | eht_ml_elem = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_EHT_MULTI_LINK, |
4957 | ies: cbss_ies->data, len: cbss_ies->len); |
4958 | |
4959 | /* data + 1 / datalen - 1 since it's an extended element */ |
4960 | if (!(*conn_flags & IEEE80211_CONN_DISABLE_EHT) && |
4961 | eht_ml_elem && |
4962 | ieee80211_mle_type_ok(data: eht_ml_elem->data + 1, |
4963 | IEEE80211_ML_CONTROL_TYPE_BASIC, |
4964 | len: eht_ml_elem->datalen - 1)) { |
4965 | supports_mlo = true; |
4966 | |
4967 | sdata->vif.cfg.eml_cap = |
4968 | ieee80211_mle_get_eml_cap(data: eht_ml_elem->data + 1); |
4969 | sdata->vif.cfg.eml_med_sync_delay = |
4970 | ieee80211_mle_get_eml_med_sync_delay(data: eht_ml_elem->data + 1); |
4971 | } |
4972 | } |
4973 | |
4974 | /* Allow VHT if at least one channel on the sband supports 80 MHz */ |
4975 | have_80mhz = false; |
4976 | for (i = 0; i < sband->n_channels; i++) { |
4977 | if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | |
4978 | IEEE80211_CHAN_NO_80MHZ)) |
4979 | continue; |
4980 | |
4981 | have_80mhz = true; |
4982 | break; |
4983 | } |
4984 | |
4985 | if (!have_80mhz) { |
4986 | sdata_info(sdata, "80 MHz not supported, disabling VHT\n" ); |
4987 | *conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
4988 | } |
4989 | |
4990 | if (sband->band == NL80211_BAND_S1GHZ) { |
4991 | s1g_oper = elems->s1g_oper; |
4992 | if (!s1g_oper) |
4993 | sdata_info(sdata, |
4994 | "AP missing S1G operation element?\n" ); |
4995 | } |
4996 | |
4997 | *conn_flags |= |
4998 | ieee80211_determine_chantype(sdata, link, conn_flags: *conn_flags, |
4999 | sband, |
5000 | channel: cbss->channel, |
5001 | vht_cap_info: bss->vht_cap_info, |
5002 | ht_oper, vht_oper, |
5003 | he_oper, eht_oper, |
5004 | s1g_oper, |
5005 | chandef: &chandef, tracking: false); |
5006 | |
5007 | if (link) |
5008 | link->needed_rx_chains = |
5009 | min(ieee80211_max_rx_chains(link, cbss), |
5010 | local->rx_chains); |
5011 | |
5012 | rcu_read_unlock(); |
5013 | /* the element data was RCU protected so no longer valid anyway */ |
5014 | kfree(objp: elems); |
5015 | elems = NULL; |
5016 | |
5017 | if (*conn_flags & IEEE80211_CONN_DISABLE_HE && is_6ghz) { |
5018 | sdata_info(sdata, "Rejecting non-HE 6/7 GHz connection" ); |
5019 | return -EINVAL; |
5020 | } |
5021 | |
5022 | if (mlo && !supports_mlo) { |
5023 | sdata_info(sdata, "Rejecting MLO as it is not supported by AP\n" ); |
5024 | return -EINVAL; |
5025 | } |
5026 | |
5027 | if (!link) |
5028 | return 0; |
5029 | |
5030 | /* will change later if needed */ |
5031 | link->smps_mode = IEEE80211_SMPS_OFF; |
5032 | |
5033 | /* |
5034 | * If this fails (possibly due to channel context sharing |
5035 | * on incompatible channels, e.g. 80+80 and 160 sharing the |
5036 | * same control channel) try to use a smaller bandwidth. |
5037 | */ |
5038 | ret = ieee80211_link_use_channel(link, chandef: &chandef, |
5039 | mode: IEEE80211_CHANCTX_SHARED); |
5040 | |
5041 | /* don't downgrade for 5 and 10 MHz channels, though. */ |
5042 | if (chandef.width == NL80211_CHAN_WIDTH_5 || |
5043 | chandef.width == NL80211_CHAN_WIDTH_10) |
5044 | goto out; |
5045 | |
5046 | while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { |
5047 | *conn_flags |= |
5048 | ieee80211_chandef_downgrade(c: &chandef); |
5049 | ret = ieee80211_link_use_channel(link, chandef: &chandef, |
5050 | mode: IEEE80211_CHANCTX_SHARED); |
5051 | } |
5052 | out: |
5053 | return ret; |
5054 | } |
5055 | |
5056 | static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies, |
5057 | u8 *dtim_count, u8 *dtim_period) |
5058 | { |
5059 | const u8 *tim_ie = cfg80211_find_ie(eid: WLAN_EID_TIM, ies: ies->data, len: ies->len); |
5060 | const u8 *idx_ie = cfg80211_find_ie(eid: WLAN_EID_MULTI_BSSID_IDX, ies: ies->data, |
5061 | len: ies->len); |
5062 | const struct ieee80211_tim_ie *tim = NULL; |
5063 | const struct ieee80211_bssid_index *idx; |
5064 | bool valid = tim_ie && tim_ie[1] >= 2; |
5065 | |
5066 | if (valid) |
5067 | tim = (void *)(tim_ie + 2); |
5068 | |
5069 | if (dtim_count) |
5070 | *dtim_count = valid ? tim->dtim_count : 0; |
5071 | |
5072 | if (dtim_period) |
5073 | *dtim_period = valid ? tim->dtim_period : 0; |
5074 | |
5075 | /* Check if value is overridden by non-transmitted profile */ |
5076 | if (!idx_ie || idx_ie[1] < 3) |
5077 | return valid; |
5078 | |
5079 | idx = (void *)(idx_ie + 2); |
5080 | |
5081 | if (dtim_count) |
5082 | *dtim_count = idx->dtim_count; |
5083 | |
5084 | if (dtim_period) |
5085 | *dtim_period = idx->dtim_period; |
5086 | |
5087 | return true; |
5088 | } |
5089 | |
5090 | static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, |
5091 | struct ieee80211_mgmt *mgmt, |
5092 | struct ieee802_11_elems *elems, |
5093 | const u8 *elem_start, unsigned int elem_len) |
5094 | { |
5095 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
5096 | struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; |
5097 | struct ieee80211_local *local = sdata->local; |
5098 | unsigned int link_id; |
5099 | struct sta_info *sta; |
5100 | u64 changed[IEEE80211_MLD_MAX_NUM_LINKS] = {}; |
5101 | u16 valid_links = 0, dormant_links = 0; |
5102 | int err; |
5103 | |
5104 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
5105 | /* |
5106 | * station info was already allocated and inserted before |
5107 | * the association and should be available to us |
5108 | */ |
5109 | sta = sta_info_get(sdata, addr: assoc_data->ap_addr); |
5110 | if (WARN_ON(!sta)) |
5111 | goto out_err; |
5112 | |
5113 | if (ieee80211_vif_is_mld(vif: &sdata->vif)) { |
5114 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { |
5115 | if (!assoc_data->link[link_id].bss) |
5116 | continue; |
5117 | |
5118 | valid_links |= BIT(link_id); |
5119 | if (assoc_data->link[link_id].disabled) |
5120 | dormant_links |= BIT(link_id); |
5121 | |
5122 | if (link_id != assoc_data->assoc_link_id) { |
5123 | err = ieee80211_sta_allocate_link(sta, link_id); |
5124 | if (err) |
5125 | goto out_err; |
5126 | } |
5127 | } |
5128 | |
5129 | ieee80211_vif_set_links(sdata, new_links: valid_links, dormant_links); |
5130 | } |
5131 | |
5132 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { |
5133 | struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; |
5134 | struct ieee80211_link_data *link; |
5135 | struct link_sta_info *link_sta; |
5136 | |
5137 | if (!cbss) |
5138 | continue; |
5139 | |
5140 | link = sdata_dereference(sdata->link[link_id], sdata); |
5141 | if (WARN_ON(!link)) |
5142 | goto out_err; |
5143 | |
5144 | if (ieee80211_vif_is_mld(vif: &sdata->vif)) |
5145 | link_info(link, |
5146 | "local address %pM, AP link address %pM%s\n" , |
5147 | link->conf->addr, |
5148 | assoc_data->link[link_id].bss->bssid, |
5149 | link_id == assoc_data->assoc_link_id ? |
5150 | " (assoc)" : "" ); |
5151 | |
5152 | link_sta = rcu_dereference_protected(sta->link[link_id], |
5153 | lockdep_is_held(&local->hw.wiphy->mtx)); |
5154 | if (WARN_ON(!link_sta)) |
5155 | goto out_err; |
5156 | |
5157 | if (!link->u.mgd.have_beacon) { |
5158 | const struct cfg80211_bss_ies *ies; |
5159 | |
5160 | rcu_read_lock(); |
5161 | ies = rcu_dereference(cbss->beacon_ies); |
5162 | if (ies) |
5163 | link->u.mgd.have_beacon = true; |
5164 | else |
5165 | ies = rcu_dereference(cbss->ies); |
5166 | ieee80211_get_dtim(ies, |
5167 | dtim_count: &link->conf->sync_dtim_count, |
5168 | dtim_period: &link->u.mgd.dtim_period); |
5169 | link->conf->beacon_int = cbss->beacon_interval; |
5170 | rcu_read_unlock(); |
5171 | } |
5172 | |
5173 | link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; |
5174 | |
5175 | if (link_id != assoc_data->assoc_link_id) { |
5176 | err = ieee80211_prep_channel(sdata, link, cbss, mlo: true, |
5177 | conn_flags: &link->u.mgd.conn_flags); |
5178 | if (err) { |
5179 | link_info(link, "prep_channel failed\n" ); |
5180 | goto out_err; |
5181 | } |
5182 | } |
5183 | |
5184 | err = ieee80211_mgd_setup_link_sta(link, sta, link_sta, |
5185 | cbss: assoc_data->link[link_id].bss); |
5186 | if (err) |
5187 | goto out_err; |
5188 | |
5189 | if (!ieee80211_assoc_config_link(link, link_sta, |
5190 | cbss: assoc_data->link[link_id].bss, |
5191 | mgmt, elem_start, elem_len, |
5192 | changed: &changed[link_id])) |
5193 | goto out_err; |
5194 | |
5195 | if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) { |
5196 | valid_links &= ~BIT(link_id); |
5197 | ieee80211_sta_remove_link(sta, link_id); |
5198 | continue; |
5199 | } |
5200 | |
5201 | if (link_id != assoc_data->assoc_link_id) { |
5202 | err = ieee80211_sta_activate_link(sta, link_id); |
5203 | if (err) |
5204 | goto out_err; |
5205 | } |
5206 | } |
5207 | |
5208 | /* links might have changed due to rejected ones, set them again */ |
5209 | ieee80211_vif_set_links(sdata, new_links: valid_links, dormant_links); |
5210 | |
5211 | rate_control_rate_init(sta); |
5212 | |
5213 | if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) { |
5214 | set_sta_flag(sta, flag: WLAN_STA_MFP); |
5215 | sta->sta.mfp = true; |
5216 | } else { |
5217 | sta->sta.mfp = false; |
5218 | } |
5219 | |
5220 | ieee80211_sta_set_max_amsdu_subframes(sta, ext_capab: elems->ext_capab, |
5221 | ext_capab_len: elems->ext_capab_len); |
5222 | |
5223 | sta->sta.wme = (elems->wmm_param || elems->s1g_capab) && |
5224 | local->hw.queues >= IEEE80211_NUM_ACS; |
5225 | |
5226 | err = sta_info_move_state(sta, new_state: IEEE80211_STA_ASSOC); |
5227 | if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) |
5228 | err = sta_info_move_state(sta, new_state: IEEE80211_STA_AUTHORIZED); |
5229 | if (err) { |
5230 | sdata_info(sdata, |
5231 | "failed to move station %pM to desired state\n" , |
5232 | sta->sta.addr); |
5233 | WARN_ON(__sta_info_destroy(sta)); |
5234 | goto out_err; |
5235 | } |
5236 | |
5237 | if (sdata->wdev.use_4addr) |
5238 | drv_sta_set_4addr(local, sdata, sta: &sta->sta, enabled: true); |
5239 | |
5240 | ieee80211_set_associated(sdata, assoc_data, changed); |
5241 | |
5242 | /* |
5243 | * If we're using 4-addr mode, let the AP know that we're |
5244 | * doing so, so that it can create the STA VLAN on its side |
5245 | */ |
5246 | if (ifmgd->use_4addr) |
5247 | ieee80211_send_4addr_nullfunc(local, sdata); |
5248 | |
5249 | /* |
5250 | * Start timer to probe the connection to the AP now. |
5251 | * Also start the timer that will detect beacon loss. |
5252 | */ |
5253 | ieee80211_sta_reset_beacon_monitor(sdata); |
5254 | ieee80211_sta_reset_conn_monitor(sdata); |
5255 | |
5256 | return true; |
5257 | out_err: |
5258 | eth_zero_addr(addr: sdata->vif.cfg.ap_addr); |
5259 | return false; |
5260 | } |
5261 | |
5262 | static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, |
5263 | struct ieee80211_mgmt *mgmt, |
5264 | size_t len) |
5265 | { |
5266 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
5267 | struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; |
5268 | u16 capab_info, status_code, aid; |
5269 | struct ieee80211_elems_parse_params parse_params = { |
5270 | .bss = NULL, |
5271 | .link_id = -1, |
5272 | .from_ap = true, |
5273 | }; |
5274 | struct ieee802_11_elems *elems; |
5275 | int ac; |
5276 | const u8 *elem_start; |
5277 | unsigned int elem_len; |
5278 | bool reassoc; |
5279 | struct ieee80211_event event = { |
5280 | .type = MLME_EVENT, |
5281 | .u.mlme.data = ASSOC_EVENT, |
5282 | }; |
5283 | struct ieee80211_prep_tx_info info = {}; |
5284 | struct cfg80211_rx_assoc_resp_data resp = { |
5285 | .uapsd_queues = -1, |
5286 | }; |
5287 | u8 ap_mld_addr[ETH_ALEN] __aligned(2); |
5288 | unsigned int link_id; |
5289 | |
5290 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
5291 | |
5292 | if (!assoc_data) |
5293 | return; |
5294 | |
5295 | if (!ether_addr_equal(addr1: assoc_data->ap_addr, addr2: mgmt->bssid) || |
5296 | !ether_addr_equal(addr1: assoc_data->ap_addr, addr2: mgmt->sa)) |
5297 | return; |
5298 | |
5299 | /* |
5300 | * AssocResp and ReassocResp have identical structure, so process both |
5301 | * of them in this function. |
5302 | */ |
5303 | |
5304 | if (len < 24 + 6) |
5305 | return; |
5306 | |
5307 | reassoc = ieee80211_is_reassoc_resp(fc: mgmt->frame_control); |
5308 | capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); |
5309 | status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); |
5310 | if (assoc_data->s1g) |
5311 | elem_start = mgmt->u.s1g_assoc_resp.variable; |
5312 | else |
5313 | elem_start = mgmt->u.assoc_resp.variable; |
5314 | |
5315 | /* |
5316 | * Note: this may not be perfect, AP might misbehave - if |
5317 | * anyone needs to rely on perfect complete notification |
5318 | * with the exact right subtype, then we need to track what |
5319 | * we actually transmitted. |
5320 | */ |
5321 | info.subtype = reassoc ? IEEE80211_STYPE_REASSOC_REQ : |
5322 | IEEE80211_STYPE_ASSOC_REQ; |
5323 | |
5324 | if (assoc_data->fils_kek_len && |
5325 | fils_decrypt_assoc_resp(sdata, frame: (u8 *)mgmt, frame_len: &len, assoc_data) < 0) |
5326 | return; |
5327 | |
5328 | elem_len = len - (elem_start - (u8 *)mgmt); |
5329 | parse_params.start = elem_start; |
5330 | parse_params.len = elem_len; |
5331 | elems = ieee802_11_parse_elems_full(params: &parse_params); |
5332 | if (!elems) |
5333 | goto notify_driver; |
5334 | |
5335 | if (elems->aid_resp) |
5336 | aid = le16_to_cpu(elems->aid_resp->aid); |
5337 | else if (assoc_data->s1g) |
5338 | aid = 0; /* TODO */ |
5339 | else |
5340 | aid = le16_to_cpu(mgmt->u.assoc_resp.aid); |
5341 | |
5342 | /* |
5343 | * The 5 MSB of the AID field are reserved |
5344 | * (802.11-2016 9.4.1.8 AID field) |
5345 | */ |
5346 | aid &= 0x7ff; |
5347 | |
5348 | sdata_info(sdata, |
5349 | "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n" , |
5350 | reassoc ? "Rea" : "A" , assoc_data->ap_addr, |
5351 | capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); |
5352 | |
5353 | ifmgd->broken_ap = false; |
5354 | |
5355 | if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && |
5356 | elems->timeout_int && |
5357 | elems->timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) { |
5358 | u32 tu, ms; |
5359 | |
5360 | cfg80211_assoc_comeback(netdev: sdata->dev, ap_addr: assoc_data->ap_addr, |
5361 | le32_to_cpu(elems->timeout_int->value)); |
5362 | |
5363 | tu = le32_to_cpu(elems->timeout_int->value); |
5364 | ms = tu * 1024 / 1000; |
5365 | sdata_info(sdata, |
5366 | "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n" , |
5367 | assoc_data->ap_addr, tu, ms); |
5368 | assoc_data->timeout = jiffies + msecs_to_jiffies(m: ms); |
5369 | assoc_data->timeout_started = true; |
5370 | if (ms > IEEE80211_ASSOC_TIMEOUT) |
5371 | run_again(sdata, timeout: assoc_data->timeout); |
5372 | goto notify_driver; |
5373 | } |
5374 | |
5375 | if (status_code != WLAN_STATUS_SUCCESS) { |
5376 | sdata_info(sdata, "%pM denied association (code=%d)\n" , |
5377 | assoc_data->ap_addr, status_code); |
5378 | event.u.mlme.status = MLME_DENIED; |
5379 | event.u.mlme.reason = status_code; |
5380 | drv_event_callback(local: sdata->local, sdata, event: &event); |
5381 | } else { |
5382 | if (aid == 0 || aid > IEEE80211_MAX_AID) { |
5383 | sdata_info(sdata, |
5384 | "invalid AID value %d (out of range), turn off PS\n" , |
5385 | aid); |
5386 | aid = 0; |
5387 | ifmgd->broken_ap = true; |
5388 | } |
5389 | |
5390 | if (ieee80211_vif_is_mld(vif: &sdata->vif)) { |
5391 | if (!elems->ml_basic) { |
5392 | sdata_info(sdata, |
5393 | "MLO association with %pM but no multi-link element in response!\n" , |
5394 | assoc_data->ap_addr); |
5395 | goto abandon_assoc; |
5396 | } |
5397 | |
5398 | if (le16_get_bits(v: elems->ml_basic->control, |
5399 | IEEE80211_ML_CONTROL_TYPE) != |
5400 | IEEE80211_ML_CONTROL_TYPE_BASIC) { |
5401 | sdata_info(sdata, |
5402 | "bad multi-link element (control=0x%x)\n" , |
5403 | le16_to_cpu(elems->ml_basic->control)); |
5404 | goto abandon_assoc; |
5405 | } else { |
5406 | struct ieee80211_mle_basic_common_info *common; |
5407 | |
5408 | common = (void *)elems->ml_basic->variable; |
5409 | |
5410 | if (memcmp(p: assoc_data->ap_addr, |
5411 | q: common->mld_mac_addr, ETH_ALEN)) { |
5412 | sdata_info(sdata, |
5413 | "AP MLD MAC address mismatch: got %pM expected %pM\n" , |
5414 | common->mld_mac_addr, |
5415 | assoc_data->ap_addr); |
5416 | goto abandon_assoc; |
5417 | } |
5418 | } |
5419 | } |
5420 | |
5421 | sdata->vif.cfg.aid = aid; |
5422 | |
5423 | if (!ieee80211_assoc_success(sdata, mgmt, elems, |
5424 | elem_start, elem_len)) { |
5425 | /* oops -- internal error -- send timeout for now */ |
5426 | ieee80211_destroy_assoc_data(sdata, status: ASSOC_TIMEOUT); |
5427 | goto notify_driver; |
5428 | } |
5429 | event.u.mlme.status = MLME_SUCCESS; |
5430 | drv_event_callback(local: sdata->local, sdata, event: &event); |
5431 | sdata_info(sdata, "associated\n" ); |
5432 | |
5433 | info.success = 1; |
5434 | } |
5435 | |
5436 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { |
5437 | struct ieee80211_link_data *link; |
5438 | |
5439 | if (!assoc_data->link[link_id].bss) |
5440 | continue; |
5441 | |
5442 | resp.links[link_id].bss = assoc_data->link[link_id].bss; |
5443 | ether_addr_copy(dst: resp.links[link_id].addr, |
5444 | src: assoc_data->link[link_id].addr); |
5445 | resp.links[link_id].status = assoc_data->link[link_id].status; |
5446 | |
5447 | link = sdata_dereference(sdata->link[link_id], sdata); |
5448 | if (!link) |
5449 | continue; |
5450 | |
5451 | /* get uapsd queues configuration - same for all links */ |
5452 | resp.uapsd_queues = 0; |
5453 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) |
5454 | if (link->tx_conf[ac].uapsd) |
5455 | resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac]; |
5456 | } |
5457 | |
5458 | if (ieee80211_vif_is_mld(vif: &sdata->vif)) { |
5459 | ether_addr_copy(dst: ap_mld_addr, src: sdata->vif.cfg.ap_addr); |
5460 | resp.ap_mld_addr = ap_mld_addr; |
5461 | } |
5462 | |
5463 | ieee80211_destroy_assoc_data(sdata, |
5464 | status: status_code == WLAN_STATUS_SUCCESS ? |
5465 | ASSOC_SUCCESS : |
5466 | ASSOC_REJECTED); |
5467 | |
5468 | resp.buf = (u8 *)mgmt; |
5469 | resp.len = len; |
5470 | resp.req_ies = ifmgd->assoc_req_ies; |
5471 | resp.req_ies_len = ifmgd->assoc_req_ies_len; |
5472 | cfg80211_rx_assoc_resp(dev: sdata->dev, data: &resp); |
5473 | notify_driver: |
5474 | drv_mgd_complete_tx(local: sdata->local, sdata, info: &info); |
5475 | kfree(objp: elems); |
5476 | return; |
5477 | abandon_assoc: |
5478 | ieee80211_destroy_assoc_data(sdata, status: ASSOC_ABANDON); |
5479 | goto notify_driver; |
5480 | } |
5481 | |
5482 | static void ieee80211_rx_bss_info(struct ieee80211_link_data *link, |
5483 | struct ieee80211_mgmt *mgmt, size_t len, |
5484 | struct ieee80211_rx_status *rx_status) |
5485 | { |
5486 | struct ieee80211_sub_if_data *sdata = link->sdata; |
5487 | struct ieee80211_local *local = sdata->local; |
5488 | struct ieee80211_bss *bss; |
5489 | struct ieee80211_channel *channel; |
5490 | |
5491 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
5492 | |
5493 | channel = ieee80211_get_channel_khz(wiphy: local->hw.wiphy, |
5494 | freq: ieee80211_rx_status_to_khz(rx_status)); |
5495 | if (!channel) |
5496 | return; |
5497 | |
5498 | bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel); |
5499 | if (bss) { |
5500 | link->conf->beacon_rate = bss->beacon_rate; |
5501 | ieee80211_rx_bss_put(local, bss); |
5502 | } |
5503 | } |
5504 | |
5505 | |
5506 | static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_link_data *link, |
5507 | struct sk_buff *skb) |
5508 | { |
5509 | struct ieee80211_sub_if_data *sdata = link->sdata; |
5510 | struct ieee80211_mgmt *mgmt = (void *)skb->data; |
5511 | struct ieee80211_if_managed *ifmgd; |
5512 | struct ieee80211_rx_status *rx_status = (void *) skb->cb; |
5513 | struct ieee80211_channel *channel; |
5514 | size_t baselen, len = skb->len; |
5515 | |
5516 | ifmgd = &sdata->u.mgd; |
5517 | |
5518 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
5519 | |
5520 | /* |
5521 | * According to Draft P802.11ax D6.0 clause 26.17.2.3.2: |
5522 | * "If a 6 GHz AP receives a Probe Request frame and responds with |
5523 | * a Probe Response frame [..], the Address 1 field of the Probe |
5524 | * Response frame shall be set to the broadcast address [..]" |
5525 | * So, on 6GHz band we should also accept broadcast responses. |
5526 | */ |
5527 | channel = ieee80211_get_channel(wiphy: sdata->local->hw.wiphy, |
5528 | freq: rx_status->freq); |
5529 | if (!channel) |
5530 | return; |
5531 | |
5532 | if (!ether_addr_equal(addr1: mgmt->da, addr2: sdata->vif.addr) && |
5533 | (channel->band != NL80211_BAND_6GHZ || |
5534 | !is_broadcast_ether_addr(addr: mgmt->da))) |
5535 | return; /* ignore ProbeResp to foreign address */ |
5536 | |
5537 | baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; |
5538 | if (baselen > len) |
5539 | return; |
5540 | |
5541 | ieee80211_rx_bss_info(link, mgmt, len, rx_status); |
5542 | |
5543 | if (ifmgd->associated && |
5544 | ether_addr_equal(addr1: mgmt->bssid, addr2: link->u.mgd.bssid)) |
5545 | ieee80211_reset_ap_probe(sdata); |
5546 | } |
5547 | |
5548 | /* |
5549 | * This is the canonical list of information elements we care about, |
5550 | * the filter code also gives us all changes to the Microsoft OUI |
5551 | * (00:50:F2) vendor IE which is used for WMM which we need to track, |
5552 | * as well as the DTPC IE (part of the Cisco OUI) used for signaling |
5553 | * changes to requested client power. |
5554 | * |
5555 | * We implement beacon filtering in software since that means we can |
5556 | * avoid processing the frame here and in cfg80211, and userspace |
5557 | * will not be able to tell whether the hardware supports it or not. |
5558 | * |
5559 | * XXX: This list needs to be dynamic -- userspace needs to be able to |
5560 | * add items it requires. It also needs to be able to tell us to |
5561 | * look out for other vendor IEs. |
5562 | */ |
5563 | static const u64 care_about_ies = |
5564 | (1ULL << WLAN_EID_COUNTRY) | |
5565 | (1ULL << WLAN_EID_ERP_INFO) | |
5566 | (1ULL << WLAN_EID_CHANNEL_SWITCH) | |
5567 | (1ULL << WLAN_EID_PWR_CONSTRAINT) | |
5568 | (1ULL << WLAN_EID_HT_CAPABILITY) | |
5569 | (1ULL << WLAN_EID_HT_OPERATION) | |
5570 | (1ULL << WLAN_EID_EXT_CHANSWITCH_ANN); |
5571 | |
5572 | static void ieee80211_handle_beacon_sig(struct ieee80211_link_data *link, |
5573 | struct ieee80211_if_managed *ifmgd, |
5574 | struct ieee80211_bss_conf *bss_conf, |
5575 | struct ieee80211_local *local, |
5576 | struct ieee80211_rx_status *rx_status) |
5577 | { |
5578 | struct ieee80211_sub_if_data *sdata = link->sdata; |
5579 | |
5580 | /* Track average RSSI from the Beacon frames of the current AP */ |
5581 | |
5582 | if (!link->u.mgd.tracking_signal_avg) { |
5583 | link->u.mgd.tracking_signal_avg = true; |
5584 | ewma_beacon_signal_init(e: &link->u.mgd.ave_beacon_signal); |
5585 | link->u.mgd.last_cqm_event_signal = 0; |
5586 | link->u.mgd.count_beacon_signal = 1; |
5587 | link->u.mgd.last_ave_beacon_signal = 0; |
5588 | } else { |
5589 | link->u.mgd.count_beacon_signal++; |
5590 | } |
5591 | |
5592 | ewma_beacon_signal_add(e: &link->u.mgd.ave_beacon_signal, |
5593 | val: -rx_status->signal); |
5594 | |
5595 | if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold && |
5596 | link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { |
5597 | int sig = -ewma_beacon_signal_read(e: &link->u.mgd.ave_beacon_signal); |
5598 | int last_sig = link->u.mgd.last_ave_beacon_signal; |
5599 | struct ieee80211_event event = { |
5600 | .type = RSSI_EVENT, |
5601 | }; |
5602 | |
5603 | /* |
5604 | * if signal crosses either of the boundaries, invoke callback |
5605 | * with appropriate parameters |
5606 | */ |
5607 | if (sig > ifmgd->rssi_max_thold && |
5608 | (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { |
5609 | link->u.mgd.last_ave_beacon_signal = sig; |
5610 | event.u.rssi.data = RSSI_EVENT_HIGH; |
5611 | drv_event_callback(local, sdata, event: &event); |
5612 | } else if (sig < ifmgd->rssi_min_thold && |
5613 | (last_sig >= ifmgd->rssi_max_thold || |
5614 | last_sig == 0)) { |
5615 | link->u.mgd.last_ave_beacon_signal = sig; |
5616 | event.u.rssi.data = RSSI_EVENT_LOW; |
5617 | drv_event_callback(local, sdata, event: &event); |
5618 | } |
5619 | } |
5620 | |
5621 | if (bss_conf->cqm_rssi_thold && |
5622 | link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT && |
5623 | !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) { |
5624 | int sig = -ewma_beacon_signal_read(e: &link->u.mgd.ave_beacon_signal); |
5625 | int last_event = link->u.mgd.last_cqm_event_signal; |
5626 | int thold = bss_conf->cqm_rssi_thold; |
5627 | int hyst = bss_conf->cqm_rssi_hyst; |
5628 | |
5629 | if (sig < thold && |
5630 | (last_event == 0 || sig < last_event - hyst)) { |
5631 | link->u.mgd.last_cqm_event_signal = sig; |
5632 | ieee80211_cqm_rssi_notify( |
5633 | vif: &sdata->vif, |
5634 | rssi_event: NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, |
5635 | rssi_level: sig, GFP_KERNEL); |
5636 | } else if (sig > thold && |
5637 | (last_event == 0 || sig > last_event + hyst)) { |
5638 | link->u.mgd.last_cqm_event_signal = sig; |
5639 | ieee80211_cqm_rssi_notify( |
5640 | vif: &sdata->vif, |
5641 | rssi_event: NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, |
5642 | rssi_level: sig, GFP_KERNEL); |
5643 | } |
5644 | } |
5645 | |
5646 | if (bss_conf->cqm_rssi_low && |
5647 | link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { |
5648 | int sig = -ewma_beacon_signal_read(e: &link->u.mgd.ave_beacon_signal); |
5649 | int last_event = link->u.mgd.last_cqm_event_signal; |
5650 | int low = bss_conf->cqm_rssi_low; |
5651 | int high = bss_conf->cqm_rssi_high; |
5652 | |
5653 | if (sig < low && |
5654 | (last_event == 0 || last_event >= low)) { |
5655 | link->u.mgd.last_cqm_event_signal = sig; |
5656 | ieee80211_cqm_rssi_notify( |
5657 | vif: &sdata->vif, |
5658 | rssi_event: NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, |
5659 | rssi_level: sig, GFP_KERNEL); |
5660 | } else if (sig > high && |
5661 | (last_event == 0 || last_event <= high)) { |
5662 | link->u.mgd.last_cqm_event_signal = sig; |
5663 | ieee80211_cqm_rssi_notify( |
5664 | vif: &sdata->vif, |
5665 | rssi_event: NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, |
5666 | rssi_level: sig, GFP_KERNEL); |
5667 | } |
5668 | } |
5669 | } |
5670 | |
5671 | static bool ieee80211_rx_our_beacon(const u8 *tx_bssid, |
5672 | struct cfg80211_bss *bss) |
5673 | { |
5674 | if (ether_addr_equal(addr1: tx_bssid, addr2: bss->bssid)) |
5675 | return true; |
5676 | if (!bss->transmitted_bss) |
5677 | return false; |
5678 | return ether_addr_equal(addr1: tx_bssid, addr2: bss->transmitted_bss->bssid); |
5679 | } |
5680 | |
5681 | static bool ieee80211_config_puncturing(struct ieee80211_link_data *link, |
5682 | const struct ieee80211_eht_operation *eht_oper, |
5683 | u64 *changed) |
5684 | { |
5685 | u16 bitmap = 0, ; |
5686 | |
5687 | if ((eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) && |
5688 | (eht_oper->params & |
5689 | IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) { |
5690 | const struct ieee80211_eht_operation_info *info = |
5691 | (void *)eht_oper->optional; |
5692 | const u8 *disable_subchannel_bitmap = info->optional; |
5693 | |
5694 | bitmap = get_unaligned_le16(p: disable_subchannel_bitmap); |
5695 | } |
5696 | |
5697 | extracted = ieee80211_extract_dis_subch_bmap(eht_oper, |
5698 | chandef: &link->conf->chandef, |
5699 | bitmap); |
5700 | |
5701 | /* accept if there are no changes */ |
5702 | if (!(*changed & BSS_CHANGED_BANDWIDTH) && |
5703 | extracted == link->conf->eht_puncturing) |
5704 | return true; |
5705 | |
5706 | if (!cfg80211_valid_disable_subchannel_bitmap(bitmap: &bitmap, |
5707 | chandef: &link->conf->chandef)) { |
5708 | link_info(link, |
5709 | "Got an invalid disable subchannel bitmap from AP %pM: bitmap = 0x%x, bw = 0x%x. disconnect\n" , |
5710 | link->u.mgd.bssid, |
5711 | bitmap, |
5712 | link->conf->chandef.width); |
5713 | return false; |
5714 | } |
5715 | |
5716 | ieee80211_handle_puncturing_bitmap(link, eht_oper, bitmap, changed); |
5717 | return true; |
5718 | } |
5719 | |
5720 | static void ieee80211_ml_reconf_work(struct wiphy *wiphy, |
5721 | struct wiphy_work *work) |
5722 | { |
5723 | struct ieee80211_sub_if_data *sdata = |
5724 | container_of(work, struct ieee80211_sub_if_data, |
5725 | u.mgd.ml_reconf_work.work); |
5726 | u16 new_valid_links, new_active_links, new_dormant_links; |
5727 | int ret; |
5728 | |
5729 | if (!sdata->u.mgd.removed_links) |
5730 | return; |
5731 | |
5732 | sdata_info(sdata, |
5733 | "MLO Reconfiguration: work: valid=0x%x, removed=0x%x\n" , |
5734 | sdata->vif.valid_links, sdata->u.mgd.removed_links); |
5735 | |
5736 | new_valid_links = sdata->vif.valid_links & ~sdata->u.mgd.removed_links; |
5737 | if (new_valid_links == sdata->vif.valid_links) |
5738 | return; |
5739 | |
5740 | if (!new_valid_links || |
5741 | !(new_valid_links & ~sdata->vif.dormant_links)) { |
5742 | sdata_info(sdata, "No valid links after reconfiguration\n" ); |
5743 | ret = -EINVAL; |
5744 | goto out; |
5745 | } |
5746 | |
5747 | new_active_links = sdata->vif.active_links & ~sdata->u.mgd.removed_links; |
5748 | if (new_active_links != sdata->vif.active_links) { |
5749 | if (!new_active_links) |
5750 | new_active_links = |
5751 | BIT(ffs(new_valid_links & |
5752 | ~sdata->vif.dormant_links) - 1); |
5753 | |
5754 | ret = ieee80211_set_active_links(vif: &sdata->vif, active_links: new_active_links); |
5755 | if (ret) { |
5756 | sdata_info(sdata, |
5757 | "Failed setting active links\n" ); |
5758 | goto out; |
5759 | } |
5760 | } |
5761 | |
5762 | new_dormant_links = sdata->vif.dormant_links & ~sdata->u.mgd.removed_links; |
5763 | |
5764 | ret = ieee80211_vif_set_links(sdata, new_links: new_valid_links, |
5765 | dormant_links: new_dormant_links); |
5766 | if (ret) |
5767 | sdata_info(sdata, "Failed setting valid links\n" ); |
5768 | |
5769 | ieee80211_vif_cfg_change_notify(sdata, changed: BSS_CHANGED_MLD_VALID_LINKS); |
5770 | |
5771 | out: |
5772 | if (!ret) |
5773 | cfg80211_links_removed(dev: sdata->dev, link_mask: sdata->u.mgd.removed_links); |
5774 | else |
5775 | __ieee80211_disconnect(sdata); |
5776 | |
5777 | sdata->u.mgd.removed_links = 0; |
5778 | } |
5779 | |
5780 | static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata, |
5781 | struct ieee802_11_elems *elems) |
5782 | { |
5783 | const struct ieee80211_multi_link_elem *ml; |
5784 | const struct element *sub; |
5785 | size_t ml_len; |
5786 | unsigned long removed_links = 0; |
5787 | u16 link_removal_timeout[IEEE80211_MLD_MAX_NUM_LINKS] = {}; |
5788 | u8 link_id; |
5789 | u32 delay; |
5790 | |
5791 | if (!ieee80211_vif_is_mld(vif: &sdata->vif) || !elems->ml_reconf) |
5792 | return; |
5793 | |
5794 | ml_len = cfg80211_defragment_element(elem: elems->ml_reconf_elem, |
5795 | ies: elems->ie_start, |
5796 | ieslen: elems->total_len, |
5797 | data: elems->scratch_pos, |
5798 | data_len: elems->scratch + elems->scratch_len - |
5799 | elems->scratch_pos, |
5800 | frag_id: WLAN_EID_FRAGMENT); |
5801 | |
5802 | elems->ml_reconf = (const void *)elems->scratch_pos; |
5803 | elems->ml_reconf_len = ml_len; |
5804 | ml = elems->ml_reconf; |
5805 | |
5806 | /* Directly parse the sub elements as the common information doesn't |
5807 | * hold any useful information. |
5808 | */ |
5809 | for_each_mle_subelement(sub, (u8 *)ml, ml_len) { |
5810 | struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data; |
5811 | u8 *pos = prof->variable; |
5812 | u16 control; |
5813 | |
5814 | if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) |
5815 | continue; |
5816 | |
5817 | if (!ieee80211_mle_reconf_sta_prof_size_ok(data: sub->data, |
5818 | len: sub->datalen)) |
5819 | return; |
5820 | |
5821 | control = le16_to_cpu(prof->control); |
5822 | link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID; |
5823 | |
5824 | removed_links |= BIT(link_id); |
5825 | |
5826 | /* the MAC address should not be included, but handle it */ |
5827 | if (control & |
5828 | IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT) |
5829 | pos += 6; |
5830 | |
5831 | /* According to Draft P802.11be_D3.0, the control should |
5832 | * include the AP Removal Timer present. If the AP Removal Timer |
5833 | * is not present assume immediate removal. |
5834 | */ |
5835 | if (control & |
5836 | IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT) |
5837 | link_removal_timeout[link_id] = le16_to_cpu(*(__le16 *)pos); |
5838 | } |
5839 | |
5840 | removed_links &= sdata->vif.valid_links; |
5841 | if (!removed_links) { |
5842 | /* In case the removal was cancelled, abort it */ |
5843 | if (sdata->u.mgd.removed_links) { |
5844 | sdata->u.mgd.removed_links = 0; |
5845 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, |
5846 | dwork: &sdata->u.mgd.ml_reconf_work); |
5847 | } |
5848 | return; |
5849 | } |
5850 | |
5851 | delay = 0; |
5852 | for_each_set_bit(link_id, &removed_links, IEEE80211_MLD_MAX_NUM_LINKS) { |
5853 | struct ieee80211_bss_conf *link_conf = |
5854 | sdata_dereference(sdata->vif.link_conf[link_id], sdata); |
5855 | u32 link_delay; |
5856 | |
5857 | if (!link_conf) { |
5858 | removed_links &= ~BIT(link_id); |
5859 | continue; |
5860 | } |
5861 | |
5862 | link_delay = link_conf->beacon_int * |
5863 | link_removal_timeout[link_id]; |
5864 | |
5865 | if (!delay) |
5866 | delay = link_delay; |
5867 | else |
5868 | delay = min(delay, link_delay); |
5869 | } |
5870 | |
5871 | sdata->u.mgd.removed_links = removed_links; |
5872 | wiphy_delayed_work_queue(wiphy: sdata->local->hw.wiphy, |
5873 | dwork: &sdata->u.mgd.ml_reconf_work, |
5874 | TU_TO_JIFFIES(delay)); |
5875 | } |
5876 | |
5877 | static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy, |
5878 | struct wiphy_work *work) |
5879 | { |
5880 | u16 new_active_links, new_dormant_links; |
5881 | struct ieee80211_sub_if_data *sdata = |
5882 | container_of(work, struct ieee80211_sub_if_data, |
5883 | u.mgd.ttlm_work.work); |
5884 | int ret; |
5885 | |
5886 | new_active_links = sdata->u.mgd.ttlm_info.map & |
5887 | sdata->vif.valid_links; |
5888 | new_dormant_links = ~sdata->u.mgd.ttlm_info.map & |
5889 | sdata->vif.valid_links; |
5890 | if (!new_active_links) { |
5891 | ieee80211_disconnect(&sdata->vif, false); |
5892 | return; |
5893 | } |
5894 | |
5895 | ieee80211_vif_set_links(sdata, new_links: sdata->vif.valid_links, dormant_links: 0); |
5896 | new_active_links = BIT(ffs(new_active_links) - 1); |
5897 | ieee80211_set_active_links(vif: &sdata->vif, active_links: new_active_links); |
5898 | |
5899 | ret = ieee80211_vif_set_links(sdata, new_links: sdata->vif.valid_links, |
5900 | dormant_links: new_dormant_links); |
5901 | |
5902 | sdata->u.mgd.ttlm_info.active = true; |
5903 | sdata->u.mgd.ttlm_info.switch_time = 0; |
5904 | |
5905 | if (!ret) |
5906 | ieee80211_vif_cfg_change_notify(sdata, |
5907 | changed: BSS_CHANGED_MLD_VALID_LINKS); |
5908 | } |
5909 | |
5910 | static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data) |
5911 | { |
5912 | if (bm_size == 1) |
5913 | return *data; |
5914 | else |
5915 | return get_unaligned_le16(p: data); |
5916 | } |
5917 | |
5918 | static int |
5919 | ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata, |
5920 | const struct ieee80211_ttlm_elem *ttlm, |
5921 | struct ieee80211_adv_ttlm_info *ttlm_info) |
5922 | { |
5923 | /* The element size was already validated in |
5924 | * ieee80211_tid_to_link_map_size_ok() |
5925 | */ |
5926 | u8 control, link_map_presence, map_size, tid; |
5927 | u8 *pos; |
5928 | |
5929 | memset(ttlm_info, 0, sizeof(*ttlm_info)); |
5930 | pos = (void *)ttlm->optional; |
5931 | control = ttlm->control; |
5932 | |
5933 | if ((control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) || |
5934 | !(control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT)) |
5935 | return 0; |
5936 | |
5937 | if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) != |
5938 | IEEE80211_TTLM_DIRECTION_BOTH) { |
5939 | sdata_info(sdata, "Invalid advertised T2L map direction\n" ); |
5940 | return -EINVAL; |
5941 | } |
5942 | |
5943 | link_map_presence = *pos; |
5944 | pos++; |
5945 | |
5946 | ttlm_info->switch_time = get_unaligned_le16(p: pos); |
5947 | pos += 2; |
5948 | |
5949 | if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) { |
5950 | ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16; |
5951 | pos += 3; |
5952 | } |
5953 | |
5954 | if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE) |
5955 | map_size = 1; |
5956 | else |
5957 | map_size = 2; |
5958 | |
5959 | /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall |
5960 | * not advertise a TID-to-link mapping that does not map all TIDs to the |
5961 | * same link set, reject frame if not all links have mapping |
5962 | */ |
5963 | if (link_map_presence != 0xff) { |
5964 | sdata_info(sdata, |
5965 | "Invalid advertised T2L mapping presence indicator\n" ); |
5966 | return -EINVAL; |
5967 | } |
5968 | |
5969 | ttlm_info->map = ieee80211_get_ttlm(bm_size: map_size, data: pos); |
5970 | if (!ttlm_info->map) { |
5971 | sdata_info(sdata, |
5972 | "Invalid advertised T2L map for TID 0\n" ); |
5973 | return -EINVAL; |
5974 | } |
5975 | |
5976 | pos += map_size; |
5977 | |
5978 | for (tid = 1; tid < 8; tid++) { |
5979 | u16 map = ieee80211_get_ttlm(bm_size: map_size, data: pos); |
5980 | |
5981 | if (map != ttlm_info->map) { |
5982 | sdata_info(sdata, "Invalid advertised T2L map for tid %d\n" , |
5983 | tid); |
5984 | return -EINVAL; |
5985 | } |
5986 | |
5987 | pos += map_size; |
5988 | } |
5989 | return 0; |
5990 | } |
5991 | |
5992 | static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata, |
5993 | struct ieee802_11_elems *elems, |
5994 | u64 beacon_ts) |
5995 | { |
5996 | u8 i; |
5997 | int ret; |
5998 | |
5999 | if (!ieee80211_vif_is_mld(vif: &sdata->vif)) |
6000 | return; |
6001 | |
6002 | if (!elems->ttlm_num) { |
6003 | if (sdata->u.mgd.ttlm_info.switch_time) { |
6004 | /* if a planned TID-to-link mapping was cancelled - |
6005 | * abort it |
6006 | */ |
6007 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, |
6008 | dwork: &sdata->u.mgd.ttlm_work); |
6009 | } else if (sdata->u.mgd.ttlm_info.active) { |
6010 | /* if no TID-to-link element, set to default mapping in |
6011 | * which all TIDs are mapped to all setup links |
6012 | */ |
6013 | ret = ieee80211_vif_set_links(sdata, |
6014 | new_links: sdata->vif.valid_links, |
6015 | dormant_links: 0); |
6016 | if (ret) { |
6017 | sdata_info(sdata, "Failed setting valid/dormant links\n" ); |
6018 | return; |
6019 | } |
6020 | ieee80211_vif_cfg_change_notify(sdata, |
6021 | changed: BSS_CHANGED_MLD_VALID_LINKS); |
6022 | } |
6023 | memset(&sdata->u.mgd.ttlm_info, 0, |
6024 | sizeof(sdata->u.mgd.ttlm_info)); |
6025 | return; |
6026 | } |
6027 | |
6028 | for (i = 0; i < elems->ttlm_num; i++) { |
6029 | struct ieee80211_adv_ttlm_info ttlm_info; |
6030 | u32 res; |
6031 | |
6032 | res = ieee80211_parse_adv_t2l(sdata, ttlm: elems->ttlm[i], |
6033 | ttlm_info: &ttlm_info); |
6034 | |
6035 | if (res) { |
6036 | __ieee80211_disconnect(sdata); |
6037 | return; |
6038 | } |
6039 | |
6040 | if (ttlm_info.switch_time) { |
6041 | u32 st_us, delay = 0; |
6042 | u32 ts_l26 = beacon_ts & GENMASK(25, 0); |
6043 | |
6044 | /* The t2l map switch time is indicated with a partial |
6045 | * TSF value, convert it to TSF and calc the delay |
6046 | * to the start time. |
6047 | */ |
6048 | st_us = ieee80211_tu_to_usec(tu: ttlm_info.switch_time); |
6049 | if (st_us > ts_l26) |
6050 | delay = st_us - ts_l26; |
6051 | else |
6052 | continue; |
6053 | |
6054 | sdata->u.mgd.ttlm_info = ttlm_info; |
6055 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, |
6056 | dwork: &sdata->u.mgd.ttlm_work); |
6057 | wiphy_delayed_work_queue(wiphy: sdata->local->hw.wiphy, |
6058 | dwork: &sdata->u.mgd.ttlm_work, |
6059 | delay: usecs_to_jiffies(u: delay)); |
6060 | return; |
6061 | } |
6062 | } |
6063 | } |
6064 | |
6065 | static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, |
6066 | struct ieee80211_hdr *hdr, size_t len, |
6067 | struct ieee80211_rx_status *rx_status) |
6068 | { |
6069 | struct ieee80211_sub_if_data *sdata = link->sdata; |
6070 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
6071 | struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; |
6072 | struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; |
6073 | struct ieee80211_mgmt *mgmt = (void *) hdr; |
6074 | size_t baselen; |
6075 | struct ieee802_11_elems *elems; |
6076 | struct ieee80211_local *local = sdata->local; |
6077 | struct ieee80211_chanctx_conf *chanctx_conf; |
6078 | struct ieee80211_supported_band *sband; |
6079 | struct ieee80211_channel *chan; |
6080 | struct link_sta_info *link_sta; |
6081 | struct sta_info *sta; |
6082 | u64 changed = 0; |
6083 | bool erp_valid; |
6084 | u8 erp_value = 0; |
6085 | u32 ncrc = 0; |
6086 | u8 *bssid, *variable = mgmt->u.beacon.variable; |
6087 | u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
6088 | struct ieee80211_elems_parse_params parse_params = { |
6089 | .link_id = -1, |
6090 | .from_ap = true, |
6091 | }; |
6092 | |
6093 | lockdep_assert_wiphy(local->hw.wiphy); |
6094 | |
6095 | /* Process beacon from the current BSS */ |
6096 | bssid = ieee80211_get_bssid(hdr, len, type: sdata->vif.type); |
6097 | if (ieee80211_is_s1g_beacon(fc: mgmt->frame_control)) { |
6098 | struct ieee80211_ext *ext = (void *) mgmt; |
6099 | |
6100 | if (ieee80211_is_s1g_short_beacon(fc: ext->frame_control)) |
6101 | variable = ext->u.s1g_short_beacon.variable; |
6102 | else |
6103 | variable = ext->u.s1g_beacon.variable; |
6104 | } |
6105 | |
6106 | baselen = (u8 *) variable - (u8 *) mgmt; |
6107 | if (baselen > len) |
6108 | return; |
6109 | |
6110 | parse_params.start = variable; |
6111 | parse_params.len = len - baselen; |
6112 | |
6113 | rcu_read_lock(); |
6114 | chanctx_conf = rcu_dereference(link->conf->chanctx_conf); |
6115 | if (!chanctx_conf) { |
6116 | rcu_read_unlock(); |
6117 | return; |
6118 | } |
6119 | |
6120 | if (ieee80211_rx_status_to_khz(rx_status) != |
6121 | ieee80211_channel_to_khz(chan: chanctx_conf->def.chan)) { |
6122 | rcu_read_unlock(); |
6123 | return; |
6124 | } |
6125 | chan = chanctx_conf->def.chan; |
6126 | rcu_read_unlock(); |
6127 | |
6128 | if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && |
6129 | !WARN_ON(ieee80211_vif_is_mld(&sdata->vif)) && |
6130 | ieee80211_rx_our_beacon(tx_bssid: bssid, bss: ifmgd->assoc_data->link[0].bss)) { |
6131 | parse_params.bss = ifmgd->assoc_data->link[0].bss; |
6132 | elems = ieee802_11_parse_elems_full(params: &parse_params); |
6133 | if (!elems) |
6134 | return; |
6135 | |
6136 | ieee80211_rx_bss_info(link, mgmt, len, rx_status); |
6137 | |
6138 | if (elems->dtim_period) |
6139 | link->u.mgd.dtim_period = elems->dtim_period; |
6140 | link->u.mgd.have_beacon = true; |
6141 | ifmgd->assoc_data->need_beacon = false; |
6142 | if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) { |
6143 | link->conf->sync_tsf = |
6144 | le64_to_cpu(mgmt->u.beacon.timestamp); |
6145 | link->conf->sync_device_ts = |
6146 | rx_status->device_timestamp; |
6147 | link->conf->sync_dtim_count = elems->dtim_count; |
6148 | } |
6149 | |
6150 | if (elems->mbssid_config_ie) |
6151 | bss_conf->profile_periodicity = |
6152 | elems->mbssid_config_ie->profile_periodicity; |
6153 | else |
6154 | bss_conf->profile_periodicity = 0; |
6155 | |
6156 | if (elems->ext_capab_len >= 11 && |
6157 | (elems->ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) |
6158 | bss_conf->ema_ap = true; |
6159 | else |
6160 | bss_conf->ema_ap = false; |
6161 | |
6162 | /* continue assoc process */ |
6163 | ifmgd->assoc_data->timeout = jiffies; |
6164 | ifmgd->assoc_data->timeout_started = true; |
6165 | run_again(sdata, timeout: ifmgd->assoc_data->timeout); |
6166 | kfree(objp: elems); |
6167 | return; |
6168 | } |
6169 | |
6170 | if (!ifmgd->associated || |
6171 | !ieee80211_rx_our_beacon(tx_bssid: bssid, bss: link->u.mgd.bss)) |
6172 | return; |
6173 | bssid = link->u.mgd.bssid; |
6174 | |
6175 | if (!(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)) |
6176 | ieee80211_handle_beacon_sig(link, ifmgd, bss_conf, |
6177 | local, rx_status); |
6178 | |
6179 | if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) { |
6180 | mlme_dbg_ratelimited(sdata, |
6181 | "cancelling AP probe due to a received beacon\n" ); |
6182 | ieee80211_reset_ap_probe(sdata); |
6183 | } |
6184 | |
6185 | /* |
6186 | * Push the beacon loss detection into the future since |
6187 | * we are processing a beacon from the AP just now. |
6188 | */ |
6189 | ieee80211_sta_reset_beacon_monitor(sdata); |
6190 | |
6191 | /* TODO: CRC urrently not calculated on S1G Beacon Compatibility |
6192 | * element (which carries the beacon interval). Don't forget to add a |
6193 | * bit to care_about_ies[] above if mac80211 is interested in a |
6194 | * changing S1G element. |
6195 | */ |
6196 | if (!ieee80211_is_s1g_beacon(fc: hdr->frame_control)) |
6197 | ncrc = crc32_be(crc: 0, p: (void *)&mgmt->u.beacon.beacon_int, len: 4); |
6198 | parse_params.bss = link->u.mgd.bss; |
6199 | parse_params.filter = care_about_ies; |
6200 | parse_params.crc = ncrc; |
6201 | elems = ieee802_11_parse_elems_full(params: &parse_params); |
6202 | if (!elems) |
6203 | return; |
6204 | ncrc = elems->crc; |
6205 | |
6206 | if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && |
6207 | ieee80211_check_tim(tim: elems->tim, tim_len: elems->tim_len, aid: vif_cfg->aid)) { |
6208 | if (local->hw.conf.dynamic_ps_timeout > 0) { |
6209 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { |
6210 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; |
6211 | ieee80211_hw_config(local, |
6212 | changed: IEEE80211_CONF_CHANGE_PS); |
6213 | } |
6214 | ieee80211_send_nullfunc(local, sdata, powersave: false); |
6215 | } else if (!local->pspolling && sdata->u.mgd.powersave) { |
6216 | local->pspolling = true; |
6217 | |
6218 | /* |
6219 | * Here is assumed that the driver will be |
6220 | * able to send ps-poll frame and receive a |
6221 | * response even though power save mode is |
6222 | * enabled, but some drivers might require |
6223 | * to disable power save here. This needs |
6224 | * to be investigated. |
6225 | */ |
6226 | ieee80211_send_pspoll(local, sdata); |
6227 | } |
6228 | } |
6229 | |
6230 | if (sdata->vif.p2p || |
6231 | sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) { |
6232 | struct ieee80211_p2p_noa_attr noa = {}; |
6233 | int ret; |
6234 | |
6235 | ret = cfg80211_get_p2p_attr(ies: variable, |
6236 | len: len - baselen, |
6237 | attr: IEEE80211_P2P_ATTR_ABSENCE_NOTICE, |
6238 | buf: (u8 *) &noa, bufsize: sizeof(noa)); |
6239 | if (ret >= 2) { |
6240 | if (link->u.mgd.p2p_noa_index != noa.index) { |
6241 | /* valid noa_attr and index changed */ |
6242 | link->u.mgd.p2p_noa_index = noa.index; |
6243 | memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa)); |
6244 | changed |= BSS_CHANGED_P2P_PS; |
6245 | /* |
6246 | * make sure we update all information, the CRC |
6247 | * mechanism doesn't look at P2P attributes. |
6248 | */ |
6249 | link->u.mgd.beacon_crc_valid = false; |
6250 | } |
6251 | } else if (link->u.mgd.p2p_noa_index != -1) { |
6252 | /* noa_attr not found and we had valid noa_attr before */ |
6253 | link->u.mgd.p2p_noa_index = -1; |
6254 | memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr)); |
6255 | changed |= BSS_CHANGED_P2P_PS; |
6256 | link->u.mgd.beacon_crc_valid = false; |
6257 | } |
6258 | } |
6259 | |
6260 | if (link->u.mgd.csa_waiting_bcn) |
6261 | ieee80211_chswitch_post_beacon(link); |
6262 | |
6263 | /* |
6264 | * Update beacon timing and dtim count on every beacon appearance. This |
6265 | * will allow the driver to use the most updated values. Do it before |
6266 | * comparing this one with last received beacon. |
6267 | * IMPORTANT: These parameters would possibly be out of sync by the time |
6268 | * the driver will use them. The synchronized view is currently |
6269 | * guaranteed only in certain callbacks. |
6270 | */ |
6271 | if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY) && |
6272 | !ieee80211_is_s1g_beacon(fc: hdr->frame_control)) { |
6273 | link->conf->sync_tsf = |
6274 | le64_to_cpu(mgmt->u.beacon.timestamp); |
6275 | link->conf->sync_device_ts = |
6276 | rx_status->device_timestamp; |
6277 | link->conf->sync_dtim_count = elems->dtim_count; |
6278 | } |
6279 | |
6280 | if ((ncrc == link->u.mgd.beacon_crc && link->u.mgd.beacon_crc_valid) || |
6281 | ieee80211_is_s1g_short_beacon(fc: mgmt->frame_control)) |
6282 | goto free; |
6283 | link->u.mgd.beacon_crc = ncrc; |
6284 | link->u.mgd.beacon_crc_valid = true; |
6285 | |
6286 | ieee80211_rx_bss_info(link, mgmt, len, rx_status); |
6287 | |
6288 | ieee80211_sta_process_chanswitch(link, timestamp: rx_status->mactime, |
6289 | device_timestamp: rx_status->device_timestamp, |
6290 | elems, beacon: true); |
6291 | |
6292 | if (!link->u.mgd.disable_wmm_tracking && |
6293 | ieee80211_sta_wmm_params(local, link, wmm_param: elems->wmm_param, |
6294 | wmm_param_len: elems->wmm_param_len, |
6295 | mu_edca: elems->mu_edca_param_set)) |
6296 | changed |= BSS_CHANGED_QOS; |
6297 | |
6298 | /* |
6299 | * If we haven't had a beacon before, tell the driver about the |
6300 | * DTIM period (and beacon timing if desired) now. |
6301 | */ |
6302 | if (!link->u.mgd.have_beacon) { |
6303 | /* a few bogus AP send dtim_period = 0 or no TIM IE */ |
6304 | bss_conf->dtim_period = elems->dtim_period ?: 1; |
6305 | |
6306 | changed |= BSS_CHANGED_BEACON_INFO; |
6307 | link->u.mgd.have_beacon = true; |
6308 | |
6309 | ieee80211_recalc_ps(local); |
6310 | |
6311 | ieee80211_recalc_ps_vif(sdata); |
6312 | } |
6313 | |
6314 | if (elems->erp_info) { |
6315 | erp_valid = true; |
6316 | erp_value = elems->erp_info[0]; |
6317 | } else { |
6318 | erp_valid = false; |
6319 | } |
6320 | |
6321 | if (!ieee80211_is_s1g_beacon(fc: hdr->frame_control)) |
6322 | changed |= ieee80211_handle_bss_capability(link, |
6323 | le16_to_cpu(mgmt->u.beacon.capab_info), |
6324 | erp_valid, erp: erp_value); |
6325 | |
6326 | sta = sta_info_get(sdata, addr: sdata->vif.cfg.ap_addr); |
6327 | if (WARN_ON(!sta)) { |
6328 | goto free; |
6329 | } |
6330 | link_sta = rcu_dereference_protected(sta->link[link->link_id], |
6331 | lockdep_is_held(&local->hw.wiphy->mtx)); |
6332 | if (WARN_ON(!link_sta)) { |
6333 | goto free; |
6334 | } |
6335 | |
6336 | if (WARN_ON(!link->conf->chandef.chan)) |
6337 | goto free; |
6338 | |
6339 | sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; |
6340 | |
6341 | changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems); |
6342 | |
6343 | if (ieee80211_config_bw(link, ht_cap: elems->ht_cap_elem, |
6344 | vht_cap: elems->vht_cap_elem, ht_oper: elems->ht_operation, |
6345 | vht_oper: elems->vht_operation, he_oper: elems->he_operation, |
6346 | eht_oper: elems->eht_operation, |
6347 | s1g_oper: elems->s1g_oper, bssid, changed: &changed)) { |
6348 | sdata_info(sdata, |
6349 | "failed to follow AP %pM bandwidth change, disconnect\n" , |
6350 | bssid); |
6351 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
6352 | reason: WLAN_REASON_DEAUTH_LEAVING, |
6353 | tx: true, frame_buf: deauth_buf); |
6354 | ieee80211_report_disconnect(sdata, buf: deauth_buf, |
6355 | len: sizeof(deauth_buf), tx: true, |
6356 | reason: WLAN_REASON_DEAUTH_LEAVING, |
6357 | reconnect: false); |
6358 | goto free; |
6359 | } |
6360 | |
6361 | if (elems->opmode_notif) |
6362 | ieee80211_vht_handle_opmode(sdata, sta: link_sta, |
6363 | opmode: *elems->opmode_notif, |
6364 | band: rx_status->band); |
6365 | |
6366 | changed |= ieee80211_handle_pwr_constr(link, channel: chan, mgmt, |
6367 | country_ie: elems->country_elem, |
6368 | country_ie_len: elems->country_elem_len, |
6369 | pwr_constr_ie: elems->pwr_constr_elem, |
6370 | cisco_dtpc_ie: elems->cisco_dtpc_elem); |
6371 | |
6372 | if (elems->eht_operation && |
6373 | !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) { |
6374 | if (!ieee80211_config_puncturing(link, eht_oper: elems->eht_operation, |
6375 | changed: &changed)) { |
6376 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
6377 | reason: WLAN_REASON_DEAUTH_LEAVING, |
6378 | tx: true, frame_buf: deauth_buf); |
6379 | ieee80211_report_disconnect(sdata, buf: deauth_buf, |
6380 | len: sizeof(deauth_buf), tx: true, |
6381 | reason: WLAN_REASON_DEAUTH_LEAVING, |
6382 | reconnect: false); |
6383 | goto free; |
6384 | } |
6385 | } |
6386 | |
6387 | ieee80211_ml_reconfiguration(sdata, elems); |
6388 | ieee80211_process_adv_ttlm(sdata, elems, |
6389 | le64_to_cpu(mgmt->u.beacon.timestamp)); |
6390 | |
6391 | ieee80211_link_info_change_notify(sdata, link, changed); |
6392 | free: |
6393 | kfree(objp: elems); |
6394 | } |
6395 | |
6396 | void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, |
6397 | struct sk_buff *skb) |
6398 | { |
6399 | struct ieee80211_link_data *link = &sdata->deflink; |
6400 | struct ieee80211_rx_status *rx_status; |
6401 | struct ieee80211_hdr *hdr; |
6402 | u16 fc; |
6403 | |
6404 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
6405 | |
6406 | rx_status = (struct ieee80211_rx_status *) skb->cb; |
6407 | hdr = (struct ieee80211_hdr *) skb->data; |
6408 | fc = le16_to_cpu(hdr->frame_control); |
6409 | |
6410 | switch (fc & IEEE80211_FCTL_STYPE) { |
6411 | case IEEE80211_STYPE_S1G_BEACON: |
6412 | ieee80211_rx_mgmt_beacon(link, hdr, len: skb->len, rx_status); |
6413 | break; |
6414 | } |
6415 | } |
6416 | |
6417 | void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, |
6418 | struct sk_buff *skb) |
6419 | { |
6420 | struct ieee80211_link_data *link = &sdata->deflink; |
6421 | struct ieee80211_rx_status *rx_status; |
6422 | struct ieee80211_mgmt *mgmt; |
6423 | u16 fc; |
6424 | int ies_len; |
6425 | |
6426 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
6427 | |
6428 | rx_status = (struct ieee80211_rx_status *) skb->cb; |
6429 | mgmt = (struct ieee80211_mgmt *) skb->data; |
6430 | fc = le16_to_cpu(mgmt->frame_control); |
6431 | |
6432 | if (rx_status->link_valid) { |
6433 | link = sdata_dereference(sdata->link[rx_status->link_id], |
6434 | sdata); |
6435 | if (!link) |
6436 | return; |
6437 | } |
6438 | |
6439 | switch (fc & IEEE80211_FCTL_STYPE) { |
6440 | case IEEE80211_STYPE_BEACON: |
6441 | ieee80211_rx_mgmt_beacon(link, hdr: (void *)mgmt, |
6442 | len: skb->len, rx_status); |
6443 | break; |
6444 | case IEEE80211_STYPE_PROBE_RESP: |
6445 | ieee80211_rx_mgmt_probe_resp(link, skb); |
6446 | break; |
6447 | case IEEE80211_STYPE_AUTH: |
6448 | ieee80211_rx_mgmt_auth(sdata, mgmt, len: skb->len); |
6449 | break; |
6450 | case IEEE80211_STYPE_DEAUTH: |
6451 | ieee80211_rx_mgmt_deauth(sdata, mgmt, len: skb->len); |
6452 | break; |
6453 | case IEEE80211_STYPE_DISASSOC: |
6454 | ieee80211_rx_mgmt_disassoc(sdata, mgmt, len: skb->len); |
6455 | break; |
6456 | case IEEE80211_STYPE_ASSOC_RESP: |
6457 | case IEEE80211_STYPE_REASSOC_RESP: |
6458 | ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, len: skb->len); |
6459 | break; |
6460 | case IEEE80211_STYPE_ACTION: |
6461 | if (!sdata->u.mgd.associated || |
6462 | !ether_addr_equal(addr1: mgmt->bssid, addr2: sdata->vif.cfg.ap_addr)) |
6463 | break; |
6464 | |
6465 | if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { |
6466 | struct ieee802_11_elems *elems; |
6467 | |
6468 | ies_len = skb->len - |
6469 | offsetof(struct ieee80211_mgmt, |
6470 | u.action.u.chan_switch.variable); |
6471 | |
6472 | if (ies_len < 0) |
6473 | break; |
6474 | |
6475 | /* CSA IE cannot be overridden, no need for BSSID */ |
6476 | elems = ieee802_11_parse_elems( |
6477 | start: mgmt->u.action.u.chan_switch.variable, |
6478 | len: ies_len, action: true, NULL); |
6479 | |
6480 | if (elems && !elems->parse_error) |
6481 | ieee80211_sta_process_chanswitch(link, |
6482 | timestamp: rx_status->mactime, |
6483 | device_timestamp: rx_status->device_timestamp, |
6484 | elems, beacon: false); |
6485 | kfree(objp: elems); |
6486 | } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { |
6487 | struct ieee802_11_elems *elems; |
6488 | |
6489 | ies_len = skb->len - |
6490 | offsetof(struct ieee80211_mgmt, |
6491 | u.action.u.ext_chan_switch.variable); |
6492 | |
6493 | if (ies_len < 0) |
6494 | break; |
6495 | |
6496 | /* |
6497 | * extended CSA IE can't be overridden, no need for |
6498 | * BSSID |
6499 | */ |
6500 | elems = ieee802_11_parse_elems( |
6501 | start: mgmt->u.action.u.ext_chan_switch.variable, |
6502 | len: ies_len, action: true, NULL); |
6503 | |
6504 | if (elems && !elems->parse_error) { |
6505 | /* for the handling code pretend it was an IE */ |
6506 | elems->ext_chansw_ie = |
6507 | &mgmt->u.action.u.ext_chan_switch.data; |
6508 | |
6509 | ieee80211_sta_process_chanswitch(link, |
6510 | timestamp: rx_status->mactime, |
6511 | device_timestamp: rx_status->device_timestamp, |
6512 | elems, beacon: false); |
6513 | } |
6514 | |
6515 | kfree(objp: elems); |
6516 | } |
6517 | break; |
6518 | } |
6519 | } |
6520 | |
6521 | static void ieee80211_sta_timer(struct timer_list *t) |
6522 | { |
6523 | struct ieee80211_sub_if_data *sdata = |
6524 | from_timer(sdata, t, u.mgd.timer); |
6525 | |
6526 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, work: &sdata->work); |
6527 | } |
6528 | |
6529 | void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, |
6530 | u8 reason, bool tx) |
6531 | { |
6532 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
6533 | |
6534 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, |
6535 | tx, frame_buf); |
6536 | |
6537 | ieee80211_report_disconnect(sdata, buf: frame_buf, len: sizeof(frame_buf), tx: true, |
6538 | reason, reconnect: false); |
6539 | } |
6540 | |
6541 | static int ieee80211_auth(struct ieee80211_sub_if_data *sdata) |
6542 | { |
6543 | struct ieee80211_local *local = sdata->local; |
6544 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
6545 | struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; |
6546 | u32 tx_flags = 0; |
6547 | u16 trans = 1; |
6548 | u16 status = 0; |
6549 | struct ieee80211_prep_tx_info info = { |
6550 | .subtype = IEEE80211_STYPE_AUTH, |
6551 | }; |
6552 | |
6553 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
6554 | |
6555 | if (WARN_ON_ONCE(!auth_data)) |
6556 | return -EINVAL; |
6557 | |
6558 | auth_data->tries++; |
6559 | |
6560 | if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { |
6561 | sdata_info(sdata, "authentication with %pM timed out\n" , |
6562 | auth_data->ap_addr); |
6563 | |
6564 | /* |
6565 | * Most likely AP is not in the range so remove the |
6566 | * bss struct for that AP. |
6567 | */ |
6568 | cfg80211_unlink_bss(wiphy: local->hw.wiphy, bss: auth_data->bss); |
6569 | |
6570 | return -ETIMEDOUT; |
6571 | } |
6572 | |
6573 | if (auth_data->algorithm == WLAN_AUTH_SAE) |
6574 | info.duration = jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE); |
6575 | |
6576 | info.link_id = auth_data->link_id; |
6577 | drv_mgd_prepare_tx(local, sdata, info: &info); |
6578 | |
6579 | sdata_info(sdata, "send auth to %pM (try %d/%d)\n" , |
6580 | auth_data->ap_addr, auth_data->tries, |
6581 | IEEE80211_AUTH_MAX_TRIES); |
6582 | |
6583 | auth_data->expected_transaction = 2; |
6584 | |
6585 | if (auth_data->algorithm == WLAN_AUTH_SAE) { |
6586 | trans = auth_data->sae_trans; |
6587 | status = auth_data->sae_status; |
6588 | auth_data->expected_transaction = trans; |
6589 | } |
6590 | |
6591 | if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) |
6592 | tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | |
6593 | IEEE80211_TX_INTFL_MLME_CONN_TX; |
6594 | |
6595 | ieee80211_send_auth(sdata, transaction: trans, auth_alg: auth_data->algorithm, status, |
6596 | extra: auth_data->data, extra_len: auth_data->data_len, |
6597 | bssid: auth_data->ap_addr, da: auth_data->ap_addr, |
6598 | NULL, key_len: 0, key_idx: 0, tx_flags); |
6599 | |
6600 | if (tx_flags == 0) { |
6601 | if (auth_data->algorithm == WLAN_AUTH_SAE) |
6602 | auth_data->timeout = jiffies + |
6603 | IEEE80211_AUTH_TIMEOUT_SAE; |
6604 | else |
6605 | auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; |
6606 | } else { |
6607 | auth_data->timeout = |
6608 | round_jiffies_up(j: jiffies + IEEE80211_AUTH_TIMEOUT_LONG); |
6609 | } |
6610 | |
6611 | auth_data->timeout_started = true; |
6612 | run_again(sdata, timeout: auth_data->timeout); |
6613 | |
6614 | return 0; |
6615 | } |
6616 | |
6617 | static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) |
6618 | { |
6619 | struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; |
6620 | struct ieee80211_local *local = sdata->local; |
6621 | int ret; |
6622 | |
6623 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
6624 | |
6625 | assoc_data->tries++; |
6626 | if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { |
6627 | sdata_info(sdata, "association with %pM timed out\n" , |
6628 | assoc_data->ap_addr); |
6629 | |
6630 | /* |
6631 | * Most likely AP is not in the range so remove the |
6632 | * bss struct for that AP. |
6633 | */ |
6634 | cfg80211_unlink_bss(wiphy: local->hw.wiphy, |
6635 | bss: assoc_data->link[assoc_data->assoc_link_id].bss); |
6636 | |
6637 | return -ETIMEDOUT; |
6638 | } |
6639 | |
6640 | sdata_info(sdata, "associate with %pM (try %d/%d)\n" , |
6641 | assoc_data->ap_addr, assoc_data->tries, |
6642 | IEEE80211_ASSOC_MAX_TRIES); |
6643 | ret = ieee80211_send_assoc(sdata); |
6644 | if (ret) |
6645 | return ret; |
6646 | |
6647 | if (!ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) { |
6648 | assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; |
6649 | assoc_data->timeout_started = true; |
6650 | run_again(sdata, timeout: assoc_data->timeout); |
6651 | } else { |
6652 | assoc_data->timeout = |
6653 | round_jiffies_up(j: jiffies + |
6654 | IEEE80211_ASSOC_TIMEOUT_LONG); |
6655 | assoc_data->timeout_started = true; |
6656 | run_again(sdata, timeout: assoc_data->timeout); |
6657 | } |
6658 | |
6659 | return 0; |
6660 | } |
6661 | |
6662 | void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, |
6663 | __le16 fc, bool acked) |
6664 | { |
6665 | struct ieee80211_local *local = sdata->local; |
6666 | |
6667 | sdata->u.mgd.status_fc = fc; |
6668 | sdata->u.mgd.status_acked = acked; |
6669 | sdata->u.mgd.status_received = true; |
6670 | |
6671 | wiphy_work_queue(wiphy: local->hw.wiphy, work: &sdata->work); |
6672 | } |
6673 | |
6674 | void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) |
6675 | { |
6676 | struct ieee80211_local *local = sdata->local; |
6677 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
6678 | |
6679 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
6680 | |
6681 | if (ifmgd->status_received) { |
6682 | __le16 fc = ifmgd->status_fc; |
6683 | bool status_acked = ifmgd->status_acked; |
6684 | |
6685 | ifmgd->status_received = false; |
6686 | if (ifmgd->auth_data && ieee80211_is_auth(fc)) { |
6687 | if (status_acked) { |
6688 | if (ifmgd->auth_data->algorithm == |
6689 | WLAN_AUTH_SAE) |
6690 | ifmgd->auth_data->timeout = |
6691 | jiffies + |
6692 | IEEE80211_AUTH_TIMEOUT_SAE; |
6693 | else |
6694 | ifmgd->auth_data->timeout = |
6695 | jiffies + |
6696 | IEEE80211_AUTH_TIMEOUT_SHORT; |
6697 | run_again(sdata, timeout: ifmgd->auth_data->timeout); |
6698 | } else { |
6699 | ifmgd->auth_data->timeout = jiffies - 1; |
6700 | } |
6701 | ifmgd->auth_data->timeout_started = true; |
6702 | } else if (ifmgd->assoc_data && |
6703 | (ieee80211_is_assoc_req(fc) || |
6704 | ieee80211_is_reassoc_req(fc))) { |
6705 | if (status_acked) { |
6706 | ifmgd->assoc_data->timeout = |
6707 | jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT; |
6708 | run_again(sdata, timeout: ifmgd->assoc_data->timeout); |
6709 | } else { |
6710 | ifmgd->assoc_data->timeout = jiffies - 1; |
6711 | } |
6712 | ifmgd->assoc_data->timeout_started = true; |
6713 | } |
6714 | } |
6715 | |
6716 | if (ifmgd->auth_data && ifmgd->auth_data->timeout_started && |
6717 | time_after(jiffies, ifmgd->auth_data->timeout)) { |
6718 | if (ifmgd->auth_data->done || ifmgd->auth_data->waiting) { |
6719 | /* |
6720 | * ok ... we waited for assoc or continuation but |
6721 | * userspace didn't do it, so kill the auth data |
6722 | */ |
6723 | ieee80211_destroy_auth_data(sdata, assoc: false); |
6724 | } else if (ieee80211_auth(sdata)) { |
6725 | u8 ap_addr[ETH_ALEN]; |
6726 | struct ieee80211_event event = { |
6727 | .type = MLME_EVENT, |
6728 | .u.mlme.data = AUTH_EVENT, |
6729 | .u.mlme.status = MLME_TIMEOUT, |
6730 | }; |
6731 | |
6732 | memcpy(ap_addr, ifmgd->auth_data->ap_addr, ETH_ALEN); |
6733 | |
6734 | ieee80211_destroy_auth_data(sdata, assoc: false); |
6735 | |
6736 | cfg80211_auth_timeout(dev: sdata->dev, addr: ap_addr); |
6737 | drv_event_callback(local: sdata->local, sdata, event: &event); |
6738 | } |
6739 | } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) |
6740 | run_again(sdata, timeout: ifmgd->auth_data->timeout); |
6741 | |
6742 | if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started && |
6743 | time_after(jiffies, ifmgd->assoc_data->timeout)) { |
6744 | if ((ifmgd->assoc_data->need_beacon && |
6745 | !sdata->deflink.u.mgd.have_beacon) || |
6746 | ieee80211_do_assoc(sdata)) { |
6747 | struct ieee80211_event event = { |
6748 | .type = MLME_EVENT, |
6749 | .u.mlme.data = ASSOC_EVENT, |
6750 | .u.mlme.status = MLME_TIMEOUT, |
6751 | }; |
6752 | |
6753 | ieee80211_destroy_assoc_data(sdata, status: ASSOC_TIMEOUT); |
6754 | drv_event_callback(local: sdata->local, sdata, event: &event); |
6755 | } |
6756 | } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) |
6757 | run_again(sdata, timeout: ifmgd->assoc_data->timeout); |
6758 | |
6759 | if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL && |
6760 | ifmgd->associated) { |
6761 | u8 *bssid = sdata->deflink.u.mgd.bssid; |
6762 | int max_tries; |
6763 | |
6764 | if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) |
6765 | max_tries = max_nullfunc_tries; |
6766 | else |
6767 | max_tries = max_probe_tries; |
6768 | |
6769 | /* ACK received for nullfunc probing frame */ |
6770 | if (!ifmgd->probe_send_count) |
6771 | ieee80211_reset_ap_probe(sdata); |
6772 | else if (ifmgd->nullfunc_failed) { |
6773 | if (ifmgd->probe_send_count < max_tries) { |
6774 | mlme_dbg(sdata, |
6775 | "No ack for nullfunc frame to AP %pM, try %d/%i\n" , |
6776 | bssid, ifmgd->probe_send_count, |
6777 | max_tries); |
6778 | ieee80211_mgd_probe_ap_send(sdata); |
6779 | } else { |
6780 | mlme_dbg(sdata, |
6781 | "No ack for nullfunc frame to AP %pM, disconnecting.\n" , |
6782 | bssid); |
6783 | ieee80211_sta_connection_lost(sdata, |
6784 | reason: WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, |
6785 | tx: false); |
6786 | } |
6787 | } else if (time_is_after_jiffies(ifmgd->probe_timeout)) |
6788 | run_again(sdata, timeout: ifmgd->probe_timeout); |
6789 | else if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) { |
6790 | mlme_dbg(sdata, |
6791 | "Failed to send nullfunc to AP %pM after %dms, disconnecting\n" , |
6792 | bssid, probe_wait_ms); |
6793 | ieee80211_sta_connection_lost(sdata, |
6794 | reason: WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, tx: false); |
6795 | } else if (ifmgd->probe_send_count < max_tries) { |
6796 | mlme_dbg(sdata, |
6797 | "No probe response from AP %pM after %dms, try %d/%i\n" , |
6798 | bssid, probe_wait_ms, |
6799 | ifmgd->probe_send_count, max_tries); |
6800 | ieee80211_mgd_probe_ap_send(sdata); |
6801 | } else { |
6802 | /* |
6803 | * We actually lost the connection ... or did we? |
6804 | * Let's make sure! |
6805 | */ |
6806 | mlme_dbg(sdata, |
6807 | "No probe response from AP %pM after %dms, disconnecting.\n" , |
6808 | bssid, probe_wait_ms); |
6809 | |
6810 | ieee80211_sta_connection_lost(sdata, |
6811 | reason: WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, tx: false); |
6812 | } |
6813 | } |
6814 | } |
6815 | |
6816 | static void ieee80211_sta_bcn_mon_timer(struct timer_list *t) |
6817 | { |
6818 | struct ieee80211_sub_if_data *sdata = |
6819 | from_timer(sdata, t, u.mgd.bcn_mon_timer); |
6820 | |
6821 | if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) |
6822 | return; |
6823 | |
6824 | if (sdata->vif.bss_conf.csa_active && |
6825 | !sdata->deflink.u.mgd.csa_waiting_bcn) |
6826 | return; |
6827 | |
6828 | if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) |
6829 | return; |
6830 | |
6831 | sdata->u.mgd.connection_loss = false; |
6832 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
6833 | work: &sdata->u.mgd.beacon_connection_loss_work); |
6834 | } |
6835 | |
6836 | static void ieee80211_sta_conn_mon_timer(struct timer_list *t) |
6837 | { |
6838 | struct ieee80211_sub_if_data *sdata = |
6839 | from_timer(sdata, t, u.mgd.conn_mon_timer); |
6840 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
6841 | struct ieee80211_local *local = sdata->local; |
6842 | struct sta_info *sta; |
6843 | unsigned long timeout; |
6844 | |
6845 | if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) |
6846 | return; |
6847 | |
6848 | if (sdata->vif.bss_conf.csa_active && |
6849 | !sdata->deflink.u.mgd.csa_waiting_bcn) |
6850 | return; |
6851 | |
6852 | sta = sta_info_get(sdata, addr: sdata->vif.cfg.ap_addr); |
6853 | if (!sta) |
6854 | return; |
6855 | |
6856 | timeout = sta->deflink.status_stats.last_ack; |
6857 | if (time_before(sta->deflink.status_stats.last_ack, sta->deflink.rx_stats.last_rx)) |
6858 | timeout = sta->deflink.rx_stats.last_rx; |
6859 | timeout += IEEE80211_CONNECTION_IDLE_TIME; |
6860 | |
6861 | /* If timeout is after now, then update timer to fire at |
6862 | * the later date, but do not actually probe at this time. |
6863 | */ |
6864 | if (time_is_after_jiffies(timeout)) { |
6865 | mod_timer(timer: &ifmgd->conn_mon_timer, expires: round_jiffies_up(j: timeout)); |
6866 | return; |
6867 | } |
6868 | |
6869 | wiphy_work_queue(wiphy: local->hw.wiphy, work: &sdata->u.mgd.monitor_work); |
6870 | } |
6871 | |
6872 | static void ieee80211_sta_monitor_work(struct wiphy *wiphy, |
6873 | struct wiphy_work *work) |
6874 | { |
6875 | struct ieee80211_sub_if_data *sdata = |
6876 | container_of(work, struct ieee80211_sub_if_data, |
6877 | u.mgd.monitor_work); |
6878 | |
6879 | ieee80211_mgd_probe_ap(sdata, beacon: false); |
6880 | } |
6881 | |
6882 | static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) |
6883 | { |
6884 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
6885 | __ieee80211_stop_poll(sdata); |
6886 | |
6887 | /* let's probe the connection once */ |
6888 | if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) |
6889 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
6890 | work: &sdata->u.mgd.monitor_work); |
6891 | } |
6892 | } |
6893 | |
6894 | #ifdef CONFIG_PM |
6895 | void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) |
6896 | { |
6897 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
6898 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
6899 | |
6900 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
6901 | |
6902 | if (ifmgd->auth_data || ifmgd->assoc_data) { |
6903 | const u8 *ap_addr = ifmgd->auth_data ? |
6904 | ifmgd->auth_data->ap_addr : |
6905 | ifmgd->assoc_data->ap_addr; |
6906 | |
6907 | /* |
6908 | * If we are trying to authenticate / associate while suspending, |
6909 | * cfg80211 won't know and won't actually abort those attempts, |
6910 | * thus we need to do that ourselves. |
6911 | */ |
6912 | ieee80211_send_deauth_disassoc(sdata, da: ap_addr, bssid: ap_addr, |
6913 | IEEE80211_STYPE_DEAUTH, |
6914 | reason: WLAN_REASON_DEAUTH_LEAVING, |
6915 | send_frame: false, frame_buf); |
6916 | if (ifmgd->assoc_data) |
6917 | ieee80211_destroy_assoc_data(sdata, status: ASSOC_ABANDON); |
6918 | if (ifmgd->auth_data) |
6919 | ieee80211_destroy_auth_data(sdata, assoc: false); |
6920 | cfg80211_tx_mlme_mgmt(dev: sdata->dev, buf: frame_buf, |
6921 | IEEE80211_DEAUTH_FRAME_LEN, |
6922 | reconnect: false); |
6923 | } |
6924 | |
6925 | /* This is a bit of a hack - we should find a better and more generic |
6926 | * solution to this. Normally when suspending, cfg80211 will in fact |
6927 | * deauthenticate. However, it doesn't (and cannot) stop an ongoing |
6928 | * auth (not so important) or assoc (this is the problem) process. |
6929 | * |
6930 | * As a consequence, it can happen that we are in the process of both |
6931 | * associating and suspending, and receive an association response |
6932 | * after cfg80211 has checked if it needs to disconnect, but before |
6933 | * we actually set the flag to drop incoming frames. This will then |
6934 | * cause the workqueue flush to process the association response in |
6935 | * the suspend, resulting in a successful association just before it |
6936 | * tries to remove the interface from the driver, which now though |
6937 | * has a channel context assigned ... this results in issues. |
6938 | * |
6939 | * To work around this (for now) simply deauth here again if we're |
6940 | * now connected. |
6941 | */ |
6942 | if (ifmgd->associated && !sdata->local->wowlan) { |
6943 | u8 bssid[ETH_ALEN]; |
6944 | struct cfg80211_deauth_request req = { |
6945 | .reason_code = WLAN_REASON_DEAUTH_LEAVING, |
6946 | .bssid = bssid, |
6947 | }; |
6948 | |
6949 | memcpy(bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); |
6950 | ieee80211_mgd_deauth(sdata, req: &req); |
6951 | } |
6952 | } |
6953 | #endif |
6954 | |
6955 | void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) |
6956 | { |
6957 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
6958 | |
6959 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
6960 | |
6961 | if (!ifmgd->associated) |
6962 | return; |
6963 | |
6964 | if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { |
6965 | sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; |
6966 | mlme_dbg(sdata, "driver requested disconnect after resume\n" ); |
6967 | ieee80211_sta_connection_lost(sdata, |
6968 | reason: WLAN_REASON_UNSPECIFIED, |
6969 | tx: true); |
6970 | return; |
6971 | } |
6972 | |
6973 | if (sdata->flags & IEEE80211_SDATA_DISCONNECT_HW_RESTART) { |
6974 | sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_HW_RESTART; |
6975 | mlme_dbg(sdata, "driver requested disconnect after hardware restart\n" ); |
6976 | ieee80211_sta_connection_lost(sdata, |
6977 | reason: WLAN_REASON_UNSPECIFIED, |
6978 | tx: true); |
6979 | return; |
6980 | } |
6981 | } |
6982 | |
6983 | static void ieee80211_request_smps_mgd_work(struct wiphy *wiphy, |
6984 | struct wiphy_work *work) |
6985 | { |
6986 | struct ieee80211_link_data *link = |
6987 | container_of(work, struct ieee80211_link_data, |
6988 | u.mgd.request_smps_work); |
6989 | |
6990 | __ieee80211_request_smps_mgd(sdata: link->sdata, link, |
6991 | smps_mode: link->u.mgd.driver_smps_mode); |
6992 | } |
6993 | |
6994 | /* interface setup */ |
6995 | void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) |
6996 | { |
6997 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
6998 | |
6999 | wiphy_work_init(work: &ifmgd->monitor_work, func: ieee80211_sta_monitor_work); |
7000 | wiphy_work_init(work: &ifmgd->beacon_connection_loss_work, |
7001 | func: ieee80211_beacon_connection_loss_work); |
7002 | wiphy_work_init(work: &ifmgd->csa_connection_drop_work, |
7003 | func: ieee80211_csa_connection_drop_work); |
7004 | wiphy_delayed_work_init(dwork: &ifmgd->tdls_peer_del_work, |
7005 | func: ieee80211_tdls_peer_del_work); |
7006 | wiphy_delayed_work_init(dwork: &ifmgd->ml_reconf_work, |
7007 | func: ieee80211_ml_reconf_work); |
7008 | timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0); |
7009 | timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0); |
7010 | timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0); |
7011 | wiphy_delayed_work_init(dwork: &ifmgd->tx_tspec_wk, |
7012 | func: ieee80211_sta_handle_tspec_ac_params_wk); |
7013 | wiphy_delayed_work_init(dwork: &ifmgd->ttlm_work, |
7014 | func: ieee80211_tid_to_link_map_work); |
7015 | |
7016 | ifmgd->flags = 0; |
7017 | ifmgd->powersave = sdata->wdev.ps; |
7018 | ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues; |
7019 | ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; |
7020 | /* Setup TDLS data */ |
7021 | spin_lock_init(&ifmgd->teardown_lock); |
7022 | ifmgd->teardown_skb = NULL; |
7023 | ifmgd->orig_teardown_skb = NULL; |
7024 | } |
7025 | |
7026 | static void ieee80211_recalc_smps_work(struct wiphy *wiphy, |
7027 | struct wiphy_work *work) |
7028 | { |
7029 | struct ieee80211_link_data *link = |
7030 | container_of(work, struct ieee80211_link_data, |
7031 | u.mgd.recalc_smps); |
7032 | |
7033 | ieee80211_recalc_smps(sdata: link->sdata, link); |
7034 | } |
7035 | |
7036 | void ieee80211_mgd_setup_link(struct ieee80211_link_data *link) |
7037 | { |
7038 | struct ieee80211_sub_if_data *sdata = link->sdata; |
7039 | struct ieee80211_local *local = sdata->local; |
7040 | unsigned int link_id = link->link_id; |
7041 | |
7042 | link->u.mgd.p2p_noa_index = -1; |
7043 | link->u.mgd.conn_flags = 0; |
7044 | link->conf->bssid = link->u.mgd.bssid; |
7045 | |
7046 | wiphy_work_init(work: &link->u.mgd.request_smps_work, |
7047 | func: ieee80211_request_smps_mgd_work); |
7048 | wiphy_work_init(work: &link->u.mgd.recalc_smps, |
7049 | func: ieee80211_recalc_smps_work); |
7050 | if (local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS) |
7051 | link->u.mgd.req_smps = IEEE80211_SMPS_AUTOMATIC; |
7052 | else |
7053 | link->u.mgd.req_smps = IEEE80211_SMPS_OFF; |
7054 | |
7055 | wiphy_delayed_work_init(dwork: &link->u.mgd.chswitch_work, |
7056 | func: ieee80211_chswitch_work); |
7057 | |
7058 | if (sdata->u.mgd.assoc_data) |
7059 | ether_addr_copy(dst: link->conf->addr, |
7060 | src: sdata->u.mgd.assoc_data->link[link_id].addr); |
7061 | else if (!is_valid_ether_addr(addr: link->conf->addr)) |
7062 | eth_random_addr(addr: link->conf->addr); |
7063 | } |
7064 | |
7065 | /* scan finished notification */ |
7066 | void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) |
7067 | { |
7068 | struct ieee80211_sub_if_data *sdata; |
7069 | |
7070 | /* Restart STA timers */ |
7071 | rcu_read_lock(); |
7072 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { |
7073 | if (ieee80211_sdata_running(sdata)) |
7074 | ieee80211_restart_sta_timer(sdata); |
7075 | } |
7076 | rcu_read_unlock(); |
7077 | } |
7078 | |
7079 | static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, |
7080 | struct cfg80211_bss *cbss, s8 link_id, |
7081 | const u8 *ap_mld_addr, bool assoc, |
7082 | bool override) |
7083 | { |
7084 | struct ieee80211_local *local = sdata->local; |
7085 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
7086 | struct ieee80211_bss *bss = (void *)cbss->priv; |
7087 | struct sta_info *new_sta = NULL; |
7088 | struct ieee80211_link_data *link; |
7089 | bool have_sta = false; |
7090 | bool mlo; |
7091 | int err; |
7092 | |
7093 | if (link_id >= 0) { |
7094 | mlo = true; |
7095 | if (WARN_ON(!ap_mld_addr)) |
7096 | return -EINVAL; |
7097 | err = ieee80211_vif_set_links(sdata, BIT(link_id), dormant_links: 0); |
7098 | } else { |
7099 | if (WARN_ON(ap_mld_addr)) |
7100 | return -EINVAL; |
7101 | ap_mld_addr = cbss->bssid; |
7102 | err = ieee80211_vif_set_links(sdata, new_links: 0, dormant_links: 0); |
7103 | link_id = 0; |
7104 | mlo = false; |
7105 | } |
7106 | |
7107 | if (err) |
7108 | return err; |
7109 | |
7110 | link = sdata_dereference(sdata->link[link_id], sdata); |
7111 | if (WARN_ON(!link)) { |
7112 | err = -ENOLINK; |
7113 | goto out_err; |
7114 | } |
7115 | |
7116 | if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) { |
7117 | err = -EINVAL; |
7118 | goto out_err; |
7119 | } |
7120 | |
7121 | /* If a reconfig is happening, bail out */ |
7122 | if (local->in_reconfig) { |
7123 | err = -EBUSY; |
7124 | goto out_err; |
7125 | } |
7126 | |
7127 | if (assoc) { |
7128 | rcu_read_lock(); |
7129 | have_sta = sta_info_get(sdata, addr: ap_mld_addr); |
7130 | rcu_read_unlock(); |
7131 | } |
7132 | |
7133 | if (!have_sta) { |
7134 | if (mlo) |
7135 | new_sta = sta_info_alloc_with_link(sdata, mld_addr: ap_mld_addr, |
7136 | link_id, link_addr: cbss->bssid, |
7137 | GFP_KERNEL); |
7138 | else |
7139 | new_sta = sta_info_alloc(sdata, addr: ap_mld_addr, GFP_KERNEL); |
7140 | |
7141 | if (!new_sta) { |
7142 | err = -ENOMEM; |
7143 | goto out_err; |
7144 | } |
7145 | |
7146 | new_sta->sta.mlo = mlo; |
7147 | } |
7148 | |
7149 | /* |
7150 | * Set up the information for the new channel before setting the |
7151 | * new channel. We can't - completely race-free - change the basic |
7152 | * rates bitmap and the channel (sband) that it refers to, but if |
7153 | * we set it up before we at least avoid calling into the driver's |
7154 | * bss_info_changed() method with invalid information (since we do |
7155 | * call that from changing the channel - only for IDLE and perhaps |
7156 | * some others, but ...). |
7157 | * |
7158 | * So to avoid that, just set up all the new information before the |
7159 | * channel, but tell the driver to apply it only afterwards, since |
7160 | * it might need the new channel for that. |
7161 | */ |
7162 | if (new_sta) { |
7163 | const struct cfg80211_bss_ies *ies; |
7164 | struct link_sta_info *link_sta; |
7165 | |
7166 | rcu_read_lock(); |
7167 | link_sta = rcu_dereference(new_sta->link[link_id]); |
7168 | if (WARN_ON(!link_sta)) { |
7169 | rcu_read_unlock(); |
7170 | sta_info_free(local, sta: new_sta); |
7171 | err = -EINVAL; |
7172 | goto out_err; |
7173 | } |
7174 | |
7175 | err = ieee80211_mgd_setup_link_sta(link, sta: new_sta, |
7176 | link_sta, cbss); |
7177 | if (err) { |
7178 | rcu_read_unlock(); |
7179 | sta_info_free(local, sta: new_sta); |
7180 | goto out_err; |
7181 | } |
7182 | |
7183 | memcpy(link->u.mgd.bssid, cbss->bssid, ETH_ALEN); |
7184 | |
7185 | /* set timing information */ |
7186 | link->conf->beacon_int = cbss->beacon_interval; |
7187 | ies = rcu_dereference(cbss->beacon_ies); |
7188 | if (ies) { |
7189 | link->conf->sync_tsf = ies->tsf; |
7190 | link->conf->sync_device_ts = |
7191 | bss->device_ts_beacon; |
7192 | |
7193 | ieee80211_get_dtim(ies, |
7194 | dtim_count: &link->conf->sync_dtim_count, |
7195 | NULL); |
7196 | } else if (!ieee80211_hw_check(&sdata->local->hw, |
7197 | TIMING_BEACON_ONLY)) { |
7198 | ies = rcu_dereference(cbss->proberesp_ies); |
7199 | /* must be non-NULL since beacon IEs were NULL */ |
7200 | link->conf->sync_tsf = ies->tsf; |
7201 | link->conf->sync_device_ts = |
7202 | bss->device_ts_presp; |
7203 | link->conf->sync_dtim_count = 0; |
7204 | } else { |
7205 | link->conf->sync_tsf = 0; |
7206 | link->conf->sync_device_ts = 0; |
7207 | link->conf->sync_dtim_count = 0; |
7208 | } |
7209 | rcu_read_unlock(); |
7210 | } |
7211 | |
7212 | if (new_sta || override) { |
7213 | err = ieee80211_prep_channel(sdata, link, cbss, mlo, |
7214 | conn_flags: &link->u.mgd.conn_flags); |
7215 | if (err) { |
7216 | if (new_sta) |
7217 | sta_info_free(local, sta: new_sta); |
7218 | goto out_err; |
7219 | } |
7220 | } |
7221 | |
7222 | if (new_sta) { |
7223 | /* |
7224 | * tell driver about BSSID, basic rates and timing |
7225 | * this was set up above, before setting the channel |
7226 | */ |
7227 | ieee80211_link_info_change_notify(sdata, link, |
7228 | changed: BSS_CHANGED_BSSID | |
7229 | BSS_CHANGED_BASIC_RATES | |
7230 | BSS_CHANGED_BEACON_INT); |
7231 | |
7232 | if (assoc) |
7233 | sta_info_pre_move_state(sta: new_sta, new_state: IEEE80211_STA_AUTH); |
7234 | |
7235 | err = sta_info_insert(sta: new_sta); |
7236 | new_sta = NULL; |
7237 | if (err) { |
7238 | sdata_info(sdata, |
7239 | "failed to insert STA entry for the AP (error %d)\n" , |
7240 | err); |
7241 | goto out_err; |
7242 | } |
7243 | } else |
7244 | WARN_ON_ONCE(!ether_addr_equal(link->u.mgd.bssid, cbss->bssid)); |
7245 | |
7246 | /* Cancel scan to ensure that nothing interferes with connection */ |
7247 | if (local->scanning) |
7248 | ieee80211_scan_cancel(local); |
7249 | |
7250 | return 0; |
7251 | |
7252 | out_err: |
7253 | ieee80211_link_release_channel(link: &sdata->deflink); |
7254 | ieee80211_vif_set_links(sdata, new_links: 0, dormant_links: 0); |
7255 | return err; |
7256 | } |
7257 | |
7258 | /* config hooks */ |
7259 | int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, |
7260 | struct cfg80211_auth_request *req) |
7261 | { |
7262 | struct ieee80211_local *local = sdata->local; |
7263 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
7264 | struct ieee80211_mgd_auth_data *auth_data; |
7265 | struct ieee80211_link_data *link; |
7266 | const struct element *csa_elem, *ecsa_elem; |
7267 | u16 auth_alg; |
7268 | int err; |
7269 | bool cont_auth; |
7270 | |
7271 | lockdep_assert_wiphy(sdata->local->hw.wiphy); |
7272 | |
7273 | /* prepare auth data structure */ |
7274 | |
7275 | switch (req->auth_type) { |
7276 | case NL80211_AUTHTYPE_OPEN_SYSTEM: |
7277 | auth_alg = WLAN_AUTH_OPEN; |
7278 | break; |
7279 | case NL80211_AUTHTYPE_SHARED_KEY: |
7280 | if (fips_enabled) |
7281 | return -EOPNOTSUPP; |
7282 | auth_alg = WLAN_AUTH_SHARED_KEY; |
7283 | break; |
7284 | case NL80211_AUTHTYPE_FT: |
7285 | auth_alg = WLAN_AUTH_FT; |
7286 | break; |
7287 | case NL80211_AUTHTYPE_NETWORK_EAP: |
7288 | auth_alg = WLAN_AUTH_LEAP; |
7289 | break; |
7290 | case NL80211_AUTHTYPE_SAE: |
7291 | auth_alg = WLAN_AUTH_SAE; |
7292 | break; |
7293 | case NL80211_AUTHTYPE_FILS_SK: |
7294 | auth_alg = WLAN_AUTH_FILS_SK; |
7295 | break; |
7296 | case NL80211_AUTHTYPE_FILS_SK_PFS: |
7297 | auth_alg = WLAN_AUTH_FILS_SK_PFS; |
7298 | break; |
7299 | case NL80211_AUTHTYPE_FILS_PK: |
7300 | auth_alg = WLAN_AUTH_FILS_PK; |
7301 | break; |
7302 | default: |
7303 | return -EOPNOTSUPP; |
7304 | } |
7305 | |
7306 | if (ifmgd->assoc_data) |
7307 | return -EBUSY; |
7308 | |
7309 | rcu_read_lock(); |
7310 | csa_elem = ieee80211_bss_get_elem(bss: req->bss, id: WLAN_EID_CHANNEL_SWITCH); |
7311 | ecsa_elem = ieee80211_bss_get_elem(bss: req->bss, |
7312 | id: WLAN_EID_EXT_CHANSWITCH_ANN); |
7313 | if ((csa_elem && |
7314 | csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) && |
7315 | ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) || |
7316 | (ecsa_elem && |
7317 | ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) && |
7318 | ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) { |
7319 | rcu_read_unlock(); |
7320 | sdata_info(sdata, "AP is in CSA process, reject auth\n" ); |
7321 | return -EINVAL; |
7322 | } |
7323 | rcu_read_unlock(); |
7324 | |
7325 | auth_data = kzalloc(size: sizeof(*auth_data) + req->auth_data_len + |
7326 | req->ie_len, GFP_KERNEL); |
7327 | if (!auth_data) |
7328 | return -ENOMEM; |
7329 | |
7330 | memcpy(auth_data->ap_addr, |
7331 | req->ap_mld_addr ?: req->bss->bssid, |
7332 | ETH_ALEN); |
7333 | auth_data->bss = req->bss; |
7334 | auth_data->link_id = req->link_id; |
7335 | |
7336 | if (req->auth_data_len >= 4) { |
7337 | if (req->auth_type == NL80211_AUTHTYPE_SAE) { |
7338 | __le16 *pos = (__le16 *) req->auth_data; |
7339 | |
7340 | auth_data->sae_trans = le16_to_cpu(pos[0]); |
7341 | auth_data->sae_status = le16_to_cpu(pos[1]); |
7342 | } |
7343 | memcpy(auth_data->data, req->auth_data + 4, |
7344 | req->auth_data_len - 4); |
7345 | auth_data->data_len += req->auth_data_len - 4; |
7346 | } |
7347 | |
7348 | /* Check if continuing authentication or trying to authenticate with the |
7349 | * same BSS that we were in the process of authenticating with and avoid |
7350 | * removal and re-addition of the STA entry in |
7351 | * ieee80211_prep_connection(). |
7352 | */ |
7353 | cont_auth = ifmgd->auth_data && req->bss == ifmgd->auth_data->bss && |
7354 | ifmgd->auth_data->link_id == req->link_id; |
7355 | |
7356 | if (req->ie && req->ie_len) { |
7357 | memcpy(&auth_data->data[auth_data->data_len], |
7358 | req->ie, req->ie_len); |
7359 | auth_data->data_len += req->ie_len; |
7360 | } |
7361 | |
7362 | if (req->key && req->key_len) { |
7363 | auth_data->key_len = req->key_len; |
7364 | auth_data->key_idx = req->key_idx; |
7365 | memcpy(auth_data->key, req->key, req->key_len); |
7366 | } |
7367 | |
7368 | auth_data->algorithm = auth_alg; |
7369 | |
7370 | /* try to authenticate/probe */ |
7371 | |
7372 | if (ifmgd->auth_data) { |
7373 | if (cont_auth && req->auth_type == NL80211_AUTHTYPE_SAE) { |
7374 | auth_data->peer_confirmed = |
7375 | ifmgd->auth_data->peer_confirmed; |
7376 | } |
7377 | ieee80211_destroy_auth_data(sdata, assoc: cont_auth); |
7378 | } |
7379 | |
7380 | /* prep auth_data so we don't go into idle on disassoc */ |
7381 | ifmgd->auth_data = auth_data; |
7382 | |
7383 | /* If this is continuation of an ongoing SAE authentication exchange |
7384 | * (i.e., request to send SAE Confirm) and the peer has already |
7385 | * confirmed, mark authentication completed since we are about to send |
7386 | * out SAE Confirm. |
7387 | */ |
7388 | if (cont_auth && req->auth_type == NL80211_AUTHTYPE_SAE && |
7389 | auth_data->peer_confirmed && auth_data->sae_trans == 2) |
7390 | ieee80211_mark_sta_auth(sdata); |
7391 | |
7392 | if (ifmgd->associated) { |
7393 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
7394 | |
7395 | sdata_info(sdata, |
7396 | "disconnect from AP %pM for new auth to %pM\n" , |
7397 | sdata->vif.cfg.ap_addr, auth_data->ap_addr); |
7398 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
7399 | reason: WLAN_REASON_UNSPECIFIED, |
7400 | tx: false, frame_buf); |
7401 | |
7402 | ieee80211_report_disconnect(sdata, buf: frame_buf, |
7403 | len: sizeof(frame_buf), tx: true, |
7404 | reason: WLAN_REASON_UNSPECIFIED, |
7405 | reconnect: false); |
7406 | } |
7407 | |
7408 | /* needed for transmitting the auth frame(s) properly */ |
7409 | memcpy(sdata->vif.cfg.ap_addr, auth_data->ap_addr, ETH_ALEN); |
7410 | |
7411 | err = ieee80211_prep_connection(sdata, cbss: req->bss, link_id: req->link_id, |
7412 | ap_mld_addr: req->ap_mld_addr, assoc: cont_auth, override: false); |
7413 | if (err) |
7414 | goto err_clear; |
7415 | |
7416 | if (req->link_id > 0) |
7417 | link = sdata_dereference(sdata->link[req->link_id], sdata); |
7418 | else |
7419 | link = sdata_dereference(sdata->link[0], sdata); |
7420 | |
7421 | if (WARN_ON(!link)) { |
7422 | err = -ENOLINK; |
7423 | goto err_clear; |
7424 | } |
7425 | |
7426 | sdata_info(sdata, "authenticate with %pM (local address=%pM)\n" , |
7427 | auth_data->ap_addr, link->conf->addr); |
7428 | |
7429 | err = ieee80211_auth(sdata); |
7430 | if (err) { |
7431 | sta_info_destroy_addr(sdata, addr: auth_data->ap_addr); |
7432 | goto err_clear; |
7433 | } |
7434 | |
7435 | /* hold our own reference */ |
7436 | cfg80211_ref_bss(wiphy: local->hw.wiphy, bss: auth_data->bss); |
7437 | return 0; |
7438 | |
7439 | err_clear: |
7440 | if (!ieee80211_vif_is_mld(vif: &sdata->vif)) { |
7441 | eth_zero_addr(addr: sdata->deflink.u.mgd.bssid); |
7442 | ieee80211_link_info_change_notify(sdata, link: &sdata->deflink, |
7443 | changed: BSS_CHANGED_BSSID); |
7444 | ieee80211_link_release_channel(link: &sdata->deflink); |
7445 | } |
7446 | ifmgd->auth_data = NULL; |
7447 | kfree(objp: auth_data); |
7448 | return err; |
7449 | } |
7450 | |
7451 | static ieee80211_conn_flags_t |
7452 | ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, |
7453 | struct ieee80211_mgd_assoc_data *assoc_data, |
7454 | struct cfg80211_assoc_request *req, |
7455 | ieee80211_conn_flags_t conn_flags, |
7456 | unsigned int link_id) |
7457 | { |
7458 | struct ieee80211_local *local = sdata->local; |
7459 | const struct cfg80211_bss_ies *bss_ies; |
7460 | struct ieee80211_supported_band *sband; |
7461 | const struct element *ht_elem, *vht_elem; |
7462 | struct ieee80211_link_data *link; |
7463 | struct cfg80211_bss *cbss; |
7464 | struct ieee80211_bss *bss; |
7465 | bool is_5ghz, is_6ghz; |
7466 | |
7467 | cbss = assoc_data->link[link_id].bss; |
7468 | if (WARN_ON(!cbss)) |
7469 | return 0; |
7470 | |
7471 | bss = (void *)cbss->priv; |
7472 | |
7473 | sband = local->hw.wiphy->bands[cbss->channel->band]; |
7474 | if (WARN_ON(!sband)) |
7475 | return 0; |
7476 | |
7477 | link = sdata_dereference(sdata->link[link_id], sdata); |
7478 | if (WARN_ON(!link)) |
7479 | return 0; |
7480 | |
7481 | is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; |
7482 | is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; |
7483 | |
7484 | /* for MLO connections assume advertising all rates is OK */ |
7485 | if (!req->ap_mld_addr) { |
7486 | assoc_data->supp_rates = bss->supp_rates; |
7487 | assoc_data->supp_rates_len = bss->supp_rates_len; |
7488 | } |
7489 | |
7490 | /* copy and link elems for the STA profile */ |
7491 | if (req->links[link_id].elems_len) { |
7492 | memcpy(assoc_data->ie_pos, req->links[link_id].elems, |
7493 | req->links[link_id].elems_len); |
7494 | assoc_data->link[link_id].elems = assoc_data->ie_pos; |
7495 | assoc_data->link[link_id].elems_len = req->links[link_id].elems_len; |
7496 | assoc_data->ie_pos += req->links[link_id].elems_len; |
7497 | } |
7498 | |
7499 | rcu_read_lock(); |
7500 | ht_elem = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_HT_OPERATION); |
7501 | if (ht_elem && ht_elem->datalen >= sizeof(struct ieee80211_ht_operation)) |
7502 | assoc_data->link[link_id].ap_ht_param = |
7503 | ((struct ieee80211_ht_operation *)(ht_elem->data))->ht_param; |
7504 | else if (!is_6ghz) |
7505 | conn_flags |= IEEE80211_CONN_DISABLE_HT; |
7506 | vht_elem = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_VHT_CAPABILITY); |
7507 | if (vht_elem && vht_elem->datalen >= sizeof(struct ieee80211_vht_cap)) { |
7508 | memcpy(&assoc_data->link[link_id].ap_vht_cap, vht_elem->data, |
7509 | sizeof(struct ieee80211_vht_cap)); |
7510 | } else if (is_5ghz) { |
7511 | link_info(link, |
7512 | "VHT capa missing/short, disabling VHT/HE/EHT\n" ); |
7513 | conn_flags |= IEEE80211_CONN_DISABLE_VHT | |
7514 | IEEE80211_CONN_DISABLE_HE | |
7515 | IEEE80211_CONN_DISABLE_EHT; |
7516 | } |
7517 | rcu_read_unlock(); |
7518 | |
7519 | link->u.mgd.beacon_crc_valid = false; |
7520 | link->u.mgd.dtim_period = 0; |
7521 | link->u.mgd.have_beacon = false; |
7522 | |
7523 | /* override HT/VHT configuration only if the AP and we support it */ |
7524 | if (!(conn_flags & IEEE80211_CONN_DISABLE_HT)) { |
7525 | struct ieee80211_sta_ht_cap sta_ht_cap; |
7526 | |
7527 | memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); |
7528 | ieee80211_apply_htcap_overrides(sdata, ht_cap: &sta_ht_cap); |
7529 | } |
7530 | |
7531 | link->conf->eht_puncturing = 0; |
7532 | |
7533 | rcu_read_lock(); |
7534 | bss_ies = rcu_dereference(cbss->beacon_ies); |
7535 | if (bss_ies) { |
7536 | u8 dtim_count = 0; |
7537 | |
7538 | ieee80211_get_dtim(ies: bss_ies, dtim_count: &dtim_count, |
7539 | dtim_period: &link->u.mgd.dtim_period); |
7540 | |
7541 | sdata->deflink.u.mgd.have_beacon = true; |
7542 | |
7543 | if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) { |
7544 | link->conf->sync_tsf = bss_ies->tsf; |
7545 | link->conf->sync_device_ts = bss->device_ts_beacon; |
7546 | link->conf->sync_dtim_count = dtim_count; |
7547 | } |
7548 | } else { |
7549 | bss_ies = rcu_dereference(cbss->ies); |
7550 | } |
7551 | |
7552 | if (bss_ies) { |
7553 | const struct ieee80211_eht_operation *eht_oper; |
7554 | const struct element *elem; |
7555 | |
7556 | elem = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION, |
7557 | ies: bss_ies->data, len: bss_ies->len); |
7558 | if (elem && elem->datalen >= 3) |
7559 | link->conf->profile_periodicity = elem->data[2]; |
7560 | else |
7561 | link->conf->profile_periodicity = 0; |
7562 | |
7563 | elem = cfg80211_find_elem(eid: WLAN_EID_EXT_CAPABILITY, |
7564 | ies: bss_ies->data, len: bss_ies->len); |
7565 | if (elem && elem->datalen >= 11 && |
7566 | (elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) |
7567 | link->conf->ema_ap = true; |
7568 | else |
7569 | link->conf->ema_ap = false; |
7570 | |
7571 | elem = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_EHT_OPERATION, |
7572 | ies: bss_ies->data, len: bss_ies->len); |
7573 | eht_oper = (const void *)(elem->data + 1); |
7574 | |
7575 | if (elem && |
7576 | ieee80211_eht_oper_size_ok(data: (const void *)(elem->data + 1), |
7577 | len: elem->datalen - 1) && |
7578 | (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) && |
7579 | (eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) { |
7580 | const struct ieee80211_eht_operation_info *info = |
7581 | (void *)eht_oper->optional; |
7582 | const u8 *disable_subchannel_bitmap = info->optional; |
7583 | u16 bitmap; |
7584 | |
7585 | bitmap = get_unaligned_le16(p: disable_subchannel_bitmap); |
7586 | if (cfg80211_valid_disable_subchannel_bitmap(bitmap: &bitmap, |
7587 | chandef: &link->conf->chandef)) |
7588 | ieee80211_handle_puncturing_bitmap(link, |
7589 | eht_oper, |
7590 | bitmap, |
7591 | NULL); |
7592 | else |
7593 | conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
7594 | } |
7595 | } |
7596 | rcu_read_unlock(); |
7597 | |
7598 | if (bss->corrupt_data) { |
7599 | char *corrupt_type = "data" ; |
7600 | |
7601 | if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_BEACON) { |
7602 | if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) |
7603 | corrupt_type = "beacon and probe response" ; |
7604 | else |
7605 | corrupt_type = "beacon" ; |
7606 | } else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) { |
7607 | corrupt_type = "probe response" ; |
7608 | } |
7609 | sdata_info(sdata, "associating to AP %pM with corrupt %s\n" , |
7610 | cbss->bssid, corrupt_type); |
7611 | } |
7612 | |
7613 | if (link->u.mgd.req_smps == IEEE80211_SMPS_AUTOMATIC) { |
7614 | if (sdata->u.mgd.powersave) |
7615 | link->smps_mode = IEEE80211_SMPS_DYNAMIC; |
7616 | else |
7617 | link->smps_mode = IEEE80211_SMPS_OFF; |
7618 | } else { |
7619 | link->smps_mode = link->u.mgd.req_smps; |
7620 | } |
7621 | |
7622 | return conn_flags; |
7623 | } |
7624 | |
7625 | int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, |
7626 | struct cfg80211_assoc_request *req) |
7627 | { |
7628 | unsigned int assoc_link_id = req->link_id < 0 ? 0 : req->link_id; |
7629 | struct ieee80211_local *local = sdata->local; |
7630 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
7631 | struct ieee80211_mgd_assoc_data *assoc_data; |
7632 | const struct element *ssid_elem, *csa_elem, *ecsa_elem; |
7633 | struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; |
7634 | ieee80211_conn_flags_t conn_flags = 0; |
7635 | struct ieee80211_link_data *link; |
7636 | struct cfg80211_bss *cbss; |
7637 | struct ieee80211_bss *bss; |
7638 | bool override; |
7639 | int i, err; |
7640 | size_t size = sizeof(*assoc_data) + req->ie_len; |
7641 | |
7642 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) |
7643 | size += req->links[i].elems_len; |
7644 | |
7645 | /* FIXME: no support for 4-addr MLO yet */ |
7646 | if (sdata->u.mgd.use_4addr && req->link_id >= 0) |
7647 | return -EOPNOTSUPP; |
7648 | |
7649 | assoc_data = kzalloc(size, GFP_KERNEL); |
7650 | if (!assoc_data) |
7651 | return -ENOMEM; |
7652 | |
7653 | cbss = req->link_id < 0 ? req->bss : req->links[req->link_id].bss; |
7654 | |
7655 | rcu_read_lock(); |
7656 | ssid_elem = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_SSID); |
7657 | if (!ssid_elem || ssid_elem->datalen > sizeof(assoc_data->ssid)) { |
7658 | rcu_read_unlock(); |
7659 | kfree(objp: assoc_data); |
7660 | return -EINVAL; |
7661 | } |
7662 | |
7663 | csa_elem = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_CHANNEL_SWITCH); |
7664 | ecsa_elem = ieee80211_bss_get_elem(bss: cbss, id: WLAN_EID_EXT_CHANSWITCH_ANN); |
7665 | if ((csa_elem && |
7666 | csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) && |
7667 | ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) || |
7668 | (ecsa_elem && |
7669 | ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) && |
7670 | ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) { |
7671 | sdata_info(sdata, "AP is in CSA process, reject assoc\n" ); |
7672 | rcu_read_unlock(); |
7673 | kfree(objp: assoc_data); |
7674 | return -EINVAL; |
7675 | } |
7676 | |
7677 | memcpy(assoc_data->ssid, ssid_elem->data, ssid_elem->datalen); |
7678 | assoc_data->ssid_len = ssid_elem->datalen; |
7679 | memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len); |
7680 | vif_cfg->ssid_len = assoc_data->ssid_len; |
7681 | rcu_read_unlock(); |
7682 | |
7683 | if (req->ap_mld_addr) { |
7684 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { |
7685 | if (!req->links[i].bss) |
7686 | continue; |
7687 | link = sdata_dereference(sdata->link[i], sdata); |
7688 | if (link) |
7689 | ether_addr_copy(dst: assoc_data->link[i].addr, |
7690 | src: link->conf->addr); |
7691 | else |
7692 | eth_random_addr(addr: assoc_data->link[i].addr); |
7693 | } |
7694 | } else { |
7695 | memcpy(assoc_data->link[0].addr, sdata->vif.addr, ETH_ALEN); |
7696 | } |
7697 | |
7698 | assoc_data->s1g = cbss->channel->band == NL80211_BAND_S1GHZ; |
7699 | |
7700 | memcpy(assoc_data->ap_addr, |
7701 | req->ap_mld_addr ?: req->bss->bssid, |
7702 | ETH_ALEN); |
7703 | |
7704 | if (ifmgd->associated) { |
7705 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
7706 | |
7707 | sdata_info(sdata, |
7708 | "disconnect from AP %pM for new assoc to %pM\n" , |
7709 | sdata->vif.cfg.ap_addr, assoc_data->ap_addr); |
7710 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
7711 | reason: WLAN_REASON_UNSPECIFIED, |
7712 | tx: false, frame_buf); |
7713 | |
7714 | ieee80211_report_disconnect(sdata, buf: frame_buf, |
7715 | len: sizeof(frame_buf), tx: true, |
7716 | reason: WLAN_REASON_UNSPECIFIED, |
7717 | reconnect: false); |
7718 | } |
7719 | |
7720 | if (ifmgd->auth_data && !ifmgd->auth_data->done) { |
7721 | err = -EBUSY; |
7722 | goto err_free; |
7723 | } |
7724 | |
7725 | if (ifmgd->assoc_data) { |
7726 | err = -EBUSY; |
7727 | goto err_free; |
7728 | } |
7729 | |
7730 | if (ifmgd->auth_data) { |
7731 | bool match; |
7732 | |
7733 | /* keep sta info, bssid if matching */ |
7734 | match = ether_addr_equal(addr1: ifmgd->auth_data->ap_addr, |
7735 | addr2: assoc_data->ap_addr) && |
7736 | ifmgd->auth_data->link_id == req->link_id; |
7737 | |
7738 | /* Cleanup is delayed if auth_data matches */ |
7739 | if (!match) |
7740 | ieee80211_destroy_auth_data(sdata, assoc: false); |
7741 | } |
7742 | |
7743 | /* prepare assoc data */ |
7744 | |
7745 | bss = (void *)cbss->priv; |
7746 | assoc_data->wmm = bss->wmm_used && |
7747 | (local->hw.queues >= IEEE80211_NUM_ACS); |
7748 | |
7749 | /* |
7750 | * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. |
7751 | * We still associate in non-HT mode (11a/b/g) if any one of these |
7752 | * ciphers is configured as pairwise. |
7753 | * We can set this to true for non-11n hardware, that'll be checked |
7754 | * separately along with the peer capabilities. |
7755 | */ |
7756 | for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) { |
7757 | if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || |
7758 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || |
7759 | req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { |
7760 | conn_flags |= IEEE80211_CONN_DISABLE_HT; |
7761 | conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
7762 | conn_flags |= IEEE80211_CONN_DISABLE_HE; |
7763 | conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
7764 | netdev_info(dev: sdata->dev, |
7765 | format: "disabling HT/VHT/HE due to WEP/TKIP use\n" ); |
7766 | } |
7767 | } |
7768 | |
7769 | /* also disable HT/VHT/HE/EHT if the AP doesn't use WMM */ |
7770 | if (!bss->wmm_used) { |
7771 | conn_flags |= IEEE80211_CONN_DISABLE_HT; |
7772 | conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
7773 | conn_flags |= IEEE80211_CONN_DISABLE_HE; |
7774 | conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
7775 | netdev_info(dev: sdata->dev, |
7776 | format: "disabling HT/VHT/HE as WMM/QoS is not supported by the AP\n" ); |
7777 | } |
7778 | |
7779 | if (req->flags & ASSOC_REQ_DISABLE_HT) { |
7780 | mlme_dbg(sdata, "HT disabled by flag, disabling HT/VHT/HE\n" ); |
7781 | conn_flags |= IEEE80211_CONN_DISABLE_HT; |
7782 | conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
7783 | conn_flags |= IEEE80211_CONN_DISABLE_HE; |
7784 | conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
7785 | } |
7786 | |
7787 | if (req->flags & ASSOC_REQ_DISABLE_VHT) { |
7788 | mlme_dbg(sdata, "VHT disabled by flag, disabling VHT\n" ); |
7789 | conn_flags |= IEEE80211_CONN_DISABLE_VHT; |
7790 | } |
7791 | |
7792 | if (req->flags & ASSOC_REQ_DISABLE_HE) { |
7793 | mlme_dbg(sdata, "HE disabled by flag, disabling HE/EHT\n" ); |
7794 | conn_flags |= IEEE80211_CONN_DISABLE_HE; |
7795 | conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
7796 | } |
7797 | |
7798 | if (req->flags & ASSOC_REQ_DISABLE_EHT) |
7799 | conn_flags |= IEEE80211_CONN_DISABLE_EHT; |
7800 | |
7801 | memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa)); |
7802 | memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask, |
7803 | sizeof(ifmgd->ht_capa_mask)); |
7804 | |
7805 | memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa)); |
7806 | memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask, |
7807 | sizeof(ifmgd->vht_capa_mask)); |
7808 | |
7809 | memcpy(&ifmgd->s1g_capa, &req->s1g_capa, sizeof(ifmgd->s1g_capa)); |
7810 | memcpy(&ifmgd->s1g_capa_mask, &req->s1g_capa_mask, |
7811 | sizeof(ifmgd->s1g_capa_mask)); |
7812 | |
7813 | if (req->ie && req->ie_len) { |
7814 | memcpy(assoc_data->ie, req->ie, req->ie_len); |
7815 | assoc_data->ie_len = req->ie_len; |
7816 | assoc_data->ie_pos = assoc_data->ie + assoc_data->ie_len; |
7817 | } else { |
7818 | assoc_data->ie_pos = assoc_data->ie; |
7819 | } |
7820 | |
7821 | if (req->fils_kek) { |
7822 | /* should already be checked in cfg80211 - so warn */ |
7823 | if (WARN_ON(req->fils_kek_len > FILS_MAX_KEK_LEN)) { |
7824 | err = -EINVAL; |
7825 | goto err_free; |
7826 | } |
7827 | memcpy(assoc_data->fils_kek, req->fils_kek, |
7828 | req->fils_kek_len); |
7829 | assoc_data->fils_kek_len = req->fils_kek_len; |
7830 | } |
7831 | |
7832 | if (req->fils_nonces) |
7833 | memcpy(assoc_data->fils_nonces, req->fils_nonces, |
7834 | 2 * FILS_NONCE_LEN); |
7835 | |
7836 | /* default timeout */ |
7837 | assoc_data->timeout = jiffies; |
7838 | assoc_data->timeout_started = true; |
7839 | |
7840 | assoc_data->assoc_link_id = assoc_link_id; |
7841 | |
7842 | if (req->ap_mld_addr) { |
7843 | for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) { |
7844 | assoc_data->link[i].conn_flags = conn_flags; |
7845 | assoc_data->link[i].bss = req->links[i].bss; |
7846 | assoc_data->link[i].disabled = req->links[i].disabled; |
7847 | } |
7848 | |
7849 | /* if there was no authentication, set up the link */ |
7850 | err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id), dormant_links: 0); |
7851 | if (err) |
7852 | goto err_clear; |
7853 | } else { |
7854 | assoc_data->link[0].conn_flags = conn_flags; |
7855 | assoc_data->link[0].bss = cbss; |
7856 | } |
7857 | |
7858 | link = sdata_dereference(sdata->link[assoc_link_id], sdata); |
7859 | if (WARN_ON(!link)) { |
7860 | err = -EINVAL; |
7861 | goto err_clear; |
7862 | } |
7863 | |
7864 | /* keep old conn_flags from ieee80211_prep_channel() from auth */ |
7865 | conn_flags |= link->u.mgd.conn_flags; |
7866 | conn_flags |= ieee80211_setup_assoc_link(sdata, assoc_data, req, |
7867 | conn_flags, link_id: assoc_link_id); |
7868 | override = link->u.mgd.conn_flags != conn_flags; |
7869 | link->u.mgd.conn_flags |= conn_flags; |
7870 | |
7871 | if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) && |
7872 | ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK), |
7873 | "U-APSD not supported with HW_PS_NULLFUNC_STACK\n" )) |
7874 | sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; |
7875 | |
7876 | if (bss->wmm_used && bss->uapsd_supported && |
7877 | (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) { |
7878 | assoc_data->uapsd = true; |
7879 | ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED; |
7880 | } else { |
7881 | assoc_data->uapsd = false; |
7882 | ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; |
7883 | } |
7884 | |
7885 | if (req->prev_bssid) |
7886 | memcpy(assoc_data->prev_ap_addr, req->prev_bssid, ETH_ALEN); |
7887 | |
7888 | if (req->use_mfp) { |
7889 | ifmgd->mfp = IEEE80211_MFP_REQUIRED; |
7890 | ifmgd->flags |= IEEE80211_STA_MFP_ENABLED; |
7891 | } else { |
7892 | ifmgd->mfp = IEEE80211_MFP_DISABLED; |
7893 | ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED; |
7894 | } |
7895 | |
7896 | if (req->flags & ASSOC_REQ_USE_RRM) |
7897 | ifmgd->flags |= IEEE80211_STA_ENABLE_RRM; |
7898 | else |
7899 | ifmgd->flags &= ~IEEE80211_STA_ENABLE_RRM; |
7900 | |
7901 | if (req->crypto.control_port) |
7902 | ifmgd->flags |= IEEE80211_STA_CONTROL_PORT; |
7903 | else |
7904 | ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; |
7905 | |
7906 | sdata->control_port_protocol = req->crypto.control_port_ethertype; |
7907 | sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt; |
7908 | sdata->control_port_over_nl80211 = |
7909 | req->crypto.control_port_over_nl80211; |
7910 | sdata->control_port_no_preauth = req->crypto.control_port_no_preauth; |
7911 | |
7912 | /* kick off associate process */ |
7913 | ifmgd->assoc_data = assoc_data; |
7914 | |
7915 | for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) { |
7916 | if (!assoc_data->link[i].bss) |
7917 | continue; |
7918 | if (i == assoc_data->assoc_link_id) |
7919 | continue; |
7920 | /* only calculate the flags, hence link == NULL */ |
7921 | err = ieee80211_prep_channel(sdata, NULL, |
7922 | cbss: assoc_data->link[i].bss, mlo: true, |
7923 | conn_flags: &assoc_data->link[i].conn_flags); |
7924 | if (err) { |
7925 | req->links[i].error = err; |
7926 | goto err_clear; |
7927 | } |
7928 | } |
7929 | |
7930 | /* needed for transmitting the assoc frames properly */ |
7931 | memcpy(sdata->vif.cfg.ap_addr, assoc_data->ap_addr, ETH_ALEN); |
7932 | |
7933 | err = ieee80211_prep_connection(sdata, cbss, link_id: req->link_id, |
7934 | ap_mld_addr: req->ap_mld_addr, assoc: true, override); |
7935 | if (err) |
7936 | goto err_clear; |
7937 | |
7938 | assoc_data->link[assoc_data->assoc_link_id].conn_flags = |
7939 | link->u.mgd.conn_flags; |
7940 | |
7941 | if (ieee80211_hw_check(&sdata->local->hw, NEED_DTIM_BEFORE_ASSOC)) { |
7942 | const struct cfg80211_bss_ies *beacon_ies; |
7943 | |
7944 | rcu_read_lock(); |
7945 | beacon_ies = rcu_dereference(req->bss->beacon_ies); |
7946 | |
7947 | if (beacon_ies) { |
7948 | /* |
7949 | * Wait up to one beacon interval ... |
7950 | * should this be more if we miss one? |
7951 | */ |
7952 | sdata_info(sdata, "waiting for beacon from %pM\n" , |
7953 | link->u.mgd.bssid); |
7954 | assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); |
7955 | assoc_data->timeout_started = true; |
7956 | assoc_data->need_beacon = true; |
7957 | } |
7958 | rcu_read_unlock(); |
7959 | } |
7960 | |
7961 | run_again(sdata, timeout: assoc_data->timeout); |
7962 | |
7963 | /* We are associating, clean up auth_data */ |
7964 | if (ifmgd->auth_data) |
7965 | ieee80211_destroy_auth_data(sdata, assoc: true); |
7966 | |
7967 | return 0; |
7968 | err_clear: |
7969 | if (!ifmgd->auth_data) { |
7970 | eth_zero_addr(addr: sdata->deflink.u.mgd.bssid); |
7971 | ieee80211_link_info_change_notify(sdata, link: &sdata->deflink, |
7972 | changed: BSS_CHANGED_BSSID); |
7973 | } |
7974 | ifmgd->assoc_data = NULL; |
7975 | err_free: |
7976 | kfree(objp: assoc_data); |
7977 | return err; |
7978 | } |
7979 | |
7980 | int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, |
7981 | struct cfg80211_deauth_request *req) |
7982 | { |
7983 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
7984 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
7985 | bool tx = !req->local_state_change; |
7986 | struct ieee80211_prep_tx_info info = { |
7987 | .subtype = IEEE80211_STYPE_DEAUTH, |
7988 | }; |
7989 | |
7990 | if (ifmgd->auth_data && |
7991 | ether_addr_equal(addr1: ifmgd->auth_data->ap_addr, addr2: req->bssid)) { |
7992 | sdata_info(sdata, |
7993 | "aborting authentication with %pM by local choice (Reason: %u=%s)\n" , |
7994 | req->bssid, req->reason_code, |
7995 | ieee80211_get_reason_code_string(req->reason_code)); |
7996 | |
7997 | info.link_id = ifmgd->auth_data->link_id; |
7998 | drv_mgd_prepare_tx(local: sdata->local, sdata, info: &info); |
7999 | ieee80211_send_deauth_disassoc(sdata, da: req->bssid, bssid: req->bssid, |
8000 | IEEE80211_STYPE_DEAUTH, |
8001 | reason: req->reason_code, send_frame: tx, |
8002 | frame_buf); |
8003 | ieee80211_destroy_auth_data(sdata, assoc: false); |
8004 | ieee80211_report_disconnect(sdata, buf: frame_buf, |
8005 | len: sizeof(frame_buf), tx: true, |
8006 | reason: req->reason_code, reconnect: false); |
8007 | drv_mgd_complete_tx(local: sdata->local, sdata, info: &info); |
8008 | return 0; |
8009 | } |
8010 | |
8011 | if (ifmgd->assoc_data && |
8012 | ether_addr_equal(addr1: ifmgd->assoc_data->ap_addr, addr2: req->bssid)) { |
8013 | sdata_info(sdata, |
8014 | "aborting association with %pM by local choice (Reason: %u=%s)\n" , |
8015 | req->bssid, req->reason_code, |
8016 | ieee80211_get_reason_code_string(req->reason_code)); |
8017 | |
8018 | info.link_id = ifmgd->assoc_data->assoc_link_id; |
8019 | drv_mgd_prepare_tx(local: sdata->local, sdata, info: &info); |
8020 | ieee80211_send_deauth_disassoc(sdata, da: req->bssid, bssid: req->bssid, |
8021 | IEEE80211_STYPE_DEAUTH, |
8022 | reason: req->reason_code, send_frame: tx, |
8023 | frame_buf); |
8024 | ieee80211_destroy_assoc_data(sdata, status: ASSOC_ABANDON); |
8025 | ieee80211_report_disconnect(sdata, buf: frame_buf, |
8026 | len: sizeof(frame_buf), tx: true, |
8027 | reason: req->reason_code, reconnect: false); |
8028 | return 0; |
8029 | } |
8030 | |
8031 | if (ifmgd->associated && |
8032 | ether_addr_equal(addr1: sdata->vif.cfg.ap_addr, addr2: req->bssid)) { |
8033 | sdata_info(sdata, |
8034 | "deauthenticating from %pM by local choice (Reason: %u=%s)\n" , |
8035 | req->bssid, req->reason_code, |
8036 | ieee80211_get_reason_code_string(req->reason_code)); |
8037 | |
8038 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, |
8039 | reason: req->reason_code, tx, frame_buf); |
8040 | ieee80211_report_disconnect(sdata, buf: frame_buf, |
8041 | len: sizeof(frame_buf), tx: true, |
8042 | reason: req->reason_code, reconnect: false); |
8043 | drv_mgd_complete_tx(local: sdata->local, sdata, info: &info); |
8044 | return 0; |
8045 | } |
8046 | |
8047 | return -ENOTCONN; |
8048 | } |
8049 | |
8050 | int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, |
8051 | struct cfg80211_disassoc_request *req) |
8052 | { |
8053 | u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; |
8054 | |
8055 | if (!sdata->u.mgd.associated || |
8056 | memcmp(p: sdata->vif.cfg.ap_addr, q: req->ap_addr, ETH_ALEN)) |
8057 | return -ENOTCONN; |
8058 | |
8059 | sdata_info(sdata, |
8060 | "disassociating from %pM by local choice (Reason: %u=%s)\n" , |
8061 | req->ap_addr, req->reason_code, |
8062 | ieee80211_get_reason_code_string(req->reason_code)); |
8063 | |
8064 | ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, |
8065 | reason: req->reason_code, tx: !req->local_state_change, |
8066 | frame_buf); |
8067 | |
8068 | ieee80211_report_disconnect(sdata, buf: frame_buf, len: sizeof(frame_buf), tx: true, |
8069 | reason: req->reason_code, reconnect: false); |
8070 | |
8071 | return 0; |
8072 | } |
8073 | |
8074 | void ieee80211_mgd_stop_link(struct ieee80211_link_data *link) |
8075 | { |
8076 | wiphy_work_cancel(wiphy: link->sdata->local->hw.wiphy, |
8077 | work: &link->u.mgd.request_smps_work); |
8078 | wiphy_work_cancel(wiphy: link->sdata->local->hw.wiphy, |
8079 | work: &link->u.mgd.recalc_smps); |
8080 | wiphy_delayed_work_cancel(wiphy: link->sdata->local->hw.wiphy, |
8081 | dwork: &link->u.mgd.chswitch_work); |
8082 | } |
8083 | |
8084 | void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) |
8085 | { |
8086 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
8087 | |
8088 | /* |
8089 | * Make sure some work items will not run after this, |
8090 | * they will not do anything but might not have been |
8091 | * cancelled when disconnecting. |
8092 | */ |
8093 | wiphy_work_cancel(wiphy: sdata->local->hw.wiphy, |
8094 | work: &ifmgd->monitor_work); |
8095 | wiphy_work_cancel(wiphy: sdata->local->hw.wiphy, |
8096 | work: &ifmgd->beacon_connection_loss_work); |
8097 | wiphy_work_cancel(wiphy: sdata->local->hw.wiphy, |
8098 | work: &ifmgd->csa_connection_drop_work); |
8099 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, |
8100 | dwork: &ifmgd->tdls_peer_del_work); |
8101 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, |
8102 | dwork: &ifmgd->ml_reconf_work); |
8103 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, dwork: &ifmgd->ttlm_work); |
8104 | |
8105 | if (ifmgd->assoc_data) |
8106 | ieee80211_destroy_assoc_data(sdata, status: ASSOC_TIMEOUT); |
8107 | if (ifmgd->auth_data) |
8108 | ieee80211_destroy_auth_data(sdata, assoc: false); |
8109 | spin_lock_bh(lock: &ifmgd->teardown_lock); |
8110 | if (ifmgd->teardown_skb) { |
8111 | kfree_skb(skb: ifmgd->teardown_skb); |
8112 | ifmgd->teardown_skb = NULL; |
8113 | ifmgd->orig_teardown_skb = NULL; |
8114 | } |
8115 | kfree(objp: ifmgd->assoc_req_ies); |
8116 | ifmgd->assoc_req_ies = NULL; |
8117 | ifmgd->assoc_req_ies_len = 0; |
8118 | spin_unlock_bh(lock: &ifmgd->teardown_lock); |
8119 | del_timer_sync(timer: &ifmgd->timer); |
8120 | } |
8121 | |
8122 | void (struct ieee80211_vif *vif, |
8123 | enum nl80211_cqm_rssi_threshold_event , |
8124 | s32 , |
8125 | gfp_t gfp) |
8126 | { |
8127 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
8128 | |
8129 | trace_api_cqm_rssi_notify(sdata, rssi_event, rssi_level); |
8130 | |
8131 | cfg80211_cqm_rssi_notify(dev: sdata->dev, rssi_event, rssi_level, gfp); |
8132 | } |
8133 | EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); |
8134 | |
8135 | void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp) |
8136 | { |
8137 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
8138 | |
8139 | trace_api_cqm_beacon_loss_notify(local: sdata->local, sdata); |
8140 | |
8141 | cfg80211_cqm_beacon_loss_notify(dev: sdata->dev, gfp); |
8142 | } |
8143 | EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify); |
8144 | |
8145 | static void (struct ieee80211_sub_if_data *sdata, |
8146 | int , |
8147 | int ) |
8148 | { |
8149 | trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); |
8150 | |
8151 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) |
8152 | return; |
8153 | |
8154 | /* |
8155 | * Scale up threshold values before storing it, as the RSSI averaging |
8156 | * algorithm uses a scaled up value as well. Change this scaling |
8157 | * factor if the RSSI averaging algorithm changes. |
8158 | */ |
8159 | sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; |
8160 | sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; |
8161 | } |
8162 | |
8163 | void (struct ieee80211_vif *vif, |
8164 | int , |
8165 | int ) |
8166 | { |
8167 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
8168 | |
8169 | WARN_ON(rssi_min_thold == rssi_max_thold || |
8170 | rssi_min_thold > rssi_max_thold); |
8171 | |
8172 | _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, |
8173 | rssi_max_thold); |
8174 | } |
8175 | EXPORT_SYMBOL(ieee80211_enable_rssi_reports); |
8176 | |
8177 | void (struct ieee80211_vif *vif) |
8178 | { |
8179 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
8180 | |
8181 | _ieee80211_enable_rssi_reports(sdata, rssi_min_thold: 0, rssi_max_thold: 0); |
8182 | } |
8183 | EXPORT_SYMBOL(ieee80211_disable_rssi_reports); |
8184 | |