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