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