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