blob: b15ac9e3247324d1ec0498b75b457f1a13aec9c4 [file] [log] [blame]
Dmitry Shmidt29333592017-01-09 12:27:11 -08001/*
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"
17
18
Hai Shalomc3565922019-10-28 11:58:20 -070019static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode,
20 u8 op_class, u8 chan,
Dmitry Shmidt29333592017-01-09 12:27:11 -080021 unsigned int *flags)
22{
23 int i;
Hai Shalomc3565922019-10-28 11:58:20 -070024 int is_6ghz = op_class >= 131 && op_class <= 135;
Dmitry Shmidt29333592017-01-09 12:27:11 -080025
26 for (i = 0; i < mode->num_channels; i++) {
Hai Shalomc3565922019-10-28 11:58:20 -070027 int chan_is_6ghz;
28
29 chan_is_6ghz = mode->channels[i].freq > 5940 &&
30 mode->channels[i].freq <= 7105;
31 if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
Dmitry Shmidt29333592017-01-09 12:27:11 -080032 break;
33 }
34
35 if (i == mode->num_channels ||
36 (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
37 return NOT_ALLOWED;
38
39 if (flags)
40 *flags = mode->channels[i].flag;
41
42 if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
43 return NO_IR;
44
45 return ALLOWED;
46}
47
48
49static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel)
50{
51 u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
52 size_t i;
53
54 if (mode->mode != HOSTAPD_MODE_IEEE80211A)
55 return 0;
56
57 for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
58 /*
59 * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
60 * so the center channel is 6 channels away from the start/end.
61 */
62 if (channel >= center_channels[i] - 6 &&
63 channel <= center_channels[i] + 6)
64 return center_channels[i];
65 }
66
67 return 0;
68}
69
70
Hai Shalomc3565922019-10-28 11:58:20 -070071static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode,
72 u8 op_class, u8 channel)
Dmitry Shmidt29333592017-01-09 12:27:11 -080073{
74 u8 center_chan;
75 unsigned int i;
76 unsigned int no_ir = 0;
77
78 center_chan = get_center_80mhz(mode, channel);
79 if (!center_chan)
80 return NOT_ALLOWED;
81
82 /* check all the channels are available */
83 for (i = 0; i < 4; i++) {
84 unsigned int flags;
85 u8 adj_chan = center_chan - 6 + i * 4;
86
Hai Shalomc3565922019-10-28 11:58:20 -070087 if (allow_channel(mode, op_class, adj_chan, &flags) ==
88 NOT_ALLOWED)
Dmitry Shmidt29333592017-01-09 12:27:11 -080089 return NOT_ALLOWED;
90
91 if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
92 (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
93 (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
94 (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
95 return NOT_ALLOWED;
96
97 if (flags & HOSTAPD_CHAN_NO_IR)
98 no_ir = 1;
99 }
100
101 if (no_ir)
102 return NO_IR;
103
104 return ALLOWED;
105}
106
107
108static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel)
109{
110 u8 center_channels[] = { 50, 114 };
111 unsigned int i;
112
113 if (mode->mode != HOSTAPD_MODE_IEEE80211A)
114 return 0;
115
116 for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
117 /*
118 * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
119 * so the center channel is 14 channels away from the start/end.
120 */
121 if (channel >= center_channels[i] - 14 &&
122 channel <= center_channels[i] + 14)
123 return center_channels[i];
124 }
125
126 return 0;
127}
128
129
130static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
Hai Shalomc3565922019-10-28 11:58:20 -0700131 u8 op_class, u8 channel)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800132{
133 u8 center_chan;
134 unsigned int i;
135 unsigned int no_ir = 0;
136
137 center_chan = get_center_160mhz(mode, channel);
138 if (!center_chan)
139 return NOT_ALLOWED;
140
141 /* Check all the channels are available */
142 for (i = 0; i < 8; i++) {
143 unsigned int flags;
144 u8 adj_chan = center_chan - 14 + i * 4;
145
Hai Shalomc3565922019-10-28 11:58:20 -0700146 if (allow_channel(mode, op_class, adj_chan, &flags) ==
147 NOT_ALLOWED)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800148 return NOT_ALLOWED;
149
150 if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
151 (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
152 (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
153 (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
154 (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
155 (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
156 (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
157 (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
158 return NOT_ALLOWED;
159
160 if (flags & HOSTAPD_CHAN_NO_IR)
161 no_ir = 1;
162 }
163
164 if (no_ir)
165 return NO_IR;
166
167 return ALLOWED;
168}
169
170
Hai Shalomc3565922019-10-28 11:58:20 -0700171enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
172 u8 channel, u8 bw)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800173{
174 unsigned int flag = 0;
175 enum chan_allowed res, res2;
176
Hai Shalomc3565922019-10-28 11:58:20 -0700177 res2 = res = allow_channel(mode, op_class, channel, &flag);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800178 if (bw == BW40MINUS) {
179 if (!(flag & HOSTAPD_CHAN_HT40MINUS))
180 return NOT_ALLOWED;
Hai Shalomc3565922019-10-28 11:58:20 -0700181 res2 = allow_channel(mode, op_class, channel - 4, NULL);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800182 } else if (bw == BW40PLUS) {
183 if (!(flag & HOSTAPD_CHAN_HT40PLUS))
184 return NOT_ALLOWED;
Hai Shalomc3565922019-10-28 11:58:20 -0700185 res2 = allow_channel(mode, op_class, channel + 4, NULL);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800186 } else if (bw == BW80) {
187 /*
188 * channel is a center channel and as such, not necessarily a
189 * valid 20 MHz channels. Override earlier allow_channel()
190 * result and use only the 80 MHz specific version.
191 */
Hai Shalomc3565922019-10-28 11:58:20 -0700192 res2 = res = verify_80mhz(mode, op_class, channel);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800193 } else if (bw == BW160) {
194 /*
195 * channel is a center channel and as such, not necessarily a
196 * valid 20 MHz channels. Override earlier allow_channel()
197 * result and use only the 160 MHz specific version.
198 */
Hai Shalomc3565922019-10-28 11:58:20 -0700199 res2 = res = verify_160mhz(mode, op_class, channel);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800200 } else if (bw == BW80P80) {
201 /*
202 * channel is a center channel and as such, not necessarily a
203 * valid 20 MHz channels. Override earlier allow_channel()
204 * result and use only the 80 MHz specific version.
205 */
Hai Shalomc3565922019-10-28 11:58:20 -0700206 res2 = res = verify_80mhz(mode, op_class, channel);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800207 }
208
209 if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
210 return NOT_ALLOWED;
211
212 if (res == NO_IR || res2 == NO_IR)
213 return NO_IR;
214
215 return ALLOWED;
216}
217
218
219static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
Hai Shalom74f70d42019-02-11 14:42:39 -0800220 struct wpa_ssid *ssid,
Dmitry Shmidt29333592017-01-09 12:27:11 -0800221 const struct oper_class_map *op_class)
222{
223 int chan;
224 size_t i;
225 struct hostapd_hw_modes *mode;
226 int found;
Hai Shalom74f70d42019-02-11 14:42:39 -0800227 int z;
228 int freq2 = 0;
229 int freq5 = 0;
Dmitry Shmidt29333592017-01-09 12:27:11 -0800230
231 mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode);
232 if (!mode)
233 return 0;
234
Hai Shalom74f70d42019-02-11 14:42:39 -0800235 /* If we are configured to disable certain things, take that into
236 * account here. */
Hai Shalomc3565922019-10-28 11:58:20 -0700237 if (ssid && ssid->freq_list && ssid->freq_list[0]) {
Hai Shalom74f70d42019-02-11 14:42:39 -0800238 for (z = 0; ; z++) {
239 int f = ssid->freq_list[z];
240
241 if (f == 0)
242 break; /* end of list */
243 if (f > 4000 && f < 6000)
244 freq5 = 1;
245 else if (f > 2400 && f < 2500)
246 freq2 = 1;
247 }
248 } else {
249 /* No frequencies specified, can use anything hardware supports.
250 */
251 freq2 = freq5 = 1;
252 }
253
254 if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5)
255 return 0;
256 if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2)
257 return 0;
258
259#ifdef CONFIG_HT_OVERRIDES
Hai Shalomc3565922019-10-28 11:58:20 -0700260 if (ssid && ssid->disable_ht) {
Hai Shalom74f70d42019-02-11 14:42:39 -0800261 switch (op_class->op_class) {
262 case 83:
263 case 84:
264 case 104:
265 case 105:
266 case 116:
267 case 117:
268 case 119:
269 case 120:
270 case 122:
271 case 123:
272 case 126:
273 case 127:
274 case 128:
275 case 129:
276 case 130:
277 /* Disable >= 40 MHz channels if HT is disabled */
278 return 0;
279 }
280 }
281#endif /* CONFIG_HT_OVERRIDES */
282
283#ifdef CONFIG_VHT_OVERRIDES
Hai Shalomc3565922019-10-28 11:58:20 -0700284 if (ssid && ssid->disable_vht) {
Hai Shalom74f70d42019-02-11 14:42:39 -0800285 if (op_class->op_class >= 128 && op_class->op_class <= 130) {
286 /* Disable >= 80 MHz channels if VHT is disabled */
287 return 0;
288 }
289 }
290#endif /* CONFIG_VHT_OVERRIDES */
291
Dmitry Shmidt29333592017-01-09 12:27:11 -0800292 if (op_class->op_class == 128) {
293 u8 channels[] = { 42, 58, 106, 122, 138, 155 };
294
295 for (i = 0; i < ARRAY_SIZE(channels); i++) {
Hai Shalomc3565922019-10-28 11:58:20 -0700296 if (verify_channel(mode, op_class->op_class,
297 channels[i], op_class->bw) !=
Dmitry Shmidt29333592017-01-09 12:27:11 -0800298 NOT_ALLOWED)
299 return 1;
300 }
301
302 return 0;
303 }
304
305 if (op_class->op_class == 129) {
306 /* Check if either 160 MHz channels is allowed */
Hai Shalomc3565922019-10-28 11:58:20 -0700307 return verify_channel(mode, op_class->op_class, 50,
308 op_class->bw) != NOT_ALLOWED ||
309 verify_channel(mode, op_class->op_class, 114,
310 op_class->bw) != NOT_ALLOWED;
Dmitry Shmidt29333592017-01-09 12:27:11 -0800311 }
312
313 if (op_class->op_class == 130) {
314 /* Need at least two non-contiguous 80 MHz segments */
315 found = 0;
316
Hai Shalomc3565922019-10-28 11:58:20 -0700317 if (verify_channel(mode, op_class->op_class, 42,
318 op_class->bw) != NOT_ALLOWED ||
319 verify_channel(mode, op_class->op_class, 58,
320 op_class->bw) != NOT_ALLOWED)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800321 found++;
Hai Shalomc3565922019-10-28 11:58:20 -0700322 if (verify_channel(mode, op_class->op_class, 106,
323 op_class->bw) != NOT_ALLOWED ||
324 verify_channel(mode, op_class->op_class, 122,
325 op_class->bw) != NOT_ALLOWED ||
326 verify_channel(mode, op_class->op_class, 138,
327 op_class->bw) != NOT_ALLOWED)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800328 found++;
Hai Shalomc3565922019-10-28 11:58:20 -0700329 if (verify_channel(mode, op_class->op_class, 106,
330 op_class->bw) != NOT_ALLOWED &&
331 verify_channel(mode, op_class->op_class, 138,
332 op_class->bw) != NOT_ALLOWED)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800333 found++;
Hai Shalomc3565922019-10-28 11:58:20 -0700334 if (verify_channel(mode, op_class->op_class, 155,
335 op_class->bw) != NOT_ALLOWED)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800336 found++;
337
338 if (found >= 2)
339 return 1;
340
341 return 0;
342 }
343
344 found = 0;
345 for (chan = op_class->min_chan; chan <= op_class->max_chan;
346 chan += op_class->inc) {
Hai Shalomc3565922019-10-28 11:58:20 -0700347 if (verify_channel(mode, op_class->op_class, chan,
348 op_class->bw) != NOT_ALLOWED) {
Dmitry Shmidt29333592017-01-09 12:27:11 -0800349 found = 1;
350 break;
351 }
352 }
353
354 return found;
355}
356
357
Hai Shalom74f70d42019-02-11 14:42:39 -0800358size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s,
359 struct wpa_ssid *ssid,
360 int freq, u8 *pos, size_t len)
Dmitry Shmidt29333592017-01-09 12:27:11 -0800361{
362 struct wpabuf *buf;
363 u8 op, current, chan;
364 u8 *ie_len;
365 size_t res;
366
367 /*
368 * Assume 20 MHz channel for now.
369 * TODO: Use the secondary channel and VHT channel width that will be
370 * used after association.
371 */
Hai Shalom81f62d82019-07-22 12:10:00 -0700372 if (ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT,
Dmitry Shmidt29333592017-01-09 12:27:11 -0800373 &current, &chan) == NUM_HOSTAPD_MODES)
374 return 0;
375
376 /*
377 * Need 3 bytes for EID, length, and current operating class, plus
378 * 1 byte for every other supported operating class.
379 */
380 buf = wpabuf_alloc(global_op_class_size + 3);
381 if (!buf)
382 return 0;
383
384 wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
385 /* Will set the length later, putting a placeholder */
386 ie_len = wpabuf_put(buf, 1);
387 wpabuf_put_u8(buf, current);
388
389 for (op = 0; global_op_class[op].op_class; op++) {
Hai Shalom74f70d42019-02-11 14:42:39 -0800390 if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op]))
Dmitry Shmidt29333592017-01-09 12:27:11 -0800391 wpabuf_put_u8(buf, global_op_class[op].op_class);
392 }
393
394 *ie_len = wpabuf_len(buf) - 2;
395 if (*ie_len < 2 || wpabuf_len(buf) > len) {
396 wpa_printf(MSG_ERROR,
397 "Failed to add supported operating classes IE");
398 res = 0;
399 } else {
400 os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
401 res = wpabuf_len(buf);
402 wpa_hexdump_buf(MSG_DEBUG,
403 "Added supported operating classes IE", buf);
404 }
405
406 wpabuf_free(buf);
407 return res;
408}
Hai Shalomc3565922019-10-28 11:58:20 -0700409
410
411int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s)
412{
413 int op;
414 unsigned int pos, max_num = 0;
415 int *classes;
416
417 for (op = 0; global_op_class[op].op_class; op++)
418 max_num++;
419 classes = os_zalloc((max_num + 1) * sizeof(int));
420 if (!classes)
421 return NULL;
422
423 for (op = 0, pos = 0; global_op_class[op].op_class; op++) {
424 if (wpas_op_class_supported(wpa_s, NULL, &global_op_class[op]))
425 classes[pos++] = global_op_class[op].op_class;
426 }
427
428 return classes;
429}