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