• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/netifd/ucode.c

  1 /*
  2  * netifd - network interface daemon
  3  * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License version 2
  7  * as published by the Free Software Foundation
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  */
 14 #include <ucode/vm.h>
 15 #include <ucode/lib.h>
 16 #include <ucode/compiler.h>
 17 #include "netifd.h"
 18 #include "device.h"
 19 #include "interface.h"
 20 #include "ucode.h"
 21 
 22 static uc_vm_t vm;
 23 static uc_value_t *netifd_obj;
 24 static struct blob_buf b;
 25 
 26 struct uc_netifd_process {
 27         struct netifd_process proc;
 28         uc_value_t *res;
 29 };
 30 
 31 static uc_value_t *
 32 prop_get(uc_value_t *obj, const char *name, uc_type_t type)
 33 {
 34         uc_value_t *data = ucv_object_get(obj, name, NULL);
 35 
 36         if (!type || ucv_type(data) != type)
 37                 return NULL;
 38 
 39         return data;
 40 }
 41 
 42 static bool
 43 prop_get_bool(uc_value_t *obj, const char *name, bool *val)
 44 {
 45         uc_value_t *data = prop_get(obj, name, UC_BOOLEAN);
 46 
 47         if (data)
 48                 *val = ucv_boolean_get(data);
 49         return !!data;
 50 }
 51 
 52 
 53 static bool
 54 prop_get_int(uc_value_t *obj, const char *name, int *val)
 55 {
 56         uc_value_t *data = prop_get(obj, name, UC_INTEGER);
 57 
 58         if (data)
 59                 *val = ucv_int64_get(data);
 60 
 61         return !!data;
 62 }
 63 
 64 static uc_value_t *
 65 uc_netifd_device_set(uc_vm_t *vm, size_t nargs)
 66 {
 67         uc_value_t *name = uc_fn_arg(0);
 68         uc_value_t *data = uc_fn_arg(1);
 69         struct device *dev;
 70         bool check_vlan = true;
 71         int external = 0;
 72         bool bval;
 73         int ival;
 74 
 75         if (ucv_type(name) != UC_STRING || ucv_type(data) != UC_OBJECT)
 76                 return NULL;
 77 
 78         prop_get_int(data, "external", &external);
 79         prop_get_bool(data, "check_vlan", &check_vlan);
 80         dev = __device_get(ucv_string_get(name), external, check_vlan);
 81         if (!dev)
 82                 return NULL;
 83 
 84         if (prop_get_bool(data, "isolate", &bval)) {
 85                 dev->settings.flags |= DEV_OPT_ISOLATE;
 86                 dev->settings.isolate = bval;
 87         }
 88 
 89         if (prop_get_int(data, "multicast_to_unicast", &ival)) {
 90                 if (ival < 0) {
 91                         dev->settings.flags &= ~DEV_OPT_MULTICAST_TO_UNICAST;
 92                 } else {
 93                         dev->settings.flags |= DEV_OPT_MULTICAST_TO_UNICAST;
 94                         dev->settings.multicast_to_unicast = !!ival;
 95                 }
 96         }
 97 
 98         if (prop_get_bool(data, "wireless", &bval))
 99                 dev->wireless = bval;
100         if (prop_get_bool(data, "wireless_isolate", &bval))
101                 dev->wireless_isolate = bval;
102         if (prop_get_bool(data, "wireless_proxyarp", &bval))
103                 dev->wireless_proxyarp = bval;
104         if (prop_get_bool(data, "wireless_ap", &bval)) {
105                 dev->wireless_ap = bval;
106                 if (bval)
107                         dev->bpdu_filter = 1;
108         }
109 
110         return ucv_boolean_new(true);
111 }
112 
113 static uc_value_t *
114 uc_netifd_interface_get_bridge(uc_vm_t *vm, size_t nargs)
115 {
116         uc_value_t *name = uc_fn_arg(0);
117         uc_value_t *obj = uc_fn_arg(1);
118         struct device *dev, *orig_dev;
119         struct interface *iface;
120 
121         if (ucv_type(name) != UC_STRING)
122                 return NULL;
123 
124         iface = vlist_find(&interfaces, ucv_string_get(name), iface, node);
125         if (!iface)
126                 return NULL;
127 
128         dev = orig_dev = iface->main_dev.dev;
129         if (!dev)
130                 return NULL;
131 
132         if (ucv_type(obj) == UC_OBJECT)
133                 ucv_get(obj);
134         else
135                 obj = ucv_object_new(vm);
136 
137         if (!dev->hotplug_ops)
138                 return obj;
139 
140         if (dev->hotplug_ops && dev->hotplug_ops->prepare)
141                 dev->hotplug_ops->prepare(dev, &dev);
142 
143         if (!dev || !dev->type->bridge_capability)
144                 return obj;
145 
146         ucv_object_add(obj, "bridge", ucv_string_new(dev->ifname));
147         ucv_object_add(obj, "bridge-ifname", ucv_string_new(orig_dev->ifname));
148         if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST)
149                 ucv_object_add(obj, "multicast_to_unicast",
150                                ucv_boolean_new(dev->settings.multicast_to_unicast));
151 
152         return obj;
153 }
154 
155 static uc_value_t *
156 uc_netifd_interface_handle_link(uc_vm_t *vm, size_t nargs)
157 {
158         uc_value_t *args = uc_fn_arg(0);
159         uc_value_t *name = ucv_object_get(args, "name", NULL);
160         uc_value_t *ifname = ucv_object_get(args, "ifname", NULL);
161         uc_value_t *vlan = ucv_object_get(args, "vlan", NULL);
162         bool up = ucv_is_truish(ucv_object_get(args, "up", NULL));
163         bool link_ext = ucv_is_truish(ucv_object_get(args, "link_ext", NULL));
164         struct blob_attr *vlan_attr = NULL;
165         struct interface *iface;
166         const char *net;
167         int ret;
168 
169         if (ucv_type(name) != UC_STRING || ucv_type(ifname) != UC_STRING ||
170             (vlan && ucv_type(vlan) != UC_ARRAY))
171                 return NULL;
172 
173         net = ucv_string_get(name);
174         iface = vlist_find(&interfaces, net, iface, node);
175         if (!iface)
176                 return NULL;
177 
178         if (vlan) {
179                 size_t len = ucv_array_length(vlan);
180                 void *c;
181 
182                 blob_buf_init(&b, 0);
183                 c = blobmsg_open_array(&b, "vlan");
184                 for (size_t i = 0; i < len; i++) {
185                         uc_value_t *val = ucv_array_get(vlan, i);
186                         if (ucv_type(val) == UC_STRING)
187                                 blobmsg_add_string(&b, NULL, ucv_string_get(val));
188                 }
189                 blobmsg_close_array(&b, c);
190 
191                 vlan_attr = blobmsg_data(b.head);
192         }
193 
194         ret = interface_handle_link(iface, ucv_string_get(ifname), vlan_attr, up, link_ext);
195         return ucv_boolean_new(ret == 0);
196 }
197 
198 static void
199 netifd_call_cb(const char *name, size_t nargs, ...)
200 {
201         uc_value_t *val;
202         va_list ap;
203 
204         va_start(ap, nargs);
205         val = ucv_object_get(netifd_obj, "cb", NULL);
206         if (ucv_type(val) != UC_OBJECT)
207                 goto out;
208 
209         val = ucv_object_get(val, name, NULL);
210         if (!ucv_is_callable(val))
211                 goto out;
212 
213         uc_vm_stack_push(&vm, ucv_get(val));
214         for (size_t i = 0; i < nargs; i++)
215                 uc_vm_stack_push(&vm, va_arg(ap, void *));
216         va_end(ap);
217 
218         if (uc_vm_call(&vm, false, nargs) == EXCEPTION_NONE)
219                 ucv_put(uc_vm_stack_pop(&vm));
220 
221         return;
222 
223 out:
224         for (size_t i = 0; i < nargs; i++)
225                 ucv_put(va_arg(ap, void *));
226         va_end(ap);
227 }
228 
229 void netifd_ucode_config_load(bool start)
230 {
231         netifd_call_cb(start ? "config_start" : "config_init", 0);
232 }
233 
234 void netifd_ucode_check_network_enabled(void)
235 {
236         netifd_call_cb("check_interfaces", 0);
237 }
238 
239 void netifd_ucode_hotplug_event(const char *name, bool add)
240 {
241         netifd_call_cb("hotplug", 2, ucv_string_new(name), ucv_boolean_new(add));
242 }
243 
244 static uc_value_t *
245 uc_netifd_interface_get_enabled(uc_vm_t *vm, size_t nargs)
246 {
247         uc_value_t *name = uc_fn_arg(0);
248         struct interface *iface;
249         struct device *dev;
250         uc_value_t *val;
251 
252         if (ucv_type(name) != UC_STRING)
253                 return NULL;
254 
255         iface = vlist_find(&interfaces, ucv_string_get(name), iface, node);
256         if (!iface)
257                 return NULL;
258 
259         val = ucv_object_new(vm);
260         ucv_object_add(val, "enabled", ucv_boolean_new(!!iface->autostart));
261         dev = iface->main_dev.dev;
262         if (dev && dev->hotplug_ops)
263                 ucv_object_add(val, "ifindex", ucv_int64_new(dev->ifindex));
264 
265         return val;
266 }
267 
268 static void
269 uc_netifd_process_cb(struct netifd_process *proc, int ret)
270 {
271         struct uc_netifd_process *up = container_of(proc, struct uc_netifd_process, proc);
272 
273         uc_vm_stack_push(&vm, ucv_get(up->res));
274         uc_vm_stack_push(&vm, ucv_get(ucv_resource_value_get(up->res, 0)));
275         uc_vm_stack_push(&vm, ucv_int64_new(ret));
276 
277         if (uc_vm_call(&vm, true, 1) == EXCEPTION_NONE)
278                 ucv_put(uc_vm_stack_pop(&vm));
279 }
280 
281 static bool
282 fill_array(char **dest, uc_value_t *arr, size_t len)
283 {
284         if (ucv_type(arr) != UC_ARRAY)
285                 return false;
286 
287         for (size_t i = 0; i < len; i++) {
288                 uc_value_t *str = ucv_array_get(arr, i);
289                 if (ucv_type(str) != UC_STRING)
290                         return false;
291 
292                 dest[i] = strdup(ucv_string_get(str));
293         }
294         dest[len] = NULL;
295 
296         return true;
297 }
298 
299 static int
300 uc_netifd_start_process(uc_value_t *dir, uc_value_t *arg, uc_value_t *env, int *fd)
301 {
302         uc_value_t *fn;
303         char **argv;
304         size_t len;
305         int pfds[2];
306         int pid;
307 
308         len = ucv_array_length(arg);
309         if (!len)
310                 return -1;
311 
312         if (pipe(pfds) < 0)
313                 return -1;
314 
315         if ((pid = fork()) < 0)
316                 goto error;
317 
318         if (pid > 0) {
319                 close(pfds[1]);
320                 *fd = pfds[0];
321                 return pid;
322         }
323 
324         switch (ucv_type(dir)) {
325         case UC_OBJECT:
326                 fn = ucv_property_get(dir, "fileno");
327                 if (!ucv_is_callable(fn))
328                         break;
329 
330                 uc_vm_stack_push(&vm, ucv_get(dir));
331                 uc_vm_stack_push(&vm, ucv_get(fn));
332                 if (uc_vm_call(&vm, true, 0) != EXCEPTION_NONE)
333                         break;
334 
335                 dir = uc_vm_stack_pop(&vm);
336                 if (ucv_type(dir) != UC_INTEGER)
337                         break;
338                 fallthrough;
339         case UC_INTEGER:
340                 if (fchdir(ucv_int64_get(dir)) < 0)
341                         exit(1);
342                 break;
343         case UC_STRING:
344                 if (chdir(ucv_string_get(dir)) < 0)
345                         exit(1);
346                 break;
347         default:
348                 break;
349         }
350 
351         argv = calloc(len + 1, sizeof(*argv));
352         if (!fill_array(argv, arg, len))
353                 exit(127);
354 
355         len = ucv_array_length(env);
356         for (size_t i = 0; i < len; i++) {
357                 uc_value_t *strval = ucv_array_get(env, i);
358                 char *str = ucv_string_get(strval);
359 
360                 if (!str)
361                         continue;
362 
363                 putenv(strdup(str));
364         }
365 
366         for (int i = 0; i <= 2; i++) {
367                 if (pfds[1] == i)
368                         continue;
369 
370                 dup2(pfds[1], i);
371         }
372 
373         if (pfds[1] > 2)
374                 close(pfds[1]);
375 
376         execvp(argv[0], (char **) argv);
377         exit(127);
378 
379 error:
380         close(pfds[0]);
381         close(pfds[1]);
382         return -1;
383 }
384 
385 static uc_value_t *
386 uc_netifd_log(uc_vm_t *vm, size_t nargs)
387 {
388         uc_value_t *prio = uc_fn_arg(0);
389         uc_value_t *msg = uc_fn_arg(1);
390 
391         if (ucv_type(prio) != UC_INTEGER ||
392             ucv_type(msg) != UC_STRING)
393                 return NULL;
394 
395         netifd_log_message(ucv_int64_get(prio), "%s", ucv_string_get(msg));
396         return NULL;
397 }
398 
399 static uc_value_t *
400 uc_netifd_debug(uc_vm_t *vm, size_t nargs)
401 {
402         uc_value_t *msg = uc_fn_arg(0);
403 
404         if (ucv_type(msg) != UC_STRING)
405                 return NULL;
406 
407         netifd_udebug_printf("%s", ucv_string_get(msg));
408         return NULL;
409 }
410 
411 static uc_value_t *
412 uc_netifd_process(uc_vm_t *vm, size_t nargs)
413 {
414         uc_value_t *res, *cb, *arg, *env, *dir, *prefix;
415         uc_value_t *args = uc_fn_arg(0);
416         struct uc_netifd_process *up;
417         const char *prefix_str;
418         int pid, fd;
419 
420         if (ucv_type(args) != UC_OBJECT)
421                 return NULL;
422 
423         arg = ucv_object_get(args, "argv", NULL);
424         if (!ucv_array_length(arg))
425                 return NULL;
426 
427         env = ucv_object_get(args, "envp", NULL);
428         if (env && ucv_type(env) != UC_ARRAY)
429                 return NULL;
430 
431         dir = ucv_object_get(args, "dir", NULL);
432 
433         cb = ucv_object_get(args, "cb", NULL);
434         if (!ucv_is_callable(cb))
435                 return NULL;
436 
437         prefix = ucv_object_get(args, "log_prefix", NULL);
438         if (!prefix)
439                 prefix = ucv_array_get(arg, 0);
440         if (ucv_type(prefix) != UC_STRING)
441                 return NULL;
442 
443         prefix_str = ucv_string_get(prefix);
444 
445         res = ucv_resource_create_ex(vm, "netifd.process", (void **)&up, 1, sizeof(*up) + strlen(prefix_str + 1));
446         if (!res)
447                 return NULL;
448 
449         up->res = res;
450 
451         pid = uc_netifd_start_process(dir, arg, env, &fd);
452         if (pid < 0) {
453                 ucv_put(res);
454                 return NULL;
455         }
456 
457         up->proc.log_prefix = strcpy((char *)(up + 1), prefix_str);
458         up->proc.cb = uc_netifd_process_cb;
459         netifd_add_process(&up->proc, fd, pid);
460         ucv_resource_persistent_set(res, true);
461         ucv_resource_value_set(res, 0, ucv_get(cb));
462 
463         return res;
464 }
465 
466 static uc_value_t *
467 uc_netifd_process_cancel(uc_vm_t *vm, size_t nargs)
468 {
469         struct uc_netifd_process *up;
470         bool cancelled;
471 
472         up = uc_fn_thisval("netifd.process");
473         if (!up)
474                 return NULL;
475 
476         cancelled = up->proc.uloop.pending;
477         ucv_resource_persistent_set(up->res, false);
478         ucv_resource_value_set(up->res, 0, NULL);
479         netifd_kill_process(&up->proc);
480 
481         return ucv_boolean_new(cancelled);
482 }
483 
484 static void close_proc(void *ud)
485 {
486         netifd_kill_process(ud);
487 }
488 
489 static uc_value_t *
490 uc_netifd_process_check(uc_vm_t *vm, size_t nargs)
491 {
492         uc_value_t *pid = uc_fn_arg(0);
493         uc_value_t *exe = uc_fn_arg(1);
494         bool ret;
495 
496         if (ucv_type(pid) != UC_INTEGER || ucv_type(exe) != UC_STRING)
497                 return NULL;
498 
499         ret = check_pid_path(ucv_int64_get(pid), ucv_string_get(exe));
500 
501         return ucv_boolean_new(ret);
502 }
503 
504 static const uc_function_list_t proc_fns[] = {
505         { "cancel",                     uc_netifd_process_cancel },
506 };
507 
508 static const uc_function_list_t netifd_fns[] = {
509         { "log",                        uc_netifd_log },
510         { "debug",                      uc_netifd_debug },
511         { "process",                    uc_netifd_process },
512         { "process_check",              uc_netifd_process_check },
513         { "device_set",                 uc_netifd_device_set },
514         { "interface_get_enabled",      uc_netifd_interface_get_enabled },
515         { "interface_handle_link",      uc_netifd_interface_handle_link },
516         { "interface_get_bridge",       uc_netifd_interface_get_bridge },
517 };
518 
519 
520 void netifd_ucode_init(void)
521 {
522         static uc_parse_config_t config = {
523                 .strict_declarations = true,
524                 .lstrip_blocks = true,
525                 .trim_blocks = true,
526                 .raw_mode = true
527         };
528         uc_value_t *obj, *val;
529         uc_source_t *source;
530         uc_program_t *prog;
531         char *err;
532 
533         source = uc_source_new_file(DEFAULT_MAIN_PATH "/main.uc");
534         if (!source)
535                 return;
536 
537         uc_search_path_init(&config.module_search_path);
538         uc_search_path_add(&config.module_search_path, DEFAULT_MAIN_PATH "/*.so");
539         uc_search_path_add(&config.module_search_path, DEFAULT_MAIN_PATH "/*.uc");
540 
541         uc_vm_init(&vm, &config);
542         uc_stdlib_load(uc_vm_scope_get(&vm));
543         uc_type_declare(&vm, "netifd.process", proc_fns, close_proc);
544 
545         obj = netifd_obj = ucv_object_new(&vm);
546 
547         uc_vm_registry_set(&vm, "netifd.obj", ucv_get(obj));
548         ucv_object_add(uc_vm_scope_get(&vm), "netifd", obj);
549         ucv_object_add(obj, "cb", ucv_object_new(&vm));
550         ucv_object_add(obj, "main_path", ucv_string_new(DEFAULT_MAIN_PATH));
551         if (config_path)
552                 ucv_object_add(obj, "config_path", ucv_string_new(config_path));
553 #ifdef DUMMY_MODE
554         ucv_object_add(obj, "dummy_mode", ucv_boolean_new(true));
555 #endif
556 
557 #define ADD_CONST(n) ucv_object_add(obj, #n, ucv_int64_new(n))
558         ADD_CONST(L_CRIT);
559         ADD_CONST(L_WARNING);
560         ADD_CONST(L_NOTICE);
561         ADD_CONST(L_INFO);
562         ADD_CONST(L_DEBUG);
563 #undef ADD_CONST
564 
565         uc_function_list_register(obj, netifd_fns);
566 
567         prog = uc_compile(vm.config, source, &err);
568         uc_source_put(source);
569 
570         if (!prog) {
571                 netifd_log_message(L_CRIT, "Error loading ucode script: %s\n", err);
572                 netifd_ucode_free();
573                 return;
574         }
575 
576         uc_vm_execute(&vm, prog, &val);
577         uc_program_put(prog);
578         ucv_put(val);
579 }
580 
581 void netifd_ucode_free(void)
582 {
583         if (!vm.config)
584                 return;
585 
586         uc_search_path_free(&vm.config->module_search_path);
587         uc_vm_free(&vm);
588 }
589 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt