hostapd: use new udebug ubus api to make debug rings configurable
[openwrt/staging/hauke.git] / package / network / services / hostapd / src / src / utils / ucode.c
1 #include <unistd.h>
2 #include "ucode.h"
3 #include "utils/eloop.h"
4 #include "crypto/crypto.h"
5 #include "crypto/sha1.h"
6 #include "common/ieee802_11_common.h"
7 #include <linux/netlink.h>
8 #include <linux/genetlink.h>
9 #include <linux/nl80211.h>
10 #include <libubox/uloop.h>
11 #include <ucode/compiler.h>
12 #include <udebug.h>
13
14 static uc_value_t *registry;
15 static uc_vm_t vm;
16 static struct uloop_timeout gc_timer;
17 static struct udebug ud;
18 static struct udebug_buf ud_log, ud_nl[3];
19 static const struct udebug_buf_meta meta_log = {
20 .name = "wpa_log",
21 .format = UDEBUG_FORMAT_STRING,
22 };
23 static const struct udebug_buf_meta meta_nl_ll = {
24 .name = "wpa_nl_ctrl",
25 .format = UDEBUG_FORMAT_PACKET,
26 .sub_format = UDEBUG_DLT_NETLINK,
27 };
28 static const struct udebug_buf_meta meta_nl_tx = {
29 .name = "wpa_nl_tx",
30 .format = UDEBUG_FORMAT_PACKET,
31 .sub_format = UDEBUG_DLT_NETLINK,
32 };
33 #define UDEBUG_FLAG_RX_FRAME (1ULL << 0)
34 static const struct udebug_buf_flag rx_flags[] = {
35 { "rx_frame", UDEBUG_FLAG_RX_FRAME },
36 };
37 static const struct udebug_buf_meta meta_nl_rx = {
38 .name = "wpa_nl_rx",
39 .format = UDEBUG_FORMAT_PACKET,
40 .sub_format = UDEBUG_DLT_NETLINK,
41 .flags = rx_flags,
42 .n_flags = ARRAY_SIZE(rx_flags),
43 };
44 static struct udebug_ubus_ring udebug_rings[] = {
45 {
46 .buf = &ud_log,
47 .meta = &meta_log,
48 .default_entries = 1024,
49 .default_size = 64 * 1024
50 },
51 {
52 .buf = &ud_nl[0],
53 .meta = &meta_nl_rx,
54 .default_entries = 1024,
55 .default_size = 256 * 1024,
56 },
57 {
58 .buf = &ud_nl[1],
59 .meta = &meta_nl_tx,
60 .default_entries = 1024,
61 .default_size = 64 * 1024,
62 },
63 {
64 .buf = &ud_nl[2],
65 .meta = &meta_nl_ll,
66 .default_entries = 1024,
67 .default_size = 32 * 1024,
68 }
69 };
70 char *udebug_service;
71 struct udebug_ubus ud_ubus;
72
73 static void uc_gc_timer(struct uloop_timeout *timeout)
74 {
75 ucv_gc(&vm);
76 }
77
78 uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
79 {
80 uc_value_t *level = uc_fn_arg(0);
81 uc_value_t *ret, **args;
82 uc_cfn_ptr_t _sprintf;
83 int l = MSG_INFO;
84 int i, start = 0;
85
86 _sprintf = uc_stdlib_function("sprintf");
87 if (!sprintf)
88 return NULL;
89
90 if (ucv_type(level) == UC_INTEGER) {
91 l = ucv_int64_get(level);
92 start++;
93 }
94
95 if (nargs <= start)
96 return NULL;
97
98 ret = _sprintf(vm, nargs - start);
99 if (ucv_type(ret) != UC_STRING)
100 return NULL;
101
102 wpa_printf(l, "%s", ucv_string_get(ret));
103 ucv_put(ret);
104
105 return NULL;
106 }
107
108 uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
109 {
110 uc_value_t *freq = uc_fn_arg(0);
111 uc_value_t *sec = uc_fn_arg(1);
112 int width = ucv_uint64_get(uc_fn_arg(2));
113 int freq_val, center_idx, center_ofs;
114 enum oper_chan_width chanwidth;
115 enum hostapd_hw_mode hw_mode;
116 u8 op_class, channel, tmp_channel;
117 const char *modestr;
118 int sec_channel = 0;
119 uc_value_t *ret;
120
121 if (ucv_type(freq) != UC_INTEGER)
122 return NULL;
123
124 freq_val = ucv_int64_get(freq);
125 if (ucv_type(sec) == UC_INTEGER)
126 sec_channel = ucv_int64_get(sec);
127 else if (sec)
128 return NULL;
129 else if (freq_val > 4000)
130 sec_channel = (freq_val / 20) & 1 ? 1 : -1;
131 else
132 sec_channel = freq_val < 2442 ? 1 : -1;
133
134 if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
135 return NULL;
136
137 switch (width) {
138 case 0:
139 chanwidth = CONF_OPER_CHWIDTH_USE_HT;
140 break;
141 case 1:
142 chanwidth = CONF_OPER_CHWIDTH_80MHZ;
143 break;
144 case 2:
145 chanwidth = CONF_OPER_CHWIDTH_160MHZ;
146 break;
147 default:
148 return NULL;
149 }
150
151 hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
152 chanwidth, &op_class, &channel);
153 switch (hw_mode) {
154 case HOSTAPD_MODE_IEEE80211B:
155 modestr = "b";
156 break;
157 case HOSTAPD_MODE_IEEE80211G:
158 modestr = "g";
159 break;
160 case HOSTAPD_MODE_IEEE80211A:
161 modestr = "a";
162 break;
163 case HOSTAPD_MODE_IEEE80211AD:
164 modestr = "ad";
165 break;
166 default:
167 return NULL;
168 }
169
170 ret = ucv_object_new(vm);
171 ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
172 ucv_object_add(ret, "channel", ucv_int64_new(channel));
173 ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
174 ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
175 ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
176 ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
177
178 if (!sec_channel)
179 return ret;
180
181 if (freq_val >= 5900)
182 center_ofs = 0;
183 else if (freq_val >= 5745)
184 center_ofs = 20;
185 else
186 center_ofs = 35;
187 tmp_channel = channel - center_ofs;
188 tmp_channel &= ~((8 << width) - 1);
189 center_idx = tmp_channel + center_ofs + (4 << width) - 1;
190
191 if (freq_val < 3000)
192 ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(0));
193 else
194 ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
195 center_idx = (center_idx - channel) * 5 + freq_val;
196 ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
197
198 out:
199 return ret;
200 }
201
202 uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
203 {
204 return ucv_int64_new(getpid());
205 }
206
207 uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
208 {
209 u8 hash[SHA1_MAC_LEN];
210 char hash_hex[2 * ARRAY_SIZE(hash) + 1];
211 uc_value_t *val;
212 size_t *lens;
213 const u8 **args;
214 int i;
215
216 if (!nargs)
217 return NULL;
218
219 args = alloca(nargs * sizeof(*args));
220 lens = alloca(nargs * sizeof(*lens));
221 for (i = 0; i < nargs; i++) {
222 val = uc_fn_arg(i);
223 if (ucv_type(val) != UC_STRING)
224 return NULL;
225
226 args[i] = ucv_string_get(val);
227 lens[i] = ucv_string_length(val);
228 }
229
230 if (sha1_vector(nargs, args, lens, hash))
231 return NULL;
232
233 for (i = 0; i < ARRAY_SIZE(hash); i++)
234 sprintf(hash_hex + 2 * i, "%02x", hash[i]);
235
236 return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
237 }
238
239 uc_vm_t *wpa_ucode_create_vm(void)
240 {
241 static uc_parse_config_t config = {
242 .strict_declarations = true,
243 .lstrip_blocks = true,
244 .trim_blocks = true,
245 .raw_mode = true
246 };
247
248 uc_search_path_init(&config.module_search_path);
249 uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
250 uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
251
252 uc_vm_init(&vm, &config);
253
254 uc_stdlib_load(uc_vm_scope_get(&vm));
255 eloop_add_uloop();
256 gc_timer.cb = uc_gc_timer;
257
258 return &vm;
259 }
260
261 int wpa_ucode_run(const char *script)
262 {
263 uc_source_t *source;
264 uc_program_t *prog;
265 uc_value_t *ops;
266 char *err;
267 int ret;
268
269 source = uc_source_new_file(script);
270 if (!source)
271 return -1;
272
273 prog = uc_compile(vm.config, source, &err);
274 uc_source_put(source);
275 if (!prog) {
276 wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
277 return -1;
278 }
279
280 ret = uc_vm_execute(&vm, prog, &ops);
281 uc_program_put(prog);
282 if (ret || !ops)
283 return -1;
284
285 registry = ucv_array_new(&vm);
286 uc_vm_registry_set(&vm, "hostap.registry", registry);
287 ucv_array_set(registry, 0, ucv_get(ops));
288
289 return 0;
290 }
291
292 int wpa_ucode_call_prepare(const char *fname)
293 {
294 uc_value_t *obj, *func;
295
296 if (!registry)
297 return -1;
298
299 obj = ucv_array_get(registry, 0);
300 if (!obj)
301 return -1;
302
303 func = ucv_object_get(obj, fname, NULL);
304 if (!ucv_is_callable(func))
305 return -1;
306
307 uc_vm_stack_push(&vm, ucv_get(obj));
308 uc_vm_stack_push(&vm, ucv_get(func));
309
310 return 0;
311 }
312
313 static void udebug_printf_hook(int level, const char *fmt, va_list ap)
314 {
315 udebug_entry_init(&ud_log);
316 udebug_entry_vprintf(&ud_log, fmt, ap);
317 udebug_entry_add(&ud_log);
318 }
319
320 static void udebug_hexdump_hook(int level, const char *title,
321 const void *data, size_t len)
322 {
323 char *buf;
324
325 udebug_entry_init(&ud_log);
326 udebug_entry_printf(&ud_log, "%s - hexdump:", title);
327 buf = udebug_entry_append(&ud_log, NULL, 3 * len);
328 for (size_t i = 0; i < len; i++)
329 buf += sprintf(buf, " %02x", *(uint8_t *)(data + i));
330 udebug_entry_add(&ud_log);
331 }
332
333 static void udebug_netlink_hook(int tx, const void *data, size_t len)
334 {
335 struct {
336 uint16_t pkttype;
337 uint16_t arphdr;
338 uint16_t _pad[5];
339 uint16_t proto;
340 } hdr = {
341 .pkttype = host_to_be16(tx ? 7 : 6),
342 .arphdr = host_to_be16(824),
343 .proto = host_to_be16(16),
344 };
345 const struct nlmsghdr *nlh = data;
346 const struct genlmsghdr *gnlh = data + NLMSG_HDRLEN;
347 struct udebug_buf *buf = &ud_nl[!!tx];
348
349 if (nlh->nlmsg_type == 0x10)
350 buf = &ud_nl[2];
351 else if (!tx && gnlh->cmd == NL80211_CMD_FRAME &&
352 !(udebug_buf_flags(buf) & UDEBUG_FLAG_RX_FRAME))
353 return;
354
355 if (!udebug_buf_valid(buf))
356 return;
357
358 udebug_entry_init(buf);
359 udebug_entry_append(buf, &hdr, sizeof(hdr));
360 udebug_entry_append(buf, data, len);
361 udebug_entry_add(buf);
362 }
363
364 static void
365 wpa_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data,
366 bool enabled)
367 {
368 udebug_ubus_apply_config(&ud, udebug_rings, ARRAY_SIZE(udebug_rings),
369 data, enabled);
370
371 if (udebug_buf_valid(&ud_log)) {
372 wpa_printf_hook = udebug_printf_hook;
373 wpa_hexdump_hook = udebug_hexdump_hook;
374 } else {
375 wpa_printf_hook = NULL;
376 wpa_hexdump_hook = NULL;
377 }
378
379 if (udebug_buf_valid(&ud_nl[0]) ||
380 udebug_buf_valid(&ud_nl[1]) ||
381 udebug_buf_valid(&ud_nl[2]))
382 wpa_netlink_hook = udebug_netlink_hook;
383 else
384 wpa_netlink_hook = NULL;
385 }
386
387 uc_value_t *uc_wpa_udebug_set(uc_vm_t *vm, size_t nargs)
388 {
389 uc_value_t *name = uc_fn_arg(0);
390 uc_value_t *ubus = uc_fn_arg(1);
391 static bool enabled = false;
392 struct ubus_context *ctx;
393 bool cur_en;
394
395 cur_en = ucv_type(name) == UC_STRING;
396 ctx = ucv_resource_data(ubus, "ubus.connection");
397 if (!ctx)
398 cur_en = false;
399
400 if (enabled == cur_en)
401 return ucv_boolean_new(true);
402
403 enabled = cur_en;
404 if (enabled) {
405 udebug_service = strdup(ucv_string_get(name));
406 udebug_init(&ud);
407 udebug_auto_connect(&ud, NULL);
408 udebug_ubus_init(&ud_ubus, ctx, udebug_service, wpa_udebug_config);
409 } else {
410 udebug_ubus_free(&ud_ubus);
411 for (size_t i = 0; i < ARRAY_SIZE(udebug_rings); i++)
412 if (udebug_buf_valid(udebug_rings[i].buf))
413 udebug_buf_free(udebug_rings[i].buf);
414 udebug_free(&ud);
415 free(udebug_service);
416 }
417
418 return ucv_boolean_new(true);
419 }
420
421 uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
422 {
423 uc_value_t *global = uc_resource_new(global_type, NULL);
424 uc_value_t *proto;
425
426 uc_vm_registry_set(&vm, "hostap.global", global);
427 proto = ucv_prototype_get(global);
428 ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
429
430 #define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
431 ADD_CONST(MSG_EXCESSIVE);
432 ADD_CONST(MSG_MSGDUMP);
433 ADD_CONST(MSG_DEBUG);
434 ADD_CONST(MSG_INFO);
435 ADD_CONST(MSG_WARNING);
436 ADD_CONST(MSG_ERROR);
437 #undef ADD_CONST
438
439 ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
440
441 return global;
442 }
443
444 int wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val)
445 {
446 uc_value_t *data;
447 int i = 0;
448
449 while (ucv_array_get(reg, i))
450 i++;
451
452 ucv_array_set(reg, i, ucv_get(val));
453
454 return i + 1;
455 }
456
457 uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
458 {
459 if (!idx)
460 return NULL;
461
462 return ucv_array_get(reg, idx - 1);
463 }
464
465 uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
466 {
467 uc_value_t *val = wpa_ucode_registry_get(reg, idx);
468 void **dataptr;
469
470 if (!val)
471 return NULL;
472
473 ucv_array_set(reg, idx - 1, NULL);
474 dataptr = ucv_resource_dataptr(val, NULL);
475 if (dataptr)
476 *dataptr = NULL;
477
478 return val;
479 }
480
481
482 uc_value_t *wpa_ucode_call(size_t nargs)
483 {
484 if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
485 return NULL;
486
487 if (!gc_timer.pending)
488 uloop_timeout_set(&gc_timer, 10);
489
490 return uc_vm_stack_pop(&vm);
491 }
492
493 void wpa_ucode_free_vm(void)
494 {
495 if (!vm.config)
496 return;
497
498 uc_search_path_free(&vm.config->module_search_path);
499 uc_vm_free(&vm);
500 registry = NULL;
501 vm = (uc_vm_t){};
502 }