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