]>
Commit | Line | Data |
---|---|---|
065c029a | 1 | /* |
2 | * Operating classes | |
3 | * Copyright(c) 2015 Intel Deutschland GmbH | |
4 | * Contact Information: | |
5 | * Intel Linux Wireless <ilw@linux.intel.com> | |
6 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
7 | * | |
8 | * This software may be distributed under the terms of the BSD license. | |
9 | * See README for more details. | |
10 | */ | |
11 | ||
12 | #include "utils/includes.h" | |
13 | ||
14 | #include "utils/common.h" | |
15 | #include "common/ieee802_11_common.h" | |
16 | #include "wpa_supplicant_i.h" | |
2b9713d6 | 17 | #include "bss.h" |
065c029a | 18 | |
19 | ||
89450024 JM |
20 | static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, |
21 | u8 op_class, u8 chan, | |
065c029a | 22 | unsigned int *flags) |
23 | { | |
24 | int i; | |
89450024 | 25 | int is_6ghz = op_class >= 131 && op_class <= 135; |
065c029a | 26 | |
27 | for (i = 0; i < mode->num_channels; i++) { | |
89450024 JM |
28 | int chan_is_6ghz; |
29 | ||
30 | chan_is_6ghz = mode->channels[i].freq > 5940 && | |
31 | mode->channels[i].freq <= 7105; | |
32 | if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan) | |
065c029a | 33 | break; |
34 | } | |
35 | ||
36 | if (i == mode->num_channels || | |
37 | (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) | |
38 | return NOT_ALLOWED; | |
39 | ||
40 | if (flags) | |
41 | *flags = mode->channels[i].flag; | |
42 | ||
1ac4dba3 AS |
43 | if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) |
44 | return NO_IR; | |
45 | ||
065c029a | 46 | return ALLOWED; |
47 | } | |
48 | ||
49 | ||
50 | static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel) | |
51 | { | |
52 | u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; | |
53 | size_t i; | |
54 | ||
55 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) | |
56 | return 0; | |
57 | ||
58 | for (i = 0; i < ARRAY_SIZE(center_channels); i++) { | |
59 | /* | |
60 | * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), | |
61 | * so the center channel is 6 channels away from the start/end. | |
62 | */ | |
63 | if (channel >= center_channels[i] - 6 && | |
64 | channel <= center_channels[i] + 6) | |
65 | return center_channels[i]; | |
66 | } | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | ||
89450024 JM |
72 | static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, |
73 | u8 op_class, u8 channel) | |
065c029a | 74 | { |
75 | u8 center_chan; | |
76 | unsigned int i; | |
1ac4dba3 | 77 | unsigned int no_ir = 0; |
065c029a | 78 | |
79 | center_chan = get_center_80mhz(mode, channel); | |
80 | if (!center_chan) | |
81 | return NOT_ALLOWED; | |
82 | ||
83 | /* check all the channels are available */ | |
84 | for (i = 0; i < 4; i++) { | |
85 | unsigned int flags; | |
86 | u8 adj_chan = center_chan - 6 + i * 4; | |
87 | ||
89450024 JM |
88 | if (allow_channel(mode, op_class, adj_chan, &flags) == |
89 | NOT_ALLOWED) | |
065c029a | 90 | return NOT_ALLOWED; |
91 | ||
92 | if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) || | |
93 | (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) || | |
94 | (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) || | |
95 | (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))) | |
96 | return NOT_ALLOWED; | |
1ac4dba3 AS |
97 | |
98 | if (flags & HOSTAPD_CHAN_NO_IR) | |
99 | no_ir = 1; | |
065c029a | 100 | } |
101 | ||
1ac4dba3 AS |
102 | if (no_ir) |
103 | return NO_IR; | |
104 | ||
065c029a | 105 | return ALLOWED; |
106 | } | |
107 | ||
108 | ||
109 | static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel) | |
110 | { | |
111 | u8 center_channels[] = { 50, 114 }; | |
112 | unsigned int i; | |
113 | ||
114 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) | |
115 | return 0; | |
116 | ||
117 | for (i = 0; i < ARRAY_SIZE(center_channels); i++) { | |
118 | /* | |
119 | * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), | |
120 | * so the center channel is 14 channels away from the start/end. | |
121 | */ | |
122 | if (channel >= center_channels[i] - 14 && | |
123 | channel <= center_channels[i] + 14) | |
124 | return center_channels[i]; | |
125 | } | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | ||
131 | static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, | |
89450024 | 132 | u8 op_class, u8 channel) |
065c029a | 133 | { |
134 | u8 center_chan; | |
135 | unsigned int i; | |
1ac4dba3 | 136 | unsigned int no_ir = 0; |
065c029a | 137 | |
138 | center_chan = get_center_160mhz(mode, channel); | |
139 | if (!center_chan) | |
140 | return NOT_ALLOWED; | |
141 | ||
142 | /* Check all the channels are available */ | |
143 | for (i = 0; i < 8; i++) { | |
144 | unsigned int flags; | |
145 | u8 adj_chan = center_chan - 14 + i * 4; | |
146 | ||
89450024 JM |
147 | if (allow_channel(mode, op_class, adj_chan, &flags) == |
148 | NOT_ALLOWED) | |
065c029a | 149 | return NOT_ALLOWED; |
150 | ||
151 | if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) || | |
152 | (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) || | |
153 | (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) || | |
154 | (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) || | |
155 | (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) || | |
156 | (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) || | |
157 | (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) || | |
158 | (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))) | |
159 | return NOT_ALLOWED; | |
1ac4dba3 AS |
160 | |
161 | if (flags & HOSTAPD_CHAN_NO_IR) | |
162 | no_ir = 1; | |
065c029a | 163 | } |
164 | ||
1ac4dba3 AS |
165 | if (no_ir) |
166 | return NO_IR; | |
167 | ||
065c029a | 168 | return ALLOWED; |
169 | } | |
170 | ||
171 | ||
89450024 JM |
172 | enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class, |
173 | u8 channel, u8 bw) | |
065c029a | 174 | { |
175 | unsigned int flag = 0; | |
176 | enum chan_allowed res, res2; | |
177 | ||
89450024 | 178 | res2 = res = allow_channel(mode, op_class, channel, &flag); |
065c029a | 179 | if (bw == BW40MINUS) { |
180 | if (!(flag & HOSTAPD_CHAN_HT40MINUS)) | |
181 | return NOT_ALLOWED; | |
89450024 | 182 | res2 = allow_channel(mode, op_class, channel - 4, NULL); |
065c029a | 183 | } else if (bw == BW40PLUS) { |
184 | if (!(flag & HOSTAPD_CHAN_HT40PLUS)) | |
185 | return NOT_ALLOWED; | |
89450024 | 186 | res2 = allow_channel(mode, op_class, channel + 4, NULL); |
065c029a | 187 | } else if (bw == BW80) { |
188 | /* | |
189 | * channel is a center channel and as such, not necessarily a | |
190 | * valid 20 MHz channels. Override earlier allow_channel() | |
191 | * result and use only the 80 MHz specific version. | |
192 | */ | |
89450024 | 193 | res2 = res = verify_80mhz(mode, op_class, channel); |
065c029a | 194 | } else if (bw == BW160) { |
195 | /* | |
196 | * channel is a center channel and as such, not necessarily a | |
197 | * valid 20 MHz channels. Override earlier allow_channel() | |
198 | * result and use only the 160 MHz specific version. | |
199 | */ | |
89450024 | 200 | res2 = res = verify_160mhz(mode, op_class, channel); |
065c029a | 201 | } else if (bw == BW80P80) { |
202 | /* | |
203 | * channel is a center channel and as such, not necessarily a | |
204 | * valid 20 MHz channels. Override earlier allow_channel() | |
205 | * result and use only the 80 MHz specific version. | |
206 | */ | |
89450024 | 207 | res2 = res = verify_80mhz(mode, op_class, channel); |
065c029a | 208 | } |
209 | ||
210 | if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) | |
211 | return NOT_ALLOWED; | |
212 | ||
1ac4dba3 AS |
213 | if (res == NO_IR || res2 == NO_IR) |
214 | return NO_IR; | |
215 | ||
065c029a | 216 | return ALLOWED; |
217 | } | |
218 | ||
219 | ||
220 | static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, | |
cb828507 | 221 | struct wpa_ssid *ssid, |
065c029a | 222 | const struct oper_class_map *op_class) |
223 | { | |
224 | int chan; | |
225 | size_t i; | |
226 | struct hostapd_hw_modes *mode; | |
227 | int found; | |
b06d60a9 BG |
228 | int z; |
229 | int freq2 = 0; | |
230 | int freq5 = 0; | |
065c029a | 231 | |
d0e116f6 VK |
232 | mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode, |
233 | is_6ghz_op_class(op_class->op_class)); | |
065c029a | 234 | if (!mode) |
235 | return 0; | |
236 | ||
b06d60a9 BG |
237 | /* If we are configured to disable certain things, take that into |
238 | * account here. */ | |
8f8c423a | 239 | if (ssid && ssid->freq_list && ssid->freq_list[0]) { |
b06d60a9 BG |
240 | for (z = 0; ; z++) { |
241 | int f = ssid->freq_list[z]; | |
242 | ||
243 | if (f == 0) | |
244 | break; /* end of list */ | |
245 | if (f > 4000 && f < 6000) | |
246 | freq5 = 1; | |
247 | else if (f > 2400 && f < 2500) | |
248 | freq2 = 1; | |
249 | } | |
250 | } else { | |
251 | /* No frequencies specified, can use anything hardware supports. | |
252 | */ | |
253 | freq2 = freq5 = 1; | |
254 | } | |
255 | ||
256 | if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5) | |
257 | return 0; | |
258 | if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2) | |
259 | return 0; | |
260 | ||
cb828507 | 261 | #ifdef CONFIG_HT_OVERRIDES |
8f8c423a | 262 | if (ssid && ssid->disable_ht) { |
cb828507 BG |
263 | switch (op_class->op_class) { |
264 | case 83: | |
265 | case 84: | |
266 | case 104: | |
267 | case 105: | |
268 | case 116: | |
269 | case 117: | |
270 | case 119: | |
271 | case 120: | |
272 | case 122: | |
273 | case 123: | |
274 | case 126: | |
275 | case 127: | |
276 | case 128: | |
277 | case 129: | |
278 | case 130: | |
279 | /* Disable >= 40 MHz channels if HT is disabled */ | |
280 | return 0; | |
281 | } | |
282 | } | |
283 | #endif /* CONFIG_HT_OVERRIDES */ | |
284 | ||
285 | #ifdef CONFIG_VHT_OVERRIDES | |
8f8c423a | 286 | if (ssid && ssid->disable_vht) { |
cb828507 BG |
287 | if (op_class->op_class >= 128 && op_class->op_class <= 130) { |
288 | /* Disable >= 80 MHz channels if VHT is disabled */ | |
289 | return 0; | |
290 | } | |
291 | } | |
292 | #endif /* CONFIG_VHT_OVERRIDES */ | |
293 | ||
065c029a | 294 | if (op_class->op_class == 128) { |
295 | u8 channels[] = { 42, 58, 106, 122, 138, 155 }; | |
296 | ||
297 | for (i = 0; i < ARRAY_SIZE(channels); i++) { | |
89450024 JM |
298 | if (verify_channel(mode, op_class->op_class, |
299 | channels[i], op_class->bw) != | |
1ac4dba3 | 300 | NOT_ALLOWED) |
065c029a | 301 | return 1; |
302 | } | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | if (op_class->op_class == 129) { | |
308 | /* Check if either 160 MHz channels is allowed */ | |
89450024 JM |
309 | return verify_channel(mode, op_class->op_class, 50, |
310 | op_class->bw) != NOT_ALLOWED || | |
311 | verify_channel(mode, op_class->op_class, 114, | |
312 | op_class->bw) != NOT_ALLOWED; | |
065c029a | 313 | } |
314 | ||
315 | if (op_class->op_class == 130) { | |
316 | /* Need at least two non-contiguous 80 MHz segments */ | |
317 | found = 0; | |
318 | ||
89450024 JM |
319 | if (verify_channel(mode, op_class->op_class, 42, |
320 | op_class->bw) != NOT_ALLOWED || | |
321 | verify_channel(mode, op_class->op_class, 58, | |
322 | op_class->bw) != NOT_ALLOWED) | |
065c029a | 323 | found++; |
89450024 JM |
324 | if (verify_channel(mode, op_class->op_class, 106, |
325 | op_class->bw) != NOT_ALLOWED || | |
326 | verify_channel(mode, op_class->op_class, 122, | |
327 | op_class->bw) != NOT_ALLOWED || | |
328 | verify_channel(mode, op_class->op_class, 138, | |
329 | op_class->bw) != NOT_ALLOWED) | |
065c029a | 330 | found++; |
89450024 JM |
331 | if (verify_channel(mode, op_class->op_class, 106, |
332 | op_class->bw) != NOT_ALLOWED && | |
333 | verify_channel(mode, op_class->op_class, 138, | |
334 | op_class->bw) != NOT_ALLOWED) | |
065c029a | 335 | found++; |
89450024 JM |
336 | if (verify_channel(mode, op_class->op_class, 155, |
337 | op_class->bw) != NOT_ALLOWED) | |
065c029a | 338 | found++; |
339 | ||
340 | if (found >= 2) | |
341 | return 1; | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
346 | found = 0; | |
347 | for (chan = op_class->min_chan; chan <= op_class->max_chan; | |
348 | chan += op_class->inc) { | |
89450024 JM |
349 | if (verify_channel(mode, op_class->op_class, chan, |
350 | op_class->bw) != NOT_ALLOWED) { | |
065c029a | 351 | found = 1; |
352 | break; | |
353 | } | |
354 | } | |
355 | ||
356 | return found; | |
357 | } | |
358 | ||
359 | ||
2b9713d6 AB |
360 | static int wpas_sta_secondary_channel_offset(struct wpa_bss *bss, u8 *current, |
361 | u8 *channel) | |
362 | { | |
363 | ||
364 | u8 *ies, phy_type; | |
365 | size_t ies_len; | |
366 | ||
367 | if (!bss) | |
368 | return -1; | |
369 | ies = (u8 *) (bss + 1); | |
370 | ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; | |
371 | return wpas_get_op_chan_phy(bss->freq, ies, ies_len, current, | |
372 | channel, &phy_type); | |
373 | } | |
374 | ||
375 | ||
cb828507 BG |
376 | size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, |
377 | struct wpa_ssid *ssid, | |
2b9713d6 | 378 | struct wpa_bss *bss, u8 *pos, size_t len) |
065c029a | 379 | { |
380 | struct wpabuf *buf; | |
381 | u8 op, current, chan; | |
382 | u8 *ie_len; | |
383 | size_t res; | |
384 | ||
385 | /* | |
2b9713d6 AB |
386 | * Determine the current operating class correct mode based on |
387 | * advertised BSS capabilities, if available. Fall back to a less | |
388 | * accurate guess based on frequency if the needed IEs are not available | |
389 | * or used. | |
065c029a | 390 | */ |
2b9713d6 AB |
391 | if (wpas_sta_secondary_channel_offset(bss, ¤t, &chan) < 0 && |
392 | ieee80211_freq_to_channel_ext(bss->freq, 0, CHANWIDTH_USE_HT, | |
065c029a | 393 | ¤t, &chan) == NUM_HOSTAPD_MODES) |
394 | return 0; | |
395 | ||
396 | /* | |
397 | * Need 3 bytes for EID, length, and current operating class, plus | |
398 | * 1 byte for every other supported operating class. | |
399 | */ | |
400 | buf = wpabuf_alloc(global_op_class_size + 3); | |
401 | if (!buf) | |
402 | return 0; | |
403 | ||
404 | wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); | |
405 | /* Will set the length later, putting a placeholder */ | |
406 | ie_len = wpabuf_put(buf, 1); | |
407 | wpabuf_put_u8(buf, current); | |
408 | ||
409 | for (op = 0; global_op_class[op].op_class; op++) { | |
cb828507 | 410 | if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op])) |
065c029a | 411 | wpabuf_put_u8(buf, global_op_class[op].op_class); |
412 | } | |
413 | ||
414 | *ie_len = wpabuf_len(buf) - 2; | |
415 | if (*ie_len < 2 || wpabuf_len(buf) > len) { | |
416 | wpa_printf(MSG_ERROR, | |
417 | "Failed to add supported operating classes IE"); | |
418 | res = 0; | |
419 | } else { | |
420 | os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); | |
421 | res = wpabuf_len(buf); | |
422 | wpa_hexdump_buf(MSG_DEBUG, | |
423 | "Added supported operating classes IE", buf); | |
424 | } | |
425 | ||
426 | wpabuf_free(buf); | |
427 | return res; | |
428 | } | |
8f8c423a JM |
429 | |
430 | ||
431 | int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s) | |
432 | { | |
433 | int op; | |
434 | unsigned int pos, max_num = 0; | |
435 | int *classes; | |
436 | ||
437 | for (op = 0; global_op_class[op].op_class; op++) | |
438 | max_num++; | |
439 | classes = os_zalloc((max_num + 1) * sizeof(int)); | |
440 | if (!classes) | |
441 | return NULL; | |
442 | ||
443 | for (op = 0, pos = 0; global_op_class[op].op_class; op++) { | |
444 | if (wpas_op_class_supported(wpa_s, NULL, &global_op_class[op])) | |
445 | classes[pos++] = global_op_class[op].op_class; | |
446 | } | |
447 | ||
448 | return classes; | |
449 | } |