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 <limits.h> 15 16 #include <ucode/vm.h> 17 #include <ucode/lib.h> 18 #include <ucode/types.h> 19 #include <ucode/program.h> 20 21 #include "netifd.h" 22 #include "proto.h" 23 #include "proto-ext.h" 24 #include "proto-ucode.h" 25 #include "ucode.h" 26 27 struct proto_ucode_handler { 28 struct proto_handler proto; 29 uc_value_t *res; 30 char *script_name; 31 }; 32 33 static const struct blobmsg_policy proto_ucode_policy = { 34 .name = "_ucode_config", .type = BLOBMSG_TYPE_STRING, 35 }; 36 37 static const struct uci_blob_param_list proto_ucode_config_params = { 38 .n_params = 1, .params = &proto_ucode_policy, 39 }; 40 41 static void 42 proto_ucode_config_load(const struct proto_handler *h, 43 struct uci_section *s, struct blob_buf *b) 44 { 45 struct proto_ucode_handler *handler; 46 uc_value_t *netifd_obj, *cb_obj, *fn, *config_cb, *ret; 47 const char *str; 48 49 handler = container_of(h, struct proto_ucode_handler, proto); 50 51 netifd_obj = uc_vm_registry_get(&vm, "netifd.obj"); 52 if (!netifd_obj) 53 return; 54 55 cb_obj = ucv_object_get(netifd_obj, "cb", NULL); 56 if (ucv_type(cb_obj) != UC_OBJECT) 57 return; 58 59 fn = ucv_object_get(cb_obj, "proto_config_load", NULL); 60 if (!ucv_is_callable(fn)) 61 return; 62 63 config_cb = ucv_resource_value_get(handler->res, 0); 64 65 uc_vm_stack_push(&vm, ucv_get(fn)); 66 uc_vm_stack_push(&vm, ucv_get(config_cb)); 67 uc_vm_stack_push(&vm, ucv_string_new(s->e.name)); 68 69 if (uc_vm_call(&vm, false, 2) != EXCEPTION_NONE) { 70 D(INTERFACE, "proto_config_load callback failed for '%s'", 71 handler->proto.name); 72 return; 73 } 74 75 ret = uc_vm_stack_pop(&vm); 76 str = ucv_string_get(ret); 77 if (str) 78 blobmsg_add_string(b, "_ucode_config", str); 79 80 ucv_put(ret); 81 } 82 83 static int 84 proto_ucode_start(struct proto_ext_state *state, const char *action, 85 const char *config, char **envp) 86 { 87 struct proto_ucode_handler *handler; 88 char helper[PATH_MAX]; 89 const char *argv[9]; 90 int i = 0; 91 92 handler = container_of(state->proto.handler, struct proto_ucode_handler, proto); 93 94 snprintf(helper, sizeof(helper), "%s/proto-ucode.uc", main_path); 95 96 argv[i++] = "ucode"; 97 argv[i++] = helper; 98 argv[i++] = handler->script_name; 99 argv[i++] = handler->proto.name; 100 argv[i++] = action; 101 argv[i++] = state->proto.iface->name; 102 argv[i++] = config; 103 if (state->proto.iface->main_dev.dev) 104 argv[i++] = state->proto.iface->main_dev.dev->ifname; 105 argv[i] = NULL; 106 107 return netifd_start_process(argv, envp, &state->script_task); 108 } 109 110 static int 111 proto_ucode_handler(struct interface_proto_state *proto, 112 enum interface_proto_cmd cmd, bool force) 113 { 114 struct proto_ext_state *state; 115 116 state = container_of(proto, struct proto_ext_state, proto); 117 return proto_ext_run(state, cmd, force, proto_ucode_start); 118 } 119 120 static struct interface_proto_state * 121 proto_ucode_attach(const struct proto_handler *h, struct interface *iface, 122 struct blob_attr *attr) 123 { 124 struct proto_ext_state *state; 125 126 state = calloc(1, sizeof(*state)); 127 if (!state) 128 return NULL; 129 130 proto_ext_state_init(state, iface, attr, -1); 131 if (!state->config) { 132 free(state); 133 return NULL; 134 } 135 136 state->proto.cb = proto_ucode_handler; 137 138 return &state->proto; 139 } 140 141 static char * 142 proto_ucode_get_script_path(uc_vm_t *vm) 143 { 144 uc_callframe_t *frame; 145 uc_source_t *source; 146 147 for (size_t i = vm->callframes.count; i > 0; i--) { 148 frame = &vm->callframes.entries[i - 1]; 149 150 if (!frame->closure) 151 continue; 152 153 source = uc_program_function_source(frame->closure->function); 154 if (source && source->runpath) 155 return source->runpath; 156 } 157 158 return NULL; 159 } 160 161 uc_value_t * 162 uc_netifd_add_proto_fn(uc_vm_t *vm, size_t nargs) 163 { 164 uc_value_t *obj = uc_fn_arg(0); 165 uc_value_t *name_val, *config_val, *flag_val, *res; 166 struct proto_ucode_handler *handler; 167 struct proto_handler *proto; 168 const char *name; 169 char *script_path, *proto_name, *script_name; 170 171 if (ucv_type(obj) != UC_OBJECT) 172 return NULL; 173 174 name_val = ucv_object_get(obj, "name", NULL); 175 name = ucv_string_get(name_val); 176 if (!name) 177 return NULL; 178 179 script_path = proto_ucode_get_script_path(vm); 180 if (!script_path) 181 return NULL; 182 183 res = ucv_resource_create_ex(vm, "netifd.proto_handler", 184 (void **)&handler, 1, 185 sizeof(*handler) + strlen(name) + 1 + 186 strlen(script_path) + 1); 187 if (!res) 188 return NULL; 189 190 proto_name = (char *)(handler + 1); 191 script_name = proto_name + strlen(name) + 1; 192 193 handler->res = res; 194 handler->script_name = strcpy(script_name, script_path); 195 196 proto = &handler->proto; 197 proto->name = strcpy(proto_name, name); 198 proto->attach = proto_ucode_attach; 199 proto->config_load = proto_ucode_config_load; 200 proto->config_params = &proto_ucode_config_params; 201 202 config_val = ucv_object_get(obj, "config", NULL); 203 if (ucv_is_callable(config_val)) { 204 ucv_resource_value_set(res, 0, ucv_get(config_val)); 205 } 206 207 flag_val = ucv_object_get(obj, "no-device", NULL); 208 if (ucv_is_truish(flag_val)) 209 proto->flags |= PROTO_FLAG_NODEV; 210 211 flag_val = ucv_object_get(obj, "no-device-config", NULL); 212 if (ucv_is_truish(flag_val)) 213 proto->flags |= PROTO_FLAG_NODEV_CONFIG; 214 215 flag_val = ucv_object_get(obj, "no_proto_task", NULL); 216 if (ucv_is_truish(flag_val)) 217 proto->flags |= PROTO_FLAG_NO_TASK; 218 219 flag_val = ucv_object_get(obj, "available", NULL); 220 if (ucv_is_truish(flag_val)) 221 proto->flags |= PROTO_FLAG_INIT_AVAILABLE; 222 223 flag_val = ucv_object_get(obj, "renew-handler", NULL); 224 if (ucv_is_truish(flag_val)) 225 proto->flags |= PROTO_FLAG_RENEW_AVAILABLE; 226 227 flag_val = ucv_object_get(obj, "lasterror", NULL); 228 if (ucv_is_truish(flag_val)) 229 proto->flags |= PROTO_FLAG_LASTERROR; 230 231 flag_val = ucv_object_get(obj, "teardown-on-l3-link-down", NULL); 232 if (ucv_is_truish(flag_val)) 233 proto->flags |= PROTO_FLAG_TEARDOWN_ON_L3_LINK_DOWN; 234 235 ucv_resource_persistent_set(res, true); 236 237 D(INTERFACE, "Add ucode handler for script %s: %s", 238 handler->script_name, proto->name); 239 add_proto_handler(proto); 240 241 return ucv_boolean_new(true); 242 } 243
This page was automatically generated by LXR 0.3.1. • OpenWrt