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