1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
6 * Copyright 2002-2005, Instant802 Networks, Inc.
7 * Copyright 2005-2006, Devicescape Software, Inc.
8 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
9 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
10 * Copyright 2007-2008, Intel Corporation
11 * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
12 * Copyright (C) 2018, 2020, 2022-2024 Intel Corporation
15 #include <linux/ieee80211.h>
16 #include <net/cfg80211.h>
17 #include <net/mac80211.h>
18 #include "ieee80211_i.h"
23 wbcs_elem_to_chandef(const struct ieee80211_wide_bw_chansw_ie
*wbcs_elem
,
24 struct cfg80211_chan_def
*chandef
)
26 u8 ccfs0
= wbcs_elem
->new_center_freq_seg0
;
27 u8 ccfs1
= wbcs_elem
->new_center_freq_seg1
;
28 u32 cf0
= ieee80211_channel_to_frequency(ccfs0
, chandef
->chan
->band
);
29 u32 cf1
= ieee80211_channel_to_frequency(ccfs1
, chandef
->chan
->band
);
31 switch (wbcs_elem
->new_channel_width
) {
32 case IEEE80211_VHT_CHANWIDTH_160MHZ
:
33 /* deprecated encoding */
34 chandef
->width
= NL80211_CHAN_WIDTH_160
;
35 chandef
->center_freq1
= cf0
;
37 case IEEE80211_VHT_CHANWIDTH_80P80MHZ
:
38 /* deprecated encoding */
39 chandef
->width
= NL80211_CHAN_WIDTH_80P80
;
40 chandef
->center_freq1
= cf0
;
41 chandef
->center_freq2
= cf1
;
43 case IEEE80211_VHT_CHANWIDTH_80MHZ
:
44 chandef
->width
= NL80211_CHAN_WIDTH_80
;
45 chandef
->center_freq1
= cf0
;
48 u8 diff
= abs(ccfs0
- ccfs1
);
51 chandef
->width
= NL80211_CHAN_WIDTH_160
;
52 chandef
->center_freq1
= cf1
;
53 } else if (diff
> 8) {
54 chandef
->width
= NL80211_CHAN_WIDTH_80P80
;
55 chandef
->center_freq2
= cf1
;
59 case IEEE80211_VHT_CHANWIDTH_USE_HT
:
61 /* If the WBCS Element is present, new channel bandwidth is
64 chandef
->width
= NL80211_CHAN_WIDTH_40
;
65 chandef
->center_freq1
= cf0
;
69 return cfg80211_chandef_valid(chandef
);
73 validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data
*sdata
,
74 struct ieee80211_conn_settings
*conn
,
76 struct cfg80211_chan_def
*chandef
)
78 u32 control_freq
, center_freq1
, center_freq2
;
79 enum nl80211_chan_width chan_width
;
80 struct ieee80211_ht_operation ht_oper
;
81 struct ieee80211_vht_operation vht_oper
;
83 if (conn
->mode
< IEEE80211_CONN_MODE_HT
||
84 conn
->bw_limit
< IEEE80211_CONN_BW_LIMIT_40
) {
89 control_freq
= chandef
->chan
->center_freq
;
90 center_freq1
= chandef
->center_freq1
;
91 center_freq2
= chandef
->center_freq2
;
92 chan_width
= chandef
->width
;
94 ht_oper
.primary_chan
= ieee80211_frequency_to_channel(control_freq
);
95 if (control_freq
!= center_freq1
)
96 ht_oper
.ht_param
= control_freq
> center_freq1
?
97 IEEE80211_HT_PARAM_CHA_SEC_BELOW
:
98 IEEE80211_HT_PARAM_CHA_SEC_ABOVE
;
100 ht_oper
.ht_param
= IEEE80211_HT_PARAM_CHA_SEC_NONE
;
102 ieee80211_chandef_ht_oper(&ht_oper
, chandef
);
104 if (conn
->mode
< IEEE80211_CONN_MODE_VHT
)
107 vht_oper
.center_freq_seg0_idx
=
108 ieee80211_frequency_to_channel(center_freq1
);
109 vht_oper
.center_freq_seg1_idx
= center_freq2
?
110 ieee80211_frequency_to_channel(center_freq2
) : 0;
112 switch (chan_width
) {
113 case NL80211_CHAN_WIDTH_320
:
116 case NL80211_CHAN_WIDTH_160
:
117 vht_oper
.chan_width
= IEEE80211_VHT_CHANWIDTH_80MHZ
;
118 vht_oper
.center_freq_seg1_idx
= vht_oper
.center_freq_seg0_idx
;
119 vht_oper
.center_freq_seg0_idx
+=
120 control_freq
< center_freq1
? -8 : 8;
122 case NL80211_CHAN_WIDTH_80P80
:
123 vht_oper
.chan_width
= IEEE80211_VHT_CHANWIDTH_80MHZ
;
125 case NL80211_CHAN_WIDTH_80
:
126 vht_oper
.chan_width
= IEEE80211_VHT_CHANWIDTH_80MHZ
;
129 vht_oper
.chan_width
= IEEE80211_VHT_CHANWIDTH_USE_HT
;
133 ht_oper
.operation_mode
=
134 le16_encode_bits(vht_oper
.center_freq_seg1_idx
,
135 IEEE80211_HT_OP_MODE_CCFS2_MASK
);
137 if (!ieee80211_chandef_vht_oper(&sdata
->local
->hw
, vht_cap_info
,
138 &vht_oper
, &ht_oper
, chandef
))
139 chandef
->chan
= NULL
;
143 validate_chandef_by_6ghz_he_eht_oper(struct ieee80211_sub_if_data
*sdata
,
144 struct ieee80211_conn_settings
*conn
,
145 struct cfg80211_chan_def
*chandef
)
147 struct ieee80211_local
*local
= sdata
->local
;
148 u32 control_freq
, center_freq1
, center_freq2
;
149 enum nl80211_chan_width chan_width
;
151 struct ieee80211_he_operation _oper
;
152 struct ieee80211_he_6ghz_oper _6ghz_oper
;
155 struct ieee80211_eht_operation _oper
;
156 struct ieee80211_eht_operation_info _oper_info
;
158 const struct ieee80211_eht_operation
*eht_oper
;
160 if (conn
->mode
< IEEE80211_CONN_MODE_HE
) {
161 chandef
->chan
= NULL
;
165 control_freq
= chandef
->chan
->center_freq
;
166 center_freq1
= chandef
->center_freq1
;
167 center_freq2
= chandef
->center_freq2
;
168 chan_width
= chandef
->width
;
170 he
._oper
.he_oper_params
=
171 le32_encode_bits(1, IEEE80211_HE_OPERATION_6GHZ_OP_INFO
);
172 he
._6ghz_oper
.primary
=
173 ieee80211_frequency_to_channel(control_freq
);
174 he
._6ghz_oper
.ccfs0
= ieee80211_frequency_to_channel(center_freq1
);
175 he
._6ghz_oper
.ccfs1
= center_freq2
?
176 ieee80211_frequency_to_channel(center_freq2
) : 0;
178 switch (chan_width
) {
179 case NL80211_CHAN_WIDTH_320
:
180 he
._6ghz_oper
.ccfs1
= he
._6ghz_oper
.ccfs0
;
181 he
._6ghz_oper
.ccfs0
+= control_freq
< center_freq1
? -16 : 16;
182 he
._6ghz_oper
.control
= IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ
;
184 case NL80211_CHAN_WIDTH_160
:
185 he
._6ghz_oper
.ccfs1
= he
._6ghz_oper
.ccfs0
;
186 he
._6ghz_oper
.ccfs0
+= control_freq
< center_freq1
? -8 : 8;
188 case NL80211_CHAN_WIDTH_80P80
:
189 he
._6ghz_oper
.control
=
190 IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ
;
192 case NL80211_CHAN_WIDTH_80
:
193 he
._6ghz_oper
.control
=
194 IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ
;
196 case NL80211_CHAN_WIDTH_40
:
197 he
._6ghz_oper
.control
=
198 IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ
;
201 he
._6ghz_oper
.control
=
202 IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ
;
206 if (conn
->mode
< IEEE80211_CONN_MODE_EHT
) {
209 eht
._oper
.params
= IEEE80211_EHT_OPER_INFO_PRESENT
;
210 eht
._oper_info
.control
= he
._6ghz_oper
.control
;
211 eht
._oper_info
.ccfs0
= he
._6ghz_oper
.ccfs0
;
212 eht
._oper_info
.ccfs1
= he
._6ghz_oper
.ccfs1
;
213 eht_oper
= &eht
._oper
;
216 if (!ieee80211_chandef_he_6ghz_oper(local
, &he
._oper
,
218 chandef
->chan
= NULL
;
221 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data
*sdata
,
222 struct ieee802_11_elems
*elems
,
223 enum nl80211_band current_band
,
225 struct ieee80211_conn_settings
*conn
,
226 u8
*bssid
, bool unprot_action
,
227 struct ieee80211_csa_ie
*csa_ie
)
229 enum nl80211_band new_band
= current_band
;
231 u8 new_chan_no
= 0, new_op_class
= 0;
232 struct ieee80211_channel
*new_chan
;
233 struct cfg80211_chan_def new_chandef
= {};
234 const struct ieee80211_sec_chan_offs_ie
*sec_chan_offs
;
235 const struct ieee80211_wide_bw_chansw_ie
*wide_bw_chansw_ie
;
236 const struct ieee80211_bandwidth_indication
*bwi
;
237 const struct ieee80211_ext_chansw_ie
*ext_chansw_elem
;
238 int secondary_channel_offset
= -1;
240 memset(csa_ie
, 0, sizeof(*csa_ie
));
242 sec_chan_offs
= elems
->sec_chan_offs
;
243 wide_bw_chansw_ie
= elems
->wide_bw_chansw_ie
;
244 bwi
= elems
->bandwidth_indication
;
245 ext_chansw_elem
= elems
->ext_chansw_ie
;
247 if (conn
->mode
< IEEE80211_CONN_MODE_HT
||
248 conn
->bw_limit
< IEEE80211_CONN_BW_LIMIT_40
) {
249 sec_chan_offs
= NULL
;
250 wide_bw_chansw_ie
= NULL
;
253 if (conn
->mode
< IEEE80211_CONN_MODE_VHT
)
254 wide_bw_chansw_ie
= NULL
;
256 if (ext_chansw_elem
) {
257 new_op_class
= ext_chansw_elem
->new_operating_class
;
259 if (!ieee80211_operating_class_to_band(new_op_class
, &new_band
)) {
263 "cannot understand ECSA IE operating class, %d, ignoring\n",
264 ext_chansw_elem
->new_operating_class
);
266 new_chan_no
= ext_chansw_elem
->new_ch_num
;
267 csa_ie
->count
= ext_chansw_elem
->count
;
268 csa_ie
->mode
= ext_chansw_elem
->mode
;
272 if (!new_op_class
&& elems
->ch_switch_ie
) {
273 new_chan_no
= elems
->ch_switch_ie
->new_ch_num
;
274 csa_ie
->count
= elems
->ch_switch_ie
->count
;
275 csa_ie
->mode
= elems
->ch_switch_ie
->mode
;
278 /* nothing here we understand */
282 /* Mesh Channel Switch Parameters Element */
283 if (elems
->mesh_chansw_params_ie
) {
284 csa_ie
->ttl
= elems
->mesh_chansw_params_ie
->mesh_ttl
;
285 csa_ie
->mode
= elems
->mesh_chansw_params_ie
->mesh_flags
;
286 csa_ie
->pre_value
= le16_to_cpu(
287 elems
->mesh_chansw_params_ie
->mesh_pre_value
);
289 if (elems
->mesh_chansw_params_ie
->mesh_flags
&
290 WLAN_EID_CHAN_SWITCH_PARAM_REASON
)
291 csa_ie
->reason_code
= le16_to_cpu(
292 elems
->mesh_chansw_params_ie
->mesh_reason
);
295 new_freq
= ieee80211_channel_to_frequency(new_chan_no
, new_band
);
296 new_chan
= ieee80211_get_channel(sdata
->local
->hw
.wiphy
, new_freq
);
297 if (!new_chan
|| new_chan
->flags
& IEEE80211_CHAN_DISABLED
) {
300 "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
306 secondary_channel_offset
= sec_chan_offs
->sec_chan_offs
;
307 } else if (conn
->mode
>= IEEE80211_CONN_MODE_HT
) {
308 /* If the secondary channel offset IE is not present,
309 * we can't know what's the post-CSA offset, so the
310 * best we can do is use 20MHz.
312 secondary_channel_offset
= IEEE80211_HT_PARAM_CHA_SEC_NONE
;
315 switch (secondary_channel_offset
) {
317 /* secondary_channel_offset was present but is invalid */
318 case IEEE80211_HT_PARAM_CHA_SEC_NONE
:
319 cfg80211_chandef_create(&csa_ie
->chanreq
.oper
, new_chan
,
322 case IEEE80211_HT_PARAM_CHA_SEC_ABOVE
:
323 cfg80211_chandef_create(&csa_ie
->chanreq
.oper
, new_chan
,
324 NL80211_CHAN_HT40PLUS
);
326 case IEEE80211_HT_PARAM_CHA_SEC_BELOW
:
327 cfg80211_chandef_create(&csa_ie
->chanreq
.oper
, new_chan
,
328 NL80211_CHAN_HT40MINUS
);
331 cfg80211_chandef_create(&csa_ie
->chanreq
.oper
, new_chan
,
333 /* keep width for 5/10 MHz channels */
334 switch (sdata
->vif
.bss_conf
.chanreq
.oper
.width
) {
335 case NL80211_CHAN_WIDTH_5
:
336 case NL80211_CHAN_WIDTH_10
:
337 csa_ie
->chanreq
.oper
.width
=
338 sdata
->vif
.bss_conf
.chanreq
.oper
.width
;
346 /* capture the AP configuration */
347 csa_ie
->chanreq
.ap
= csa_ie
->chanreq
.oper
;
349 /* parse one of the Elements to build a new chandef */
350 memset(&new_chandef
, 0, sizeof(new_chandef
));
351 new_chandef
.chan
= new_chan
;
353 /* start with the CSA one */
354 new_chandef
= csa_ie
->chanreq
.oper
;
355 /* and update the width accordingly */
356 ieee80211_chandef_eht_oper(&bwi
->info
, &new_chandef
);
358 if (bwi
->params
& IEEE80211_BW_IND_DIS_SUBCH_PRESENT
)
359 new_chandef
.punctured
=
360 get_unaligned_le16(bwi
->info
.optional
);
361 } else if (!wide_bw_chansw_ie
|| !wbcs_elem_to_chandef(wide_bw_chansw_ie
,
363 if (!ieee80211_operating_class_to_chandef(new_op_class
, new_chan
,
365 new_chandef
= csa_ie
->chanreq
.oper
;
368 /* check if the new chandef fits the capabilities */
369 if (new_band
== NL80211_BAND_6GHZ
)
370 validate_chandef_by_6ghz_he_eht_oper(sdata
, conn
, &new_chandef
);
372 validate_chandef_by_ht_vht_oper(sdata
, conn
, vht_cap_info
,
375 /* if data is there validate the bandwidth & use it */
376 if (new_chandef
.chan
) {
377 /* capture the AP chandef before (potential) downgrading */
378 csa_ie
->chanreq
.ap
= new_chandef
;
380 if (conn
->bw_limit
< IEEE80211_CONN_BW_LIMIT_320
&&
381 new_chandef
.width
== NL80211_CHAN_WIDTH_320
)
382 ieee80211_chandef_downgrade(&new_chandef
, NULL
);
384 if (conn
->bw_limit
< IEEE80211_CONN_BW_LIMIT_160
&&
385 (new_chandef
.width
== NL80211_CHAN_WIDTH_80P80
||
386 new_chandef
.width
== NL80211_CHAN_WIDTH_160
))
387 ieee80211_chandef_downgrade(&new_chandef
, NULL
);
389 if (!cfg80211_chandef_compatible(&new_chandef
,
390 &csa_ie
->chanreq
.oper
)) {
392 "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
397 csa_ie
->chanreq
.oper
= new_chandef
;
400 if (elems
->max_channel_switch_time
)
401 csa_ie
->max_switch_time
=
402 (elems
->max_channel_switch_time
[0] << 0) |
403 (elems
->max_channel_switch_time
[1] << 8) |
404 (elems
->max_channel_switch_time
[2] << 16);
409 static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data
*sdata
,
410 struct ieee80211_msrment_ie
*request_ie
,
411 const u8
*da
, const u8
*bssid
,
414 struct ieee80211_local
*local
= sdata
->local
;
416 struct ieee80211_mgmt
*msr_report
;
418 skb
= dev_alloc_skb(sizeof(*msr_report
) + local
->hw
.extra_tx_headroom
+
419 sizeof(struct ieee80211_msrment_ie
));
423 skb_reserve(skb
, local
->hw
.extra_tx_headroom
);
424 msr_report
= skb_put_zero(skb
, 24);
425 memcpy(msr_report
->da
, da
, ETH_ALEN
);
426 memcpy(msr_report
->sa
, sdata
->vif
.addr
, ETH_ALEN
);
427 memcpy(msr_report
->bssid
, bssid
, ETH_ALEN
);
428 msr_report
->frame_control
= cpu_to_le16(IEEE80211_FTYPE_MGMT
|
429 IEEE80211_STYPE_ACTION
);
431 skb_put(skb
, 1 + sizeof(msr_report
->u
.action
.u
.measurement
));
432 msr_report
->u
.action
.category
= WLAN_CATEGORY_SPECTRUM_MGMT
;
433 msr_report
->u
.action
.u
.measurement
.action_code
=
434 WLAN_ACTION_SPCT_MSR_RPRT
;
435 msr_report
->u
.action
.u
.measurement
.dialog_token
= dialog_token
;
437 msr_report
->u
.action
.u
.measurement
.element_id
= WLAN_EID_MEASURE_REPORT
;
438 msr_report
->u
.action
.u
.measurement
.length
=
439 sizeof(struct ieee80211_msrment_ie
);
441 memset(&msr_report
->u
.action
.u
.measurement
.msr_elem
, 0,
442 sizeof(struct ieee80211_msrment_ie
));
443 msr_report
->u
.action
.u
.measurement
.msr_elem
.token
= request_ie
->token
;
444 msr_report
->u
.action
.u
.measurement
.msr_elem
.mode
|=
445 IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED
;
446 msr_report
->u
.action
.u
.measurement
.msr_elem
.type
= request_ie
->type
;
448 ieee80211_tx_skb(sdata
, skb
);
451 void ieee80211_process_measurement_req(struct ieee80211_sub_if_data
*sdata
,
452 struct ieee80211_mgmt
*mgmt
,
456 * Ignoring measurement request is spec violation.
457 * Mandatory measurements must be reported optional
458 * measurements might be refused or reported incapable
459 * For now just refuse
460 * TODO: Answer basic measurement as unmeasured
462 ieee80211_send_refuse_measurement_request(sdata
,
463 &mgmt
->u
.action
.u
.measurement
.msr_elem
,
464 mgmt
->sa
, mgmt
->bssid
,
465 mgmt
->u
.action
.u
.measurement
.dialog_token
);