hostapd: fix bringing up AP+STA when the new channel is on a DFS channel
[openwrt/openwrt.git] / package / network / services / hostapd / src / src / ap / ucode.c
1 #include <sys/un.h>
2
3 #include "utils/includes.h"
4 #include "utils/common.h"
5 #include "utils/ucode.h"
6 #include "hostapd.h"
7 #include "beacon.h"
8 #include "hw_features.h"
9 #include "ap_drv_ops.h"
10 #include "dfs.h"
11 #include <libubox/uloop.h>
12
13 static uc_resource_type_t *global_type, *bss_type, *iface_type;
14 static struct hapd_interfaces *interfaces;
15 static uc_value_t *global, *bss_registry, *iface_registry;
16 static uc_vm_t *vm;
17
18 static uc_value_t *
19 hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
20 {
21 uc_value_t *val;
22
23 if (hapd->ucode.idx)
24 return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
25
26 val = uc_resource_new(bss_type, hapd);
27 hapd->ucode.idx = wpa_ucode_registry_add(bss_registry, val);
28
29 return val;
30 }
31
32 static uc_value_t *
33 hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
34 {
35 uc_value_t *val;
36
37 if (hapd->ucode.idx)
38 return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
39
40 val = uc_resource_new(iface_type, hapd);
41 hapd->ucode.idx = wpa_ucode_registry_add(iface_registry, val);
42
43 return val;
44 }
45
46 static void
47 hostapd_ucode_update_bss_list(struct hostapd_iface *iface, uc_value_t *if_bss, uc_value_t *bss)
48 {
49 uc_value_t *list;
50 int i;
51
52 list = ucv_array_new(vm);
53 for (i = 0; i < iface->num_bss; i++) {
54 struct hostapd_data *hapd = iface->bss[i];
55 uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
56
57 ucv_array_set(list, i, ucv_get(ucv_string_new(hapd->conf->iface)));
58 ucv_object_add(bss, hapd->conf->iface, ucv_get(val));
59 }
60 ucv_object_add(if_bss, iface->phy, ucv_get(list));
61 }
62
63 static void
64 hostapd_ucode_update_interfaces(void)
65 {
66 uc_value_t *ifs = ucv_object_new(vm);
67 uc_value_t *if_bss = ucv_array_new(vm);
68 uc_value_t *bss = ucv_object_new(vm);
69 int i;
70
71 for (i = 0; i < interfaces->count; i++) {
72 struct hostapd_iface *iface = interfaces->iface[i];
73
74 ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
75 hostapd_ucode_update_bss_list(iface, if_bss, bss);
76 }
77
78 ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
79 ucv_object_add(ucv_prototype_get(global), "interface_bss", ucv_get(if_bss));
80 ucv_object_add(ucv_prototype_get(global), "bss", ucv_get(bss));
81 ucv_gc(vm);
82 }
83
84 static uc_value_t *
85 uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
86 {
87 uc_value_t *iface = uc_fn_arg(0);
88 int ret;
89
90 if (ucv_type(iface) != UC_STRING)
91 return ucv_int64_new(-1);
92
93 ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
94 hostapd_ucode_update_interfaces();
95
96 return ucv_int64_new(ret);
97 }
98
99 static uc_value_t *
100 uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
101 {
102 uc_value_t *iface = uc_fn_arg(0);
103
104 if (ucv_type(iface) != UC_STRING)
105 return NULL;
106
107 hostapd_remove_iface(interfaces, ucv_string_get(iface));
108 hostapd_ucode_update_interfaces();
109
110 return NULL;
111 }
112
113 static uc_value_t *
114 uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
115 {
116 struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
117 struct hostapd_bss_config *old_bss;
118 struct hostapd_iface *iface;
119 struct hostapd_config *conf;
120 uc_value_t *file = uc_fn_arg(0);
121 uc_value_t *index = uc_fn_arg(1);
122 unsigned int i, idx = 0;
123 int ret = -1;
124
125 if (!hapd || ucv_type(file) != UC_STRING)
126 goto out;
127
128 if (ucv_type(index) == UC_INTEGER)
129 idx = ucv_int64_get(index);
130
131 iface = hapd->iface;
132 conf = interfaces->config_read_cb(ucv_string_get(file));
133 if (!conf || idx > conf->num_bss || !conf->bss[idx])
134 goto out;
135
136 hostapd_bss_deinit_no_free(hapd);
137 hostapd_drv_stop_ap(hapd);
138 hostapd_free_hapd_data(hapd);
139
140 old_bss = hapd->conf;
141 for (i = 0; i < iface->conf->num_bss; i++)
142 if (iface->conf->bss[i] == hapd->conf)
143 iface->conf->bss[i] = conf->bss[idx];
144 hapd->conf = conf->bss[idx];
145 conf->bss[idx] = old_bss;
146 hostapd_config_free(conf);
147
148 hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->mbssid);
149
150 ret = 0;
151
152 out:
153 return ucv_int64_new(ret);
154 }
155
156 static void
157 hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
158 struct hostapd_bss_config *conf)
159 {
160 int i;
161
162 for (i = 0; i < iconf->num_bss; i++)
163 if (iconf->bss[i] == conf)
164 break;
165
166 if (i == iconf->num_bss)
167 return;
168
169 for (i++; i < iconf->num_bss; i++)
170 iconf->bss[i - 1] = iconf->bss[i];
171 iconf->num_bss--;
172 }
173
174
175 static uc_value_t *
176 uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
177 {
178 struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
179 struct hostapd_iface *iface;
180 int i, idx;
181
182 if (!hapd || hapd == hapd->iface->bss[0])
183 return NULL;
184
185 iface = hapd->iface;
186 for (idx = 0; idx < iface->num_bss; idx++)
187 if (iface->bss[idx] == hapd)
188 break;
189
190 if (idx == iface->num_bss)
191 return NULL;
192
193 for (i = idx + 1; i < iface->num_bss; i++)
194 iface->bss[i - 1] = iface->bss[i];
195 iface->num_bss--;
196
197 hostapd_drv_stop_ap(hapd);
198 hostapd_bss_deinit(hapd);
199 hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
200 hostapd_config_free_bss(hapd->conf);
201 os_free(hapd);
202
203 hostapd_ucode_update_interfaces();
204 ucv_gc(vm);
205
206 return NULL;
207 }
208
209 static uc_value_t *
210 uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
211 {
212 struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
213 struct hostapd_bss_config *bss;
214 struct hostapd_config *conf;
215 struct hostapd_data *hapd;
216 uc_value_t *file = uc_fn_arg(0);
217 uc_value_t *index = uc_fn_arg(1);
218 unsigned int idx = 0;
219 uc_value_t *ret = NULL;
220
221 if (!iface || ucv_type(file) != UC_STRING)
222 goto out;
223
224 if (ucv_type(index) == UC_INTEGER)
225 idx = ucv_int64_get(index);
226
227 conf = interfaces->config_read_cb(ucv_string_get(file));
228 if (!conf || idx > conf->num_bss || !conf->bss[idx])
229 goto out;
230
231 bss = conf->bss[idx];
232 hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
233 if (!hapd)
234 goto out;
235
236 hapd->driver = iface->bss[0]->driver;
237 hapd->drv_priv = iface->bss[0]->drv_priv;
238 if (interfaces->ctrl_iface_init &&
239 interfaces->ctrl_iface_init(hapd) < 0)
240 goto free_hapd;
241
242 if (iface->state == HAPD_IFACE_ENABLED &&
243 hostapd_setup_bss(hapd, -1, true))
244 goto deinit_ctrl;
245
246 iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
247 sizeof(*iface->bss));
248 iface->bss[iface->num_bss++] = hapd;
249
250 iface->conf->bss = os_realloc_array(iface->conf->bss,
251 iface->conf->num_bss + 1,
252 sizeof(*iface->conf->bss));
253 iface->conf->bss[iface->conf->num_bss] = bss;
254 conf->bss[idx] = NULL;
255 ret = hostapd_ucode_bss_get_uval(hapd);
256 hostapd_ucode_update_interfaces();
257 goto out;
258
259 deinit_ctrl:
260 if (interfaces->ctrl_iface_deinit)
261 interfaces->ctrl_iface_deinit(hapd);
262 free_hapd:
263 hostapd_free_hapd_data(hapd);
264 os_free(hapd);
265 out:
266 hostapd_config_free(conf);
267 return ret;
268 }
269
270 static uc_value_t *
271 uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
272 {
273 struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
274 uc_value_t *arg = uc_fn_arg(0);
275 struct sockaddr_storage from = {};
276 static char reply[4096];
277 int reply_len;
278
279 if (!hapd || !interfaces->ctrl_iface_recv ||
280 ucv_type(arg) != UC_STRING)
281 return NULL;
282
283 reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
284 reply, sizeof(reply),
285 &from, sizeof(from));
286 if (reply_len < 0)
287 return NULL;
288
289 if (reply_len && reply[reply_len - 1] == '\n')
290 reply_len--;
291
292 return ucv_string_new_length(reply, reply_len);
293 }
294
295 static uc_value_t *
296 uc_hostapd_iface_stop(uc_vm_t *vm, size_t nargs)
297 {
298 struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
299 int i;
300
301 for (i = 0; i < iface->num_bss; i++) {
302 struct hostapd_data *hapd = iface->bss[i];
303
304 hostapd_drv_stop_ap(hapd);
305 hapd->started = 0;
306 }
307 }
308
309 static uc_value_t *
310 uc_hostapd_iface_start(uc_vm_t *vm, size_t nargs)
311 {
312 struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
313 uc_value_t *info = uc_fn_arg(0);
314 struct hostapd_config *conf;
315 uint64_t intval;
316 int i;
317
318 if (!iface)
319 return NULL;
320
321 if (!info)
322 goto out;
323
324 if (ucv_type(info) != UC_OBJECT)
325 return NULL;
326
327 conf = iface->conf;
328 if ((intval = ucv_int64_get(ucv_object_get(info, "op_class", NULL))) && !errno)
329 conf->op_class = intval;
330 if ((intval = ucv_int64_get(ucv_object_get(info, "hw_mode", NULL))) && !errno)
331 conf->hw_mode = intval;
332 if ((intval = ucv_int64_get(ucv_object_get(info, "channel", NULL))) && !errno)
333 conf->channel = intval;
334 if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
335 conf->secondary_channel = intval;
336 #ifdef CONFIG_IEEE80211AC
337 if ((intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL))) && !errno) {
338 conf->vht_oper_centr_freq_seg0_idx = intval;
339 #ifdef CONFIG_IEEE80211AX
340 conf->he_oper_centr_freq_seg0_idx = intval;
341 #endif
342 #ifdef CONFIG_IEEE80211BE
343 conf->eht_oper_centr_freq_seg0_idx = intval;
344 #endif
345 }
346 if ((intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL))) && !errno) {
347 conf->vht_oper_centr_freq_seg1_idx = intval;
348 #ifdef CONFIG_IEEE80211AX
349 conf->he_oper_centr_freq_seg1_idx = intval;
350 #endif
351 #ifdef CONFIG_IEEE80211BE
352 conf->eht_oper_centr_freq_seg1_idx = intval;
353 #endif
354 }
355 intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
356 if (!errno) {
357 conf->vht_oper_chwidth = intval;
358 #ifdef CONFIG_IEEE80211AX
359 conf->he_oper_chwidth = intval;
360 #endif
361 #ifdef CONFIG_IEEE80211BE
362 conf->eht_oper_chwidth = intval;
363 #endif
364 }
365 #endif
366
367 out:
368 if (conf->channel)
369 iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
370
371 if (hostapd_is_dfs_required(iface) && !hostapd_is_dfs_chan_available(iface)) {
372 wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
373 hostapd_disable_iface(iface);
374 hostapd_enable_iface(iface);
375 return ucv_boolean_new(true);
376 }
377
378 for (i = 0; i < iface->num_bss; i++) {
379 struct hostapd_data *hapd = iface->bss[i];
380 int ret;
381
382 hapd->started = 1;
383 hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
384 conf->channel,
385 conf->enable_edmg,
386 conf->edmg_channel,
387 conf->ieee80211n,
388 conf->ieee80211ac,
389 conf->ieee80211ax,
390 conf->ieee80211be,
391 conf->secondary_channel,
392 hostapd_get_oper_chwidth(conf),
393 hostapd_get_oper_centr_freq_seg0_idx(conf),
394 hostapd_get_oper_centr_freq_seg1_idx(conf));
395
396 ieee802_11_set_beacon(hapd);
397 }
398
399 return ucv_boolean_new(true);
400 }
401
402 static uc_value_t *
403 uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
404 {
405 struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
406 uc_value_t *info = uc_fn_arg(0);
407 struct hostapd_config *conf;
408 struct csa_settings csa = {};
409 uint64_t intval;
410 int i, ret = 0;
411
412 if (!iface || ucv_type(info) != UC_OBJECT)
413 return NULL;
414
415 conf = iface->conf;
416 if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
417 csa.cs_count = intval;
418 if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
419 csa.freq_params.sec_channel_offset = intval;
420
421 csa.freq_params.ht_enabled = conf->ieee80211n;
422 csa.freq_params.vht_enabled = conf->ieee80211ac;
423 csa.freq_params.he_enabled = conf->ieee80211ax;
424 #ifdef CONFIG_IEEE80211BE
425 csa.freq_params.eht_enabled = conf->ieee80211be;
426 #endif
427 intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
428 if (errno)
429 intval = hostapd_get_oper_chwidth(conf);
430 if (intval)
431 csa.freq_params.bandwidth = 40 << intval;
432 else
433 csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
434
435 if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
436 csa.freq_params.freq = intval;
437 if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
438 csa.freq_params.center_freq1 = intval;
439 if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
440 csa.freq_params.center_freq2 = intval;
441
442 for (i = 0; i < iface->num_bss; i++)
443 ret = hostapd_switch_channel(iface->bss[i], &csa);
444
445 return ucv_boolean_new(!ret);
446 }
447
448 int hostapd_ucode_init(struct hapd_interfaces *ifaces)
449 {
450 static const uc_function_list_t global_fns[] = {
451 { "printf", uc_wpa_printf },
452 { "getpid", uc_wpa_getpid },
453 { "sha1", uc_wpa_sha1 },
454 { "freq_info", uc_wpa_freq_info },
455 { "add_iface", uc_hostapd_add_iface },
456 { "remove_iface", uc_hostapd_remove_iface },
457 };
458 static const uc_function_list_t bss_fns[] = {
459 { "ctrl", uc_hostapd_bss_ctrl },
460 { "set_config", uc_hostapd_bss_set_config },
461 { "delete", uc_hostapd_bss_delete },
462 };
463 static const uc_function_list_t iface_fns[] = {
464 { "add_bss", uc_hostapd_iface_add_bss },
465 { "stop", uc_hostapd_iface_stop },
466 { "start", uc_hostapd_iface_start },
467 { "switch_channel", uc_hostapd_iface_switch_channel },
468 };
469 uc_value_t *data, *proto;
470
471 interfaces = ifaces;
472 vm = wpa_ucode_create_vm();
473
474 global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
475 bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
476 iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
477
478 bss_registry = ucv_array_new(vm);
479 uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
480
481 iface_registry = ucv_array_new(vm);
482 uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
483
484 global = wpa_ucode_global_init("hostapd", global_type);
485
486 if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
487 goto free_vm;
488 ucv_gc(vm);
489
490 return 0;
491
492 free_vm:
493 wpa_ucode_free_vm();
494 return -1;
495 }
496
497 void hostapd_ucode_free(void)
498 {
499 if (wpa_ucode_call_prepare("shutdown") == 0)
500 ucv_put(wpa_ucode_call(0));
501 wpa_ucode_free_vm();
502 }
503
504 void hostapd_ucode_free_iface(struct hostapd_iface *iface)
505 {
506 wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
507 }
508
509 void hostapd_ucode_add_bss(struct hostapd_data *hapd)
510 {
511 uc_value_t *val;
512
513 if (wpa_ucode_call_prepare("bss_add"))
514 return;
515
516 val = hostapd_ucode_bss_get_uval(hapd);
517 uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
518 uc_value_push(ucv_get(val));
519 ucv_put(wpa_ucode_call(2));
520 ucv_gc(vm);
521 }
522
523 void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
524 {
525 uc_value_t *val;
526
527 if (wpa_ucode_call_prepare("bss_reload"))
528 return;
529
530 val = hostapd_ucode_bss_get_uval(hapd);
531 uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
532 uc_value_push(ucv_get(val));
533 ucv_put(wpa_ucode_call(2));
534 ucv_gc(vm);
535 }
536
537 void hostapd_ucode_free_bss(struct hostapd_data *hapd)
538 {
539 uc_value_t *val;
540
541 val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
542 if (!val)
543 return;
544
545 hapd->ucode.idx = 0;
546 if (wpa_ucode_call_prepare("bss_remove"))
547 return;
548
549 uc_value_push(ucv_string_new(hapd->conf->iface));
550 uc_value_push(ucv_get(val));
551 ucv_put(wpa_ucode_call(2));
552 ucv_gc(vm);
553 }