hostapd: allow adding initial AP without breaking STA interface connection
[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
337 intval = ucv_int64_get(ucv_object_get(info, "center_seg0_idx", NULL));
338 if (!errno)
339 hostapd_set_oper_centr_freq_seg0_idx(conf, intval);
340
341 intval = ucv_int64_get(ucv_object_get(info, "center_seg1_idx", NULL));
342 if (!errno)
343 hostapd_set_oper_centr_freq_seg1_idx(conf, intval);
344
345 intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
346 if (!errno)
347 hostapd_set_oper_chwidth(conf, intval);
348
349 out:
350 if (conf->channel)
351 iface->freq = hostapd_hw_get_freq(iface->bss[0], conf->channel);
352
353 if (hostapd_is_dfs_required(iface) && !hostapd_is_dfs_chan_available(iface)) {
354 wpa_printf(MSG_INFO, "DFS CAC required on new channel, restart interface");
355 hostapd_disable_iface(iface);
356 hostapd_enable_iface(iface);
357 return ucv_boolean_new(true);
358 }
359
360 for (i = 0; i < iface->num_bss; i++) {
361 struct hostapd_data *hapd = iface->bss[i];
362 int ret;
363
364 hapd->started = 1;
365 hapd->conf->start_disabled = 0;
366 hostapd_set_freq(hapd, conf->hw_mode, iface->freq,
367 conf->channel,
368 conf->enable_edmg,
369 conf->edmg_channel,
370 conf->ieee80211n,
371 conf->ieee80211ac,
372 conf->ieee80211ax,
373 conf->ieee80211be,
374 conf->secondary_channel,
375 hostapd_get_oper_chwidth(conf),
376 hostapd_get_oper_centr_freq_seg0_idx(conf),
377 hostapd_get_oper_centr_freq_seg1_idx(conf));
378
379 ieee802_11_set_beacon(hapd);
380 }
381
382 return ucv_boolean_new(true);
383 }
384
385 static uc_value_t *
386 uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
387 {
388 struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
389 uc_value_t *info = uc_fn_arg(0);
390 struct hostapd_config *conf;
391 struct csa_settings csa = {};
392 uint64_t intval;
393 int i, ret = 0;
394
395 if (!iface || ucv_type(info) != UC_OBJECT)
396 return NULL;
397
398 conf = iface->conf;
399 if ((intval = ucv_int64_get(ucv_object_get(info, "csa_count", NULL))) && !errno)
400 csa.cs_count = intval;
401 if ((intval = ucv_int64_get(ucv_object_get(info, "sec_channel", NULL))) && !errno)
402 csa.freq_params.sec_channel_offset = intval;
403
404 csa.freq_params.ht_enabled = conf->ieee80211n;
405 csa.freq_params.vht_enabled = conf->ieee80211ac;
406 csa.freq_params.he_enabled = conf->ieee80211ax;
407 #ifdef CONFIG_IEEE80211BE
408 csa.freq_params.eht_enabled = conf->ieee80211be;
409 #endif
410 intval = ucv_int64_get(ucv_object_get(info, "oper_chwidth", NULL));
411 if (errno)
412 intval = hostapd_get_oper_chwidth(conf);
413 if (intval)
414 csa.freq_params.bandwidth = 40 << intval;
415 else
416 csa.freq_params.bandwidth = csa.freq_params.sec_channel_offset ? 40 : 20;
417
418 if ((intval = ucv_int64_get(ucv_object_get(info, "frequency", NULL))) && !errno)
419 csa.freq_params.freq = intval;
420 if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq1", NULL))) && !errno)
421 csa.freq_params.center_freq1 = intval;
422 if ((intval = ucv_int64_get(ucv_object_get(info, "center_freq2", NULL))) && !errno)
423 csa.freq_params.center_freq2 = intval;
424
425 for (i = 0; i < iface->num_bss; i++)
426 ret = hostapd_switch_channel(iface->bss[i], &csa);
427
428 return ucv_boolean_new(!ret);
429 }
430
431 int hostapd_ucode_init(struct hapd_interfaces *ifaces)
432 {
433 static const uc_function_list_t global_fns[] = {
434 { "printf", uc_wpa_printf },
435 { "getpid", uc_wpa_getpid },
436 { "sha1", uc_wpa_sha1 },
437 { "freq_info", uc_wpa_freq_info },
438 { "add_iface", uc_hostapd_add_iface },
439 { "remove_iface", uc_hostapd_remove_iface },
440 };
441 static const uc_function_list_t bss_fns[] = {
442 { "ctrl", uc_hostapd_bss_ctrl },
443 { "set_config", uc_hostapd_bss_set_config },
444 { "delete", uc_hostapd_bss_delete },
445 };
446 static const uc_function_list_t iface_fns[] = {
447 { "add_bss", uc_hostapd_iface_add_bss },
448 { "stop", uc_hostapd_iface_stop },
449 { "start", uc_hostapd_iface_start },
450 { "switch_channel", uc_hostapd_iface_switch_channel },
451 };
452 uc_value_t *data, *proto;
453
454 interfaces = ifaces;
455 vm = wpa_ucode_create_vm();
456
457 global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
458 bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
459 iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
460
461 bss_registry = ucv_array_new(vm);
462 uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
463
464 iface_registry = ucv_array_new(vm);
465 uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
466
467 global = wpa_ucode_global_init("hostapd", global_type);
468
469 if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
470 goto free_vm;
471 ucv_gc(vm);
472
473 return 0;
474
475 free_vm:
476 wpa_ucode_free_vm();
477 return -1;
478 }
479
480 void hostapd_ucode_free(void)
481 {
482 if (wpa_ucode_call_prepare("shutdown") == 0)
483 ucv_put(wpa_ucode_call(0));
484 wpa_ucode_free_vm();
485 }
486
487 void hostapd_ucode_free_iface(struct hostapd_iface *iface)
488 {
489 wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
490 }
491
492 void hostapd_ucode_add_bss(struct hostapd_data *hapd)
493 {
494 uc_value_t *val;
495
496 if (wpa_ucode_call_prepare("bss_add"))
497 return;
498
499 val = hostapd_ucode_bss_get_uval(hapd);
500 uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
501 uc_value_push(ucv_get(val));
502 ucv_put(wpa_ucode_call(2));
503 ucv_gc(vm);
504 }
505
506 void hostapd_ucode_reload_bss(struct hostapd_data *hapd)
507 {
508 uc_value_t *val;
509
510 if (wpa_ucode_call_prepare("bss_reload"))
511 return;
512
513 val = hostapd_ucode_bss_get_uval(hapd);
514 uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
515 uc_value_push(ucv_get(val));
516 ucv_put(wpa_ucode_call(2));
517 ucv_gc(vm);
518 }
519
520 void hostapd_ucode_free_bss(struct hostapd_data *hapd)
521 {
522 uc_value_t *val;
523
524 val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
525 if (!val)
526 return;
527
528 hapd->ucode.idx = 0;
529 if (wpa_ucode_call_prepare("bss_remove"))
530 return;
531
532 uc_value_push(ucv_string_new(hapd->conf->iface));
533 uc_value_push(ucv_get(val));
534 ucv_put(wpa_ucode_call(2));
535 ucv_gc(vm);
536 }