b9e7d871c7a5e456c16fcc204bc15a97d4ae4c24
[openwrt/staging/neocturne.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 <libubox/uloop.h>
8 #include <ucode/compiler.h>
9
10 static uc_value_t *registry;
11 static uc_vm_t vm;
12 static struct uloop_timeout gc_timer;
13
14 static void uc_gc_timer(struct uloop_timeout *timeout)
15 {
16 ucv_gc(&vm);
17 }
18
19 uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
20 {
21 uc_value_t *level = uc_fn_arg(0);
22 uc_value_t *ret, **args;
23 uc_cfn_ptr_t _sprintf;
24 int l = MSG_INFO;
25 int i, start = 0;
26
27 _sprintf = uc_stdlib_function("sprintf");
28 if (!sprintf)
29 return NULL;
30
31 if (ucv_type(level) == UC_INTEGER) {
32 l = ucv_int64_get(level);
33 start++;
34 }
35
36 if (nargs <= start)
37 return NULL;
38
39 ret = _sprintf(vm, nargs - start);
40 if (ucv_type(ret) != UC_STRING)
41 return NULL;
42
43 wpa_printf(l, "%s", ucv_string_get(ret));
44 ucv_put(ret);
45
46 return NULL;
47 }
48
49 uc_value_t *uc_wpa_freq_info(uc_vm_t *vm, size_t nargs)
50 {
51 uc_value_t *freq = uc_fn_arg(0);
52 uc_value_t *sec = uc_fn_arg(1);
53 int width = ucv_uint64_get(uc_fn_arg(2));
54 int freq_val, center_idx, center_ofs;
55 enum oper_chan_width chanwidth;
56 enum hostapd_hw_mode hw_mode;
57 u8 op_class, channel, tmp_channel;
58 const char *modestr;
59 int sec_channel = 0;
60 uc_value_t *ret;
61
62 if (ucv_type(freq) != UC_INTEGER)
63 return NULL;
64
65 freq_val = ucv_int64_get(freq);
66 if (ucv_type(sec) == UC_INTEGER)
67 sec_channel = ucv_int64_get(sec);
68 else if (sec)
69 return NULL;
70 else if (freq_val > 4000)
71 sec_channel = (freq_val / 20) & 1 ? 1 : -1;
72 else
73 sec_channel = freq_val < 2442 ? 1 : -1;
74
75 if (sec_channel != -1 && sec_channel != 1 && sec_channel != 0)
76 return NULL;
77
78 switch (width) {
79 case 0:
80 chanwidth = CONF_OPER_CHWIDTH_USE_HT;
81 break;
82 case 1:
83 chanwidth = CONF_OPER_CHWIDTH_80MHZ;
84 break;
85 case 2:
86 chanwidth = CONF_OPER_CHWIDTH_160MHZ;
87 break;
88 default:
89 return NULL;
90 }
91
92 hw_mode = ieee80211_freq_to_channel_ext(freq_val, sec_channel,
93 chanwidth, &op_class, &channel);
94 switch (hw_mode) {
95 case HOSTAPD_MODE_IEEE80211B:
96 modestr = "b";
97 break;
98 case HOSTAPD_MODE_IEEE80211G:
99 modestr = "g";
100 break;
101 case HOSTAPD_MODE_IEEE80211A:
102 modestr = "a";
103 break;
104 case HOSTAPD_MODE_IEEE80211AD:
105 modestr = "ad";
106 break;
107 default:
108 return NULL;
109 }
110
111 ret = ucv_object_new(vm);
112 ucv_object_add(ret, "op_class", ucv_int64_new(op_class));
113 ucv_object_add(ret, "channel", ucv_int64_new(channel));
114 ucv_object_add(ret, "hw_mode", ucv_int64_new(hw_mode));
115 ucv_object_add(ret, "hw_mode_str", ucv_get(ucv_string_new(modestr)));
116 ucv_object_add(ret, "sec_channel", ucv_int64_new(sec_channel));
117 ucv_object_add(ret, "frequency", ucv_int64_new(freq_val));
118
119 if (!sec_channel)
120 return ret;
121
122 if (freq_val >= 5900)
123 center_ofs = 0;
124 else if (freq_val >= 5745)
125 center_ofs = 20;
126 else
127 center_ofs = 35;
128 tmp_channel = channel - center_ofs;
129 tmp_channel &= ~((8 << width) - 1);
130 center_idx = tmp_channel + center_ofs + (4 << width) - 1;
131
132 ucv_object_add(ret, "center_seg0_idx", ucv_int64_new(center_idx));
133 center_idx = (center_idx - channel) * 5 + freq_val;
134 ucv_object_add(ret, "center_freq1", ucv_int64_new(center_idx));
135
136 out:
137 return ret;
138 }
139
140 uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
141 {
142 return ucv_int64_new(getpid());
143 }
144
145 uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
146 {
147 u8 hash[SHA1_MAC_LEN];
148 char hash_hex[2 * ARRAY_SIZE(hash) + 1];
149 uc_value_t *val;
150 size_t *lens;
151 const u8 **args;
152 int i;
153
154 if (!nargs)
155 return NULL;
156
157 args = alloca(nargs * sizeof(*args));
158 lens = alloca(nargs * sizeof(*lens));
159 for (i = 0; i < nargs; i++) {
160 val = uc_fn_arg(i);
161 if (ucv_type(val) != UC_STRING)
162 return NULL;
163
164 args[i] = ucv_string_get(val);
165 lens[i] = ucv_string_length(val);
166 }
167
168 if (sha1_vector(nargs, args, lens, hash))
169 return NULL;
170
171 for (i = 0; i < ARRAY_SIZE(hash); i++)
172 sprintf(hash_hex + 2 * i, "%02x", hash[i]);
173
174 return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
175 }
176
177 uc_vm_t *wpa_ucode_create_vm(void)
178 {
179 static uc_parse_config_t config = {
180 .strict_declarations = true,
181 .lstrip_blocks = true,
182 .trim_blocks = true,
183 .raw_mode = true
184 };
185
186 uc_search_path_init(&config.module_search_path);
187 uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
188 uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
189
190 uc_vm_init(&vm, &config);
191
192 uc_stdlib_load(uc_vm_scope_get(&vm));
193 eloop_add_uloop();
194 gc_timer.cb = uc_gc_timer;
195
196 return &vm;
197 }
198
199 int wpa_ucode_run(const char *script)
200 {
201 uc_source_t *source;
202 uc_program_t *prog;
203 uc_value_t *ops;
204 char *err;
205 int ret;
206
207 source = uc_source_new_file(script);
208 if (!source)
209 return -1;
210
211 prog = uc_compile(vm.config, source, &err);
212 uc_source_put(source);
213 if (!prog) {
214 wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
215 return -1;
216 }
217
218 ret = uc_vm_execute(&vm, prog, &ops);
219 uc_program_put(prog);
220 if (ret || !ops)
221 return -1;
222
223 registry = ucv_array_new(&vm);
224 uc_vm_registry_set(&vm, "hostap.registry", registry);
225 ucv_array_set(registry, 0, ucv_get(ops));
226
227 return 0;
228 }
229
230 int wpa_ucode_call_prepare(const char *fname)
231 {
232 uc_value_t *obj, *func;
233
234 if (!registry)
235 return -1;
236
237 obj = ucv_array_get(registry, 0);
238 if (!obj)
239 return -1;
240
241 func = ucv_object_get(obj, fname, NULL);
242 if (!ucv_is_callable(func))
243 return -1;
244
245 uc_vm_stack_push(&vm, ucv_get(obj));
246 uc_vm_stack_push(&vm, ucv_get(func));
247
248 return 0;
249 }
250
251 uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
252 {
253 uc_value_t *global = uc_resource_new(global_type, NULL);
254 uc_value_t *proto;
255
256 uc_vm_registry_set(&vm, "hostap.global", global);
257 proto = ucv_prototype_get(global);
258 ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
259
260 #define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
261 ADD_CONST(MSG_EXCESSIVE);
262 ADD_CONST(MSG_MSGDUMP);
263 ADD_CONST(MSG_DEBUG);
264 ADD_CONST(MSG_INFO);
265 ADD_CONST(MSG_WARNING);
266 ADD_CONST(MSG_ERROR);
267 #undef ADD_CONST
268
269 ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
270
271 return global;
272 }
273
274 static uc_value_t *wpa_ucode_prototype_clone(uc_value_t *uval)
275 {
276 uc_value_t *proto, *proto_new;
277
278 proto = ucv_prototype_get(uval);
279 proto_new = ucv_object_new(&vm);
280
281 ucv_object_foreach(proto, key, val)
282 ucv_object_add(proto_new, key, ucv_get(val));
283 ucv_prototype_set(uval, ucv_get(proto));
284
285 return proto;
286 }
287
288 void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx)
289 {
290 uc_value_t *data;
291 int i = 0;
292
293 while (ucv_array_get(reg, i))
294 i++;
295
296 ucv_array_set(reg, i, ucv_get(val));
297
298 data = ucv_object_new(&vm);
299 ucv_object_add(wpa_ucode_prototype_clone(val), "data", ucv_get(data));
300
301 *idx = i + 1;
302 }
303
304 uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
305 {
306 if (!idx)
307 return NULL;
308
309 return ucv_array_get(reg, idx - 1);
310 }
311
312 uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
313 {
314 uc_value_t *val = wpa_ucode_registry_get(reg, idx);
315
316 if (val)
317 ucv_array_set(reg, idx - 1, NULL);
318
319 return val;
320 }
321
322
323 uc_value_t *wpa_ucode_call(size_t nargs)
324 {
325 if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
326 return NULL;
327
328 if (!gc_timer.pending)
329 uloop_timeout_set(&gc_timer, 10);
330
331 return uc_vm_stack_pop(&vm);
332 }
333
334 void wpa_ucode_free_vm(void)
335 {
336 if (!vm.config)
337 return;
338
339 uc_search_path_free(&vm.config->module_search_path);
340 uc_vm_free(&vm);
341 registry = NULL;
342 vm = (uc_vm_t){};
343 }