hostapd: add support for authenticating with multiple PSKs via ubus helper
[openwrt/staging/nbd.git] / package / network / services / hostapd / patches / 802-hostapd-ap-add-AFC-client-support.patch
1 From: Lorenzo Bianconi <lorenzo@kernel.org>
2 Date: Wed, 17 Jan 2024 15:25:08 +0100
3 Subject: [PATCH] hostapd: ap: add AFC client support
4
5 Introduce Automated Frequency Coordination (AFC) support for UNII-5 and
6 UNII-7 6GHz bands.
7 AFC client will connect to AFCD providing AP related parameter for AFC
8 coordinator (e.g. geolocation, supported frequencies, ..).
9 AFC is required for Standard Power Devices (SPDs) to determine a lists
10 of channels and EIRP/PSD powers that are available in the 6GHz spectrum.
11 AFC hostapd client is tested with AFC DUT Test Harness [0].
12
13 [0] https://github.com/Wi-FiTestSuite/AFC-DUT/tree/main
14
15 Tested-by: Allen Ye <allen.ye@mediatek.com>
16 ---
17 create mode 100644 src/ap/afc.c
18
19 --- a/hostapd/Makefile
20 +++ b/hostapd/Makefile
21 @@ -108,6 +108,14 @@ CFLAGS += -DCONFIG_TAXONOMY
22 OBJS += ../src/ap/taxonomy.o
23 endif
24
25 +ifdef CONFIG_IEEE80211AX
26 +ifdef CONFIG_AFC
27 +CFLAGS += -DCONFIG_AFC
28 +OBJS += ../src/ap/afc.o
29 +LIBS += -ljson-c
30 +endif
31 +endif
32 +
33 ifdef CONFIG_MODULE_TESTS
34 CFLAGS += -DCONFIG_MODULE_TESTS
35 OBJS += hapd_module_tests.o
36 --- a/hostapd/config_file.c
37 +++ b/hostapd/config_file.c
38 @@ -1283,6 +1283,190 @@ static int hostapd_parse_he_srg_bitmap(u
39 return 0;
40 }
41
42 +
43 +#ifdef CONFIG_AFC
44 +static int hostapd_afc_parse_cert_ids(struct hostapd_config *conf, char *pos)
45 +{
46 + struct cert_id *c = NULL;
47 + int i, count = 0;
48 +
49 + while (pos && pos[0]) {
50 + char *p;
51 +
52 + c = os_realloc_array(c, count + 1, sizeof(*c));
53 + if (!c)
54 + return -ENOMEM;
55 +
56 + i = count;
57 + count++;
58 +
59 + p = os_strchr(pos, ':');
60 + if (!p)
61 + goto error;
62 +
63 + *p++ = '\0';
64 + if (!p || !p[0])
65 + goto error;
66 +
67 + c[i].rulset = os_malloc(os_strlen(pos) + 1);
68 + if (!c[i].rulset)
69 + goto error;
70 +
71 + os_strlcpy(c[i].rulset, pos, os_strlen(pos) + 1);
72 + pos = p;
73 + p = os_strchr(pos, ',');
74 + if (p)
75 + *p++ = '\0';
76 +
77 + c[i].id = os_malloc(os_strlen(pos) + 1);
78 + if (!c[i].id)
79 + goto error;
80 +
81 + os_strlcpy(c[i].id, pos, os_strlen(pos) + 1);
82 + pos = p;
83 + }
84 +
85 + conf->afc.n_cert_ids = count;
86 + conf->afc.cert_ids = c;
87 +
88 + return 0;
89 +
90 +error:
91 + for (i = 0; i < count; i++) {
92 + os_free(c[i].rulset);
93 + os_free(c[i].id);
94 + }
95 + os_free(c);
96 +
97 + return -ENOMEM;
98 +}
99 +
100 +
101 +static int hostapd_afc_parse_position_data(struct afc_linear_polygon **data,
102 + unsigned int *n_linear_polygon_data,
103 + char *pos)
104 +{
105 + struct afc_linear_polygon *d = NULL;
106 + int i, count = 0;
107 +
108 + while (pos && pos[0]) {
109 + char *p, *end;
110 +
111 + d = os_realloc_array(d, count + 1, sizeof(*d));
112 + if (!d)
113 + return -ENOMEM;
114 +
115 + i = count;
116 + count++;
117 +
118 + p = os_strchr(pos, ':');
119 + if (!p)
120 + goto error;
121 +
122 + *p++ = '\0';
123 + if (!p || !p[0])
124 + goto error;
125 +
126 + d[i].longitude = strtod(pos, &end);
127 + if (*end)
128 + goto error;
129 +
130 + pos = p;
131 + p = os_strchr(pos, ',');
132 + if (p)
133 + *p++ = '\0';
134 +
135 + d[i].latitude = strtod(pos, &end);
136 + if (*end)
137 + goto error;
138 +
139 + pos = p;
140 + }
141 +
142 + *n_linear_polygon_data = count;
143 + *data = d;
144 +
145 + return 0;
146 +
147 +error:
148 + os_free(d);
149 + return -ENOMEM;
150 +}
151 +
152 +
153 +static int hostapd_afc_parse_freq_range(struct hostapd_config *conf, char *pos)
154 +{
155 + struct afc_freq_range *f = NULL;
156 + int i, count = 0;
157 +
158 + while (pos && pos[0]) {
159 + char *p;
160 +
161 + f = os_realloc_array(f, count + 1, sizeof(*f));
162 + if (!f)
163 + return -ENOMEM;
164 +
165 + i = count;
166 + count++;
167 +
168 + p = os_strchr(pos, ':');
169 + if (!p)
170 + goto error;
171 +
172 + *p++ = '\0';
173 + if (!p || !p[0])
174 + goto error;
175 +
176 + f[i].low_freq = atoi(pos);
177 + pos = p;
178 + p = os_strchr(pos, ',');
179 + if (p)
180 + *p++ = '\0';
181 +
182 + f[i].high_freq = atoi(pos);
183 + pos = p;
184 + }
185 +
186 + conf->afc.n_freq_range = count;
187 + conf->afc.freq_range = f;
188 +
189 + return 0;
190 +
191 +error:
192 + os_free(f);
193 + return -ENOMEM;
194 +}
195 +
196 +
197 +static int hostapd_afc_parse_op_class(struct hostapd_config *conf, char *pos)
198 +{
199 + unsigned int *oc = NULL;
200 + int i, count = 0;
201 +
202 + while (pos && pos[0]) {
203 + char *p;
204 +
205 + oc = os_realloc_array(oc, count + 1, sizeof(*oc));
206 + if (!oc)
207 + return -ENOMEM;
208 +
209 + i = count;
210 + count++;
211 +
212 + p = os_strchr(pos, ',');
213 + if (p)
214 + *p++ = '\0';
215 +
216 + oc[i] = atoi(pos);
217 + pos = p;
218 + }
219 +
220 + conf->afc.n_op_class = count;
221 + conf->afc.op_class = oc;
222 +
223 + return 0;
224 +}
225 +#endif /* CONFIG_AFC */
226 #endif /* CONFIG_IEEE80211AX */
227
228
229 @@ -3888,6 +4072,83 @@ static int hostapd_config_fill(struct ho
230 return 1;
231 }
232 bss->unsol_bcast_probe_resp_interval = val;
233 +#ifdef CONFIG_AFC
234 + } else if (os_strcmp(buf, "afcd_sock") == 0) {
235 + conf->afc.socket = os_malloc(os_strlen(pos) + 1);
236 + if (!conf->afc.socket)
237 + return 1;
238 +
239 + os_strlcpy(conf->afc.socket, pos, os_strlen(pos) + 1);
240 + } else if (os_strcmp(buf, "afc_request_version") == 0) {
241 + conf->afc.request.version = os_malloc(os_strlen(pos) + 1);
242 + if (!conf->afc.request.version)
243 + return 1;
244 +
245 + os_strlcpy(conf->afc.request.version, pos, os_strlen(pos) + 1);
246 + } else if (os_strcmp(buf, "afc_request_id") == 0) {
247 + conf->afc.request.id = os_malloc(os_strlen(pos) + 1);
248 + if (!conf->afc.request.id)
249 + return 1;
250 +
251 + os_strlcpy(conf->afc.request.id, pos, os_strlen(pos) + 1);
252 + } else if (os_strcmp(buf, "afc_serial_number") == 0) {
253 + conf->afc.request.sn = os_malloc(os_strlen(pos) + 1);
254 + if (!conf->afc.request.sn)
255 + return 1;
256 +
257 + os_strlcpy(conf->afc.request.sn, pos, os_strlen(pos) + 1);
258 + } else if (os_strcmp(buf, "afc_cert_ids") == 0) {
259 + if (hostapd_afc_parse_cert_ids(conf, pos))
260 + return 1;
261 + } else if (os_strcmp(buf, "afc_location_type") == 0) {
262 + conf->afc.location.type = atoi(pos);
263 + if (conf->afc.location.type != ELLIPSE &&
264 + conf->afc.location.type != LINEAR_POLYGON &&
265 + conf->afc.location.type != RADIAL_POLYGON)
266 + return 1;
267 + } else if (os_strcmp(buf, "afc_linear_polygon") == 0) {
268 + if (hostapd_afc_parse_position_data(
269 + &conf->afc.location.linear_polygon_data,
270 + &conf->afc.location.n_linear_polygon_data,
271 + pos))
272 + return 1;
273 + } else if (os_strcmp(buf, "afc_radial_polygon") == 0) {
274 + if (hostapd_afc_parse_position_data(
275 + (struct afc_linear_polygon **)
276 + &conf->afc.location.radial_polygon_data,
277 + &conf->afc.location.n_radial_polygon_data,
278 + pos))
279 + return 1;
280 + } else if (os_strcmp(buf, "afc_major_axis") == 0) {
281 + conf->afc.location.major_axis = atoi(pos);
282 + } else if (os_strcmp(buf, "afc_minor_axis") == 0) {
283 + conf->afc.location.minor_axis = atoi(pos);
284 + } else if (os_strcmp(buf, "afc_orientation") == 0) {
285 + conf->afc.location.orientation = atoi(pos);
286 + } else if (os_strcmp(buf, "afc_height") == 0) {
287 + char *end;
288 +
289 + conf->afc.location.height = strtod(pos, &end);
290 + if (*end)
291 + return 1;
292 + } else if (os_strcmp(buf, "afc_height_type") == 0) {
293 + conf->afc.location.height_type = os_malloc(os_strlen(pos) + 1);
294 + if (!conf->afc.location.height_type)
295 + return 1;
296 +
297 + os_strlcpy(conf->afc.location.height_type, pos,
298 + os_strlen(pos) + 1);
299 + } else if (os_strcmp(buf, "afc_vertical_tolerance") == 0) {
300 + conf->afc.location.vertical_tolerance = atoi(pos);
301 + } else if (os_strcmp(buf, "afc_min_power") == 0) {
302 + conf->afc.min_power = atoi(pos);
303 + } else if (os_strcmp(buf, "afc_freq_range") == 0) {
304 + if (hostapd_afc_parse_freq_range(conf, pos))
305 + return 1;
306 + } else if (os_strcmp(buf, "afc_op_class") == 0) {
307 + if (hostapd_afc_parse_op_class(conf, pos))
308 + return 1;
309 +#endif /* CONFIG_AFC */
310 } else if (os_strcmp(buf, "mbssid") == 0) {
311 int mbssid = atoi(pos);
312 if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) {
313 --- a/hostapd/defconfig
314 +++ b/hostapd/defconfig
315 @@ -438,3 +438,6 @@ CONFIG_DPP2=y
316
317 # Wi-Fi Aware unsynchronized service discovery (NAN USD)
318 #CONFIG_NAN_USD=y
319 +
320 +# Enable Automated Frequency Coordination for 6GHz outdoor
321 +#CONFIG_AFC=y
322 --- a/hostapd/hostapd.conf
323 +++ b/hostapd/hostapd.conf
324 @@ -1005,6 +1005,48 @@ wmm_ac_vo_acm=0
325 # Valid range: 0..20 TUs; default is 0 (disabled)
326 #unsol_bcast_probe_resp_interval=0
327
328 +##### Automated Frequency Coordination for 6GHz UNII-5 and UNII-7 bands #######
329 +
330 +# AFC daemon connection socket
331 +#afcd_sock=/var/run/afcd.sock
332 +
333 +# AFC request identification parameters
334 +#afc_request_version=1.1
335 +#afc_request_id=11235813
336 +#afc_serial_number=abcdefg
337 +#afc_cert_ids=US_47_CFR_PART_15_SUBPART_E:CID000
338 +#
339 +# AFC location type:
340 +# 0 = ellipse
341 +# 1 = linear polygon
342 +# 2 = radial polygon
343 +#afc_location_type=0
344 +#
345 +# AFC ellipse or linear polygon coordinations
346 +#afc_linear_polygon=-122.984157:37.425056
347 +#
348 +# AFC radial polygon coordinations
349 +#afc_radial_polygon=118.8:92.76,76.44:87.456,98.56:123.33
350 +#
351 +# AFC ellipse major/minor axis and orientation
352 +#afc_major_axis=100
353 +#afc_minor_axis=50
354 +#afc_orientation=70
355 +#
356 +# AFC device elevation parameters
357 +#afc_height=3.0
358 +#afc_height_type=AGL
359 +#afc_vertical_tolerance=7
360 +#
361 +# AFC minimum desired TX power (dbm)
362 +#afc_min_power=24
363 +#
364 +# AFC request frequency ranges
365 +#afc_freq_range=5925:6425,6525:6875
366 +#
367 +# AFC request operation classes
368 +#afc_op_class=131,132,133,134,136
369 +
370 ##### IEEE 802.11be related configuration #####################################
371
372 #ieee80211be: Whether IEEE 802.11be (EHT) is enabled
373 --- /dev/null
374 +++ b/src/ap/afc.c
375 @@ -0,0 +1,979 @@
376 +/*
377 + * Automated Frequency Coordination
378 + * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
379 + *
380 + * This software may be distributed under the terms of the BSD license.
381 + * See README for more details.
382 + */
383 +
384 +#include <json-c/json.h>
385 +#include <sys/un.h>
386 +#include <time.h>
387 +
388 +#include "utils/includes.h"
389 +#include "utils/common.h"
390 +#include "utils/eloop.h"
391 +#include "hostapd.h"
392 +#include "acs.h"
393 +#include "hw_features.h"
394 +
395 +#define HOSTAPD_AFC_RETRY_TIMEOUT 180
396 +#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */
397 +#define HOSTAPD_AFC_BUFSIZE 4096
398 +
399 +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
400 +
401 +
402 +static struct json_object *
403 +hostapd_afc_build_location_request(struct hostapd_iface *iface)
404 +{
405 + struct json_object *location_obj, *center_obj, *ellipse_obj;
406 + struct json_object *elevation_obj, *str_obj;
407 + struct hostapd_config *iconf = iface->conf;
408 + bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
409 +
410 + location_obj = json_object_new_object();
411 + if (!location_obj)
412 + return NULL;
413 +
414 + if (iconf->afc.location.type != LINEAR_POLYGON) {
415 + struct afc_linear_polygon *lp =
416 + &iconf->afc.location.linear_polygon_data[0];
417 +
418 + ellipse_obj = json_object_new_object();
419 + if (!ellipse_obj)
420 + goto error;
421 +
422 + center_obj = json_object_new_object();
423 + if (!center_obj)
424 + goto error;
425 +
426 + json_object_object_add(ellipse_obj, "center", center_obj);
427 +
428 + str_obj = json_object_new_double(lp->longitude);
429 + if (!str_obj)
430 + goto error;
431 +
432 + json_object_object_add(center_obj, "longitude", str_obj);
433 + str_obj = json_object_new_double(lp->latitude);
434 + if (!str_obj)
435 + goto error;
436 +
437 + json_object_object_add(center_obj, "latitude", str_obj);
438 +
439 + }
440 +
441 + switch (iconf->afc.location.type) {
442 + case LINEAR_POLYGON: {
443 + struct json_object *outer_boundary_obj;
444 + int i;
445 +
446 + outer_boundary_obj = json_object_new_object();
447 + if (!outer_boundary_obj)
448 + goto error;
449 +
450 + json_object_object_add(location_obj, "linearPolygon",
451 + outer_boundary_obj);
452 + ellipse_obj = json_object_new_array();
453 + if (!ellipse_obj)
454 + goto error;
455 +
456 + json_object_object_add(outer_boundary_obj, "outerBoundary",
457 + ellipse_obj);
458 + for (i = 0;
459 + i < iconf->afc.location.n_linear_polygon_data; i++) {
460 + struct afc_linear_polygon *lp =
461 + &iconf->afc.location.linear_polygon_data[i];
462 +
463 + center_obj = json_object_new_object();
464 + if (!center_obj)
465 + goto error;
466 +
467 + json_object_array_add(ellipse_obj, center_obj);
468 + str_obj = json_object_new_double(lp->longitude);
469 + if (!str_obj)
470 + goto error;
471 +
472 + json_object_object_add(center_obj, "longitude",
473 + str_obj);
474 + str_obj = json_object_new_double(lp->latitude);
475 + if (!str_obj)
476 + goto error;
477 +
478 + json_object_object_add(center_obj, "latitude",
479 + str_obj);
480 + }
481 + break;
482 + }
483 + case RADIAL_POLYGON: {
484 + struct json_object *outer_boundary_obj;
485 + int i;
486 +
487 + json_object_object_add(location_obj, "radialPolygon",
488 + ellipse_obj);
489 +
490 + outer_boundary_obj = json_object_new_array();
491 + if (!outer_boundary_obj)
492 + goto error;
493 +
494 + json_object_object_add(ellipse_obj, "outerBoundary",
495 + outer_boundary_obj);
496 + for (i = 0;
497 + i < iconf->afc.location.n_radial_polygon_data; i++) {
498 + struct afc_radial_polygon *rp =
499 + &iconf->afc.location.radial_polygon_data[i];
500 + struct json_object *angle_obj;
501 +
502 + angle_obj = json_object_new_object();
503 + if (!angle_obj)
504 + goto error;
505 +
506 + json_object_array_add(outer_boundary_obj, angle_obj);
507 +
508 + str_obj = json_object_new_double(rp->angle);
509 + if (!str_obj)
510 + goto error;
511 +
512 + json_object_object_add(angle_obj, "angle", str_obj);
513 + str_obj = json_object_new_double(rp->length);
514 + if (!str_obj)
515 + goto error;
516 +
517 + json_object_object_add(angle_obj, "length", str_obj);
518 + }
519 + break;
520 + }
521 + case ELLIPSE:
522 + default:
523 + json_object_object_add(location_obj, "ellipse", ellipse_obj);
524 +
525 + str_obj = json_object_new_int(iconf->afc.location.major_axis);
526 + if (!str_obj)
527 + goto error;
528 +
529 + json_object_object_add(ellipse_obj, "majorAxis", str_obj);
530 + str_obj = json_object_new_int(iconf->afc.location.minor_axis);
531 + if (!str_obj)
532 + goto error;
533 +
534 + json_object_object_add(ellipse_obj, "minorAxis", str_obj);
535 + str_obj = json_object_new_int(iconf->afc.location.orientation);
536 + if (!str_obj)
537 + goto error;
538 +
539 + json_object_object_add(ellipse_obj, "orientation", str_obj);
540 + break;
541 + }
542 +
543 + elevation_obj = json_object_new_object();
544 + if (!elevation_obj)
545 + goto error;
546 +
547 + json_object_object_add(location_obj, "elevation",
548 + elevation_obj);
549 + str_obj = json_object_new_double(iconf->afc.location.height);
550 + if (!str_obj)
551 + goto error;
552 +
553 + json_object_object_add(elevation_obj, "height", str_obj);
554 + str_obj = json_object_new_string(iconf->afc.location.height_type);
555 + if (!str_obj)
556 + goto error;
557 +
558 + json_object_object_add(elevation_obj, "heightType", str_obj);
559 + str_obj = json_object_new_int(iconf->afc.location.vertical_tolerance);
560 + if (!str_obj)
561 + goto error;
562 +
563 + json_object_object_add(elevation_obj, "verticalUncertainty",
564 + str_obj);
565 + str_obj = json_object_new_int(is_ap_indoor);
566 + if (!str_obj)
567 + goto error;
568 +
569 + json_object_object_add(location_obj, "indoorDeployment", str_obj);
570 +
571 + return location_obj;
572 +
573 +error:
574 + json_object_put(location_obj);
575 + return NULL;
576 +}
577 +
578 +
579 +static struct json_object * hostapd_afc_get_opclass_chan_list(u8 op_class)
580 +{
581 + struct json_object *chan_list_obj, *str_obj;
582 + const struct oper_class_map *oper_class;
583 + int chan_offset, chan;
584 +
585 + oper_class = get_oper_class(NULL, op_class);
586 + if (!oper_class)
587 + return NULL;
588 +
589 + chan_list_obj = json_object_new_array();
590 + if (!chan_list_obj)
591 + return NULL;
592 +
593 + switch (op_class) {
594 + case 132: /* 40MHz */
595 + chan_offset = 2;
596 + break;
597 + case 133: /* 80MHz */
598 + chan_offset = 6;
599 + break;
600 + case 134: /* 160MHz */
601 + chan_offset = 14;
602 + break;
603 + default:
604 + chan_offset = 0;
605 + break;
606 + }
607 +
608 + for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
609 + chan += oper_class->inc) {
610 + str_obj = json_object_new_int(chan + chan_offset);
611 + if (!str_obj) {
612 + json_object_put(chan_list_obj);
613 + return NULL;
614 + }
615 + json_object_array_add(chan_list_obj, str_obj);
616 + }
617 +
618 + return chan_list_obj;
619 +}
620 +
621 +
622 +static struct json_object *
623 +hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
624 +{
625 + struct json_object *op_class_list_obj, *str_obj;
626 + struct hostapd_config *iconf = iface->conf;
627 + int i;
628 +
629 + op_class_list_obj = json_object_new_array();
630 + if (!op_class_list_obj)
631 + return NULL;
632 +
633 + for (i = 0; i < iconf->afc.n_op_class; i++) {
634 + struct json_object *op_class_obj, *chan_list_obj;
635 + u8 op_class = iconf->afc.op_class[i];
636 +
637 + if (!is_6ghz_op_class(op_class))
638 + continue;
639 +
640 + op_class_obj = json_object_new_object();
641 + if (!op_class_obj)
642 + goto error;
643 +
644 + json_object_array_add(op_class_list_obj, op_class_obj);
645 + str_obj = json_object_new_int(op_class);
646 + if (!str_obj)
647 + goto error;
648 +
649 + json_object_object_add(op_class_obj, "globalOperatingClass",
650 + str_obj);
651 +
652 + chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
653 + if (!chan_list_obj)
654 + goto error;
655 +
656 + json_object_object_add(op_class_obj, "channelCfi",
657 + chan_list_obj);
658 + }
659 +
660 + return op_class_list_obj;
661 +
662 +error:
663 + json_object_put(op_class_list_obj);
664 + return NULL;
665 +}
666 +
667 +
668 +static struct json_object *
669 +hostapd_afc_build_request(struct hostapd_iface *iface)
670 +{
671 + struct json_object *l1_obj, *l2_obj, *la1_obj, *la2_obj;
672 + struct json_object *s2_obj, *str_obj, *location_obj;
673 + struct hostapd_config *iconf = iface->conf;
674 + struct json_object *op_class_list_obj;
675 + int i;
676 +
677 + l1_obj = json_object_new_object();
678 + if (!l1_obj)
679 + return NULL;
680 +
681 + if (iconf->afc.request.version) {
682 + str_obj = json_object_new_string(iconf->afc.request.version);
683 + if (!str_obj)
684 + goto error;
685 +
686 + json_object_object_add(l1_obj, "version", str_obj);
687 + }
688 +
689 + la1_obj = json_object_new_array();
690 + if (!la1_obj)
691 + goto error;
692 +
693 + json_object_object_add(l1_obj, "availableSpectrumInquiryRequests",
694 + la1_obj);
695 + l2_obj = json_object_new_object();
696 + if (!l2_obj)
697 + goto error;
698 +
699 + json_object_array_add(la1_obj, l2_obj);
700 + if (iconf->afc.request.id) {
701 + str_obj = json_object_new_string(iconf->afc.request.id);
702 + if (!str_obj)
703 + goto error;
704 +
705 + json_object_object_add(l2_obj, "requestId", str_obj);
706 + }
707 +
708 + s2_obj = json_object_new_object();
709 + if (!s2_obj)
710 + goto error;
711 +
712 + json_object_object_add(l2_obj, "deviceDescriptor", s2_obj);
713 + if (iconf->afc.request.sn) {
714 + str_obj = json_object_new_string(iconf->afc.request.sn);
715 + if (!str_obj)
716 + goto error;
717 +
718 + json_object_object_add(s2_obj, "serialNumber", str_obj);
719 + }
720 +
721 + la2_obj = json_object_new_array();
722 + if (!la2_obj)
723 + goto error;
724 +
725 + json_object_object_add(s2_obj, "certificationId", la2_obj);
726 + for (i = 0; i < iconf->afc.n_cert_ids; i++) {
727 + struct json_object *obj;
728 +
729 + obj = json_object_new_object();
730 + if (!obj)
731 + goto error;
732 +
733 + json_object_array_add(la2_obj, obj);
734 + str_obj =
735 + json_object_new_string(iconf->afc.cert_ids[i].rulset);
736 + if (!str_obj)
737 + goto error;
738 +
739 + json_object_object_add(obj, "rulesetId", str_obj);
740 + str_obj = json_object_new_string(iconf->afc.cert_ids[i].id);
741 + if (!str_obj)
742 + goto error;
743 +
744 + json_object_object_add(obj, "id", str_obj);
745 + }
746 +
747 + location_obj = hostapd_afc_build_location_request(iface);
748 + if (!location_obj)
749 + goto error;
750 +
751 + json_object_object_add(l2_obj, "location", location_obj);
752 + str_obj = json_object_new_int(iconf->afc.min_power);
753 + if (!str_obj)
754 + goto error;
755 +
756 + json_object_object_add(l2_obj, "minDesiredPower", str_obj);
757 +
758 + if (iconf->afc.n_freq_range) {
759 + struct json_object *freq_obj;
760 +
761 + freq_obj = json_object_new_array();
762 + if (!freq_obj)
763 + goto error;
764 +
765 + json_object_object_add(l2_obj, "inquiredFrequencyRange",
766 + freq_obj);
767 + for (i = 0; i < iconf->afc.n_freq_range; i++) {
768 + struct afc_freq_range *fr = &iconf->afc.freq_range[i];
769 + struct json_object *obj;
770 +
771 + obj = json_object_new_object();
772 + if (!obj)
773 + goto error;
774 +
775 + json_object_array_add(freq_obj, obj);
776 + str_obj = json_object_new_int(fr->low_freq);
777 + if (!str_obj)
778 + goto error;
779 +
780 + json_object_object_add(obj, "lowFrequency", str_obj);
781 + str_obj = json_object_new_int(fr->high_freq);
782 + if (!str_obj)
783 + goto error;
784 +
785 + json_object_object_add(obj, "highFrequency", str_obj);
786 + }
787 + }
788 +
789 + op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
790 + if (!op_class_list_obj)
791 + goto error;
792 +
793 + json_object_object_add(l2_obj, "inquiredChannels", op_class_list_obj);
794 +
795 + wpa_printf(MSG_DEBUG, "Pending AFC request: %s",
796 + json_object_get_string(l1_obj));
797 +
798 + return l1_obj;
799 +
800 +error:
801 + json_object_put(l1_obj);
802 +
803 + return NULL;
804 +}
805 +
806 +
807 +static int
808 +hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
809 + struct json_object *reply_elem_obj)
810 +{
811 + struct afc_freq_range_elem *f = NULL;
812 + struct json_object *obj;
813 + int i, count = 0;
814 +
815 + if (!json_object_object_get_ex(reply_elem_obj,
816 + "availableFrequencyInfo", &obj))
817 + return 0;
818 +
819 + for (i = 0; i < json_object_array_length(obj); i++) {
820 + struct json_object *range_elem_obj, *freq_range_obj;
821 + struct json_object *high_freq_obj, *low_freq_obj;
822 + struct json_object *max_psd_obj;
823 +
824 + range_elem_obj = json_object_array_get_idx(obj, i);
825 + if (!range_elem_obj)
826 + continue;
827 +
828 + if (!json_object_object_get_ex(range_elem_obj,
829 + "frequencyRange",
830 + &freq_range_obj))
831 + continue;
832 +
833 + if (!json_object_object_get_ex(freq_range_obj,
834 + "lowFrequency",
835 + &low_freq_obj))
836 + continue;
837 +
838 + if (!json_object_object_get_ex(freq_range_obj,
839 + "highFrequency",
840 + &high_freq_obj))
841 + continue;
842 +
843 + if (!json_object_object_get_ex(range_elem_obj, "maxPsd",
844 + &max_psd_obj) &&
845 + !json_object_object_get_ex(range_elem_obj, "maxPSD",
846 + &max_psd_obj))
847 + continue;
848 +
849 + f = os_realloc_array(f, count + 1, sizeof(*f));
850 + if (!f)
851 + return -ENOMEM;
852 +
853 + f[count].low_freq = json_object_get_int(low_freq_obj);
854 + f[count].high_freq = json_object_get_int(high_freq_obj);
855 + f[count++].max_psd = json_object_get_int(max_psd_obj);
856 + }
857 + iface->afc.freq_range = f;
858 + iface->afc.num_freq_range = count;
859 +
860 + return 0;
861 +}
862 +
863 +
864 +static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
865 + int *chan_list_size, u8 op_class,
866 + int center_chan, int power)
867 +{
868 + int num_low_subchan, ch, count = *chan_list_size;
869 + struct afc_chan_info_elem *c = *chan_list;
870 +
871 + switch (op_class) {
872 + case 132: /* 40MHz */
873 + num_low_subchan = 2;
874 + break;
875 + case 133: /* 80MHz */
876 + num_low_subchan = 6;
877 + break;
878 + case 134: /* 160MHz */
879 + num_low_subchan = 14;
880 + break;
881 + default:
882 + num_low_subchan = 0;
883 + break;
884 + }
885 +
886 + for (ch = center_chan - num_low_subchan;
887 + ch <= center_chan + num_low_subchan; ch += 4) {
888 + int i;
889 +
890 + for (i = 0; i < count; i++) {
891 + if (c[i].chan == ch)
892 + break;
893 + }
894 +
895 + if (i == count) {
896 + c = os_realloc_array(c, count + 1, sizeof(*c));
897 + if (!c)
898 + return -ENOMEM;
899 +
900 + c[count].chan = ch;
901 + c[count++].power = power;
902 + }
903 + }
904 +
905 + *chan_list_size = count;
906 + *chan_list = c;
907 +
908 + return 0;
909 +}
910 +
911 +
912 +static int
913 +hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
914 + struct json_object *reply_elem_obj)
915 +{
916 + struct afc_chan_info_elem *c = NULL;
917 + struct json_object *obj;
918 + int i, count = 0;
919 +
920 + if (!json_object_object_get_ex(reply_elem_obj,
921 + "availableChannelInfo", &obj))
922 + return 0;
923 +
924 + for (i = 0; i < json_object_array_length(obj); i++) {
925 + struct json_object *range_elem_obj, *op_class_obj;
926 + struct json_object *chan_cfi_obj, *max_eirp_obj;
927 + int ch, op_class;
928 +
929 + range_elem_obj = json_object_array_get_idx(obj, i);
930 + if (!range_elem_obj)
931 + continue;
932 +
933 + if (!json_object_object_get_ex(range_elem_obj,
934 + "globalOperatingClass",
935 + &op_class_obj))
936 + continue;
937 +
938 + if (!json_object_object_get_ex(range_elem_obj, "maxEirp",
939 + &max_eirp_obj))
940 + continue;
941 +
942 + if (!json_object_object_get_ex(range_elem_obj, "channelCfi",
943 + &chan_cfi_obj))
944 + continue;
945 +
946 + op_class = json_object_get_int(op_class_obj);
947 + for (ch = 0;
948 + ch < json_object_array_length(chan_cfi_obj); ch++) {
949 + struct json_object *pwr_obj;
950 + struct json_object *ch_obj;
951 + int channel, power;
952 +
953 + ch_obj = json_object_array_get_idx(chan_cfi_obj, ch);
954 + if (!ch_obj)
955 + continue;
956 +
957 + pwr_obj = json_object_array_get_idx(max_eirp_obj, ch);
958 + if (!pwr_obj)
959 + continue;
960 +
961 + channel = json_object_get_int(ch_obj);
962 + power = json_object_get_int(pwr_obj);
963 +
964 + hostad_afc_update_chan_info(&c, &count, op_class,
965 + channel, power);
966 + }
967 + iface->afc.chan_info_list = c;
968 + iface->afc.num_chan_info = count;
969 + }
970 +
971 + return 0;
972 +}
973 +
974 +
975 +static int hostad_afc_get_timeout(struct json_object *obj)
976 +{
977 + time_t t, now;
978 + struct tm tm;
979 +
980 + if (sscanf(json_object_get_string(obj), "%d-%d-%dT%d:%d:%dZ",
981 + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
982 + &tm.tm_min, &tm.tm_sec) <= 0)
983 + return HOSTAPD_AFC_TIMEOUT;
984 +
985 + tm.tm_year -= 1900;
986 + tm.tm_mon -= 1;
987 + tm.tm_isdst = -1;
988 + t = mktime(&tm);
989 + time(&now);
990 +
991 + return now > t ? HOSTAPD_AFC_RETRY_TIMEOUT : (t - now) * 80 / 100;
992 +}
993 +
994 +
995 +static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
996 +{
997 + struct json_object *payload_obj, *reply_obj, *version_obj;
998 + struct hostapd_config *iconf = iface->conf;
999 + int i, request_timeout = -1, ret = -EINVAL;
1000 +
1001 + wpa_printf(MSG_DEBUG, "Received AFC reply: %s", reply);
1002 + payload_obj = json_tokener_parse(reply);
1003 + if (!payload_obj)
1004 + return -EINVAL;
1005 +
1006 + if (!json_object_object_get_ex(payload_obj, "version", &version_obj))
1007 + return -EINVAL;
1008 +
1009 + if (iconf->afc.request.version &&
1010 + os_strcmp(iconf->afc.request.version,
1011 + json_object_get_string(version_obj)))
1012 + return -EINVAL;
1013 +
1014 + if (!json_object_object_get_ex(payload_obj,
1015 + "availableSpectrumInquiryResponses",
1016 + &reply_obj))
1017 + return -EINVAL;
1018 +
1019 + for (i = 0; i < json_object_array_length(reply_obj); i++) {
1020 + struct json_object *reply_elem_obj, *obj, *status_obj;
1021 + int j, status = -EINVAL;
1022 +
1023 + reply_elem_obj = json_object_array_get_idx(reply_obj, i);
1024 + if (!reply_elem_obj)
1025 + continue;
1026 +
1027 + if (!json_object_object_get_ex(reply_elem_obj, "requestId",
1028 + &obj))
1029 + continue;
1030 +
1031 + if (iconf->afc.request.id &&
1032 + os_strcmp(iconf->afc.request.id,
1033 + json_object_get_string(obj)))
1034 + continue;
1035 +
1036 + if (!json_object_object_get_ex(reply_elem_obj, "rulesetId",
1037 + &obj))
1038 + continue;
1039 +
1040 + for (j = 0; j < iconf->afc.n_cert_ids; j++) {
1041 + if (!os_strcmp(iconf->afc.cert_ids[j].rulset,
1042 + json_object_get_string(obj)))
1043 + break;
1044 + }
1045 +
1046 + if (j == iconf->afc.n_cert_ids)
1047 + continue;
1048 +
1049 + if (!json_object_object_get_ex(reply_elem_obj, "response",
1050 + &obj))
1051 + continue;
1052 +
1053 + if (json_object_object_get_ex(obj, "shortDescription",
1054 + &status_obj))
1055 + wpa_printf(MSG_DEBUG, "AFC reply element %d: %s",
1056 + i, json_object_get_string(status_obj));
1057 +
1058 + if (json_object_object_get_ex(obj, "responseCode",
1059 + &status_obj))
1060 + status = json_object_get_int(status_obj);
1061 +
1062 + if (status < 0)
1063 + continue;
1064 +
1065 + if (hostad_afc_parse_available_freq_info(iface,
1066 + reply_elem_obj) ||
1067 + hostad_afc_parse_available_chan_info(iface,
1068 + reply_elem_obj))
1069 + continue;
1070 +
1071 + if (json_object_object_get_ex(reply_elem_obj,
1072 + "availabilityExpireTime",
1073 + &obj)) {
1074 + int timeout = hostad_afc_get_timeout(obj);
1075 +
1076 + if (request_timeout < 0 || timeout < request_timeout)
1077 + request_timeout = timeout;
1078 + }
1079 +
1080 + ret = status;
1081 + }
1082 +
1083 + iface->afc.data_valid = true;
1084 + iface->afc.timeout = request_timeout;
1085 + if (iface->afc.timeout < 0)
1086 + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
1087 +
1088 + return ret;
1089 +}
1090 +
1091 +
1092 +static int hostapd_afc_send_receive(struct hostapd_iface *iface)
1093 +{
1094 + struct hostapd_config *iconf = iface->conf;
1095 + json_object *request_obj = NULL;
1096 + struct timeval sock_timeout = {
1097 + .tv_sec = 5,
1098 + };
1099 + struct sockaddr_un addr = {
1100 + .sun_family = AF_UNIX,
1101 +#ifdef __FreeBSD__
1102 + .sun_len = sizeof(addr),
1103 +#endif /* __FreeBSD__ */
1104 + };
1105 + char buf[HOSTAPD_AFC_BUFSIZE] = {};
1106 + const char *request;
1107 + int sockfd, ret;
1108 + fd_set read_set;
1109 +
1110 + if (iface->afc.data_valid) {
1111 + /* AFC data already downloaded from the server */
1112 + return 0;
1113 + }
1114 +
1115 + if (!iconf->afc.socket) {
1116 + wpa_printf(MSG_ERROR, "Missing AFC socket string");
1117 + return -EINVAL;
1118 + }
1119 +
1120 + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
1121 + if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
1122 + wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
1123 + iconf->afc.socket);
1124 + return -EINVAL;
1125 + }
1126 +
1127 + os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
1128 + sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
1129 + if (sockfd < 0) {
1130 + wpa_printf(MSG_ERROR, "Failed creating AFC socket");
1131 + return sockfd;
1132 + }
1133 +
1134 + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
1135 + wpa_printf(MSG_ERROR, "Failed connecting AFC socket");
1136 + ret = -EIO;
1137 + goto close_sock;
1138 + }
1139 +
1140 + request_obj = hostapd_afc_build_request(iface);
1141 + if (!request_obj) {
1142 + ret = -ENOMEM;
1143 + goto close_sock;
1144 + }
1145 +
1146 + request = json_object_to_json_string(request_obj);
1147 + if (send(sockfd, request, strlen(request), 0) < 0) {
1148 + wpa_printf(MSG_ERROR, "Failed sending AFC request");
1149 + ret = -EIO;
1150 + goto close_sock;
1151 + }
1152 +
1153 + FD_ZERO(&read_set);
1154 + FD_SET(sockfd, &read_set);
1155 + if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
1156 + wpa_printf(MSG_ERROR, "Select failed on AFC socket");
1157 + ret = -errno;
1158 + goto close_sock;
1159 + }
1160 +
1161 + if (!FD_ISSET(sockfd, &read_set)) {
1162 + ret = -EIO;
1163 + goto close_sock;
1164 + }
1165 +
1166 + ret = recv(sockfd, buf, sizeof(buf) - 1, 0);
1167 + if (ret <= 0)
1168 + goto close_sock;
1169 +
1170 + ret = hostapd_afc_parse_reply(iface, buf);
1171 +close_sock:
1172 + json_object_put(request_obj);
1173 + close(sockfd);
1174 +
1175 + return ret;
1176 +}
1177 +
1178 +
1179 +static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
1180 +{
1181 + const struct oper_class_map *oper_class;
1182 + int ch;
1183 +
1184 + oper_class = get_oper_class(NULL, iface->conf->op_class);
1185 + if (!oper_class)
1186 + return false;
1187 +
1188 + for (ch = oper_class->min_chan; ch <= oper_class->max_chan;
1189 + ch += oper_class->inc) {
1190 + struct hostapd_hw_modes *mode = iface->current_mode;
1191 + int i;
1192 +
1193 + for (i = 0; i < mode->num_channels; i++) {
1194 + struct hostapd_channel_data *chan = &mode->channels[i];
1195 +
1196 + if (chan->chan == ch &&
1197 + !(chan->flag & HOSTAPD_CHAN_DISABLED))
1198 + return true;
1199 + }
1200 + }
1201 +
1202 + return false;
1203 +}
1204 +
1205 +
1206 +int hostapd_afc_handle_request(struct hostapd_iface *iface)
1207 +{
1208 + struct hostapd_config *iconf = iface->conf;
1209 + int ret;
1210 +
1211 + /* AFC is required just for standard power AP */
1212 + if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
1213 + return 1;
1214 +
1215 + if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
1216 + return 1;
1217 +
1218 + if (iface->state == HAPD_IFACE_ACS)
1219 + return 1;
1220 +
1221 + ret = hostapd_afc_send_receive(iface);
1222 + if (ret < 0) {
1223 + /*
1224 + * If the connection to the AFCD failed, resched for a
1225 + * future attempt.
1226 + */
1227 + wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
1228 + if (ret == -EIO)
1229 + ret = 0;
1230 + goto resched;
1231 + }
1232 +
1233 + hostap_afc_disable_channels(iface);
1234 + if (!hostapd_afc_has_usable_chans(iface))
1235 + goto resched;
1236 +
1237 + /* Trigger an ACS freq scan */
1238 + iconf->channel = 0;
1239 + iface->freq = 0;
1240 +
1241 + if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
1242 + wpa_printf(MSG_ERROR, "Could not start ACS");
1243 + ret = -EINVAL;
1244 + }
1245 +
1246 +resched:
1247 + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
1248 + eloop_register_timeout(iface->afc.timeout, 0,
1249 + hostapd_afc_timeout_handler, iface, NULL);
1250 +
1251 + return ret;
1252 +}
1253 +
1254 +
1255 +static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
1256 +{
1257 + os_free(iface->afc.chan_info_list);
1258 + os_free(iface->afc.freq_range);
1259 +
1260 + iface->afc.num_freq_range = 0;
1261 + iface->afc.num_chan_info = 0;
1262 +
1263 + iface->afc.chan_info_list = NULL;
1264 + iface->afc.freq_range = NULL;
1265 +
1266 + iface->afc.data_valid = false;
1267 +}
1268 +
1269 +
1270 +static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
1271 +{
1272 + struct hostapd_iface *iface = eloop_ctx;
1273 + bool restart_iface = true;
1274 +
1275 + hostapd_afc_delete_data_from_server(iface);
1276 + if (iface->state != HAPD_IFACE_ENABLED) {
1277 + /* Hostapd is not fully enabled yet, toogle the interface */
1278 + goto restart_interface;
1279 + }
1280 +
1281 + if (hostapd_afc_send_receive(iface) < 0 ||
1282 + hostapd_get_hw_features(iface)) {
1283 + restart_iface = false;
1284 + goto restart_interface;
1285 + }
1286 +
1287 + if (hostapd_is_usable_chans(iface))
1288 + goto resched;
1289 +
1290 + restart_iface = hostapd_afc_has_usable_chans(iface);
1291 + if (restart_iface) {
1292 + /* Trigger an ACS freq scan */
1293 + iface->conf->channel = 0;
1294 + iface->freq = 0;
1295 + }
1296 +
1297 +restart_interface:
1298 + hostapd_disable_iface(iface);
1299 + if (restart_iface)
1300 + hostapd_enable_iface(iface);
1301 +resched:
1302 + eloop_register_timeout(iface->afc.timeout, 0,
1303 + hostapd_afc_timeout_handler, iface, NULL);
1304 +}
1305 +
1306 +
1307 +void hostapd_afc_stop(struct hostapd_iface *iface)
1308 +{
1309 + eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
1310 +}
1311 +
1312 +
1313 +void hostap_afc_disable_channels(struct hostapd_iface *iface)
1314 +{
1315 + struct hostapd_hw_modes *mode;
1316 + int i;
1317 +
1318 + if (iface->num_hw_features < HOSTAPD_MODE_IEEE80211A)
1319 + return;
1320 +
1321 + if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type))
1322 + return;
1323 +
1324 + if (!iface->afc.data_valid)
1325 + return;
1326 +
1327 + mode = &iface->hw_features[HOSTAPD_MODE_IEEE80211A];
1328 + for (i = 0; i < mode->num_channels; i++) {
1329 + struct hostapd_channel_data *chan = &mode->channels[i];
1330 + int j;
1331 +
1332 + if (!is_6ghz_freq(chan->freq))
1333 + continue;
1334 +
1335 + for (j = 0; j < iface->afc.num_freq_range; j++) {
1336 + if (chan->freq >= iface->afc.freq_range[j].low_freq &&
1337 + chan->freq <= iface->afc.freq_range[j].high_freq)
1338 + break;
1339 + }
1340 +
1341 + if (j != iface->afc.num_freq_range)
1342 + continue;
1343 +
1344 + for (j = 0; j < iface->afc.num_chan_info; j++) {
1345 + if (chan->chan == iface->afc.chan_info_list[j].chan)
1346 + break;
1347 + }
1348 +
1349 + if (j != iface->afc.num_chan_info)
1350 + continue;
1351 +
1352 + chan->flag |= HOSTAPD_CHAN_DISABLED;
1353 + }
1354 +}
1355 --- a/src/ap/ap_config.c
1356 +++ b/src/ap/ap_config.c
1357 @@ -1033,6 +1033,22 @@ void hostapd_config_free(struct hostapd_
1358 #endif /* CONFIG_ACS */
1359 wpabuf_free(conf->lci);
1360 wpabuf_free(conf->civic);
1361 +#ifdef CONFIG_AFC
1362 + os_free(conf->afc.socket);
1363 + os_free(conf->afc.request.version);
1364 + os_free(conf->afc.request.id);
1365 + os_free(conf->afc.request.sn);
1366 + for (i = 0; i < conf->afc.n_cert_ids; i++) {
1367 + os_free(conf->afc.cert_ids[i].rulset);
1368 + os_free(conf->afc.cert_ids[i].id);
1369 + }
1370 + os_free(conf->afc.cert_ids);
1371 + os_free(conf->afc.location.height_type);
1372 + os_free(conf->afc.location.linear_polygon_data);
1373 + os_free(conf->afc.location.radial_polygon_data);
1374 + os_free(conf->afc.freq_range);
1375 + os_free(conf->afc.op_class);
1376 +#endif /* CONFIG_AFC */
1377
1378 os_free(conf);
1379 }
1380 --- a/src/ap/ap_config.h
1381 +++ b/src/ap/ap_config.h
1382 @@ -1225,6 +1225,53 @@ struct hostapd_config {
1383 MBSSID_ENABLED = 1,
1384 ENHANCED_MBSSID_ENABLED = 2,
1385 } mbssid;
1386 +
1387 +#ifdef CONFIG_AFC
1388 + struct {
1389 + char *socket;
1390 + struct {
1391 + char *version;
1392 + char *id;
1393 + char *sn;
1394 + } request;
1395 + unsigned int n_cert_ids;
1396 + struct cert_id {
1397 + char *rulset;
1398 + char *id;
1399 + } *cert_ids;
1400 + struct {
1401 + enum afc_location_type {
1402 + ELLIPSE,
1403 + LINEAR_POLYGON,
1404 + RADIAL_POLYGON,
1405 + } type;
1406 + unsigned int n_linear_polygon_data;
1407 + struct afc_linear_polygon {
1408 + double longitude;
1409 + double latitude;
1410 + } *linear_polygon_data;
1411 + unsigned int n_radial_polygon_data;
1412 + struct afc_radial_polygon {
1413 + double length;
1414 + double angle;
1415 + } *radial_polygon_data;
1416 + int major_axis;
1417 + int minor_axis;
1418 + int orientation;
1419 + double height;
1420 + char *height_type;
1421 + int vertical_tolerance;
1422 + } location;
1423 + unsigned int n_freq_range;
1424 + struct afc_freq_range {
1425 + int low_freq;
1426 + int high_freq;
1427 + } *freq_range;
1428 + unsigned int n_op_class;
1429 + unsigned int *op_class;
1430 + int min_power;
1431 + } afc;
1432 +#endif /* CONFIG_AFC */
1433 };
1434
1435
1436 --- a/src/ap/hostapd.c
1437 +++ b/src/ap/hostapd.c
1438 @@ -683,6 +683,7 @@ static void sta_track_deinit(struct host
1439 void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
1440 {
1441 wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
1442 + hostapd_afc_stop(iface);
1443 eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
1444 #ifdef NEED_AP_MLME
1445 hostapd_stop_setup_timers(iface);
1446 @@ -2449,6 +2450,16 @@ static int hostapd_setup_interface_compl
1447 }
1448 #endif /* CONFIG_MESH */
1449
1450 +#ifdef CONFIG_IEEE80211AX
1451 + /* check AFC for 6GHz channels. */
1452 + res = hostapd_afc_handle_request(iface);
1453 + if (res <= 0) {
1454 + if (res < 0)
1455 + goto fail;
1456 + return res;
1457 + }
1458 +#endif /* CONFIG_IEEE80211AX */
1459 +
1460 if (!delay_apply_cfg &&
1461 hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
1462 hapd->iconf->channel,
1463 @@ -2846,6 +2857,7 @@ void hostapd_interface_deinit(struct hos
1464
1465 hostapd_set_state(iface, HAPD_IFACE_DISABLED);
1466
1467 + hostapd_afc_stop(iface);
1468 eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
1469 iface->wait_channel_update = 0;
1470 iface->is_no_ir = false;
1471 @@ -2881,6 +2893,10 @@ void hostapd_interface_free(struct hosta
1472 __func__, iface->bss[j]);
1473 os_free(iface->bss[j]);
1474 }
1475 +#ifdef CONFIG_AFC
1476 + os_free(iface->afc.chan_info_list);
1477 + os_free(iface->afc.freq_range);
1478 +#endif
1479 hostapd_cleanup_iface(iface);
1480 }
1481
1482 --- a/src/ap/hostapd.h
1483 +++ b/src/ap/hostapd.h
1484 @@ -702,9 +702,54 @@ struct hostapd_iface {
1485
1486 /* Configured freq of interface is NO_IR */
1487 bool is_no_ir;
1488 +
1489 +#ifdef CONFIG_AFC
1490 + struct {
1491 + int timeout;
1492 + unsigned int num_freq_range;
1493 + struct afc_freq_range_elem {
1494 + int low_freq;
1495 + int high_freq;
1496 + /**
1497 + * max eirp power spectral density received from
1498 + * the AFC coordinator for this band
1499 + */
1500 + int max_psd;
1501 + } *freq_range;
1502 + unsigned int num_chan_info;
1503 + struct afc_chan_info_elem {
1504 + int chan;
1505 + /**
1506 + * max eirp power received from the AFC coordinator
1507 + * for this channel
1508 + */
1509 + int power;
1510 + } *chan_info_list;
1511 + bool data_valid;
1512 + } afc;
1513 +#endif /* CONFIG_AFC */
1514 };
1515
1516 /* hostapd.c */
1517 +#ifdef CONFIG_AFC
1518 +int hostapd_afc_handle_request(struct hostapd_iface *iface);
1519 +void hostapd_afc_stop(struct hostapd_iface *iface);
1520 +void hostap_afc_disable_channels(struct hostapd_iface *iface);
1521 +#else
1522 +static inline int hostapd_afc_handle_request(struct hostapd_iface *iface)
1523 +{
1524 + return 1;
1525 +}
1526 +
1527 +static inline void hostapd_afc_stop(struct hostapd_iface *iface)
1528 +{
1529 +}
1530 +
1531 +static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
1532 +{
1533 +}
1534 +#endif /* CONFIG_AFC */
1535 +
1536 int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
1537 int (*cb)(struct hostapd_iface *iface,
1538 void *ctx), void *ctx);
1539 --- a/src/ap/hw_features.c
1540 +++ b/src/ap/hw_features.c
1541 @@ -114,6 +114,8 @@ int hostapd_get_hw_features(struct hosta
1542 iface->hw_features = modes;
1543 iface->num_hw_features = num_modes;
1544
1545 + hostap_afc_disable_channels(iface);
1546 +
1547 for (i = 0; i < num_modes; i++) {
1548 struct hostapd_hw_modes *feature = &modes[i];
1549 int dfs_enabled = hapd->iconf->ieee80211h &&