hostapd: add ucode support, use ucode for the main ubus object
[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 "ap_drv_ops.h"
8 #include <libubox/uloop.h>
9
10 static uc_resource_type_t *global_type, *bss_type, *iface_type;
11 static struct hapd_interfaces *interfaces;
12 static uc_value_t *global, *bss_registry, *iface_registry;
13 static uc_vm_t *vm;
14
15 static uc_value_t *
16 hostapd_ucode_bss_get_uval(struct hostapd_data *hapd)
17 {
18 uc_value_t *val;
19
20 if (hapd->ucode.idx)
21 return wpa_ucode_registry_get(bss_registry, hapd->ucode.idx);
22
23 val = uc_resource_new(bss_type, hapd);
24 wpa_ucode_registry_add(bss_registry, val, &hapd->ucode.idx);
25
26 return val;
27 }
28
29 static uc_value_t *
30 hostapd_ucode_iface_get_uval(struct hostapd_iface *hapd)
31 {
32 uc_value_t *val;
33
34 if (hapd->ucode.idx)
35 return wpa_ucode_registry_get(iface_registry, hapd->ucode.idx);
36
37 val = uc_resource_new(iface_type, hapd);
38 wpa_ucode_registry_add(iface_registry, val, &hapd->ucode.idx);
39
40 return val;
41 }
42
43 static void
44 hostapd_ucode_update_bss_list(struct hostapd_iface *iface)
45 {
46 uc_value_t *ifval, *list;
47 int i;
48
49 list = ucv_array_new(vm);
50 for (i = 0; i < iface->num_bss; i++) {
51 struct hostapd_data *hapd = iface->bss[i];
52 uc_value_t *val = hostapd_ucode_bss_get_uval(hapd);
53 uc_value_t *proto = ucv_prototype_get(val);
54
55 ucv_object_add(proto, "name", ucv_get(ucv_string_new(hapd->conf->iface)));
56 ucv_object_add(proto, "index", ucv_int64_new(i));
57 ucv_array_set(list, i, ucv_get(val));
58 }
59
60 ifval = hostapd_ucode_iface_get_uval(iface);
61 ucv_object_add(ucv_prototype_get(ifval), "bss", 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 int i;
69
70 for (i = 0; i < interfaces->count; i++) {
71 struct hostapd_iface *iface = interfaces->iface[i];
72
73 ucv_object_add(ifs, iface->phy, ucv_get(hostapd_ucode_iface_get_uval(iface)));
74 hostapd_ucode_update_bss_list(iface);
75 }
76
77 ucv_object_add(ucv_prototype_get(global), "interfaces", ucv_get(ifs));
78 ucv_gc(vm);
79 }
80
81 static uc_value_t *
82 uc_hostapd_add_iface(uc_vm_t *vm, size_t nargs)
83 {
84 uc_value_t *iface = uc_fn_arg(0);
85 int ret;
86
87 if (ucv_type(iface) != UC_STRING)
88 return ucv_int64_new(-1);
89
90 ret = hostapd_add_iface(interfaces, ucv_string_get(iface));
91 hostapd_ucode_update_interfaces();
92
93 return ucv_int64_new(ret);
94 }
95
96 static uc_value_t *
97 uc_hostapd_remove_iface(uc_vm_t *vm, size_t nargs)
98 {
99 uc_value_t *iface = uc_fn_arg(0);
100
101 if (ucv_type(iface) != UC_STRING)
102 return NULL;
103
104 hostapd_remove_iface(interfaces, ucv_string_get(iface));
105 hostapd_ucode_update_interfaces();
106
107 return NULL;
108 }
109
110 static uc_value_t *
111 uc_hostapd_bss_set_config(uc_vm_t *vm, size_t nargs)
112 {
113 struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
114 struct hostapd_bss_config *old_bss;
115 struct hostapd_iface *iface;
116 struct hostapd_config *conf;
117 uc_value_t *file = uc_fn_arg(0);
118 uc_value_t *index = uc_fn_arg(1);
119 unsigned int i, idx = 0;
120 int ret = -1;
121
122 if (!hapd || ucv_type(file) != UC_STRING)
123 goto out;
124
125 if (ucv_type(index) == UC_INTEGER)
126 idx = ucv_int64_get(index);
127
128 iface = hapd->iface;
129 conf = interfaces->config_read_cb(ucv_string_get(file));
130 if (!conf || idx > conf->num_bss || !conf->bss[idx])
131 goto out;
132
133 hostapd_bss_deinit_no_free(hapd);
134 hostapd_drv_stop_ap(hapd);
135 hostapd_free_hapd_data(hapd);
136
137 old_bss = hapd->conf;
138 for (i = 0; i < iface->conf->num_bss; i++)
139 if (iface->conf->bss[i] == hapd->conf)
140 iface->conf->bss[i] = conf->bss[idx];
141 hapd->conf = conf->bss[idx];
142 conf->bss[idx] = old_bss;
143 hostapd_config_free(conf);
144
145 hostapd_setup_bss(hapd, hapd == iface->bss[0], !iface->conf->mbssid);
146
147 ret = 0;
148
149 out:
150 return ucv_int64_new(ret);
151 }
152
153 static void
154 hostapd_remove_iface_bss_conf(struct hostapd_config *iconf,
155 struct hostapd_bss_config *conf)
156 {
157 int i;
158
159 for (i = 0; i < iconf->num_bss; i++)
160 if (iconf->bss[i] == conf)
161 break;
162
163 if (i == iconf->num_bss)
164 return;
165
166 for (i++; i < iconf->num_bss; i++)
167 iconf->bss[i - 1] = iconf->bss[i];
168 iconf->num_bss--;
169 }
170
171
172 static uc_value_t *
173 uc_hostapd_bss_delete(uc_vm_t *vm, size_t nargs)
174 {
175 struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
176 struct hostapd_iface *iface;
177 int i, idx;
178
179 if (!hapd || hapd == hapd->iface->bss[0])
180 return NULL;
181
182 iface = hapd->iface;
183 for (idx = 0; idx < iface->num_bss; idx++)
184 if (iface->bss[idx] == hapd)
185 break;
186
187 if (idx == iface->num_bss)
188 return NULL;
189
190 for (i = idx + 1; i < iface->num_bss; i++)
191 iface->bss[i - 1] = iface->bss[i];
192 iface->num_bss--;
193
194 hostapd_drv_stop_ap(hapd);
195 hostapd_bss_deinit(hapd);
196 hostapd_remove_iface_bss_conf(iface->conf, hapd->conf);
197 hostapd_config_free_bss(hapd->conf);
198 os_free(hapd);
199
200 hostapd_ucode_update_bss_list(iface);
201 ucv_gc(vm);
202
203 return NULL;
204 }
205
206 static uc_value_t *
207 uc_hostapd_iface_add_bss(uc_vm_t *vm, size_t nargs)
208 {
209 struct hostapd_iface *iface = uc_fn_thisval("hostapd.iface");
210 struct hostapd_bss_config *bss;
211 struct hostapd_config *conf;
212 struct hostapd_data *hapd;
213 uc_value_t *file = uc_fn_arg(0);
214 uc_value_t *index = uc_fn_arg(1);
215 unsigned int idx = 0;
216 uc_value_t *ret = NULL;
217
218 if (!iface || ucv_type(file) != UC_STRING)
219 goto out;
220
221 if (ucv_type(index) == UC_INTEGER)
222 idx = ucv_int64_get(index);
223
224 conf = interfaces->config_read_cb(ucv_string_get(file));
225 if (!conf || idx > conf->num_bss || !conf->bss[idx])
226 goto out;
227
228 bss = conf->bss[idx];
229 hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
230 if (!hapd)
231 goto out;
232
233 hapd->driver = iface->bss[0]->driver;
234 hapd->drv_priv = iface->bss[0]->drv_priv;
235 if (interfaces->ctrl_iface_init &&
236 interfaces->ctrl_iface_init(hapd) < 0)
237 goto free_hapd;
238
239 if (iface->state == HAPD_IFACE_ENABLED &&
240 hostapd_setup_bss(hapd, -1, true))
241 goto deinit_ctrl;
242
243 iface->bss = os_realloc_array(iface->bss, iface->num_bss + 1,
244 sizeof(*iface->bss));
245 iface->bss[iface->num_bss++] = hapd;
246
247 iface->conf->bss = os_realloc_array(iface->conf->bss,
248 iface->conf->num_bss + 1,
249 sizeof(*iface->conf->bss));
250 iface->conf->bss[iface->conf->num_bss] = bss;
251 conf->bss[idx] = NULL;
252 ret = hostapd_ucode_bss_get_uval(hapd);
253 hostapd_ucode_update_bss_list(iface);
254 goto out;
255
256 deinit_ctrl:
257 if (interfaces->ctrl_iface_deinit)
258 interfaces->ctrl_iface_deinit(hapd);
259 free_hapd:
260 hostapd_free_hapd_data(hapd);
261 os_free(hapd);
262 out:
263 hostapd_config_free(conf);
264 return ret;
265 }
266
267 static uc_value_t *
268 uc_hostapd_bss_ctrl(uc_vm_t *vm, size_t nargs)
269 {
270 struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
271 uc_value_t *arg = uc_fn_arg(0);
272 struct sockaddr_storage from = {};
273 static char reply[4096];
274 int reply_len;
275
276 if (!hapd || !interfaces->ctrl_iface_recv ||
277 ucv_type(arg) != UC_STRING)
278 return NULL;
279
280 reply_len = interfaces->ctrl_iface_recv(hapd, ucv_string_get(arg),
281 reply, sizeof(reply),
282 &from, sizeof(from));
283 if (reply_len < 0)
284 return NULL;
285
286 if (reply_len && reply[reply_len - 1] == '\n')
287 reply_len--;
288
289 return ucv_string_new_length(reply, reply_len);
290 }
291
292 int hostapd_ucode_init(struct hapd_interfaces *ifaces)
293 {
294 static const uc_function_list_t global_fns[] = {
295 { "printf", uc_wpa_printf },
296 { "getpid", uc_wpa_getpid },
297 { "sha1", uc_wpa_sha1 },
298 { "add_iface", uc_hostapd_add_iface },
299 { "remove_iface", uc_hostapd_remove_iface },
300 };
301 static const uc_function_list_t bss_fns[] = {
302 { "ctrl", uc_hostapd_bss_ctrl },
303 { "set_config", uc_hostapd_bss_set_config },
304 { "delete", uc_hostapd_bss_delete },
305 };
306 static const uc_function_list_t iface_fns[] = {
307 { "add_bss", uc_hostapd_iface_add_bss }
308 };
309 uc_value_t *data, *proto;
310
311 interfaces = ifaces;
312 vm = wpa_ucode_create_vm();
313
314 global_type = uc_type_declare(vm, "hostapd.global", global_fns, NULL);
315 bss_type = uc_type_declare(vm, "hostapd.bss", bss_fns, NULL);
316 iface_type = uc_type_declare(vm, "hostapd.iface", iface_fns, NULL);
317
318 bss_registry = ucv_array_new(vm);
319 uc_vm_registry_set(vm, "hostap.bss_registry", bss_registry);
320
321 iface_registry = ucv_array_new(vm);
322 uc_vm_registry_set(vm, "hostap.iface_registry", iface_registry);
323
324 global = wpa_ucode_global_init("hostapd", global_type);
325
326 if (wpa_ucode_run(HOSTAPD_UC_PATH "hostapd.uc"))
327 goto free_vm;
328 ucv_gc(vm);
329
330 return 0;
331
332 free_vm:
333 wpa_ucode_free_vm();
334 return -1;
335 }
336
337 void hostapd_ucode_free(void)
338 {
339 if (wpa_ucode_call_prepare("shutdown") == 0)
340 ucv_put(wpa_ucode_call(0));
341 wpa_ucode_free_vm();
342 }
343
344 void hostapd_ucode_free_iface(struct hostapd_iface *iface)
345 {
346 wpa_ucode_registry_remove(iface_registry, iface->ucode.idx);
347 }
348
349 void hostapd_ucode_add_bss(struct hostapd_data *hapd)
350 {
351 uc_value_t *val;
352
353 if (wpa_ucode_call_prepare("bss_add"))
354 return;
355
356 val = hostapd_ucode_bss_get_uval(hapd);
357 uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
358 uc_value_push(ucv_get(val));
359 ucv_put(wpa_ucode_call(2));
360 ucv_gc(vm);
361 }
362
363 void hostapd_ucode_reload_bss(struct hostapd_data *hapd, int reconf)
364 {
365 uc_value_t *val;
366
367 if (wpa_ucode_call_prepare("bss_reload"))
368 return;
369
370 val = hostapd_ucode_bss_get_uval(hapd);
371 uc_value_push(ucv_get(ucv_string_new(hapd->conf->iface)));
372 uc_value_push(ucv_get(val));
373 uc_value_push(ucv_int64_new(reconf));
374 ucv_put(wpa_ucode_call(3));
375 ucv_gc(vm);
376 }
377
378 void hostapd_ucode_free_bss(struct hostapd_data *hapd)
379 {
380 uc_value_t *val;
381
382 val = wpa_ucode_registry_remove(bss_registry, hapd->ucode.idx);
383 if (!val)
384 return;
385
386 hapd->ucode.idx = 0;
387 if (wpa_ucode_call_prepare("bss_remove"))
388 return;
389
390 uc_value_push(ucv_string_new(hapd->conf->iface));
391 uc_value_push(ucv_get(val));
392 ucv_put(wpa_ucode_call(2));
393 ucv_gc(vm);
394 }