1 /*
2 * Automated Frequency Coordination
3 * Copyright (c) 2024, Lorenzo Bianconi <lorenzo@kernel.org>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "utils/includes.h"
10 #include <sys/un.h>
11 #include <time.h>
12
13 #include "utils/common.h"
14 #include "utils/eloop.h"
15 #include "utils/json.h"
16 #include "common/hw_features_common.h"
17 #include "common/wpa_ctrl.h"
18 #include "hostapd.h"
19 #include "acs.h"
20 #include "hw_features.h"
21
22 #define HOSTAPD_AFC_RETRY_TIMEOUT 180
23 #define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */
24 #define HOSTAPD_AFC_BUFSIZE 8192
25
26 static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx);
27
28
hostapd_afc_reset_channels(struct hostapd_iface * iface)29 static int hostapd_afc_reset_channels(struct hostapd_iface *iface)
30 {
31 int ret;
32
33 ret = hostapd_get_hw_features(iface);
34 if (ret) {
35 wpa_printf(MSG_ERROR, "AFC: Failed to reset channel flags");
36 return ret;
37 }
38
39 ret = hostapd_set_current_hw_info(iface, iface->freq);
40 if (ret)
41 wpa_printf(MSG_ERROR,
42 "AFC: Failed to set current hardware info");
43
44 return ret;
45 }
46
47
48 static struct wpabuf *
hostapd_afc_build_location_request(struct hostapd_iface * iface)49 hostapd_afc_build_location_request(struct hostapd_iface *iface)
50 {
51 struct wpabuf *location_obj, *ellipse_obj = NULL;
52 struct hostapd_config *iconf = iface->conf;
53 bool is_ap_indoor = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
54 size_t location_len = 1024, ellipse_len = 128;
55 unsigned int i;
56
57 location_obj = wpabuf_alloc(location_len);
58 if (!location_obj)
59 return NULL;
60
61 json_start_object(location_obj, "location");
62 if (iconf->afc.location.type != AFC_LINEAR_POLYGON) {
63 struct afc_linear_polygon *lp =
64 iconf->afc.location.linear_polygon_data;
65
66 if (!lp)
67 goto error;
68
69 ellipse_obj = wpabuf_alloc(ellipse_len);
70 if (!ellipse_obj)
71 goto error;
72
73 json_start_object(ellipse_obj, "center");
74
75 json_add_double(ellipse_obj, "longitude", lp->longitude);
76 json_value_sep(ellipse_obj);
77
78 json_add_double(ellipse_obj, "latitude", lp->latitude);
79 json_end_object(ellipse_obj);
80 json_value_sep(ellipse_obj);
81 }
82
83 switch (iconf->afc.location.type) {
84 case AFC_LINEAR_POLYGON:
85 json_start_object(location_obj, "linearPolygon");
86 json_start_array(location_obj, "outerBoundary");
87 for (i = 0; i < iconf->afc.location.n_linear_polygon_data; i++)
88 {
89 struct afc_linear_polygon *lp =
90 &iconf->afc.location.linear_polygon_data[i];
91
92 json_start_object(location_obj, NULL);
93
94 json_add_double(location_obj, "longitude",
95 lp->longitude);
96 json_value_sep(location_obj);
97
98 json_add_double(location_obj, "latitude",
99 lp->latitude);
100
101 json_end_object(location_obj);
102 if (i < iconf->afc.location.n_linear_polygon_data - 1)
103 json_value_sep(location_obj);
104 }
105 json_end_array(location_obj);
106
107 /* linearPolygon */
108 json_end_object(location_obj);
109 json_value_sep(location_obj);
110 break;
111 case AFC_RADIAL_POLYGON:
112 json_start_object(location_obj, "radialPolygon");
113 wpabuf_put_buf(location_obj, ellipse_obj);
114
115 json_start_array(location_obj, "outerBoundary");
116 for (i = 0; i < iconf->afc.location.n_radial_polygon_data; i++)
117 {
118 struct afc_radial_polygon *rp =
119 &iconf->afc.location.radial_polygon_data[i];
120
121 json_start_object(location_obj, NULL);
122 json_add_double(location_obj, "length", rp->length);
123 json_value_sep(location_obj);
124
125 json_add_double(location_obj, "angle", rp->angle);
126 json_end_object(location_obj);
127 if (i < iconf->afc.location.n_radial_polygon_data - 1)
128 json_value_sep(location_obj);
129 }
130 json_end_array(location_obj);
131
132 /* radialPolygon */
133 json_end_object(location_obj);
134 json_value_sep(location_obj);
135 break;
136 case AFC_ELLIPSE:
137 default:
138 json_start_object(location_obj, "ellipse");
139 wpabuf_put_buf(location_obj, ellipse_obj);
140
141 json_add_int(location_obj, "majorAxis",
142 iconf->afc.location.major_axis);
143 json_value_sep(location_obj);
144 json_add_int(location_obj, "minorAxis",
145 iconf->afc.location.minor_axis);
146 json_value_sep(location_obj);
147 json_add_int(location_obj, "orientation",
148 iconf->afc.location.orientation);
149 /* ellipse */
150 json_end_object(location_obj);
151 json_value_sep(location_obj);
152 break;
153 }
154
155 json_start_object(location_obj, "elevation");
156
157 json_add_double(location_obj, "height", iconf->afc.location.height);
158 json_value_sep(location_obj);
159 if (iconf->afc.location.height_type) {
160 json_add_string(location_obj, "heightType",
161 iconf->afc.location.height_type);
162 json_value_sep(location_obj);
163 }
164 json_add_int(location_obj, "verticalUncertainty",
165 iconf->afc.location.vertical_tolerance);
166
167 /* elevation */
168 json_end_object(location_obj);
169 json_value_sep(location_obj);
170
171 json_add_int(location_obj, "indoorDeployment", is_ap_indoor);
172
173 /* location */
174 json_end_object(location_obj);
175 json_value_sep(location_obj);
176
177 wpabuf_free(ellipse_obj);
178 return location_obj;
179
180 error:
181 wpabuf_free(location_obj);
182 wpabuf_free(ellipse_obj);
183
184 return NULL;
185 }
186
187
hostapd_afc_get_opclass_chan_list(u8 op_class)188 static struct wpabuf * hostapd_afc_get_opclass_chan_list(u8 op_class)
189 {
190 struct wpabuf *chan_list_obj;
191 const struct oper_class_map *oper_class;
192 int chan_offset, chan;
193 size_t ch_len = 512;
194
195 oper_class = get_oper_class(NULL, op_class);
196 if (!oper_class)
197 return NULL;
198
199 chan_list_obj = wpabuf_alloc(ch_len);
200 if (!chan_list_obj)
201 return NULL;
202 json_start_array(chan_list_obj, "channelCfi");
203
204 switch (op_class) {
205 case 132: /* 40 MHz */
206 chan_offset = 2;
207 break;
208 case 133: /* 80 MHz */
209 chan_offset = 6;
210 break;
211 case 134: /* 160 MHz */
212 chan_offset = 14;
213 break;
214 case 137: /* 320 MHz */
215 /*
216 * global_op_class already use the central channels for
217 * 320 MHz, so fallthrough and use 0 for chan_offset.
218 */
219 default:
220 chan_offset = 0;
221 break;
222 }
223
224 for (chan = oper_class->min_chan; chan <= oper_class->max_chan;
225 chan += oper_class->inc) {
226 char ch_str[32];
227
228 if (chan + chan_offset > oper_class->max_chan)
229 break;
230
231 os_snprintf(ch_str, sizeof(ch_str), "%s%d",
232 chan != oper_class->min_chan ? ", " : "",
233 chan + chan_offset);
234 wpabuf_put_str(chan_list_obj, ch_str);
235 }
236 json_end_array(chan_list_obj);
237
238 return chan_list_obj;
239 }
240
241
242 static struct wpabuf *
hostapd_afc_build_req_chan_list(struct hostapd_iface * iface)243 hostapd_afc_build_req_chan_list(struct hostapd_iface *iface)
244 {
245 struct wpabuf *op_class_list_obj;
246 struct hostapd_config *iconf = iface->conf;
247 unsigned int i, num;
248
249 op_class_list_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE);
250 if (!op_class_list_obj)
251 return NULL;
252
253 json_start_array(op_class_list_obj, "inquiredChannels");
254 num = int_array_len(iconf->afc.op_class);
255 for (i = 0; i < num; i++) {
256 struct wpabuf *chan_list_obj = NULL;
257 u8 op_class = iconf->afc.op_class[i];
258
259 if (!is_6ghz_op_class(op_class))
260 continue;
261
262 json_start_object(op_class_list_obj, NULL);
263
264 json_add_int(op_class_list_obj, "globalOperatingClass",
265 op_class);
266 json_value_sep(op_class_list_obj);
267
268 chan_list_obj = hostapd_afc_get_opclass_chan_list(op_class);
269 if (!chan_list_obj)
270 goto error;
271
272 wpabuf_put_buf(op_class_list_obj, chan_list_obj);
273 json_end_object(op_class_list_obj);
274
275 if (i < num - 1)
276 json_value_sep(op_class_list_obj);
277
278 wpabuf_free(chan_list_obj);
279 chan_list_obj = NULL;
280 }
281 json_end_array(op_class_list_obj);
282
283 return op_class_list_obj;
284
285 error:
286 wpabuf_free(op_class_list_obj);
287 return NULL;
288 }
289
290
291 static struct wpabuf *
hostapd_afc_build_request(struct hostapd_iface * iface)292 hostapd_afc_build_request(struct hostapd_iface *iface)
293 {
294 struct wpabuf *req_obj, *location_obj, *op_class_list_obj = NULL;
295 struct hostapd_config *iconf = iface->conf;
296 unsigned int i;
297 char request_id_str[16];
298
299 req_obj = wpabuf_alloc(HOSTAPD_AFC_BUFSIZE);
300 if (!req_obj)
301 return NULL;
302
303 json_start_object(req_obj, NULL);
304
305 if (iconf->afc.request.version) {
306 json_add_string(req_obj, "version", iconf->afc.request.version);
307 json_value_sep(req_obj);
308 }
309
310 json_start_array(req_obj, "availableSpectrumInquiryRequests");
311 json_start_object(req_obj, NULL);
312
313 iface->afc.request_id++;
314 os_snprintf(request_id_str, sizeof(request_id_str), "%u",
315 iface->afc.request_id);
316 json_add_string(req_obj, "requestId", request_id_str);
317 json_value_sep(req_obj);
318
319 json_start_object(req_obj, "deviceDescriptor");
320 if (iconf->afc.request.sn) {
321 json_add_string(req_obj, "serialNumber", iconf->afc.request.sn);
322 json_value_sep(req_obj);
323 }
324
325 json_start_array(req_obj, "certificationId");
326
327 for (i = 0; i < iconf->afc.n_cert_ids; i++) {
328 json_start_object(req_obj, NULL);
329 json_add_string(req_obj, "rulesetId",
330 iconf->afc.cert_ids[i].ruleset);
331 json_value_sep(req_obj);
332
333 json_add_string(req_obj, "id", iconf->afc.cert_ids[i].id);
334 json_end_object(req_obj);
335
336 if (i < iconf->afc.n_cert_ids - 1)
337 json_value_sep(req_obj);
338 }
339 /* certificationId */
340 json_end_array(req_obj);
341
342 /* deviceDescriptor */
343 json_end_object(req_obj);
344 json_value_sep(req_obj);
345
346 location_obj = hostapd_afc_build_location_request(iface);
347 if (!location_obj)
348 goto error;
349
350 wpabuf_put_buf(req_obj, location_obj);
351
352 if (iconf->afc.freqs.num) {
353 json_start_array(req_obj, "inquiredFrequencyRange");
354 for (i = 0; i < iconf->afc.freqs.num; i++) {
355 struct wpa_freq_range *fr = &iconf->afc.freqs.range[i];
356
357 json_start_object(req_obj, NULL);
358
359 json_add_int(req_obj, "lowFrequency", fr->min);
360 json_value_sep(req_obj);
361 json_add_int(req_obj, "highFrequency", fr->max);
362
363 json_end_object(req_obj);
364 if (i < iconf->afc.freqs.num - 1)
365 json_value_sep(req_obj);
366 }
367 json_end_array(req_obj);
368 json_value_sep(req_obj);
369 }
370
371 op_class_list_obj = hostapd_afc_build_req_chan_list(iface);
372 if (!op_class_list_obj)
373 goto error;
374
375 wpabuf_put_buf(req_obj, op_class_list_obj);
376
377 if (iconf->afc.min_power) {
378 json_value_sep(req_obj);
379 json_add_int(req_obj, "minDesiredPower", iconf->afc.min_power);
380 }
381
382 /* availableSpectrumInquiryRequests */
383 json_end_object(req_obj);
384 json_end_array(req_obj);
385
386 json_end_object(req_obj);
387 wpa_hexdump_ascii_buf(MSG_MSGDUMP, "AFC: Pending request", req_obj);
388
389 wpabuf_free(location_obj);
390 wpabuf_free(op_class_list_obj);
391
392 return req_obj;
393
394 error:
395 wpabuf_free(req_obj);
396 wpabuf_free(location_obj);
397 wpabuf_free(op_class_list_obj);
398
399 return NULL;
400 }
401
402
403 static int
hostad_afc_parse_available_freq_info(struct hostapd_iface * iface,struct json_token * freq_info)404 hostad_afc_parse_available_freq_info(struct hostapd_iface *iface,
405 struct json_token *freq_info)
406 {
407 struct afc_freq_range_elem *f = NULL, *tmp;
408 struct json_token *freq_obj;
409 int count = 0;
410
411 for (freq_obj = freq_info->child; freq_obj;
412 freq_obj = freq_obj->sibling) {
413 struct json_token *freq_range_obj, *token;
414 int low_freq, high_freq;
415 double max_psd;
416
417 freq_range_obj = json_get_member(freq_obj, "frequencyRange");
418 if (!freq_range_obj || freq_range_obj->type != JSON_OBJECT)
419 continue;
420
421 token = json_get_member(freq_range_obj, "lowFrequency");
422 if (!token || token->type != JSON_NUMBER)
423 continue;
424 low_freq = token->number;
425
426 token = json_get_member(freq_range_obj, "highFrequency");
427 if (!token || token->type != JSON_NUMBER)
428 continue;
429 high_freq = token->number;
430
431 token = json_get_member(freq_obj, "maxPsd");
432 if (!token)
433 token = json_get_member(freq_obj, "maxPSD");
434 if (!token || (token->type != JSON_DOUBLE &&
435 token->type != JSON_NUMBER))
436 continue;
437 max_psd = token->type == JSON_DOUBLE ?
438 token->dnumber : (double) token->number;
439
440 tmp = os_realloc_array(f, count + 1, sizeof(*f));
441 if (!tmp) {
442 os_free(f);
443 return -ENOMEM;
444 }
445 f = tmp;
446
447 f[count].low_freq = low_freq;
448 f[count].high_freq = high_freq;
449 f[count++].max_psd = max_psd;
450 }
451 os_free(iface->afc.freq_range);
452 iface->afc.freq_range = f;
453 iface->afc.num_freq_range = count;
454
455 return 0;
456 }
457
458
hostad_afc_update_chan_info(struct afc_chan_info_elem ** chan_list,int * chan_list_size,u8 op_class,int center_chan,double power)459 static int hostad_afc_update_chan_info(struct afc_chan_info_elem **chan_list,
460 int *chan_list_size, u8 op_class,
461 int center_chan, double power)
462 {
463 int op_class_pwr_index, num_low_subchan, channel;
464 int count = *chan_list_size;
465 struct afc_chan_info_elem *c = *chan_list, *tmp;
466
467 switch (op_class) {
468 case 132: /* 40 MHz */
469 op_class_pwr_index = 1;
470 num_low_subchan = 2;
471 break;
472 case 133: /* 80 MHz */
473 op_class_pwr_index = 2;
474 num_low_subchan = 6;
475 break;
476 case 134: /* 160 MHz */
477 op_class_pwr_index = 3;
478 num_low_subchan = 14;
479 break;
480 case 137: /* 320 MHz */
481 op_class_pwr_index = 4;
482 num_low_subchan = 30;
483 break;
484 default:
485 op_class_pwr_index = 0;
486 num_low_subchan = 0;
487 break;
488 }
489
490 for (channel = center_chan - num_low_subchan;
491 channel <= center_chan + num_low_subchan; channel += 4) {
492 int i;
493
494 for (i = 0; i < count; i++) {
495 if (c[i].chan == channel)
496 break;
497 }
498
499 if (i == count) {
500 tmp = os_realloc_array(c, count + 1, sizeof(*c));
501 if (!tmp) {
502 os_free(c);
503 *chan_list = NULL;
504 *chan_list_size = 0;
505 return -ENOMEM;
506 }
507 c = tmp;
508
509 c[count].chan = channel;
510 os_memset(c[count].power, 0, sizeof(c[count].power));
511 count++;
512 }
513 c[i].power[op_class_pwr_index] = power;
514 }
515
516 *chan_list_size = count;
517 *chan_list = c;
518
519 return 0;
520 }
521
522
523 static int
hostad_afc_parse_available_chan_info(struct hostapd_iface * iface,struct json_token * chan_info)524 hostad_afc_parse_available_chan_info(struct hostapd_iface *iface,
525 struct json_token *chan_info)
526 {
527 struct afc_chan_info_elem *c = NULL;
528 struct json_token *chan_obj;
529 int count = 0;
530
531 for (chan_obj = chan_info->child; chan_obj;
532 chan_obj = chan_obj->sibling) {
533 struct json_token *chan_cfi, *max_eirp;
534 struct json_token *token, *chan_arr, *power_arr;
535 int op_class;
536
537 token = json_get_member(chan_obj, "globalOperatingClass");
538 if (!token || token->type != JSON_NUMBER)
539 continue;
540 op_class = token->number;
541
542 chan_cfi = json_get_member(chan_obj, "channelCfi");
543 if (!chan_cfi || chan_cfi->type != JSON_ARRAY)
544 continue;
545
546 max_eirp = json_get_member(chan_obj, "maxEirp");
547 if (!max_eirp || max_eirp->type != JSON_ARRAY)
548 continue;
549
550 chan_arr = chan_cfi->child;
551 power_arr = max_eirp->child;
552 while (chan_arr && power_arr) {
553 int channel, ret;
554 double power;
555
556 if (chan_arr->type != JSON_NUMBER ||
557 (power_arr->type != JSON_DOUBLE &&
558 power_arr->type != JSON_NUMBER)) {
559 wpa_printf(MSG_DEBUG,
560 "AFC: Failed to parse array at opclass %d",
561 op_class);
562 break;
563 }
564
565 channel = chan_arr->number;
566 power = power_arr->type == JSON_DOUBLE ?
567 power_arr->dnumber : (double) power_arr->number;
568
569 ret = hostad_afc_update_chan_info(&c, &count, op_class,
570 channel, power);
571 if (ret) {
572 iface->afc.chan_info_list = NULL;
573 iface->afc.num_chan_info = 0;
574 return ret;
575 }
576 chan_arr = chan_arr->sibling;
577 power_arr = power_arr->sibling;
578 }
579 }
580 os_free(iface->afc.chan_info_list);
581 iface->afc.chan_info_list = c;
582 iface->afc.num_chan_info = count;
583
584 return 0;
585 }
586
587
hostad_afc_get_timeout(struct json_token * obj)588 static int hostad_afc_get_timeout(struct json_token *obj)
589 {
590 int year, month, day, hour, min, sec;
591 os_time_t t;
592 struct os_time now;
593
594 if (sscanf(obj->string, "%d-%d-%dT%d:%d:%dZ",
595 &year, &month, &day, &hour, &min, &sec) != 6)
596 return HOSTAPD_AFC_TIMEOUT;
597
598 if (os_mktime(year, month, day, hour, min, sec, &t) < 0)
599 return HOSTAPD_AFC_TIMEOUT;
600
601 os_get_time(&now);
602
603 return t < now.sec ?
604 HOSTAPD_AFC_RETRY_TIMEOUT : (t - now.sec) * 80 / 100;
605 }
606
607
608 static int
hostapd_afc_parse_reply(struct hostapd_iface * iface,char * reply,size_t len)609 hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply, size_t len)
610 {
611 struct hostapd_config *iconf = iface->conf;
612 int request_timeout = -1, ret = -EINVAL;
613 struct json_token *root = NULL, *reply_obj, *token;
614
615 wpa_hexdump_ascii(MSG_MSGDUMP, "AFC: Received reply", reply, len);
616
617 iface->afc_response = os_malloc(len + 1);
618 if (!iface->afc_response) {
619 wpa_printf(MSG_ERROR,
620 "AFC: Failed to alloc memory for storing reply");
621 return -ENOMEM;
622 }
623 os_memcpy(iface->afc_response, reply, len);
624 iface->afc_response[len] = '\0';
625
626 root = json_parse(reply, len);
627 if (!root) {
628 wpa_printf(MSG_ERROR, "AFC: Failed to parse reply payload");
629 goto fail;
630 }
631
632 token = json_get_member(root, "version");
633 if (!token || token->type != JSON_STRING) {
634 wpa_printf(MSG_ERROR, "AFC: Missing version in reply");
635 goto fail;
636 }
637 if (iconf->afc.request.version &&
638 os_strcmp(iconf->afc.request.version, token->string) != 0) {
639 wpa_printf(MSG_ERROR, "AFC: Mismatch in reply version");
640 goto fail;
641 }
642
643 reply_obj = json_get_member(root, "availableSpectrumInquiryResponses");
644 if (!reply_obj || reply_obj->type != JSON_ARRAY) {
645 wpa_printf(MSG_ERROR,
646 "AFC: Missing availableSpectrumInquiry in reply");
647 goto fail;
648 }
649
650 for (token = reply_obj->child; token; token = token->sibling) {
651 struct json_token *reply_elem, *response;
652 int status = -EINVAL, timeout;
653 unsigned int j;
654 char request_id_str[16];
655
656 reply_elem = json_get_member(token, "requestId");
657 if (!reply_elem || reply_elem->type != JSON_STRING) {
658 wpa_printf(MSG_DEBUG,
659 "AFC: Missing requestId in reply element");
660 continue;
661 }
662
663 snprintf(request_id_str, sizeof(request_id_str), "%u",
664 iface->afc.request_id);
665 if (os_strcmp(request_id_str, reply_elem->string) != 0) {
666 wpa_printf(MSG_DEBUG,
667 "AFC: RequestId mismatch in reply element");
668 continue;
669 }
670
671 reply_elem = json_get_member(token, "rulesetId");
672 if (!reply_elem || reply_elem->type != JSON_STRING) {
673 wpa_printf(MSG_DEBUG,
674 "AFC: Missing rulesetId in reply element");
675 continue;
676 }
677
678 for (j = 0; j < iconf->afc.n_cert_ids; j++) {
679 if (os_strcmp(iconf->afc.cert_ids[j].ruleset,
680 reply_elem->string) == 0)
681 break;
682 }
683
684 if (j == iconf->afc.n_cert_ids) {
685 wpa_printf(MSG_DEBUG,
686 "AFC: RulesetId mismatch in reply element");
687 continue;
688 }
689
690 response = json_get_member(token, "response");
691 if (!response || response->type != JSON_OBJECT) {
692 wpa_printf(MSG_DEBUG,
693 "AFC: Missing response field in reply element");
694 continue;
695 }
696
697 reply_elem = json_get_member(response, "shortDescription");
698 if (reply_elem && reply_elem->type == JSON_STRING)
699 wpa_printf(MSG_DEBUG, "AFC: Reply element: %s",
700 reply_elem->string);
701
702 reply_elem = json_get_member(response, "responseCode");
703 if (reply_elem && reply_elem->type == JSON_NUMBER)
704 status = reply_elem->number;
705
706 if (status < 0) {
707 wpa_printf(MSG_DEBUG,
708 "AFC: Reply invalid responseCode: %d",
709 status);
710 continue;
711 }
712 reply_elem = json_get_member(token, "availableFrequencyInfo");
713 if (reply_elem && reply_elem->type == JSON_ARRAY &&
714 hostad_afc_parse_available_freq_info(iface, reply_elem)) {
715 wpa_printf(MSG_DEBUG,
716 "AFC: Failed to parse availableFrequencyInfo");
717 continue;
718 }
719
720 reply_elem = json_get_member(token, "availableChannelInfo");
721 if (reply_elem && reply_elem->type == JSON_ARRAY &&
722 hostad_afc_parse_available_chan_info(iface, reply_elem)) {
723 wpa_printf(MSG_DEBUG,
724 "AFC: Failed to parse availableChannelInfo");
725 continue;
726 }
727
728 reply_elem = json_get_member(token, "availabilityExpireTime");
729 if (!reply_elem || reply_elem->type != JSON_STRING) {
730 wpa_printf(MSG_DEBUG,
731 "AFC: Missing expire time, use default timeout");
732 continue;
733 }
734
735 timeout = hostad_afc_get_timeout(reply_elem);
736 if (request_timeout < 0 || timeout < request_timeout)
737 request_timeout = timeout;
738
739 ret = status;
740 }
741
742 iface->afc.data_valid = true;
743 iface->afc.timeout = request_timeout;
744 if (iface->afc.timeout < 0)
745 iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
746
747 fail:
748 json_free(root);
749 return ret;
750 }
751
752
hostapd_afc_send_receive(struct hostapd_iface * iface)753 static int hostapd_afc_send_receive(struct hostapd_iface *iface)
754 {
755 struct hostapd_config *iconf = iface->conf;
756 struct wpabuf *request_obj = NULL;
757 struct timeval sock_timeout = {
758 .tv_sec = 10,
759 };
760 struct sockaddr_un addr = {
761 .sun_family = AF_UNIX,
762 #ifdef __FreeBSD__
763 .sun_len = sizeof(addr),
764 #endif /* __FreeBSD__ */
765 };
766 char *buf = NULL;
767 int sockfd, ret;
768 fd_set read_set;
769
770 if (iface->afc.data_valid) {
771 /* AFC data already downloaded from the server */
772 return 0;
773 }
774
775 iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
776 if (!iconf->afc.socket) {
777 wpa_printf(MSG_ERROR, "AFC: Missing socket string");
778 return -EINVAL;
779 }
780
781 if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
782 wpa_printf(MSG_ERROR, "AFC: Malformed socket string %s",
783 iconf->afc.socket);
784 return -EINVAL;
785 }
786
787 os_strlcpy(addr.sun_path, iconf->afc.socket, sizeof(addr.sun_path));
788 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
789 if (sockfd < 0) {
790 wpa_printf(MSG_ERROR, "AFC: Failed creating socket");
791 return sockfd;
792 }
793
794 if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
795 wpa_printf(MSG_ERROR, "AFC: Failed connecting socket");
796 ret = -EIO;
797 goto close_sock;
798 }
799
800 request_obj = hostapd_afc_build_request(iface);
801 if (!request_obj) {
802 ret = -EINVAL;
803 goto close_sock;
804 }
805
806 ret = send(sockfd, wpabuf_head(request_obj), wpabuf_len(request_obj),
807 0);
808 if (ret < 0 || (size_t) ret != wpabuf_len(request_obj)) {
809 wpa_printf(MSG_ERROR, "AFC: Failed sending request");
810 ret = -EIO;
811 goto close_sock;
812 }
813
814 FD_ZERO(&read_set);
815 FD_SET(sockfd, &read_set);
816 if (select(sockfd + 1, &read_set, NULL, NULL, &sock_timeout) < 0) {
817 wpa_printf(MSG_ERROR, "AFC: select() failed on socket");
818 ret = -errno;
819 goto close_sock;
820 }
821
822 if (!FD_ISSET(sockfd, &read_set)) {
823 ret = -EIO;
824 goto close_sock;
825 }
826
827 buf = os_zalloc(HOSTAPD_AFC_BUFSIZE);
828 if (!buf) {
829 ret = -ENOMEM;
830 goto close_sock;
831 }
832
833 ret = recv(sockfd, buf, HOSTAPD_AFC_BUFSIZE - 1, 0);
834 if (!ret)
835 ret = -EIO;
836 if (ret < 0)
837 goto close_sock;
838
839 ret = hostapd_afc_parse_reply(iface, buf, ret);
840 if (ret) {
841 wpa_printf(MSG_ERROR, "AFC: Failed parsing reply: %d", ret);
842 goto close_sock;
843 }
844 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_RECEIVED);
845 close_sock:
846 os_free(buf);
847 wpabuf_free(iface->afc_request);
848 iface->afc_request = request_obj;
849 close(sockfd);
850
851 return ret;
852 }
853
854
hostapd_afc_has_usable_chans(struct hostapd_iface * iface)855 static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
856 {
857 const struct oper_class_map *oper_class;
858 int channel, chan_num;
859
860 oper_class = get_oper_class(NULL, iface->conf->op_class);
861 if (!oper_class)
862 return false;
863
864 switch (iface->conf->op_class) {
865 case 132: /* 40 MHz */
866 chan_num = 2;
867 break;
868 case 133: /* 80 MHz */
869 chan_num = 4;
870 break;
871 case 134: /* 160 MHz */
872 chan_num = 8;
873 break;
874 case 137: /* 320 MHz */
875 chan_num = 16;
876 break;
877 default:
878 chan_num = 1;
879 break;
880 }
881
882 for (channel = oper_class->min_chan; channel <= oper_class->max_chan;
883 channel += oper_class->inc) {
884 struct hostapd_hw_modes *mode = iface->current_mode;
885 struct hostapd_channel_data *chan = NULL;
886 int i, start_ch = channel;
887
888 if (iface->conf->op_class == 137)
889 start_ch = channel - 30;
890
891 for (i = 0; i < chan_num; i++) {
892 chan = hw_get_channel_chan(mode, start_ch + i * 4,
893 NULL);
894
895 if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
896 break;
897 }
898
899 if (i == chan_num)
900 return true;
901 }
902
903 return false;
904 }
905
906
hostapd_afc_handle_request(struct hostapd_iface * iface)907 int hostapd_afc_handle_request(struct hostapd_iface *iface)
908 {
909 struct hostapd_config *iconf = iface->conf;
910 int ret;
911
912 /* AFC is required just for standard power AP */
913 if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
914 return 1;
915
916 if (!is_6ghz_op_class(iconf->op_class) || !is_6ghz_freq(iface->freq))
917 return 1;
918
919 if (iface->state == HAPD_IFACE_ACS)
920 return 1;
921
922 iface->afc.request_id = os_random();
923 ret = hostapd_afc_send_receive(iface);
924 if (ret < 0) {
925 /*
926 * If the connection to the AFCD failed, reschedule for a
927 * future attempt.
928 */
929 wpa_printf(MSG_ERROR, "AFC: Connection failed: %d", ret);
930 if (ret == -EIO)
931 ret = 0;
932 goto resched;
933 }
934
935 hostap_afc_disable_channels(iface);
936 if (!hostapd_afc_has_usable_chans(iface))
937 goto resched;
938
939 ret = hostapd_is_usable_chans(iface);
940 if (ret != 1) {
941 /* Trigger an ACS freq scan */
942 iconf->channel = 0;
943 iface->freq = 0;
944 hostapd_set_and_check_bw320_offset(iface->conf, 0);
945
946 if (!ret && acs_init(iface) != HOSTAPD_CHAN_ACS) {
947 wpa_printf(MSG_ERROR, "AFC: Could not start ACS");
948 ret = -EINVAL;
949 }
950 }
951
952 resched:
953 eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
954 eloop_register_timeout(iface->afc.timeout, 0,
955 hostapd_afc_timeout_handler, iface, NULL);
956
957 return ret;
958 }
959
960
hostapd_afc_delete_data_from_server(struct hostapd_iface * iface)961 static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
962 {
963 wpabuf_free(iface->afc_request);
964 os_free(iface->afc_response);
965 os_free(iface->afc.chan_info_list);
966 os_free(iface->afc.freq_range);
967
968 iface->afc_request = NULL;
969 iface->afc_response = NULL;
970
971 iface->afc.num_freq_range = 0;
972 iface->afc.num_chan_info = 0;
973
974 iface->afc.chan_info_list = NULL;
975 iface->afc.freq_range = NULL;
976
977 iface->afc.data_valid = false;
978 }
979
980
hostapd_afc_timeout_handler(void * eloop_ctx,void * timeout_ctx)981 static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
982 {
983 struct hostapd_iface *iface = eloop_ctx;
984 bool restart_iface = true;
985
986 hostapd_afc_delete_data_from_server(iface);
987 if (iface->state != HAPD_IFACE_ENABLED) {
988 /* hostapd is not fully enabled yet, toggle the interface */
989 goto restart_interface;
990 }
991
992 if (hostapd_afc_send_receive(iface) < 0 ||
993 hostapd_afc_reset_channels(iface)) {
994 restart_iface = false;
995 goto restart_interface;
996 }
997
998 if (hostapd_is_usable_chans(iface) > 0) {
999 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE);
1000 goto resched;
1001 }
1002
1003 restart_iface = hostapd_afc_has_usable_chans(iface);
1004 if (restart_iface) {
1005 /* Trigger an ACS freq scan */
1006 iface->conf->channel = 0;
1007 iface->freq = 0;
1008 hostapd_set_and_check_bw320_offset(iface->conf, 0);
1009 } else {
1010 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AFC_EVENT_COMPLETE);
1011 }
1012
1013 restart_interface:
1014 hostapd_disable_iface(iface);
1015 if (restart_iface)
1016 hostapd_enable_iface(iface);
1017 resched:
1018 eloop_register_timeout(iface->afc.timeout, 0,
1019 hostapd_afc_timeout_handler, iface, NULL);
1020 }
1021
1022
hostapd_afc_send_request(struct hostapd_iface * iface)1023 void hostapd_afc_send_request(struct hostapd_iface *iface)
1024 {
1025 eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
1026 eloop_register_timeout(0, 0, hostapd_afc_timeout_handler, iface, NULL);
1027 }
1028
1029
hostapd_afc_stop(struct hostapd_iface * iface)1030 void hostapd_afc_stop(struct hostapd_iface *iface)
1031 {
1032 eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
1033 }
1034
1035
hostap_afc_disable_channels(struct hostapd_iface * iface)1036 void hostap_afc_disable_channels(struct hostapd_iface *iface)
1037 {
1038 struct hostapd_hw_modes *mode = NULL;
1039 int i;
1040
1041 for (i = 0; i < iface->num_hw_features; i++) {
1042 mode = &iface->hw_features[i];
1043 if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
1044 mode->is_6ghz)
1045 break;
1046 }
1047
1048 if (i == iface->num_hw_features ||
1049 !he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) ||
1050 !iface->afc.data_valid)
1051 return;
1052
1053 for (i = 0; i < mode->num_channels; i++) {
1054 struct hostapd_channel_data *chan = &mode->channels[i];
1055 unsigned int j;
1056
1057 if (!is_6ghz_freq(chan->freq))
1058 continue;
1059
1060 for (j = 0; j < iface->afc.num_freq_range; j++) {
1061 if (chan->freq >= iface->afc.freq_range[j].low_freq &&
1062 chan->freq <= iface->afc.freq_range[j].high_freq)
1063 break;
1064 }
1065
1066 if (j != iface->afc.num_freq_range)
1067 continue;
1068
1069 for (j = 0; j < iface->afc.num_chan_info; j++) {
1070 if (chan->chan == iface->afc.chan_info_list[j].chan)
1071 break;
1072 }
1073
1074 if (j != iface->afc.num_chan_info)
1075 continue;
1076
1077 chan->flag |= HOSTAPD_CHAN_DISABLED;
1078 wpa_printf(MSG_MSGDUMP,
1079 "AFC: Disabling freq=%d MHz (not allowed by AFC)",
1080 chan->freq);
1081 }
1082 }
1083
1084
hostap_afc_get_chan_max_eirp_power(struct hostapd_iface * iface,bool psd,int * power)1085 int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
1086 int *power)
1087 {
1088 unsigned int i;
1089
1090 if (!he_reg_is_sp(iface->conf->he_6ghz_reg_pwr_type) ||
1091 !iface->afc.data_valid)
1092 return -EINVAL;
1093
1094 if (psd) {
1095 for (i = 0; i < iface->afc.num_freq_range; i++) {
1096 struct afc_freq_range_elem *f;
1097
1098 f = &iface->afc.freq_range[i];
1099 if (iface->freq >= f->low_freq &&
1100 iface->freq <= f->high_freq) {
1101 *power = (int) (2 * f->max_psd);
1102 return 0;
1103 }
1104 }
1105 } else {
1106 for (i = 0; i < iface->afc.num_chan_info; i++) {
1107 struct afc_chan_info_elem *c;
1108
1109 c = &iface->afc.chan_info_list[i];
1110 if (c->chan == iface->conf->channel) {
1111 unsigned int j;
1112
1113 *power = 0;
1114 for (j = 0; j < ARRAY_SIZE(c->power); j++)
1115 *power = MAX(*power,
1116 (int) (2 * c->power[j]));
1117 return 0;
1118 }
1119 }
1120 }
1121
1122 return -EINVAL;
1123 }
1124