1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * mac80211 TDLS handling code |
4 | * |
5 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2014, Intel Corporation |
7 | * Copyright 2014 Intel Mobile Communications GmbH |
8 | * Copyright 2015 - 2016 Intel Deutschland GmbH |
9 | * Copyright (C) 2019, 2021-2024 Intel Corporation |
10 | */ |
11 | |
12 | #include <linux/ieee80211.h> |
13 | #include <linux/log2.h> |
14 | #include <net/cfg80211.h> |
15 | #include <linux/rtnetlink.h> |
16 | #include "ieee80211_i.h" |
17 | #include "driver-ops.h" |
18 | #include "rate.h" |
19 | #include "wme.h" |
20 | |
21 | /* give usermode some time for retries in setting up the TDLS session */ |
22 | #define TDLS_PEER_SETUP_TIMEOUT (15 * HZ) |
23 | |
24 | void ieee80211_tdls_peer_del_work(struct wiphy *wiphy, struct wiphy_work *wk) |
25 | { |
26 | struct ieee80211_sub_if_data *sdata; |
27 | struct ieee80211_local *local; |
28 | |
29 | sdata = container_of(wk, struct ieee80211_sub_if_data, |
30 | u.mgd.tdls_peer_del_work.work); |
31 | local = sdata->local; |
32 | |
33 | lockdep_assert_wiphy(local->hw.wiphy); |
34 | |
35 | if (!is_zero_ether_addr(addr: sdata->u.mgd.tdls_peer)) { |
36 | tdls_dbg(sdata, "TDLS del peer %pM\n" , sdata->u.mgd.tdls_peer); |
37 | sta_info_destroy_addr(sdata, addr: sdata->u.mgd.tdls_peer); |
38 | eth_zero_addr(addr: sdata->u.mgd.tdls_peer); |
39 | } |
40 | } |
41 | |
42 | static void ieee80211_tdls_add_ext_capab(struct ieee80211_link_data *link, |
43 | struct sk_buff *skb) |
44 | { |
45 | struct ieee80211_sub_if_data *sdata = link->sdata; |
46 | struct ieee80211_local *local = sdata->local; |
47 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
48 | bool chan_switch = local->hw.wiphy->features & |
49 | NL80211_FEATURE_TDLS_CHANNEL_SWITCH; |
50 | bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) && |
51 | !ifmgd->tdls_wider_bw_prohibited; |
52 | bool buffer_sta = ieee80211_hw_check(&local->hw, |
53 | SUPPORTS_TDLS_BUFFER_STA); |
54 | struct ieee80211_supported_band *sband = ieee80211_get_link_sband(link); |
55 | bool vht = sband && sband->vht_cap.vht_supported; |
56 | u8 *pos = skb_put(skb, len: 10); |
57 | |
58 | *pos++ = WLAN_EID_EXT_CAPABILITY; |
59 | *pos++ = 8; /* len */ |
60 | *pos++ = 0x0; |
61 | *pos++ = 0x0; |
62 | *pos++ = 0x0; |
63 | *pos++ = (chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0) | |
64 | (buffer_sta ? WLAN_EXT_CAPA4_TDLS_BUFFER_STA : 0); |
65 | *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; |
66 | *pos++ = 0; |
67 | *pos++ = 0; |
68 | *pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0; |
69 | } |
70 | |
71 | static u8 |
72 | ieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata, |
73 | struct sk_buff *skb, u16 start, u16 end, |
74 | u16 spacing) |
75 | { |
76 | u8 subband_cnt = 0, ch_cnt = 0; |
77 | struct ieee80211_channel *ch; |
78 | struct cfg80211_chan_def chandef; |
79 | int i, subband_start; |
80 | struct wiphy *wiphy = sdata->local->hw.wiphy; |
81 | |
82 | for (i = start; i <= end; i += spacing) { |
83 | if (!ch_cnt) |
84 | subband_start = i; |
85 | |
86 | ch = ieee80211_get_channel(wiphy: sdata->local->hw.wiphy, freq: i); |
87 | if (ch) { |
88 | /* we will be active on the channel */ |
89 | cfg80211_chandef_create(chandef: &chandef, channel: ch, |
90 | chantype: NL80211_CHAN_NO_HT); |
91 | if (cfg80211_reg_can_beacon_relax(wiphy, chandef: &chandef, |
92 | iftype: sdata->wdev.iftype)) { |
93 | ch_cnt++; |
94 | /* |
95 | * check if the next channel is also part of |
96 | * this allowed range |
97 | */ |
98 | continue; |
99 | } |
100 | } |
101 | |
102 | /* |
103 | * we've reached the end of a range, with allowed channels |
104 | * found |
105 | */ |
106 | if (ch_cnt) { |
107 | u8 *pos = skb_put(skb, len: 2); |
108 | *pos++ = ieee80211_frequency_to_channel(freq: subband_start); |
109 | *pos++ = ch_cnt; |
110 | |
111 | subband_cnt++; |
112 | ch_cnt = 0; |
113 | } |
114 | } |
115 | |
116 | /* all channels in the requested range are allowed - add them here */ |
117 | if (ch_cnt) { |
118 | u8 *pos = skb_put(skb, len: 2); |
119 | *pos++ = ieee80211_frequency_to_channel(freq: subband_start); |
120 | *pos++ = ch_cnt; |
121 | |
122 | subband_cnt++; |
123 | } |
124 | |
125 | return subband_cnt; |
126 | } |
127 | |
128 | static void |
129 | ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata, |
130 | struct sk_buff *skb) |
131 | { |
132 | /* |
133 | * Add possible channels for TDLS. These are channels that are allowed |
134 | * to be active. |
135 | */ |
136 | u8 subband_cnt; |
137 | u8 *pos = skb_put(skb, len: 2); |
138 | |
139 | *pos++ = WLAN_EID_SUPPORTED_CHANNELS; |
140 | |
141 | /* |
142 | * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as |
143 | * this doesn't happen in real world scenarios. |
144 | */ |
145 | |
146 | /* 2GHz, with 5MHz spacing */ |
147 | subband_cnt = ieee80211_tdls_add_subband(sdata, skb, start: 2412, end: 2472, spacing: 5); |
148 | |
149 | /* 5GHz, with 20MHz spacing */ |
150 | subband_cnt += ieee80211_tdls_add_subband(sdata, skb, start: 5000, end: 5825, spacing: 20); |
151 | |
152 | /* length */ |
153 | *pos = 2 * subband_cnt; |
154 | } |
155 | |
156 | static void ieee80211_tdls_add_oper_classes(struct ieee80211_link_data *link, |
157 | struct sk_buff *skb) |
158 | { |
159 | u8 *pos; |
160 | u8 op_class; |
161 | |
162 | if (!ieee80211_chandef_to_operating_class(chandef: &link->conf->chanreq.oper, |
163 | op_class: &op_class)) |
164 | return; |
165 | |
166 | pos = skb_put(skb, len: 4); |
167 | *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES; |
168 | *pos++ = 2; /* len */ |
169 | |
170 | *pos++ = op_class; |
171 | *pos++ = op_class; /* give current operating class as alternate too */ |
172 | } |
173 | |
174 | static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) |
175 | { |
176 | u8 *pos = skb_put(skb, len: 3); |
177 | |
178 | *pos++ = WLAN_EID_BSS_COEX_2040; |
179 | *pos++ = 1; /* len */ |
180 | |
181 | *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST; |
182 | } |
183 | |
184 | static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_link_data *link, |
185 | u16 status_code) |
186 | { |
187 | struct ieee80211_supported_band *sband; |
188 | |
189 | /* The capability will be 0 when sending a failure code */ |
190 | if (status_code != 0) |
191 | return 0; |
192 | |
193 | sband = ieee80211_get_link_sband(link); |
194 | |
195 | if (sband && sband->band == NL80211_BAND_2GHZ) { |
196 | return WLAN_CAPABILITY_SHORT_SLOT_TIME | |
197 | WLAN_CAPABILITY_SHORT_PREAMBLE; |
198 | } |
199 | |
200 | return 0; |
201 | } |
202 | |
203 | static void ieee80211_tdls_add_link_ie(struct ieee80211_link_data *link, |
204 | struct sk_buff *skb, const u8 *peer, |
205 | bool initiator) |
206 | { |
207 | struct ieee80211_sub_if_data *sdata = link->sdata; |
208 | struct ieee80211_tdls_lnkie *lnkid; |
209 | const u8 *init_addr, *rsp_addr; |
210 | |
211 | if (initiator) { |
212 | init_addr = sdata->vif.addr; |
213 | rsp_addr = peer; |
214 | } else { |
215 | init_addr = peer; |
216 | rsp_addr = sdata->vif.addr; |
217 | } |
218 | |
219 | lnkid = skb_put(skb, len: sizeof(struct ieee80211_tdls_lnkie)); |
220 | |
221 | lnkid->ie_type = WLAN_EID_LINK_ID; |
222 | lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; |
223 | |
224 | memcpy(lnkid->bssid, link->u.mgd.bssid, ETH_ALEN); |
225 | memcpy(lnkid->init_sta, init_addr, ETH_ALEN); |
226 | memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); |
227 | } |
228 | |
229 | static void |
230 | ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) |
231 | { |
232 | u8 *pos = skb_put(skb, len: 4); |
233 | |
234 | *pos++ = WLAN_EID_AID; |
235 | *pos++ = 2; /* len */ |
236 | put_unaligned_le16(val: sdata->vif.cfg.aid, p: pos); |
237 | } |
238 | |
239 | /* translate numbering in the WMM parameter IE to the mac80211 notation */ |
240 | static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) |
241 | { |
242 | switch (ac) { |
243 | default: |
244 | WARN_ON_ONCE(1); |
245 | fallthrough; |
246 | case 0: |
247 | return IEEE80211_AC_BE; |
248 | case 1: |
249 | return IEEE80211_AC_BK; |
250 | case 2: |
251 | return IEEE80211_AC_VI; |
252 | case 3: |
253 | return IEEE80211_AC_VO; |
254 | } |
255 | } |
256 | |
257 | static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci) |
258 | { |
259 | u8 ret; |
260 | |
261 | ret = aifsn & 0x0f; |
262 | if (acm) |
263 | ret |= 0x10; |
264 | ret |= (aci << 5) & 0x60; |
265 | return ret; |
266 | } |
267 | |
268 | static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max) |
269 | { |
270 | return ((ilog2(cw_min + 1) << 0x0) & 0x0f) | |
271 | ((ilog2(cw_max + 1) << 0x4) & 0xf0); |
272 | } |
273 | |
274 | static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata, |
275 | struct sk_buff *skb) |
276 | { |
277 | struct ieee80211_wmm_param_ie *wmm; |
278 | struct ieee80211_tx_queue_params *txq; |
279 | int i; |
280 | |
281 | wmm = skb_put_zero(skb, len: sizeof(*wmm)); |
282 | |
283 | wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; |
284 | wmm->len = sizeof(*wmm) - 2; |
285 | |
286 | wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ |
287 | wmm->oui[1] = 0x50; |
288 | wmm->oui[2] = 0xf2; |
289 | wmm->oui_type = 2; /* WME */ |
290 | wmm->oui_subtype = 1; /* WME param */ |
291 | wmm->version = 1; /* WME ver */ |
292 | wmm->qos_info = 0; /* U-APSD not in use */ |
293 | |
294 | /* |
295 | * Use the EDCA parameters defined for the BSS, or default if the AP |
296 | * doesn't support it, as mandated by 802.11-2012 section 10.22.4 |
297 | */ |
298 | for (i = 0; i < IEEE80211_NUM_ACS; i++) { |
299 | txq = &sdata->deflink.tx_conf[ieee80211_ac_from_wmm(ac: i)]; |
300 | wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(aifsn: txq->aifs, |
301 | acm: txq->acm, aci: i); |
302 | wmm->ac[i].cw = ieee80211_wmm_ecw(cw_min: txq->cw_min, cw_max: txq->cw_max); |
303 | wmm->ac[i].txop_limit = cpu_to_le16(txq->txop); |
304 | } |
305 | } |
306 | |
307 | static void |
308 | ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata, |
309 | struct sta_info *sta) |
310 | { |
311 | /* IEEE802.11ac-2013 Table E-4 */ |
312 | static const u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 }; |
313 | struct cfg80211_chan_def uc = sta->tdls_chandef; |
314 | enum nl80211_chan_width max_width = |
315 | ieee80211_sta_cap_chan_bw(link_sta: &sta->deflink); |
316 | int i; |
317 | |
318 | /* only support upgrading non-narrow channels up to 80Mhz */ |
319 | if (max_width == NL80211_CHAN_WIDTH_5 || |
320 | max_width == NL80211_CHAN_WIDTH_10) |
321 | return; |
322 | |
323 | if (max_width > NL80211_CHAN_WIDTH_80) |
324 | max_width = NL80211_CHAN_WIDTH_80; |
325 | |
326 | if (uc.width >= max_width) |
327 | return; |
328 | /* |
329 | * Channel usage constrains in the IEEE802.11ac-2013 specification only |
330 | * allow expanding a 20MHz channel to 80MHz in a single way. In |
331 | * addition, there are no 40MHz allowed channels that are not part of |
332 | * the allowed 80MHz range in the 5GHz spectrum (the relevant one here). |
333 | */ |
334 | for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++) |
335 | if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) { |
336 | uc.center_freq1 = centers_80mhz[i]; |
337 | uc.center_freq2 = 0; |
338 | uc.width = NL80211_CHAN_WIDTH_80; |
339 | break; |
340 | } |
341 | |
342 | if (!uc.center_freq1) |
343 | return; |
344 | |
345 | /* proceed to downgrade the chandef until usable or the same as AP BW */ |
346 | while (uc.width > max_width || |
347 | (uc.width > sta->tdls_chandef.width && |
348 | !cfg80211_reg_can_beacon_relax(wiphy: sdata->local->hw.wiphy, chandef: &uc, |
349 | iftype: sdata->wdev.iftype))) |
350 | ieee80211_chandef_downgrade(chandef: &uc, NULL); |
351 | |
352 | if (!cfg80211_chandef_identical(chandef1: &uc, chandef2: &sta->tdls_chandef)) { |
353 | tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n" , |
354 | sta->tdls_chandef.width, uc.width); |
355 | |
356 | /* |
357 | * the station is not yet authorized when BW upgrade is done, |
358 | * locking is not required |
359 | */ |
360 | sta->tdls_chandef = uc; |
361 | } |
362 | } |
363 | |
364 | static void |
365 | ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link, |
366 | struct sk_buff *skb, const u8 *peer, |
367 | u8 action_code, bool initiator, |
368 | const u8 *, size_t ) |
369 | { |
370 | struct ieee80211_sub_if_data *sdata = link->sdata; |
371 | struct ieee80211_supported_band *sband; |
372 | struct ieee80211_local *local = sdata->local; |
373 | struct ieee80211_sta_ht_cap ht_cap; |
374 | struct ieee80211_sta_vht_cap vht_cap; |
375 | const struct ieee80211_sta_he_cap *he_cap; |
376 | const struct ieee80211_sta_eht_cap *eht_cap; |
377 | struct sta_info *sta = NULL; |
378 | size_t offset = 0, noffset; |
379 | u8 *pos; |
380 | |
381 | sband = ieee80211_get_link_sband(link); |
382 | if (WARN_ON_ONCE(!sband)) |
383 | return; |
384 | |
385 | ieee80211_put_srates_elem(skb, sband, basic_rates: 0, rate_flags: 0, masked_rates: 0, element_id: WLAN_EID_SUPP_RATES); |
386 | ieee80211_put_srates_elem(skb, sband, basic_rates: 0, rate_flags: 0, masked_rates: 0, element_id: WLAN_EID_EXT_SUPP_RATES); |
387 | ieee80211_tdls_add_supp_channels(sdata, skb); |
388 | |
389 | /* add any custom IEs that go before Extended Capabilities */ |
390 | if (extra_ies_len) { |
391 | static const u8 before_ext_cap[] = { |
392 | WLAN_EID_SUPP_RATES, |
393 | WLAN_EID_COUNTRY, |
394 | WLAN_EID_EXT_SUPP_RATES, |
395 | WLAN_EID_SUPPORTED_CHANNELS, |
396 | WLAN_EID_RSN, |
397 | }; |
398 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
399 | ids: before_ext_cap, |
400 | ARRAY_SIZE(before_ext_cap), |
401 | offset); |
402 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
403 | offset = noffset; |
404 | } |
405 | |
406 | ieee80211_tdls_add_ext_capab(link, skb); |
407 | |
408 | /* add the QoS element if we support it */ |
409 | if (local->hw.queues >= IEEE80211_NUM_ACS && |
410 | action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES) |
411 | ieee80211_add_wmm_info_ie(buf: skb_put(skb, len: 9), qosinfo: 0); /* no U-APSD */ |
412 | |
413 | /* add any custom IEs that go before HT capabilities */ |
414 | if (extra_ies_len) { |
415 | static const u8 before_ht_cap[] = { |
416 | WLAN_EID_SUPP_RATES, |
417 | WLAN_EID_COUNTRY, |
418 | WLAN_EID_EXT_SUPP_RATES, |
419 | WLAN_EID_SUPPORTED_CHANNELS, |
420 | WLAN_EID_RSN, |
421 | WLAN_EID_EXT_CAPABILITY, |
422 | WLAN_EID_QOS_CAPA, |
423 | WLAN_EID_FAST_BSS_TRANSITION, |
424 | WLAN_EID_TIMEOUT_INTERVAL, |
425 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, |
426 | }; |
427 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
428 | ids: before_ht_cap, |
429 | ARRAY_SIZE(before_ht_cap), |
430 | offset); |
431 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
432 | offset = noffset; |
433 | } |
434 | |
435 | /* we should have the peer STA if we're already responding */ |
436 | if (action_code == WLAN_TDLS_SETUP_RESPONSE) { |
437 | sta = sta_info_get(sdata, addr: peer); |
438 | if (WARN_ON_ONCE(!sta)) |
439 | return; |
440 | |
441 | sta->tdls_chandef = link->conf->chanreq.oper; |
442 | } |
443 | |
444 | ieee80211_tdls_add_oper_classes(link, skb); |
445 | |
446 | /* |
447 | * with TDLS we can switch channels, and HT-caps are not necessarily |
448 | * the same on all bands. The specification limits the setup to a |
449 | * single HT-cap, so use the current band for now. |
450 | */ |
451 | memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); |
452 | |
453 | if ((action_code == WLAN_TDLS_SETUP_REQUEST || |
454 | action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && |
455 | ht_cap.ht_supported) { |
456 | ieee80211_apply_htcap_overrides(sdata, ht_cap: &ht_cap); |
457 | |
458 | /* disable SMPS in TDLS initiator */ |
459 | ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED |
460 | << IEEE80211_HT_CAP_SM_PS_SHIFT; |
461 | |
462 | pos = skb_put(skb, len: sizeof(struct ieee80211_ht_cap) + 2); |
463 | ieee80211_ie_build_ht_cap(pos, ht_cap: &ht_cap, cap: ht_cap.cap); |
464 | } else if (action_code == WLAN_TDLS_SETUP_RESPONSE && |
465 | ht_cap.ht_supported && sta->sta.deflink.ht_cap.ht_supported) { |
466 | /* the peer caps are already intersected with our own */ |
467 | memcpy(&ht_cap, &sta->sta.deflink.ht_cap, sizeof(ht_cap)); |
468 | |
469 | pos = skb_put(skb, len: sizeof(struct ieee80211_ht_cap) + 2); |
470 | ieee80211_ie_build_ht_cap(pos, ht_cap: &ht_cap, cap: ht_cap.cap); |
471 | } |
472 | |
473 | if (ht_cap.ht_supported && |
474 | (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) |
475 | ieee80211_tdls_add_bss_coex_ie(skb); |
476 | |
477 | ieee80211_tdls_add_link_ie(link, skb, peer, initiator); |
478 | |
479 | /* add any custom IEs that go before VHT capabilities */ |
480 | if (extra_ies_len) { |
481 | static const u8 before_vht_cap[] = { |
482 | WLAN_EID_SUPP_RATES, |
483 | WLAN_EID_COUNTRY, |
484 | WLAN_EID_EXT_SUPP_RATES, |
485 | WLAN_EID_SUPPORTED_CHANNELS, |
486 | WLAN_EID_RSN, |
487 | WLAN_EID_EXT_CAPABILITY, |
488 | WLAN_EID_QOS_CAPA, |
489 | WLAN_EID_FAST_BSS_TRANSITION, |
490 | WLAN_EID_TIMEOUT_INTERVAL, |
491 | WLAN_EID_SUPPORTED_REGULATORY_CLASSES, |
492 | WLAN_EID_MULTI_BAND, |
493 | }; |
494 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
495 | ids: before_vht_cap, |
496 | ARRAY_SIZE(before_vht_cap), |
497 | offset); |
498 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
499 | offset = noffset; |
500 | } |
501 | |
502 | /* add AID if VHT, HE or EHT capabilities supported */ |
503 | memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); |
504 | he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif: &sdata->vif); |
505 | eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, vif: &sdata->vif); |
506 | if ((vht_cap.vht_supported || he_cap || eht_cap) && |
507 | (action_code == WLAN_TDLS_SETUP_REQUEST || |
508 | action_code == WLAN_TDLS_SETUP_RESPONSE)) |
509 | ieee80211_tdls_add_aid(sdata, skb); |
510 | |
511 | /* build the VHT-cap similarly to the HT-cap */ |
512 | if ((action_code == WLAN_TDLS_SETUP_REQUEST || |
513 | action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && |
514 | vht_cap.vht_supported) { |
515 | ieee80211_apply_vhtcap_overrides(sdata, vht_cap: &vht_cap); |
516 | |
517 | pos = skb_put(skb, len: sizeof(struct ieee80211_vht_cap) + 2); |
518 | ieee80211_ie_build_vht_cap(pos, vht_cap: &vht_cap, cap: vht_cap.cap); |
519 | } else if (action_code == WLAN_TDLS_SETUP_RESPONSE && |
520 | vht_cap.vht_supported && sta->sta.deflink.vht_cap.vht_supported) { |
521 | /* the peer caps are already intersected with our own */ |
522 | memcpy(&vht_cap, &sta->sta.deflink.vht_cap, sizeof(vht_cap)); |
523 | |
524 | pos = skb_put(skb, len: sizeof(struct ieee80211_vht_cap) + 2); |
525 | ieee80211_ie_build_vht_cap(pos, vht_cap: &vht_cap, cap: vht_cap.cap); |
526 | |
527 | /* |
528 | * if both peers support WIDER_BW, we can expand the chandef to |
529 | * a wider compatible one, up to 80MHz |
530 | */ |
531 | if (test_sta_flag(sta, flag: WLAN_STA_TDLS_WIDER_BW)) |
532 | ieee80211_tdls_chandef_vht_upgrade(sdata, sta); |
533 | } |
534 | |
535 | /* add any custom IEs that go before HE capabilities */ |
536 | if (extra_ies_len) { |
537 | static const u8 before_he_cap[] = { |
538 | WLAN_EID_EXTENSION, |
539 | WLAN_EID_EXT_FILS_REQ_PARAMS, |
540 | WLAN_EID_AP_CSN, |
541 | }; |
542 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
543 | ids: before_he_cap, |
544 | ARRAY_SIZE(before_he_cap), |
545 | offset); |
546 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
547 | offset = noffset; |
548 | } |
549 | |
550 | /* build the HE-cap from sband */ |
551 | if (action_code == WLAN_TDLS_SETUP_REQUEST || |
552 | action_code == WLAN_TDLS_SETUP_RESPONSE || |
553 | action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { |
554 | ieee80211_put_he_cap(skb, sdata, sband, NULL); |
555 | |
556 | /* Build HE 6Ghz capa IE from sband */ |
557 | if (sband->band == NL80211_BAND_6GHZ) |
558 | ieee80211_put_he_6ghz_cap(skb, sdata, smps_mode: link->smps_mode); |
559 | } |
560 | |
561 | /* add any custom IEs that go before EHT capabilities */ |
562 | if (extra_ies_len) { |
563 | static const u8 before_he_cap[] = { |
564 | WLAN_EID_EXTENSION, |
565 | WLAN_EID_EXT_FILS_REQ_PARAMS, |
566 | WLAN_EID_AP_CSN, |
567 | }; |
568 | |
569 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
570 | ids: before_he_cap, |
571 | ARRAY_SIZE(before_he_cap), |
572 | offset); |
573 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
574 | offset = noffset; |
575 | } |
576 | |
577 | /* build the EHT-cap from sband */ |
578 | if (action_code == WLAN_TDLS_SETUP_REQUEST || |
579 | action_code == WLAN_TDLS_SETUP_RESPONSE || |
580 | action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) |
581 | ieee80211_put_eht_cap(skb, sdata, sband, NULL); |
582 | |
583 | /* add any remaining IEs */ |
584 | if (extra_ies_len) { |
585 | noffset = extra_ies_len; |
586 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
587 | } |
588 | |
589 | } |
590 | |
591 | static void |
592 | ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data *link, |
593 | struct sk_buff *skb, const u8 *peer, |
594 | bool initiator, const u8 *, |
595 | size_t ) |
596 | { |
597 | struct ieee80211_sub_if_data *sdata = link->sdata; |
598 | struct ieee80211_local *local = sdata->local; |
599 | size_t offset = 0, noffset; |
600 | struct sta_info *sta, *ap_sta; |
601 | struct ieee80211_supported_band *sband; |
602 | u8 *pos; |
603 | |
604 | sband = ieee80211_get_link_sband(link); |
605 | if (WARN_ON_ONCE(!sband)) |
606 | return; |
607 | |
608 | sta = sta_info_get(sdata, addr: peer); |
609 | ap_sta = sta_info_get(sdata, addr: sdata->vif.cfg.ap_addr); |
610 | |
611 | if (WARN_ON_ONCE(!sta || !ap_sta)) |
612 | return; |
613 | |
614 | sta->tdls_chandef = link->conf->chanreq.oper; |
615 | |
616 | /* add any custom IEs that go before the QoS IE */ |
617 | if (extra_ies_len) { |
618 | static const u8 before_qos[] = { |
619 | WLAN_EID_RSN, |
620 | }; |
621 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
622 | ids: before_qos, |
623 | ARRAY_SIZE(before_qos), |
624 | offset); |
625 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
626 | offset = noffset; |
627 | } |
628 | |
629 | /* add the QoS param IE if both the peer and we support it */ |
630 | if (local->hw.queues >= IEEE80211_NUM_ACS && sta->sta.wme) |
631 | ieee80211_tdls_add_wmm_param_ie(sdata, skb); |
632 | |
633 | /* add any custom IEs that go before HT operation */ |
634 | if (extra_ies_len) { |
635 | static const u8 before_ht_op[] = { |
636 | WLAN_EID_RSN, |
637 | WLAN_EID_QOS_CAPA, |
638 | WLAN_EID_FAST_BSS_TRANSITION, |
639 | WLAN_EID_TIMEOUT_INTERVAL, |
640 | }; |
641 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
642 | ids: before_ht_op, |
643 | ARRAY_SIZE(before_ht_op), |
644 | offset); |
645 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
646 | offset = noffset; |
647 | } |
648 | |
649 | /* |
650 | * if HT support is only added in TDLS, we need an HT-operation IE. |
651 | * add the IE as required by IEEE802.11-2012 9.23.3.2. |
652 | */ |
653 | if (!ap_sta->sta.deflink.ht_cap.ht_supported && sta->sta.deflink.ht_cap.ht_supported) { |
654 | u16 prot = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED | |
655 | IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | |
656 | IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; |
657 | |
658 | pos = skb_put(skb, len: 2 + sizeof(struct ieee80211_ht_operation)); |
659 | ieee80211_ie_build_ht_oper(pos, ht_cap: &sta->sta.deflink.ht_cap, |
660 | chandef: &link->conf->chanreq.oper, prot_mode: prot, |
661 | rifs_mode: true); |
662 | } |
663 | |
664 | ieee80211_tdls_add_link_ie(link, skb, peer, initiator); |
665 | |
666 | /* only include VHT-operation if not on the 2.4GHz band */ |
667 | if (sband->band != NL80211_BAND_2GHZ && |
668 | sta->sta.deflink.vht_cap.vht_supported) { |
669 | /* |
670 | * if both peers support WIDER_BW, we can expand the chandef to |
671 | * a wider compatible one, up to 80MHz |
672 | */ |
673 | if (test_sta_flag(sta, flag: WLAN_STA_TDLS_WIDER_BW)) |
674 | ieee80211_tdls_chandef_vht_upgrade(sdata, sta); |
675 | |
676 | pos = skb_put(skb, len: 2 + sizeof(struct ieee80211_vht_operation)); |
677 | ieee80211_ie_build_vht_oper(pos, vht_cap: &sta->sta.deflink.vht_cap, |
678 | chandef: &sta->tdls_chandef); |
679 | } |
680 | |
681 | /* add any remaining IEs */ |
682 | if (extra_ies_len) { |
683 | noffset = extra_ies_len; |
684 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
685 | } |
686 | } |
687 | |
688 | static void |
689 | ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_link_data *link, |
690 | struct sk_buff *skb, const u8 *peer, |
691 | bool initiator, const u8 *, |
692 | size_t , u8 oper_class, |
693 | struct cfg80211_chan_def *chandef) |
694 | { |
695 | struct ieee80211_tdls_data *tf; |
696 | size_t offset = 0, noffset; |
697 | |
698 | if (WARN_ON_ONCE(!chandef)) |
699 | return; |
700 | |
701 | tf = (void *)skb->data; |
702 | tf->u.chan_switch_req.target_channel = |
703 | ieee80211_frequency_to_channel(freq: chandef->chan->center_freq); |
704 | tf->u.chan_switch_req.oper_class = oper_class; |
705 | |
706 | if (extra_ies_len) { |
707 | static const u8 before_lnkie[] = { |
708 | WLAN_EID_SECONDARY_CHANNEL_OFFSET, |
709 | }; |
710 | noffset = ieee80211_ie_split(ies: extra_ies, ielen: extra_ies_len, |
711 | ids: before_lnkie, |
712 | ARRAY_SIZE(before_lnkie), |
713 | offset); |
714 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
715 | offset = noffset; |
716 | } |
717 | |
718 | ieee80211_tdls_add_link_ie(link, skb, peer, initiator); |
719 | |
720 | /* add any remaining IEs */ |
721 | if (extra_ies_len) { |
722 | noffset = extra_ies_len; |
723 | skb_put_data(skb, data: extra_ies + offset, len: noffset - offset); |
724 | } |
725 | } |
726 | |
727 | static void |
728 | ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_link_data *link, |
729 | struct sk_buff *skb, const u8 *peer, |
730 | u16 status_code, bool initiator, |
731 | const u8 *, |
732 | size_t ) |
733 | { |
734 | if (status_code == 0) |
735 | ieee80211_tdls_add_link_ie(link, skb, peer, initiator); |
736 | |
737 | if (extra_ies_len) |
738 | skb_put_data(skb, data: extra_ies, len: extra_ies_len); |
739 | } |
740 | |
741 | static void ieee80211_tdls_add_ies(struct ieee80211_link_data *link, |
742 | struct sk_buff *skb, const u8 *peer, |
743 | u8 action_code, u16 status_code, |
744 | bool initiator, const u8 *, |
745 | size_t , u8 oper_class, |
746 | struct cfg80211_chan_def *chandef) |
747 | { |
748 | switch (action_code) { |
749 | case WLAN_TDLS_SETUP_REQUEST: |
750 | case WLAN_TDLS_SETUP_RESPONSE: |
751 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: |
752 | if (status_code == 0) |
753 | ieee80211_tdls_add_setup_start_ies(link, |
754 | skb, peer, |
755 | action_code, |
756 | initiator, |
757 | extra_ies, |
758 | extra_ies_len); |
759 | break; |
760 | case WLAN_TDLS_SETUP_CONFIRM: |
761 | if (status_code == 0) |
762 | ieee80211_tdls_add_setup_cfm_ies(link, skb, peer, |
763 | initiator, extra_ies, |
764 | extra_ies_len); |
765 | break; |
766 | case WLAN_TDLS_TEARDOWN: |
767 | case WLAN_TDLS_DISCOVERY_REQUEST: |
768 | if (extra_ies_len) |
769 | skb_put_data(skb, data: extra_ies, len: extra_ies_len); |
770 | if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN) |
771 | ieee80211_tdls_add_link_ie(link, skb, |
772 | peer, initiator); |
773 | break; |
774 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: |
775 | ieee80211_tdls_add_chan_switch_req_ies(link, skb, peer, |
776 | initiator, extra_ies, |
777 | extra_ies_len, |
778 | oper_class, chandef); |
779 | break; |
780 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: |
781 | ieee80211_tdls_add_chan_switch_resp_ies(link, skb, peer, |
782 | status_code, |
783 | initiator, extra_ies, |
784 | extra_ies_len); |
785 | break; |
786 | } |
787 | |
788 | } |
789 | |
790 | static int |
791 | ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, |
792 | struct ieee80211_link_data *link, |
793 | const u8 *peer, u8 action_code, u8 dialog_token, |
794 | u16 status_code, struct sk_buff *skb) |
795 | { |
796 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
797 | struct ieee80211_tdls_data *tf; |
798 | |
799 | tf = skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); |
800 | |
801 | memcpy(tf->da, peer, ETH_ALEN); |
802 | memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); |
803 | tf->ether_type = cpu_to_be16(ETH_P_TDLS); |
804 | tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; |
805 | |
806 | /* network header is after the ethernet header */ |
807 | skb_set_network_header(skb, ETH_HLEN); |
808 | |
809 | switch (action_code) { |
810 | case WLAN_TDLS_SETUP_REQUEST: |
811 | tf->category = WLAN_CATEGORY_TDLS; |
812 | tf->action_code = WLAN_TDLS_SETUP_REQUEST; |
813 | |
814 | skb_put(skb, len: sizeof(tf->u.setup_req)); |
815 | tf->u.setup_req.dialog_token = dialog_token; |
816 | tf->u.setup_req.capability = |
817 | cpu_to_le16(ieee80211_get_tdls_sta_capab(link, |
818 | status_code)); |
819 | break; |
820 | case WLAN_TDLS_SETUP_RESPONSE: |
821 | tf->category = WLAN_CATEGORY_TDLS; |
822 | tf->action_code = WLAN_TDLS_SETUP_RESPONSE; |
823 | |
824 | skb_put(skb, len: sizeof(tf->u.setup_resp)); |
825 | tf->u.setup_resp.status_code = cpu_to_le16(status_code); |
826 | tf->u.setup_resp.dialog_token = dialog_token; |
827 | tf->u.setup_resp.capability = |
828 | cpu_to_le16(ieee80211_get_tdls_sta_capab(link, |
829 | status_code)); |
830 | break; |
831 | case WLAN_TDLS_SETUP_CONFIRM: |
832 | tf->category = WLAN_CATEGORY_TDLS; |
833 | tf->action_code = WLAN_TDLS_SETUP_CONFIRM; |
834 | |
835 | skb_put(skb, len: sizeof(tf->u.setup_cfm)); |
836 | tf->u.setup_cfm.status_code = cpu_to_le16(status_code); |
837 | tf->u.setup_cfm.dialog_token = dialog_token; |
838 | break; |
839 | case WLAN_TDLS_TEARDOWN: |
840 | tf->category = WLAN_CATEGORY_TDLS; |
841 | tf->action_code = WLAN_TDLS_TEARDOWN; |
842 | |
843 | skb_put(skb, len: sizeof(tf->u.teardown)); |
844 | tf->u.teardown.reason_code = cpu_to_le16(status_code); |
845 | break; |
846 | case WLAN_TDLS_DISCOVERY_REQUEST: |
847 | tf->category = WLAN_CATEGORY_TDLS; |
848 | tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; |
849 | |
850 | skb_put(skb, len: sizeof(tf->u.discover_req)); |
851 | tf->u.discover_req.dialog_token = dialog_token; |
852 | break; |
853 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: |
854 | tf->category = WLAN_CATEGORY_TDLS; |
855 | tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; |
856 | |
857 | skb_put(skb, len: sizeof(tf->u.chan_switch_req)); |
858 | break; |
859 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: |
860 | tf->category = WLAN_CATEGORY_TDLS; |
861 | tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; |
862 | |
863 | skb_put(skb, len: sizeof(tf->u.chan_switch_resp)); |
864 | tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code); |
865 | break; |
866 | default: |
867 | return -EINVAL; |
868 | } |
869 | |
870 | return 0; |
871 | } |
872 | |
873 | static int |
874 | ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, |
875 | const u8 *peer, struct ieee80211_link_data *link, |
876 | u8 action_code, u8 dialog_token, |
877 | u16 status_code, struct sk_buff *skb) |
878 | { |
879 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
880 | struct ieee80211_mgmt *mgmt; |
881 | |
882 | mgmt = skb_put_zero(skb, len: 24); |
883 | memcpy(mgmt->da, peer, ETH_ALEN); |
884 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); |
885 | memcpy(mgmt->bssid, link->u.mgd.bssid, ETH_ALEN); |
886 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | |
887 | IEEE80211_STYPE_ACTION); |
888 | |
889 | switch (action_code) { |
890 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: |
891 | skb_put(skb, len: 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); |
892 | mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; |
893 | mgmt->u.action.u.tdls_discover_resp.action_code = |
894 | WLAN_PUB_ACTION_TDLS_DISCOVER_RES; |
895 | mgmt->u.action.u.tdls_discover_resp.dialog_token = |
896 | dialog_token; |
897 | mgmt->u.action.u.tdls_discover_resp.capability = |
898 | cpu_to_le16(ieee80211_get_tdls_sta_capab(link, |
899 | status_code)); |
900 | break; |
901 | default: |
902 | return -EINVAL; |
903 | } |
904 | |
905 | return 0; |
906 | } |
907 | |
908 | static struct sk_buff * |
909 | ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, |
910 | const u8 *peer, int link_id, |
911 | u8 action_code, u8 dialog_token, |
912 | u16 status_code, bool initiator, |
913 | const u8 *, size_t , |
914 | u8 oper_class, |
915 | struct cfg80211_chan_def *chandef) |
916 | { |
917 | struct ieee80211_local *local = sdata->local; |
918 | struct sk_buff *skb; |
919 | int ret; |
920 | struct ieee80211_link_data *link; |
921 | |
922 | link_id = link_id >= 0 ? link_id : 0; |
923 | rcu_read_lock(); |
924 | link = rcu_dereference(sdata->link[link_id]); |
925 | if (WARN_ON(!link)) |
926 | goto unlock; |
927 | |
928 | skb = netdev_alloc_skb(dev: sdata->dev, |
929 | length: local->hw.extra_tx_headroom + |
930 | max(sizeof(struct ieee80211_mgmt), |
931 | sizeof(struct ieee80211_tdls_data)) + |
932 | 50 + /* supported rates */ |
933 | 10 + /* ext capab */ |
934 | 26 + /* max(WMM-info, WMM-param) */ |
935 | 2 + max(sizeof(struct ieee80211_ht_cap), |
936 | sizeof(struct ieee80211_ht_operation)) + |
937 | 2 + max(sizeof(struct ieee80211_vht_cap), |
938 | sizeof(struct ieee80211_vht_operation)) + |
939 | 2 + 1 + sizeof(struct ieee80211_he_cap_elem) + |
940 | sizeof(struct ieee80211_he_mcs_nss_supp) + |
941 | IEEE80211_HE_PPE_THRES_MAX_LEN + |
942 | 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) + |
943 | 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) + |
944 | sizeof(struct ieee80211_eht_mcs_nss_supp) + |
945 | IEEE80211_EHT_PPE_THRES_MAX_LEN + |
946 | 50 + /* supported channels */ |
947 | 3 + /* 40/20 BSS coex */ |
948 | 4 + /* AID */ |
949 | 4 + /* oper classes */ |
950 | extra_ies_len + |
951 | sizeof(struct ieee80211_tdls_lnkie)); |
952 | if (!skb) |
953 | goto unlock; |
954 | |
955 | skb_reserve(skb, len: local->hw.extra_tx_headroom); |
956 | |
957 | switch (action_code) { |
958 | case WLAN_TDLS_SETUP_REQUEST: |
959 | case WLAN_TDLS_SETUP_RESPONSE: |
960 | case WLAN_TDLS_SETUP_CONFIRM: |
961 | case WLAN_TDLS_TEARDOWN: |
962 | case WLAN_TDLS_DISCOVERY_REQUEST: |
963 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: |
964 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: |
965 | ret = ieee80211_prep_tdls_encap_data(wiphy: local->hw.wiphy, |
966 | dev: sdata->dev, link, peer, |
967 | action_code, dialog_token, |
968 | status_code, skb); |
969 | break; |
970 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: |
971 | ret = ieee80211_prep_tdls_direct(wiphy: local->hw.wiphy, dev: sdata->dev, |
972 | peer, link, action_code, |
973 | dialog_token, status_code, |
974 | skb); |
975 | break; |
976 | default: |
977 | ret = -EOPNOTSUPP; |
978 | break; |
979 | } |
980 | |
981 | if (ret < 0) |
982 | goto fail; |
983 | |
984 | ieee80211_tdls_add_ies(link, skb, peer, action_code, status_code, |
985 | initiator, extra_ies, extra_ies_len, oper_class, |
986 | chandef); |
987 | rcu_read_unlock(); |
988 | return skb; |
989 | |
990 | fail: |
991 | dev_kfree_skb(skb); |
992 | unlock: |
993 | rcu_read_unlock(); |
994 | return NULL; |
995 | } |
996 | |
997 | static int |
998 | ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, |
999 | const u8 *peer, int link_id, |
1000 | u8 action_code, u8 dialog_token, |
1001 | u16 status_code, u32 peer_capability, |
1002 | bool initiator, const u8 *, |
1003 | size_t , u8 oper_class, |
1004 | struct cfg80211_chan_def *chandef) |
1005 | { |
1006 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1007 | struct sk_buff *skb = NULL; |
1008 | struct sta_info *sta; |
1009 | u32 flags = 0; |
1010 | int ret = 0; |
1011 | |
1012 | rcu_read_lock(); |
1013 | sta = sta_info_get(sdata, addr: peer); |
1014 | |
1015 | /* infer the initiator if we can, to support old userspace */ |
1016 | switch (action_code) { |
1017 | case WLAN_TDLS_SETUP_REQUEST: |
1018 | if (sta) { |
1019 | set_sta_flag(sta, flag: WLAN_STA_TDLS_INITIATOR); |
1020 | sta->sta.tdls_initiator = false; |
1021 | } |
1022 | fallthrough; |
1023 | case WLAN_TDLS_SETUP_CONFIRM: |
1024 | case WLAN_TDLS_DISCOVERY_REQUEST: |
1025 | initiator = true; |
1026 | break; |
1027 | case WLAN_TDLS_SETUP_RESPONSE: |
1028 | /* |
1029 | * In some testing scenarios, we send a request and response. |
1030 | * Make the last packet sent take effect for the initiator |
1031 | * value. |
1032 | */ |
1033 | if (sta) { |
1034 | clear_sta_flag(sta, flag: WLAN_STA_TDLS_INITIATOR); |
1035 | sta->sta.tdls_initiator = true; |
1036 | } |
1037 | fallthrough; |
1038 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: |
1039 | initiator = false; |
1040 | break; |
1041 | case WLAN_TDLS_TEARDOWN: |
1042 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: |
1043 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: |
1044 | /* any value is ok */ |
1045 | break; |
1046 | default: |
1047 | ret = -EOPNOTSUPP; |
1048 | break; |
1049 | } |
1050 | |
1051 | if (sta && test_sta_flag(sta, flag: WLAN_STA_TDLS_INITIATOR)) |
1052 | initiator = true; |
1053 | |
1054 | rcu_read_unlock(); |
1055 | if (ret < 0) |
1056 | goto fail; |
1057 | |
1058 | skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, |
1059 | link_id, action_code, |
1060 | dialog_token, status_code, |
1061 | initiator, extra_ies, |
1062 | extra_ies_len, oper_class, |
1063 | chandef); |
1064 | if (!skb) { |
1065 | ret = -EINVAL; |
1066 | goto fail; |
1067 | } |
1068 | |
1069 | if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { |
1070 | ieee80211_tx_skb_tid(sdata, skb, tid: 7, link_id); |
1071 | return 0; |
1072 | } |
1073 | |
1074 | /* |
1075 | * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise |
1076 | * we should default to AC_VI. |
1077 | */ |
1078 | switch (action_code) { |
1079 | case WLAN_TDLS_SETUP_REQUEST: |
1080 | case WLAN_TDLS_SETUP_RESPONSE: |
1081 | skb->priority = 256 + 2; |
1082 | break; |
1083 | default: |
1084 | skb->priority = 256 + 5; |
1085 | break; |
1086 | } |
1087 | |
1088 | /* |
1089 | * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress. |
1090 | * Later, if no ACK is returned from peer, we will re-send the teardown |
1091 | * packet through the AP. |
1092 | */ |
1093 | if ((action_code == WLAN_TDLS_TEARDOWN) && |
1094 | ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) { |
1095 | bool try_resend; /* Should we keep skb for possible resend */ |
1096 | |
1097 | /* If not sending directly to peer - no point in keeping skb */ |
1098 | rcu_read_lock(); |
1099 | sta = sta_info_get(sdata, addr: peer); |
1100 | try_resend = sta && test_sta_flag(sta, flag: WLAN_STA_TDLS_PEER_AUTH); |
1101 | rcu_read_unlock(); |
1102 | |
1103 | spin_lock_bh(lock: &sdata->u.mgd.teardown_lock); |
1104 | if (try_resend && !sdata->u.mgd.teardown_skb) { |
1105 | /* Mark it as requiring TX status callback */ |
1106 | flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | |
1107 | IEEE80211_TX_INTFL_MLME_CONN_TX; |
1108 | |
1109 | /* |
1110 | * skb is copied since mac80211 will later set |
1111 | * properties that might not be the same as the AP, |
1112 | * such as encryption, QoS, addresses, etc. |
1113 | * |
1114 | * No problem if skb_copy() fails, so no need to check. |
1115 | */ |
1116 | sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC); |
1117 | sdata->u.mgd.orig_teardown_skb = skb; |
1118 | } |
1119 | spin_unlock_bh(lock: &sdata->u.mgd.teardown_lock); |
1120 | } |
1121 | |
1122 | /* disable bottom halves when entering the Tx path */ |
1123 | local_bh_disable(); |
1124 | __ieee80211_subif_start_xmit(skb, dev, info_flags: flags, |
1125 | IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, NULL); |
1126 | local_bh_enable(); |
1127 | |
1128 | return ret; |
1129 | |
1130 | fail: |
1131 | dev_kfree_skb(skb); |
1132 | return ret; |
1133 | } |
1134 | |
1135 | static int |
1136 | ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, |
1137 | const u8 *peer, int link_id, |
1138 | u8 action_code, u8 dialog_token, |
1139 | u16 status_code, u32 peer_capability, bool initiator, |
1140 | const u8 *, size_t ) |
1141 | { |
1142 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1143 | struct ieee80211_local *local = sdata->local; |
1144 | enum ieee80211_smps_mode smps_mode = |
1145 | sdata->deflink.u.mgd.driver_smps_mode; |
1146 | int ret; |
1147 | |
1148 | /* don't support setup with forced SMPS mode that's not off */ |
1149 | if (smps_mode != IEEE80211_SMPS_AUTOMATIC && |
1150 | smps_mode != IEEE80211_SMPS_OFF) { |
1151 | tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n" , |
1152 | smps_mode); |
1153 | return -EOPNOTSUPP; |
1154 | } |
1155 | |
1156 | lockdep_assert_wiphy(local->hw.wiphy); |
1157 | |
1158 | /* we don't support concurrent TDLS peer setups */ |
1159 | if (!is_zero_ether_addr(addr: sdata->u.mgd.tdls_peer) && |
1160 | !ether_addr_equal(addr1: sdata->u.mgd.tdls_peer, addr2: peer)) { |
1161 | ret = -EBUSY; |
1162 | goto out_unlock; |
1163 | } |
1164 | |
1165 | /* |
1166 | * make sure we have a STA representing the peer so we drop or buffer |
1167 | * non-TDLS-setup frames to the peer. We can't send other packets |
1168 | * during setup through the AP path. |
1169 | * Allow error packets to be sent - sometimes we don't even add a STA |
1170 | * before failing the setup. |
1171 | */ |
1172 | if (status_code == 0) { |
1173 | rcu_read_lock(); |
1174 | if (!sta_info_get(sdata, addr: peer)) { |
1175 | rcu_read_unlock(); |
1176 | ret = -ENOLINK; |
1177 | goto out_unlock; |
1178 | } |
1179 | rcu_read_unlock(); |
1180 | } |
1181 | |
1182 | ieee80211_flush_queues(local, sdata, drop: false); |
1183 | memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); |
1184 | |
1185 | /* we cannot take the mutex while preparing the setup packet */ |
1186 | ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, |
1187 | link_id, action_code, |
1188 | dialog_token, status_code, |
1189 | peer_capability, initiator, |
1190 | extra_ies, extra_ies_len, oper_class: 0, |
1191 | NULL); |
1192 | if (ret < 0) { |
1193 | eth_zero_addr(addr: sdata->u.mgd.tdls_peer); |
1194 | return ret; |
1195 | } |
1196 | |
1197 | wiphy_delayed_work_queue(wiphy: sdata->local->hw.wiphy, |
1198 | dwork: &sdata->u.mgd.tdls_peer_del_work, |
1199 | TDLS_PEER_SETUP_TIMEOUT); |
1200 | return 0; |
1201 | |
1202 | out_unlock: |
1203 | return ret; |
1204 | } |
1205 | |
1206 | static int |
1207 | ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev, |
1208 | const u8 *peer, int link_id, |
1209 | u8 action_code, u8 dialog_token, |
1210 | u16 status_code, u32 peer_capability, |
1211 | bool initiator, const u8 *, |
1212 | size_t ) |
1213 | { |
1214 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1215 | struct ieee80211_local *local = sdata->local; |
1216 | struct sta_info *sta; |
1217 | int ret; |
1218 | |
1219 | /* |
1220 | * No packets can be transmitted to the peer via the AP during setup - |
1221 | * the STA is set as a TDLS peer, but is not authorized. |
1222 | * During teardown, we prevent direct transmissions by stopping the |
1223 | * queues and flushing all direct packets. |
1224 | */ |
1225 | ieee80211_stop_vif_queues(local, sdata, |
1226 | reason: IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN); |
1227 | ieee80211_flush_queues(local, sdata, drop: false); |
1228 | |
1229 | ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, |
1230 | link_id, action_code, |
1231 | dialog_token, status_code, |
1232 | peer_capability, initiator, |
1233 | extra_ies, extra_ies_len, oper_class: 0, |
1234 | NULL); |
1235 | if (ret < 0) |
1236 | sdata_err(sdata, "Failed sending TDLS teardown packet %d\n" , |
1237 | ret); |
1238 | |
1239 | /* |
1240 | * Remove the STA AUTH flag to force further traffic through the AP. If |
1241 | * the STA was unreachable, it was already removed. |
1242 | */ |
1243 | rcu_read_lock(); |
1244 | sta = sta_info_get(sdata, addr: peer); |
1245 | if (sta) |
1246 | clear_sta_flag(sta, flag: WLAN_STA_TDLS_PEER_AUTH); |
1247 | rcu_read_unlock(); |
1248 | |
1249 | ieee80211_wake_vif_queues(local, sdata, |
1250 | reason: IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN); |
1251 | |
1252 | return 0; |
1253 | } |
1254 | |
1255 | int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, |
1256 | const u8 *peer, int link_id, |
1257 | u8 action_code, u8 dialog_token, u16 status_code, |
1258 | u32 peer_capability, bool initiator, |
1259 | const u8 *, size_t ) |
1260 | { |
1261 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1262 | int ret; |
1263 | |
1264 | if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) |
1265 | return -EOPNOTSUPP; |
1266 | |
1267 | /* make sure we are in managed mode, and associated */ |
1268 | if (sdata->vif.type != NL80211_IFTYPE_STATION || |
1269 | !sdata->u.mgd.associated) |
1270 | return -EINVAL; |
1271 | |
1272 | switch (action_code) { |
1273 | case WLAN_TDLS_SETUP_REQUEST: |
1274 | case WLAN_TDLS_SETUP_RESPONSE: |
1275 | ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, |
1276 | link_id, action_code, |
1277 | dialog_token, status_code, |
1278 | peer_capability, initiator, |
1279 | extra_ies, extra_ies_len); |
1280 | break; |
1281 | case WLAN_TDLS_TEARDOWN: |
1282 | ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer, link_id, |
1283 | action_code, dialog_token, |
1284 | status_code, |
1285 | peer_capability, initiator, |
1286 | extra_ies, extra_ies_len); |
1287 | break; |
1288 | case WLAN_TDLS_DISCOVERY_REQUEST: |
1289 | /* |
1290 | * Protect the discovery so we can hear the TDLS discovery |
1291 | * response frame. It is transmitted directly and not buffered |
1292 | * by the AP. |
1293 | */ |
1294 | drv_mgd_protect_tdls_discover(local: sdata->local, sdata, link_id); |
1295 | fallthrough; |
1296 | case WLAN_TDLS_SETUP_CONFIRM: |
1297 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: |
1298 | /* no special handling */ |
1299 | ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, |
1300 | link_id, action_code, |
1301 | dialog_token, |
1302 | status_code, |
1303 | peer_capability, |
1304 | initiator, extra_ies, |
1305 | extra_ies_len, oper_class: 0, NULL); |
1306 | break; |
1307 | default: |
1308 | ret = -EOPNOTSUPP; |
1309 | break; |
1310 | } |
1311 | |
1312 | tdls_dbg(sdata, "TDLS mgmt action %d peer %pM link_id %d status %d\n" , |
1313 | action_code, peer, link_id, ret); |
1314 | return ret; |
1315 | } |
1316 | |
1317 | static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata, |
1318 | struct sta_info *sta) |
1319 | { |
1320 | struct ieee80211_local *local = sdata->local; |
1321 | struct ieee80211_chanctx_conf *conf; |
1322 | struct ieee80211_chanctx *ctx; |
1323 | enum nl80211_chan_width width; |
1324 | struct ieee80211_supported_band *sband; |
1325 | |
1326 | lockdep_assert_wiphy(local->hw.wiphy); |
1327 | |
1328 | conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, |
1329 | lockdep_is_held(&local->hw.wiphy->mtx)); |
1330 | if (conf) { |
1331 | width = conf->def.width; |
1332 | sband = local->hw.wiphy->bands[conf->def.chan->band]; |
1333 | ctx = container_of(conf, struct ieee80211_chanctx, conf); |
1334 | ieee80211_recalc_chanctx_chantype(local, ctx); |
1335 | |
1336 | /* if width changed and a peer is given, update its BW */ |
1337 | if (width != conf->def.width && sta && |
1338 | test_sta_flag(sta, flag: WLAN_STA_TDLS_WIDER_BW)) { |
1339 | enum ieee80211_sta_rx_bandwidth bw; |
1340 | |
1341 | bw = ieee80211_chan_width_to_rx_bw(width: conf->def.width); |
1342 | bw = min(bw, ieee80211_sta_cap_rx_bw(&sta->deflink)); |
1343 | if (bw != sta->sta.deflink.bandwidth) { |
1344 | sta->sta.deflink.bandwidth = bw; |
1345 | rate_control_rate_update(local, sband, sta, link_id: 0, |
1346 | changed: IEEE80211_RC_BW_CHANGED); |
1347 | /* |
1348 | * if a TDLS peer BW was updated, we need to |
1349 | * recalc the chandef width again, to get the |
1350 | * correct chanctx min_def |
1351 | */ |
1352 | ieee80211_recalc_chanctx_chantype(local, ctx); |
1353 | } |
1354 | } |
1355 | |
1356 | } |
1357 | } |
1358 | |
1359 | static int iee80211_tdls_have_ht_peers(struct ieee80211_sub_if_data *sdata) |
1360 | { |
1361 | struct sta_info *sta; |
1362 | bool result = false; |
1363 | |
1364 | rcu_read_lock(); |
1365 | list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { |
1366 | if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || |
1367 | !test_sta_flag(sta, flag: WLAN_STA_AUTHORIZED) || |
1368 | !test_sta_flag(sta, flag: WLAN_STA_TDLS_PEER_AUTH) || |
1369 | !sta->sta.deflink.ht_cap.ht_supported) |
1370 | continue; |
1371 | result = true; |
1372 | break; |
1373 | } |
1374 | rcu_read_unlock(); |
1375 | |
1376 | return result; |
1377 | } |
1378 | |
1379 | static void |
1380 | iee80211_tdls_recalc_ht_protection(struct ieee80211_sub_if_data *sdata, |
1381 | struct sta_info *sta) |
1382 | { |
1383 | bool tdls_ht; |
1384 | u16 protection = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED | |
1385 | IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | |
1386 | IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; |
1387 | u16 opmode; |
1388 | |
1389 | /* Nothing to do if the BSS connection uses (at least) HT */ |
1390 | if (sdata->deflink.u.mgd.conn.mode >= IEEE80211_CONN_MODE_HT) |
1391 | return; |
1392 | |
1393 | tdls_ht = (sta && sta->sta.deflink.ht_cap.ht_supported) || |
1394 | iee80211_tdls_have_ht_peers(sdata); |
1395 | |
1396 | opmode = sdata->vif.bss_conf.ht_operation_mode; |
1397 | |
1398 | if (tdls_ht) |
1399 | opmode |= protection; |
1400 | else |
1401 | opmode &= ~protection; |
1402 | |
1403 | if (opmode == sdata->vif.bss_conf.ht_operation_mode) |
1404 | return; |
1405 | |
1406 | sdata->vif.bss_conf.ht_operation_mode = opmode; |
1407 | ieee80211_link_info_change_notify(sdata, link: &sdata->deflink, |
1408 | changed: BSS_CHANGED_HT); |
1409 | } |
1410 | |
1411 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, |
1412 | const u8 *peer, enum nl80211_tdls_operation oper) |
1413 | { |
1414 | struct sta_info *sta; |
1415 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1416 | struct ieee80211_local *local = sdata->local; |
1417 | int ret; |
1418 | |
1419 | lockdep_assert_wiphy(local->hw.wiphy); |
1420 | |
1421 | if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) |
1422 | return -EOPNOTSUPP; |
1423 | |
1424 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
1425 | return -EINVAL; |
1426 | |
1427 | switch (oper) { |
1428 | case NL80211_TDLS_ENABLE_LINK: |
1429 | case NL80211_TDLS_DISABLE_LINK: |
1430 | break; |
1431 | case NL80211_TDLS_TEARDOWN: |
1432 | case NL80211_TDLS_SETUP: |
1433 | case NL80211_TDLS_DISCOVERY_REQ: |
1434 | /* We don't support in-driver setup/teardown/discovery */ |
1435 | return -EOPNOTSUPP; |
1436 | } |
1437 | |
1438 | /* protect possible bss_conf changes and avoid concurrency in |
1439 | * ieee80211_bss_info_change_notify() |
1440 | */ |
1441 | tdls_dbg(sdata, "TDLS oper %d peer %pM\n" , oper, peer); |
1442 | |
1443 | switch (oper) { |
1444 | case NL80211_TDLS_ENABLE_LINK: |
1445 | if (sdata->vif.bss_conf.csa_active) { |
1446 | tdls_dbg(sdata, "TDLS: disallow link during CSA\n" ); |
1447 | return -EBUSY; |
1448 | } |
1449 | |
1450 | sta = sta_info_get(sdata, addr: peer); |
1451 | if (!sta) |
1452 | return -ENOLINK; |
1453 | |
1454 | iee80211_tdls_recalc_chanctx(sdata, sta); |
1455 | iee80211_tdls_recalc_ht_protection(sdata, sta); |
1456 | |
1457 | set_sta_flag(sta, flag: WLAN_STA_TDLS_PEER_AUTH); |
1458 | |
1459 | WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) || |
1460 | !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)); |
1461 | break; |
1462 | case NL80211_TDLS_DISABLE_LINK: |
1463 | /* |
1464 | * The teardown message in ieee80211_tdls_mgmt_teardown() was |
1465 | * created while the queues were stopped, so it might still be |
1466 | * pending. Before flushing the queues we need to be sure the |
1467 | * message is handled by the tasklet handling pending messages, |
1468 | * otherwise we might start destroying the station before |
1469 | * sending the teardown packet. |
1470 | * Note that this only forces the tasklet to flush pendings - |
1471 | * not to stop the tasklet from rescheduling itself. |
1472 | */ |
1473 | tasklet_kill(t: &local->tx_pending_tasklet); |
1474 | /* flush a potentially queued teardown packet */ |
1475 | ieee80211_flush_queues(local, sdata, drop: false); |
1476 | |
1477 | ret = sta_info_destroy_addr(sdata, addr: peer); |
1478 | |
1479 | iee80211_tdls_recalc_ht_protection(sdata, NULL); |
1480 | |
1481 | iee80211_tdls_recalc_chanctx(sdata, NULL); |
1482 | if (ret) |
1483 | return ret; |
1484 | break; |
1485 | default: |
1486 | return -EOPNOTSUPP; |
1487 | } |
1488 | |
1489 | if (ether_addr_equal(addr1: sdata->u.mgd.tdls_peer, addr2: peer)) { |
1490 | wiphy_delayed_work_cancel(wiphy: sdata->local->hw.wiphy, |
1491 | dwork: &sdata->u.mgd.tdls_peer_del_work); |
1492 | eth_zero_addr(addr: sdata->u.mgd.tdls_peer); |
1493 | } |
1494 | |
1495 | wiphy_work_queue(wiphy: sdata->local->hw.wiphy, |
1496 | work: &sdata->deflink.u.mgd.request_smps_work); |
1497 | |
1498 | return 0; |
1499 | } |
1500 | |
1501 | void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, |
1502 | enum nl80211_tdls_operation oper, |
1503 | u16 reason_code, gfp_t gfp) |
1504 | { |
1505 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(p: vif); |
1506 | |
1507 | if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) { |
1508 | sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n" , |
1509 | oper); |
1510 | return; |
1511 | } |
1512 | |
1513 | cfg80211_tdls_oper_request(dev: sdata->dev, peer, oper, reason_code, gfp); |
1514 | } |
1515 | EXPORT_SYMBOL(ieee80211_tdls_oper_request); |
1516 | |
1517 | static void |
1518 | iee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout) |
1519 | { |
1520 | struct ieee80211_ch_switch_timing *ch_sw; |
1521 | |
1522 | *buf++ = WLAN_EID_CHAN_SWITCH_TIMING; |
1523 | *buf++ = sizeof(struct ieee80211_ch_switch_timing); |
1524 | |
1525 | ch_sw = (void *)buf; |
1526 | ch_sw->switch_time = cpu_to_le16(switch_time); |
1527 | ch_sw->switch_timeout = cpu_to_le16(switch_timeout); |
1528 | } |
1529 | |
1530 | /* find switch timing IE in SKB ready for Tx */ |
1531 | static const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb) |
1532 | { |
1533 | struct ieee80211_tdls_data *tf; |
1534 | const u8 *ie_start; |
1535 | |
1536 | /* |
1537 | * Get the offset for the new location of the switch timing IE. |
1538 | * The SKB network header will now point to the "payload_type" |
1539 | * element of the TDLS data frame struct. |
1540 | */ |
1541 | tf = container_of(skb->data + skb_network_offset(skb), |
1542 | struct ieee80211_tdls_data, payload_type); |
1543 | ie_start = tf->u.chan_switch_req.variable; |
1544 | return cfg80211_find_ie(eid: WLAN_EID_CHAN_SWITCH_TIMING, ies: ie_start, |
1545 | len: skb->len - (ie_start - skb->data)); |
1546 | } |
1547 | |
1548 | static struct sk_buff * |
1549 | ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class, |
1550 | struct cfg80211_chan_def *chandef, |
1551 | u32 *ch_sw_tm_ie_offset) |
1552 | { |
1553 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
1554 | u8 [2 + sizeof(struct ieee80211_sec_chan_offs_ie) + |
1555 | 2 + sizeof(struct ieee80211_ch_switch_timing)]; |
1556 | int = 2 + sizeof(struct ieee80211_ch_switch_timing); |
1557 | u8 *pos = extra_ies; |
1558 | struct sk_buff *skb; |
1559 | int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0; |
1560 | |
1561 | /* |
1562 | * if chandef points to a wide channel add a Secondary-Channel |
1563 | * Offset information element |
1564 | */ |
1565 | if (chandef->width == NL80211_CHAN_WIDTH_40) { |
1566 | struct ieee80211_sec_chan_offs_ie *sec_chan_ie; |
1567 | bool ht40plus; |
1568 | |
1569 | *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; |
1570 | *pos++ = sizeof(*sec_chan_ie); |
1571 | sec_chan_ie = (void *)pos; |
1572 | |
1573 | ht40plus = cfg80211_get_chandef_type(chandef) == |
1574 | NL80211_CHAN_HT40PLUS; |
1575 | sec_chan_ie->sec_chan_offs = ht40plus ? |
1576 | IEEE80211_HT_PARAM_CHA_SEC_ABOVE : |
1577 | IEEE80211_HT_PARAM_CHA_SEC_BELOW; |
1578 | pos += sizeof(*sec_chan_ie); |
1579 | |
1580 | extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie); |
1581 | } |
1582 | |
1583 | /* just set the values to 0, this is a template */ |
1584 | iee80211_tdls_add_ch_switch_timing(buf: pos, switch_time: 0, switch_timeout: 0); |
1585 | |
1586 | skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer: sta->sta.addr, |
1587 | link_id, |
1588 | action_code: WLAN_TDLS_CHANNEL_SWITCH_REQUEST, |
1589 | dialog_token: 0, status_code: 0, initiator: !sta->sta.tdls_initiator, |
1590 | extra_ies, extra_ies_len, |
1591 | oper_class, chandef); |
1592 | if (!skb) |
1593 | return NULL; |
1594 | |
1595 | skb = ieee80211_build_data_template(sdata, skb, info_flags: 0); |
1596 | if (IS_ERR(ptr: skb)) { |
1597 | tdls_dbg(sdata, "Failed building TDLS channel switch frame\n" ); |
1598 | return NULL; |
1599 | } |
1600 | |
1601 | if (ch_sw_tm_ie_offset) { |
1602 | const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb); |
1603 | |
1604 | if (!tm_ie) { |
1605 | tdls_dbg(sdata, "No switch timing IE in TDLS switch\n" ); |
1606 | dev_kfree_skb_any(skb); |
1607 | return NULL; |
1608 | } |
1609 | |
1610 | *ch_sw_tm_ie_offset = tm_ie - skb->data; |
1611 | } |
1612 | |
1613 | tdls_dbg(sdata, |
1614 | "TDLS channel switch request template for %pM ch %d width %d\n" , |
1615 | sta->sta.addr, chandef->chan->center_freq, chandef->width); |
1616 | return skb; |
1617 | } |
1618 | |
1619 | int |
1620 | ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, |
1621 | const u8 *addr, u8 oper_class, |
1622 | struct cfg80211_chan_def *chandef) |
1623 | { |
1624 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1625 | struct ieee80211_local *local = sdata->local; |
1626 | struct sta_info *sta; |
1627 | struct sk_buff *skb = NULL; |
1628 | u32 ch_sw_tm_ie; |
1629 | int ret; |
1630 | |
1631 | lockdep_assert_wiphy(local->hw.wiphy); |
1632 | |
1633 | if (chandef->chan->freq_offset) |
1634 | /* this may work, but is untested */ |
1635 | return -EOPNOTSUPP; |
1636 | |
1637 | sta = sta_info_get(sdata, addr); |
1638 | if (!sta) { |
1639 | tdls_dbg(sdata, |
1640 | "Invalid TDLS peer %pM for channel switch request\n" , |
1641 | addr); |
1642 | ret = -ENOENT; |
1643 | goto out; |
1644 | } |
1645 | |
1646 | if (!test_sta_flag(sta, flag: WLAN_STA_TDLS_CHAN_SWITCH)) { |
1647 | tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n" , |
1648 | addr); |
1649 | ret = -EOPNOTSUPP; |
1650 | goto out; |
1651 | } |
1652 | |
1653 | skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef, |
1654 | ch_sw_tm_ie_offset: &ch_sw_tm_ie); |
1655 | if (!skb) { |
1656 | ret = -ENOENT; |
1657 | goto out; |
1658 | } |
1659 | |
1660 | ret = drv_tdls_channel_switch(local, sdata, sta: &sta->sta, oper_class, |
1661 | chandef, tmpl_skb: skb, ch_sw_tm_ie); |
1662 | if (!ret) |
1663 | set_sta_flag(sta, flag: WLAN_STA_TDLS_OFF_CHANNEL); |
1664 | |
1665 | out: |
1666 | dev_kfree_skb_any(skb); |
1667 | return ret; |
1668 | } |
1669 | |
1670 | void |
1671 | ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, |
1672 | struct net_device *dev, |
1673 | const u8 *addr) |
1674 | { |
1675 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1676 | struct ieee80211_local *local = sdata->local; |
1677 | struct sta_info *sta; |
1678 | |
1679 | lockdep_assert_wiphy(local->hw.wiphy); |
1680 | |
1681 | sta = sta_info_get(sdata, addr); |
1682 | if (!sta) { |
1683 | tdls_dbg(sdata, |
1684 | "Invalid TDLS peer %pM for channel switch cancel\n" , |
1685 | addr); |
1686 | return; |
1687 | } |
1688 | |
1689 | if (!test_sta_flag(sta, flag: WLAN_STA_TDLS_OFF_CHANNEL)) { |
1690 | tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n" , |
1691 | addr); |
1692 | return; |
1693 | } |
1694 | |
1695 | drv_tdls_cancel_channel_switch(local, sdata, sta: &sta->sta); |
1696 | clear_sta_flag(sta, flag: WLAN_STA_TDLS_OFF_CHANNEL); |
1697 | } |
1698 | |
1699 | static struct sk_buff * |
1700 | ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta, |
1701 | u32 *ch_sw_tm_ie_offset) |
1702 | { |
1703 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
1704 | struct sk_buff *skb; |
1705 | u8 [2 + sizeof(struct ieee80211_ch_switch_timing)]; |
1706 | int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0; |
1707 | |
1708 | /* initial timing are always zero in the template */ |
1709 | iee80211_tdls_add_ch_switch_timing(buf: extra_ies, switch_time: 0, switch_timeout: 0); |
1710 | |
1711 | skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer: sta->sta.addr, |
1712 | link_id, |
1713 | action_code: WLAN_TDLS_CHANNEL_SWITCH_RESPONSE, |
1714 | dialog_token: 0, status_code: 0, initiator: !sta->sta.tdls_initiator, |
1715 | extra_ies, extra_ies_len: sizeof(extra_ies), oper_class: 0, NULL); |
1716 | if (!skb) |
1717 | return NULL; |
1718 | |
1719 | skb = ieee80211_build_data_template(sdata, skb, info_flags: 0); |
1720 | if (IS_ERR(ptr: skb)) { |
1721 | tdls_dbg(sdata, |
1722 | "Failed building TDLS channel switch resp frame\n" ); |
1723 | return NULL; |
1724 | } |
1725 | |
1726 | if (ch_sw_tm_ie_offset) { |
1727 | const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb); |
1728 | |
1729 | if (!tm_ie) { |
1730 | tdls_dbg(sdata, |
1731 | "No switch timing IE in TDLS switch resp\n" ); |
1732 | dev_kfree_skb_any(skb); |
1733 | return NULL; |
1734 | } |
1735 | |
1736 | *ch_sw_tm_ie_offset = tm_ie - skb->data; |
1737 | } |
1738 | |
1739 | tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n" , |
1740 | sta->sta.addr); |
1741 | return skb; |
1742 | } |
1743 | |
1744 | static int |
1745 | ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, |
1746 | struct sk_buff *skb) |
1747 | { |
1748 | struct ieee80211_local *local = sdata->local; |
1749 | struct ieee802_11_elems *elems = NULL; |
1750 | struct sta_info *sta; |
1751 | struct ieee80211_tdls_data *tf = (void *)skb->data; |
1752 | bool local_initiator; |
1753 | struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); |
1754 | int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable); |
1755 | struct ieee80211_tdls_ch_sw_params params = {}; |
1756 | int ret; |
1757 | |
1758 | lockdep_assert_wiphy(local->hw.wiphy); |
1759 | |
1760 | params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; |
1761 | params.timestamp = rx_status->device_timestamp; |
1762 | |
1763 | if (skb->len < baselen) { |
1764 | tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n" , |
1765 | skb->len); |
1766 | return -EINVAL; |
1767 | } |
1768 | |
1769 | sta = sta_info_get(sdata, addr: tf->sa); |
1770 | if (!sta || !test_sta_flag(sta, flag: WLAN_STA_TDLS_PEER_AUTH)) { |
1771 | tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n" , |
1772 | tf->sa); |
1773 | ret = -EINVAL; |
1774 | goto out; |
1775 | } |
1776 | |
1777 | params.sta = &sta->sta; |
1778 | params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code); |
1779 | if (params.status != 0) { |
1780 | ret = 0; |
1781 | goto call_drv; |
1782 | } |
1783 | |
1784 | elems = ieee802_11_parse_elems(start: tf->u.chan_switch_resp.variable, |
1785 | len: skb->len - baselen, action: false, NULL); |
1786 | if (!elems) { |
1787 | ret = -ENOMEM; |
1788 | goto out; |
1789 | } |
1790 | |
1791 | if (elems->parse_error) { |
1792 | tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n" ); |
1793 | ret = -EINVAL; |
1794 | goto out; |
1795 | } |
1796 | |
1797 | if (!elems->ch_sw_timing || !elems->lnk_id) { |
1798 | tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n" ); |
1799 | ret = -EINVAL; |
1800 | goto out; |
1801 | } |
1802 | |
1803 | /* validate the initiator is set correctly */ |
1804 | local_initiator = |
1805 | !memcmp(p: elems->lnk_id->init_sta, q: sdata->vif.addr, ETH_ALEN); |
1806 | if (local_initiator == sta->sta.tdls_initiator) { |
1807 | tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n" ); |
1808 | ret = -EINVAL; |
1809 | goto out; |
1810 | } |
1811 | |
1812 | params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time); |
1813 | params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout); |
1814 | |
1815 | params.tmpl_skb = |
1816 | ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ch_sw_tm_ie_offset: ¶ms.ch_sw_tm_ie); |
1817 | if (!params.tmpl_skb) { |
1818 | ret = -ENOENT; |
1819 | goto out; |
1820 | } |
1821 | |
1822 | ret = 0; |
1823 | call_drv: |
1824 | drv_tdls_recv_channel_switch(local: sdata->local, sdata, params: ¶ms); |
1825 | |
1826 | tdls_dbg(sdata, |
1827 | "TDLS channel switch response received from %pM status %d\n" , |
1828 | tf->sa, params.status); |
1829 | |
1830 | out: |
1831 | dev_kfree_skb_any(skb: params.tmpl_skb); |
1832 | kfree(objp: elems); |
1833 | return ret; |
1834 | } |
1835 | |
1836 | static int |
1837 | ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, |
1838 | struct sk_buff *skb) |
1839 | { |
1840 | struct ieee80211_local *local = sdata->local; |
1841 | struct ieee802_11_elems *elems; |
1842 | struct cfg80211_chan_def chandef; |
1843 | struct ieee80211_channel *chan; |
1844 | enum nl80211_channel_type chan_type; |
1845 | int freq; |
1846 | u8 target_channel, oper_class; |
1847 | bool local_initiator; |
1848 | struct sta_info *sta; |
1849 | enum nl80211_band band; |
1850 | struct ieee80211_tdls_data *tf = (void *)skb->data; |
1851 | struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); |
1852 | int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable); |
1853 | struct ieee80211_tdls_ch_sw_params params = {}; |
1854 | int ret = 0; |
1855 | |
1856 | lockdep_assert_wiphy(local->hw.wiphy); |
1857 | |
1858 | params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; |
1859 | params.timestamp = rx_status->device_timestamp; |
1860 | |
1861 | if (skb->len < baselen) { |
1862 | tdls_dbg(sdata, "TDLS channel switch req too short: %d\n" , |
1863 | skb->len); |
1864 | return -EINVAL; |
1865 | } |
1866 | |
1867 | target_channel = tf->u.chan_switch_req.target_channel; |
1868 | oper_class = tf->u.chan_switch_req.oper_class; |
1869 | |
1870 | /* |
1871 | * We can't easily infer the channel band. The operating class is |
1872 | * ambiguous - there are multiple tables (US/Europe/JP/Global). The |
1873 | * solution here is to treat channels with number >14 as 5GHz ones, |
1874 | * and specifically check for the (oper_class, channel) combinations |
1875 | * where this doesn't hold. These are thankfully unique according to |
1876 | * IEEE802.11-2012. |
1877 | * We consider only the 2GHz and 5GHz bands and 20MHz+ channels as |
1878 | * valid here. |
1879 | */ |
1880 | if ((oper_class == 112 || oper_class == 2 || oper_class == 3 || |
1881 | oper_class == 4 || oper_class == 5 || oper_class == 6) && |
1882 | target_channel < 14) |
1883 | band = NL80211_BAND_5GHZ; |
1884 | else |
1885 | band = target_channel < 14 ? NL80211_BAND_2GHZ : |
1886 | NL80211_BAND_5GHZ; |
1887 | |
1888 | freq = ieee80211_channel_to_frequency(chan: target_channel, band); |
1889 | if (freq == 0) { |
1890 | tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n" , |
1891 | target_channel); |
1892 | return -EINVAL; |
1893 | } |
1894 | |
1895 | chan = ieee80211_get_channel(wiphy: sdata->local->hw.wiphy, freq); |
1896 | if (!chan) { |
1897 | tdls_dbg(sdata, |
1898 | "Unsupported channel for TDLS chan switch: %d\n" , |
1899 | target_channel); |
1900 | return -EINVAL; |
1901 | } |
1902 | |
1903 | elems = ieee802_11_parse_elems(start: tf->u.chan_switch_req.variable, |
1904 | len: skb->len - baselen, action: false, NULL); |
1905 | if (!elems) |
1906 | return -ENOMEM; |
1907 | |
1908 | if (elems->parse_error) { |
1909 | tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n" ); |
1910 | ret = -EINVAL; |
1911 | goto free; |
1912 | } |
1913 | |
1914 | if (!elems->ch_sw_timing || !elems->lnk_id) { |
1915 | tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n" ); |
1916 | ret = -EINVAL; |
1917 | goto free; |
1918 | } |
1919 | |
1920 | if (!elems->sec_chan_offs) { |
1921 | chan_type = NL80211_CHAN_HT20; |
1922 | } else { |
1923 | switch (elems->sec_chan_offs->sec_chan_offs) { |
1924 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: |
1925 | chan_type = NL80211_CHAN_HT40PLUS; |
1926 | break; |
1927 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: |
1928 | chan_type = NL80211_CHAN_HT40MINUS; |
1929 | break; |
1930 | default: |
1931 | chan_type = NL80211_CHAN_HT20; |
1932 | break; |
1933 | } |
1934 | } |
1935 | |
1936 | cfg80211_chandef_create(chandef: &chandef, channel: chan, chantype: chan_type); |
1937 | |
1938 | /* we will be active on the TDLS link */ |
1939 | if (!cfg80211_reg_can_beacon_relax(wiphy: sdata->local->hw.wiphy, chandef: &chandef, |
1940 | iftype: sdata->wdev.iftype)) { |
1941 | tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n" ); |
1942 | ret = -EINVAL; |
1943 | goto free; |
1944 | } |
1945 | |
1946 | sta = sta_info_get(sdata, addr: tf->sa); |
1947 | if (!sta || !test_sta_flag(sta, flag: WLAN_STA_TDLS_PEER_AUTH)) { |
1948 | tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n" , |
1949 | tf->sa); |
1950 | ret = -EINVAL; |
1951 | goto out; |
1952 | } |
1953 | |
1954 | params.sta = &sta->sta; |
1955 | |
1956 | /* validate the initiator is set correctly */ |
1957 | local_initiator = |
1958 | !memcmp(p: elems->lnk_id->init_sta, q: sdata->vif.addr, ETH_ALEN); |
1959 | if (local_initiator == sta->sta.tdls_initiator) { |
1960 | tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n" ); |
1961 | ret = -EINVAL; |
1962 | goto out; |
1963 | } |
1964 | |
1965 | /* peer should have known better */ |
1966 | if (!sta->sta.deflink.ht_cap.ht_supported && elems->sec_chan_offs && |
1967 | elems->sec_chan_offs->sec_chan_offs) { |
1968 | tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n" ); |
1969 | ret = -EOPNOTSUPP; |
1970 | goto out; |
1971 | } |
1972 | |
1973 | params.chandef = &chandef; |
1974 | params.switch_time = le16_to_cpu(elems->ch_sw_timing->switch_time); |
1975 | params.switch_timeout = le16_to_cpu(elems->ch_sw_timing->switch_timeout); |
1976 | |
1977 | params.tmpl_skb = |
1978 | ieee80211_tdls_ch_sw_resp_tmpl_get(sta, |
1979 | ch_sw_tm_ie_offset: ¶ms.ch_sw_tm_ie); |
1980 | if (!params.tmpl_skb) { |
1981 | ret = -ENOENT; |
1982 | goto out; |
1983 | } |
1984 | |
1985 | drv_tdls_recv_channel_switch(local: sdata->local, sdata, params: ¶ms); |
1986 | |
1987 | tdls_dbg(sdata, |
1988 | "TDLS ch switch request received from %pM ch %d width %d\n" , |
1989 | tf->sa, params.chandef->chan->center_freq, |
1990 | params.chandef->width); |
1991 | out: |
1992 | dev_kfree_skb_any(skb: params.tmpl_skb); |
1993 | free: |
1994 | kfree(objp: elems); |
1995 | return ret; |
1996 | } |
1997 | |
1998 | void |
1999 | ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, |
2000 | struct sk_buff *skb) |
2001 | { |
2002 | struct ieee80211_tdls_data *tf = (void *)skb->data; |
2003 | struct wiphy *wiphy = sdata->local->hw.wiphy; |
2004 | |
2005 | lockdep_assert_wiphy(wiphy); |
2006 | |
2007 | /* make sure the driver supports it */ |
2008 | if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) |
2009 | return; |
2010 | |
2011 | /* we want to access the entire packet */ |
2012 | if (skb_linearize(skb)) |
2013 | return; |
2014 | /* |
2015 | * The packet/size was already validated by mac80211 Rx path, only look |
2016 | * at the action type. |
2017 | */ |
2018 | switch (tf->action_code) { |
2019 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: |
2020 | ieee80211_process_tdls_channel_switch_req(sdata, skb); |
2021 | break; |
2022 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: |
2023 | ieee80211_process_tdls_channel_switch_resp(sdata, skb); |
2024 | break; |
2025 | default: |
2026 | WARN_ON_ONCE(1); |
2027 | return; |
2028 | } |
2029 | } |
2030 | |
2031 | void ieee80211_teardown_tdls_peers(struct ieee80211_link_data *link) |
2032 | { |
2033 | struct ieee80211_sub_if_data *sdata = link->sdata; |
2034 | struct sta_info *sta; |
2035 | u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; |
2036 | |
2037 | rcu_read_lock(); |
2038 | list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { |
2039 | if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded || |
2040 | !test_sta_flag(sta, flag: WLAN_STA_AUTHORIZED)) |
2041 | continue; |
2042 | |
2043 | if (sta->deflink.link_id != link->link_id) |
2044 | continue; |
2045 | |
2046 | ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr, |
2047 | NL80211_TDLS_TEARDOWN, reason, |
2048 | GFP_ATOMIC); |
2049 | } |
2050 | rcu_read_unlock(); |
2051 | } |
2052 | |
2053 | void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata, |
2054 | const u8 *peer, u16 reason) |
2055 | { |
2056 | struct ieee80211_sta *sta; |
2057 | |
2058 | rcu_read_lock(); |
2059 | sta = ieee80211_find_sta(vif: &sdata->vif, addr: peer); |
2060 | if (!sta || !sta->tdls) { |
2061 | rcu_read_unlock(); |
2062 | return; |
2063 | } |
2064 | rcu_read_unlock(); |
2065 | |
2066 | tdls_dbg(sdata, "disconnected from TDLS peer %pM (Reason: %u=%s)\n" , |
2067 | peer, reason, |
2068 | ieee80211_get_reason_code_string(reason)); |
2069 | |
2070 | ieee80211_tdls_oper_request(&sdata->vif, peer, |
2071 | NL80211_TDLS_TEARDOWN, |
2072 | WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE, |
2073 | GFP_ATOMIC); |
2074 | } |
2075 | |