From 8cf7493119fad82a2c5a27ccef6934fb0a787934 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 21 Mar 2025 12:13:51 +0100 Subject: [PATCH] wireless: replace with ucode scripts Wireless configuration is a lot easier to deal with in a high level language like ucode. Replace lots of C code with vastly simpler scripts. Signed-off-by: Felix Fietkau --- CMakeLists.txt | 11 +- config.c | 266 +----- config/wireless | 2 +- device.c | 4 +- examples/main.uc | 80 ++ examples/utils.uc | 131 +++ examples/wireless-device.uc | 609 +++++++++++++ examples/wireless.uc | 367 ++++++++ interface.c | 6 +- main.c | 29 +- netifd.h | 1 + scripts/netifd-wireless.sh | 7 - ubus.c | 186 ---- ucode.c | 582 ++++++++++++ ucode.h | 26 + wireless.c | 1704 ----------------------------------- wireless.h | 157 ---- 17 files changed, 1829 insertions(+), 2339 deletions(-) create mode 100644 examples/main.uc create mode 100644 examples/utils.uc create mode 100644 examples/wireless-device.uc create mode 100644 examples/wireless.uc create mode 100644 ucode.c create mode 100644 ucode.h delete mode 100644 wireless.c delete mode 100644 wireless.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 11c4f54..e30eae9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,22 +23,23 @@ SET(SOURCES interface.c interface-ip.c interface-event.c iprule.c proto.c proto-static.c proto-shell.c config.c device.c bridge.c veth.c vlan.c alias.c - macvlan.c ubus.c vlandev.c wireless.c - extdev.c bonding.c vrf.c) - + macvlan.c ubus.c vlandev.c extdev.c bonding.c + vrf.c ucode.c) FIND_LIBRARY(uci NAMES uci) FIND_LIBRARY(ubox NAMES ubox) FIND_LIBRARY(ubus NAMES ubus) +FIND_LIBRARY(ucode NAMES ucode) FIND_LIBRARY(json NAMES json-c json) FIND_LIBRARY(udebug NAMES udebug) FIND_LIBRARY(blobmsg_json NAMES blobmsg_json) -SET(LIBS ${ubox} ${ubus} ${uci} ${json} ${blobmsg_json} ${udebug}) +SET(LIBS ${ubox} ${ubus} ${uci} ${json} ${blobmsg_json} ${udebug} ${ucode}) FIND_PATH(ubox_include_dir libubox/usock.h) +FIND_PATH(ucode_include_dir ucode/vm.h) FIND_PATH(udebug_include_dir udebug.h) -INCLUDE_DIRECTORIES(${ubox_include_dir}) +INCLUDE_DIRECTORIES(${ubox_include_dir} ${ucode_include_dir}) IF (NOT DEFINED LIBNL_LIBS) include(FindPkgConfig) diff --git a/config.c b/config.c index 0ea39b5..f752da0 100644 --- a/config.c +++ b/config.c @@ -25,38 +25,18 @@ #include "interface-ip.h" #include "iprule.h" #include "proto.h" -#include "wireless.h" #include "config.h" #include "ubus.h" #include "system.h" +#include "ucode.h" bool config_init = false; static struct uci_context *uci_ctx; static struct uci_package *uci_network; -static struct uci_package *uci_wireless; static struct blob_attr *board_netdevs; static struct blob_buf b; -static int -config_section_idx(struct uci_section *s) -{ - struct uci_element *e; - int idx = 0; - - uci_foreach_element(&uci_wireless->sections, e) { - struct uci_section *cur = uci_to_section(e); - - if (s == cur) - return idx; - - if (!strcmp(cur->type, s->type)) - idx++; - } - - return -1; -} - static bool config_bridge_has_vlans(const char *br_name) { @@ -550,238 +530,6 @@ config_init_globals(void) system_udp_l3mdev(!strcmp(udp_l3mdev, "1")); } -static void -config_parse_wireless_device(struct uci_section *s) -{ - struct wireless_driver *drv; - const char *driver_name; - - driver_name = uci_lookup_option_string(uci_ctx, s, "type"); - if (!driver_name) - return; - - drv = avl_find_element(&wireless_drivers, driver_name, drv, node); - if (!drv) - return; - - blob_buf_init(&b, 0); - uci_to_blob(&b, s, drv->device.config); - wireless_device_create(drv, s->e.name, b.head); -} - -static void -config_parse_wireless_vlan(struct wireless_interface *vif, struct uci_section *s) -{ - char *name; - - name = alloca(strlen(s->type) + 16); - sprintf(name, "@%s[%d]", s->type, config_section_idx(s)); - - blob_buf_init(&b, 0); - uci_to_blob(&b, s, vif->wdev->drv->vlan.config); - wireless_vlan_create(vif, b.head, s->anonymous ? name : s->e.name); -} - -static void -config_parse_wireless_station(struct wireless_interface *vif, struct uci_section *s) -{ - char *name; - - name = alloca(strlen(s->type) + 16); - sprintf(name, "@%s[%d]", s->type, config_section_idx(s)); - - blob_buf_init(&b, 0); - uci_to_blob(&b, s, vif->wdev->drv->station.config); - wireless_station_create(vif, b.head, s->anonymous ? name : s->e.name); -} - -static void -config_wdev_parse_wireless_interface(struct wireless_device *wdev, struct uci_section *s) -{ - struct wireless_interface *vif; - struct uci_element *f; - char *name; - - name = alloca(strlen(s->type) + 16); - sprintf(name, "@%s[%d]", s->type, config_section_idx(s)); - - blob_buf_init(&b, 0); - uci_to_blob(&b, s, wdev->drv->interface.config); - vif = wireless_interface_create(wdev, b.head, s->anonymous ? name : s->e.name); - if (!vif) - return; - - if (s->anonymous) - goto out; - - uci_foreach_element(&uci_wireless->sections, f) { - struct uci_section *cur = uci_to_section(f); - const char *vif_name; - - if (strcmp(cur->type, "wifi-vlan") != 0) - continue; - - vif_name = uci_lookup_option_string(uci_ctx, cur, "iface"); - if (vif_name && strcmp(s->e.name, vif_name)) - continue; - config_parse_wireless_vlan(vif, cur); - } - - uci_foreach_element(&uci_wireless->sections, f) { - struct uci_section *cur = uci_to_section(f); - const char *vif_name; - - if (strcmp(cur->type, "wifi-station") != 0) - continue; - - vif_name = uci_lookup_option_string(uci_ctx, cur, "iface"); - if (vif_name && strcmp(s->e.name, vif_name)) - continue; - config_parse_wireless_station(vif, cur); - } - -out: - vlist_flush(&vif->vlans); - vlist_flush(&vif->stations); -} - -static void -config_init_procd_wireless_interface(const char *wdev_name, const char *vif_name, - struct blob_attr *config, - struct blob_attr *vlans, - struct blob_attr *stations) -{ - struct wireless_interface *vif; - struct wireless_device *wdev; - struct blob_attr *cur; - char name[16]; - int idx = 0; - size_t rem; - - wdev = vlist_find(&wireless_devices, wdev_name, wdev, node); - if (!wdev) { - D(WIRELESS, "device %s not found!", wdev_name); - return; - } - - vif = wireless_interface_create(wdev, config, vif_name); - if (!vif) - return; - - blobmsg_for_each_attr(cur, vlans, rem) { - snprintf(name, sizeof(name), "%d", ++idx); - wireless_vlan_create(vif, cur, name); - } - - blobmsg_for_each_attr(cur, stations, rem) { - snprintf(name, sizeof(name), "%d", ++idx); - wireless_station_create(vif, cur, name); - } - - vlist_flush(&vif->vlans); - vlist_flush(&vif->stations); -} - -static void -config_procd_wireless_interface_cb(struct blob_attr *data) -{ - enum { - UDATA_ATTR_DEVICE, - UDATA_ATTR_CONFIG, - UDATA_ATTR_STATIONS, - UDATA_ATTR_VLANS, - __UDATA_ATTR_MAX, - }; - static const struct blobmsg_policy policy[__UDATA_ATTR_MAX] = { - [UDATA_ATTR_DEVICE] = { "device", BLOBMSG_TYPE_STRING }, - [UDATA_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE }, - [UDATA_ATTR_STATIONS] = { "stations", BLOBMSG_TYPE_ARRAY }, - [UDATA_ATTR_VLANS] = { "vlans", BLOBMSG_TYPE_ARRAY }, - }; - struct blob_attr *tb[__UDATA_ATTR_MAX]; - const char *dev; - - blobmsg_parse_attr(policy, __UDATA_ATTR_MAX, tb, data); - if (!tb[UDATA_ATTR_DEVICE] || !tb[UDATA_ATTR_CONFIG]) - return; - - dev = blobmsg_get_string(tb[UDATA_ATTR_DEVICE]); - config_init_procd_wireless_interface(dev, blobmsg_name(data), - tb[UDATA_ATTR_CONFIG], - tb[UDATA_ATTR_VLANS], - tb[UDATA_ATTR_STATIONS]); -} - -static void -config_parse_wireless_interface(const char *dev_name, struct uci_section *s) -{ - struct wireless_device *wdev; - - wdev = vlist_find(&wireless_devices, dev_name, wdev, node); - if (!wdev) { - D(WIRELESS, "device %s not found!", dev_name); - return; - } - - config_wdev_parse_wireless_interface(wdev, s); -} - -static void -config_init_wireless(void) -{ - struct wireless_device *wdev; - struct uci_element *e; - - if (!uci_wireless) { - D(WIRELESS, "No wireless configuration found"); - return; - } - - vlist_update(&wireless_devices); - - uci_foreach_element(&uci_wireless->sections, e) { - struct uci_section *s = uci_to_section(e); - if (strcmp(s->type, "wifi-device") != 0) - continue; - - config_parse_wireless_device(s); - } - - vlist_flush(&wireless_devices); - - vlist_for_each_element(&wireless_devices, wdev, node) { - wdev->vif_idx = 0; - vlist_update(&wdev->interfaces); - } - - uci_foreach_element(&uci_wireless->sections, e) { - struct uci_section *s = uci_to_section(e); - struct uci_element *val; - struct uci_option *o; - - if (strcmp(s->type, "wifi-iface") != 0) - continue; - - o = uci_lookup_option(uci_ctx, s, "device"); - if (!o) - continue; - - if (o->type == UCI_TYPE_STRING) { - config_parse_wireless_interface(o->v.string, s); - continue; - } - - uci_foreach_element(&o->v.list, val) - config_parse_wireless_interface(val->name, s); - } - - netifd_ubus_get_procd_data("wifi-iface", config_procd_wireless_interface_cb); - - vlist_for_each_element(&wireless_devices, wdev, node) - vlist_flush(&wdev->interfaces); -} - - static struct blob_attr * config_find_blobmsg_attr(struct blob_attr *attr, const char *name, int type) { @@ -882,14 +630,6 @@ config_init_all(void) return -1; } - uci_wireless = config_init_package("wireless"); - if (!uci_wireless && uci_ctx->err != UCI_ERR_NOTFOUND) { - uci_get_errorstr(uci_ctx, &err, NULL); - netifd_log_message(L_CRIT, "Failed to load wireless config (%s)\n", err); - free(err); - ret = -1; - } - config_init_board(); vlist_update(&interfaces); @@ -903,7 +643,7 @@ config_init_all(void) config_init_ip(); config_init_rules(); config_init_globals(); - config_init_wireless(); + netifd_ucode_config_load(false); config_init = false; @@ -912,7 +652,7 @@ config_init_all(void) vlist_flush(&interfaces); interface_refresh_assignments(false); interface_start_pending(); - wireless_start_pending(0); + netifd_ucode_config_load(true); return ret; } diff --git a/config/wireless b/config/wireless index c92d2d3..b2c688d 100644 --- a/config/wireless +++ b/config/wireless @@ -4,7 +4,7 @@ config wifi-device radio0 option band 2g option channel 11 option htmode HE20 - error foo +# option disabled 1 config wifi-iface vif0 option device radio0 diff --git a/device.c b/device.c index 06f52cc..e07a817 100644 --- a/device.c +++ b/device.c @@ -24,7 +24,7 @@ #include "netifd.h" #include "system.h" #include "config.h" -#include "wireless.h" +#include "ucode.h" #include "ubus.h" static struct list_head devtypes = LIST_HEAD_INIT(devtypes); @@ -1457,7 +1457,7 @@ void device_hotplug_event(const char *name, bool add) { struct device *dev; - wireless_device_hotplug_event(name, add); + netifd_ucode_hotplug_event(name, add); dev = device_find(name); if (!dev || dev->type != &simple_device_type) diff --git a/examples/main.uc b/examples/main.uc new file mode 100644 index 0000000..a007394 --- /dev/null +++ b/examples/main.uc @@ -0,0 +1,80 @@ +import * as uci from "uci"; +import * as uloop from "uloop"; +import * as libubus from "ubus"; +import { access, dirname } from "fs"; + +function ex_handler(e) +{ + netifd.log(netifd.L_WARNING, `Exception: ${e}\n${e.stacktrace[0].context}\n`); +} + +uloop.exception_handler_set(ex_handler); +libubus.exception_handler_set(ex_handler); + +let ubus = netifd.ubus = libubus.connect(); +let wireless; + +function uci_ctx() +{ + let savedir = netifd.dummy_mode ? "./tmp" : null; + let ctx = uci.cursor(netifd.config_path, savedir, null, { + strict: false + }); + return ctx; +} + +function config_init() +{ + let ctx = uci_ctx(); + + if (wireless) + wireless.config_init(ctx); +} + +function config_start() +{ + if (wireless) + wireless.config_start(); +} + +function check_interfaces() +{ + if (wireless) + wireless.check_interfaces(); +} + +function hotplug(name, add) +{ + if (wireless) + wireless.hotplug(name, add); +} + +function ex_wrap(cb) +{ + let fn = cb; + return (...args) => { + try { + return fn(...args); + } catch (e) { + netifd.log(netifd.L_WARNING, `${e}\n${e.stacktrace[0].context}`); + } + }; +} + +netifd.cb = { + hotplug: ex_wrap(hotplug), + config_init: ex_wrap(config_init), + config_start: ex_wrap(config_start), + check_interfaces: ex_wrap(check_interfaces), +}; + +const wireless_module = dirname(sourcepath()) + "/wireless.uc"; +if (access(wireless_module, "r")) { + try { + wireless = loadfile(wireless_module)(); + } catch (e) { + netifd.log(netifd.L_WARNING, `Error loading wireless module: ${e}\n${e.stacktrace[0].context}\n`); + } +} else { + netifd.log(netifd.L_WARNING, `Wireless module not found\n`); +} diff --git a/examples/utils.uc b/examples/utils.uc new file mode 100644 index 0000000..84db69d --- /dev/null +++ b/examples/utils.uc @@ -0,0 +1,131 @@ +'use strict'; + +import { glob, basename, realpath, chdir, mkstemp } from "fs"; + +export const TYPE_ARRAY = 1; +export const TYPE_STRING = 3; +export const TYPE_INT = 5; +export const TYPE_BOOL = 7; + +export function parse_bool(val) +{ + switch (val) { + case "1": + case "true": + return true; + case "0": + case "false": + return false; + } +}; + +export function parse_array(val) +{ + if (type(val) != "array") + val = split(val, /\s+/); + return val; +}; + +function __type_parsers() +{ + let ret = []; + + ret[TYPE_ARRAY] = parse_array; + ret[TYPE_STRING] = function(val) { + return val; + }; + ret[TYPE_INT] = function(val) { + return +val; + }; + ret[TYPE_BOOL] = parse_bool; + + return ret; +} +export const type_parser = __type_parsers(); + +export function handler_load(path, cb) +{ + for (let script in glob(path + "/*.sh")) { + script = basename(script); + + let f = mkstemp(); + let prev_dir = realpath("."); + chdir(path); + system(`./${script} "" "dump" >&${f.fileno()}`); + chdir(prev_dir); + f.seek(); + while (!f.error()) { + let data = trim(f.read("line")); + try { + data = json(data); + } catch (e) { + continue; + } + + if (type(data) != "object") + continue; + + cb(script, data); + } + f.close(); + } +}; + +export function handler_attributes(data, extra, validate) +{ + let ret = { ...extra }; + for (let cur in data) { + let name_data = split(cur[0], ":", 2); + let name = name_data[0]; + ret[name] = cur[1]; + if (validate && name_data[1]) + validate[name] = name_data[1]; + } + return ret; +}; + +export function parse_attribute_list(data, spec) +{ + let ret = {}; + + for (let name, type_id in spec) { + if (!(name in data)) + continue; + + let val = data[name]; + let parser = type_parser[type_id]; + if (parser) + val = parser(val); + ret[name] = val; + } + + return ret; +}; + +export function is_equal(val1, val2) { + let t1 = type(val1); + + if (t1 != type(val2)) + return false; + + if (t1 == "array") { + if (length(val1) != length(val2)) + return false; + + for (let i = 0; i < length(val1); i++) + if (!is_equal(val1[i], val2[i])) + return false; + + return true; + } else if (t1 == "object") { + for (let key in val1) + if (!is_equal(val1[key], val2[key])) + return false; + for (let key in val2) + if (val1[key] == null) + return false; + return true; + } else { + return val1 == val2; + } +}; diff --git a/examples/wireless-device.uc b/examples/wireless-device.uc new file mode 100644 index 0000000..ceb4425 --- /dev/null +++ b/examples/wireless-device.uc @@ -0,0 +1,609 @@ +'use strict'; +import * as libubus from "ubus"; +import * as uloop from "uloop"; +import { is_equal } from "./utils.uc"; +import { access } from "fs"; + +const NOTIFY_CMD_UP = 0; +const NOTIFY_CMD_SET_DATA = 1; +const NOTIFY_CMD_PROCESS_ADD = 2; +const NOTIFY_CMD_SET_RETRY = 4; + +const DEFAULT_RETRY = 3; +const DEFAULT_SCRIPT_TIMEOUT = 30 * 1000; + +let wdev_cur; +let wdev_handler = {}; +let wdev_script_task, wdev_script_timeout; +let handler_timer; + +function delete_wdev(name) +{ + delete netifd.wireless.devices[name]; + gc(); +} + +function handle_link(dev, data, up) +{ + let config = data.config; + let bridge_isolate; + let ap = false; + if (dev == data.ifname) + ap = data.type == "vlan" || + (data.type == "vif" && config.mode == "ap"); + + let dev_data = { + isolate: !!config.bridge_isolate, + wireless: true, + wireless_ap: ap, + }; + + if (ap && config.multicast_to_unicast != null) + dev_data.multicast_to_unicast = config.multicast_to_unicast; + + if (data.type == "vif" && config.mode == "ap") { + dev_data.wireless_proxyarp = !!config.proxy_arp; + dev_data.wireless_isolate = !!config.isolate; + } + + if (up) + netifd.device_set(dev, dev_data); + + for (let net in config.network) + netifd.interface_handle_link({ + name: net, + ifname: dev, + vlan: config.network_vlan, + link_ext: true, + up, + }); +} + +function wdev_config_init(wdev) +{ + let data = wdev.data; + let config = data.config; + let interfaces = {}; + + let vif_idx = 0; + for (let vif in data.vif) { + let vlan_idx = 0, sta_idx = 0; + let vlans = {}, stas = {}; + + if (wdev.disabled_vifs[vif.name]) + continue; + for (let vlan in vif.vlan) { + let vlan_name = sprintf("%02d", ++vlan_idx); + let cur_vlan = vlans[vlan_name] = { + name: vlan.name, + config: vlan.config, + }; + + if (wdev.disabled_vifs[vif.name]) + continue; + for (let net in vlan.config.network) + if (netifd.interface_get_bridge(net, cur_vlan)) + break; + } + + for (let sta in vif.sta) { + let sta_name = sprintf("%02d", ++sta_idx); + stas[sta_name] = { + name: sta.name, + config: sta.config, + }; + } + + let vif_name = sprintf("%02d", ++vif_idx); + let iface = interfaces[vif_name] = { + name: vif.name, + config: vif.config, + vlans, stas, + }; + + for (let net in vif.config.network) + if (netifd.interface_get_bridge(net, iface)) + break; + } + + wdev.handler_config = { + config, + interfaces, + }; + + let prev = wdev.handler_data; + wdev.handler_data = {}; + + if (prev && prev[wdev.name]) + wdev.handler_data[wdev.name] = prev[wdev.name]; +} + +function wdev_setup_cb(wdev) +{ + if (wdev.state != "setup") + return; + + if (wdev.retry > 0) + wdev.retry--; + else + wdev.retry_setup_failed = true; + + wdev.teardown(); +} + +function wdev_teardown_cb(wdev) +{ + for (let section, data in wdev.handler_data) { + if (data.ifname) + handle_link(data.ifname, data, false); + } + + wdev.handler_data = {}; + wdev.state = "down"; + + if (wdev.delete) { + delete_wdev(wdev.data.name); + return; + } + + wdev.setup(); +} + +function run_handler_cb(wdev, cb) +{ + if (wdev != wdev_cur.wdev) + return; + + wdev.dbg("complete " + wdev_cur.op); + if (wdev_script_timeout) + wdev_script_timeout.cancel(); + wdev_script_timeout = null; + wdev_script_task = null; + wdev_cur = null; + handler_timer.set(1); + cb(wdev); +} + +function run_handler_timeout(wdev, cb) +{ + wdev_script_task.cancel(); + run_handler_cb(wdev, cb); +} + +function handler_sort_fn(a, b) +{ + return wdev_handler[a].time - wdev_handler[b].time +} + +function __run_next_handler_name() +{ + return sort(keys(wdev_handler), handler_sort_fn)[0]; +} + +function __run_next_handler() +{ + let name = __run_next_handler_name(); + if (!name) + return; + + wdev_cur = wdev_handler[name]; + delete wdev_handler[name]; + + let wdev = wdev_cur.wdev; + let op = wdev_cur.op; + let cb = wdev_cur.cb; + + wdev.dbg("run " + op); + wdev_script_task = netifd.process({ + cb: () => run_handler_cb(wdev, cb), + dir: netifd.wireless.path, + argv: [ './' + wdev.script, wdev.data.config.type, op, wdev.name, "" + wdev.handler_config ], + log_prefix: wdev.name, + }); + + if (!wdev_script_task) + return run_handler_cb(wdev, cb); + + wdev_script_timeout = uloop.timer(DEFAULT_SCRIPT_TIMEOUT, + () => run_handler_timeout(wdev, cb) + ); +} + +function run_next_handler() +{ + while (!wdev_cur && length(wdev_handler) > 0) + __run_next_handler(); +} + +function run_handler(wdev, op, cb) +{ + wdev.dbg("queue " + op); + wdev.handler_config.data = wdev.handler_data[wdev.name]; + wdev_handler[wdev.name] = { + op, wdev, cb, + time: time() + }; + + run_next_handler(); +} + +function wdev_proc_reset(wdev) +{ + if (wdev.proc_timer) { + wdev.proc_timer.cancel(); + delete wdev.proc_timer; + } + + wdev.procs = []; +} + +function __wdev_proc_check(wdev, proc) +{ + if (netifd.process_check(proc.pid, proc.exe)) + return; + + wdev.dbg(`process ${proc.exe}(${proc.pid}) no longer active`); + wdev.teardown(); + return true; +} + +function wdev_proc_check(wdev) +{ + for (let proc in wdev.procs) + if (__wdev_proc_check(wdev, proc)) + break; +} + +function wdev_proc_add(wdev, data) +{ + if (!data.pid || !data.exe) + return; + + push(wdev.procs, data); + + if (!wdev.proc_timer) + wdev.proc_timer = uloop.interval(1000, () => wdev_proc_check(wdev)); +} + + +function setup() +{ + if (this.state != "up" && this.state != "down") + return; + + this.dbg("setup, state=" + this.state); + if (!this.autostart || this.retry_setup_failed || this.data.config.disabled) + return; + + wdev_proc_reset(this); + delete this.config_change; + this.state = "setup"; + run_handler(this, "setup", wdev_setup_cb); +} + +function teardown() +{ + delete this.cancel_setup; + + this.dbg("teardown, state=" + this.state); + if (this.state == "teardown" || this.state == "down") + return; + + wdev_proc_reset(this); + this.state = "teardown"; + run_handler(this, "teardown", wdev_teardown_cb); +} + +function wdev_update_disabled_vifs(wdev) +{ + let cache = wdev.ifindex_cache; + let prev_disabled = wdev.disabled_vifs; + let disabled = wdev.disabled_vifs = {}; + let changed; + + let vifs = []; + for (let vif in wdev.data.vif) + push(vifs, vif, ...vif.vlan); + + for (let vif in vifs) { + let enabled, ifindex; + + for (let net in vif.config.network) { + let state = netifd.interface_get_enabled(net); + if (!state) + continue; + + if (state.enabled) + enabled = true; + else if (enabled == null) + enabled = false; + if (state.ifindex) + ifindex = state.ifindex; + } + + let name = vif.name; + if (enabled == false) + disabled[wdev] = true; + else if (ifindex != cache[name]) + changed = true; + + if (ifindex) + cache[name] = ifindex; + else + delete cache[name]; + } + + if (changed || !is_equal(prev_disabled, disabled)) + wdev.config_change = true; + + return wdev.config_change; +} + +function wdev_reset(wdev) +{ + wdev.retry = DEFAULT_RETRY; + delete wdev.retry_setup_failed; +} + +function update(data) +{ + if (is_equal(this.data, data)) + return; + + if (data) { + this.data = data; + this.ifindex_cache = {}; + delete this.retry_setup_failed; + delete this.delete; + } + + wdev_reset(this); + this.config_change = true; + this.check(); +} + +function start() +{ + if (this.delete || this.data.config.disabled) + return; + + this.dbg("start, state=" + this.state); + this.autostart = true; + wdev_reset(this); + + if (this.state != "down") + return; + + if (wdev_update_disabled_vifs(this)) + wdev_config_init(this); + this.setup(); +} + +function stop() +{ + this.dbg("stop, state=" + this.state); + this.autostart = false; + + switch (this.state) { + case "setup": + this.cancel_setup = true; + break; + case "up": + this.teardown(); + break; + } +} + +function check() +{ + if (!wdev_update_disabled_vifs(this)) + return; + + wdev_config_init(this); + this.setup(); +} + +function wdev_mark_up(wdev) +{ + wdev.dbg("mark up, state=" + wdev.state); + if (wdev.state != "setup") + return; + + if (wdev.config_change) { + wdev.setup(); + return; + } + + for (let section, data in wdev.handler_data) { + if (data.ifname) + handle_link(data.ifname, data, true); + } + wdev.state = "up"; + + return 0; +} + +function wdev_set_data(wdev, vif, vlan, data) +{ + let config = wdev.handler_config; + let cur = wdev; + let cur_type = "device"; + if (!config) + return libubus.STATUS_INVALID_ARGUMENT; + + if (vif) { + cur = vif = config.interfaces[vif]; + if (!vif) + return libubus.STATUS_NOT_FOUND; + cur_type = "vif"; + } + + if (vlan) { + if (!vif) + return libubus.STATUS_INVALID_ARGUMENT; + + cur = vlan = vif.vlans[vlan]; + if (!vlan) + return libubus.STATUS_NOT_FOUND; + + cur_type = "vlan"; + } + + wdev.handler_data[cur.name] = { + ...cur, + ...data, + type: cur_type, + config: cur.config, + }; + + return 0; +} + +function notify(req) +{ + let vif = req.args.interface; + let vlan = req.args.vlan; + let data = req.args.data; + + switch (req.args.command) { + case NOTIFY_CMD_UP: + if (vif || vlan || this.state != "setup") + return libubus.STATUS_INVALID_ARGUMENT; + + return wdev_mark_up(this); + case NOTIFY_CMD_SET_DATA: + return wdev_set_data(this, vif, vlan, data); + case NOTIFY_CMD_PROCESS_ADD: + if (this.state != "setup" && this.state != "up") + return 0; + + wdev_proc_add(this, data); + return 0; + case NOTIFY_CMD_SET_RETRY: + if (data.retry != null) + this.retry = data.retry; + else + this.retry = DEFAULT_RETRY; + return 0; + default: + return libubus.STATUS_INVALID_ARGUMENT; + } +} + +function hotplug(name, add) +{ + let dev = name; + let m = match(name, /(.+)\.sta.+/); + if (m) + name = m[1]; + + for (let section, data in this.handler_data) { + if (data.ifname != name || + data.type != "vif" && data.type != "vlan") + continue; + + handle_link(dev, data, up); + } +} + +function get_status_data(wdev, vif) +{ + let hdata = wdev.handler_data[vif.name]; + let data = { + section: vif.name, + config: vif.config + }; + if (hdata && hdata.ifname) + data.ifname = hdata.ifname; + return data; +} + +function get_status_vlans(wdev, vif) +{ + let vlans = []; + for (let vlan in vif.vlan) + push(vlans, get_status_data(wdev, vlan)); + return vlans; +} + +function get_status_stations(wdev, vif) +{ + let vlans = []; + for (let vlan in vif.sta) + push(vlans, get_status_data(wdev, vlan)); + return vlans; +} + +function status() +{ + let interfaces = []; + for (let vif in this.data.vif) { + let vlans = get_status_vlans(this, vif); + let stations = get_status_stations(this, vif); + let data = get_status_data(this, vif); + push(interfaces, { + ...data, + vlans, stations + }); + } + return { + up: this.state == "up", + pending: this.state == "setup" || this.state == "teardown", + autostart: this.autostart, + disabled: !!this.data.config.disabled, + retry_setup_failed: !!this.retry_setup_failed, + config: this.data.config, + interfaces + }; +} + +function destroy() +{ + this.dbg("destroy"); + this.autostart = false; + this.delete = true; + if (this.state != "down") { + this.stop(); + return; + } + + delete_wdev(this.data.name); +} + +function dbg(msg) +{ + netifd.log(netifd.L_DEBUG, `wireless: ${this.name}: ${msg}\n`); +} + +const wdev_proto = { + update, + destroy, + start, + stop, + setup, + status, + teardown, + check, + notify, + hotplug, + dbg, +}; + +export function new(data, script, driver) +{ + let wdev = { + name: data.name, + script, data, + procs: [], + vifs: {}, + disabled_vifs: {}, + ifindex_cache: {}, + + autostart: true, + state: "down", + }; + wdev_update_disabled_vifs(wdev); + wdev_config_init(wdev); + handler_timer = uloop.timer(1, run_next_handler); + return proto(wdev, wdev_proto); +}; diff --git a/examples/wireless.uc b/examples/wireless.uc new file mode 100644 index 0000000..1f51f44 --- /dev/null +++ b/examples/wireless.uc @@ -0,0 +1,367 @@ +'use strict'; + +import * as libubus from "ubus"; +import { realpath } from "fs"; +import { + handler_load, handler_attributes, + parse_attribute_list, parse_bool, parse_array, + TYPE_ARRAY, TYPE_STRING, TYPE_INT, TYPE_BOOL +} from "./utils.uc"; +import * as wdev from "./wireless-device.uc"; + +let ubus = netifd.ubus; +let wireless = netifd.wireless = { + handlers: {}, + devices: {}, + path: realpath(netifd.main_path + "/wireless"), +}; + +function update_config(new_devices) +{ + for (let name, dev in wireless.devices) + if (!new_devices[name]) + dev.destroy(); + + for (let name, dev in new_devices) { + let cur_dev = wireless.devices[name]; + if (cur_dev) { + cur_dev.update(dev); + continue; + } + + let handler = wireless.handlers[dev.config.type]; + cur_dev = wdev.new(dev, handler.script); + if (!cur_dev) + continue; + + wireless.devices[name] = cur_dev; + } +} + +function config_init(uci) +{ + let config = uci.get_all("wireless"); + + let handlers = {}; + let devices = {}; + let vifs = {}; + + let sections = { + device: {}, + iface: {}, + vlan: {}, + station: {}, + }; + + for (let name, data in config) { + let type = data[".type"]; + if (parse_bool(data.disabled) && type != "wifi-device") + continue; + + if (substr(type, 0, 5) != "wifi-") + continue; + + let list = sections[substr(type, 5)]; + if (list) + list[name] = data; + } + + for (let name, data in sections.device) { + if (!data.type) + continue; + + let handler = wireless.handlers[data.type]; + if (!handler) + continue; + + let config = parse_attribute_list(data, handler.device); + devices[name] = { + name, + config, + + vif: [], + }; + handlers[name] = handler; + } + + for (let name, data in sections.iface) { + let dev_names = parse_array(data.device); + for (let dev_name in dev_names) { + let dev = devices[dev_name]; + if (!dev) + continue; + + let handler = handlers[dev_name]; + if (!handler) + continue; + + let config = parse_attribute_list(data, handler.iface); + + let vif = { + name, config, + device: dev_name, + + vlan: [], + sta: [], + }; + push(dev.vif, vif); + + vifs[name] ??= []; + push(vifs[name], vif); + } + } + + for (let name, data in sections.vlan) { + if (!data.iface || !vifs[data.iface]) + continue; + + for (let vif in vifs[data.iface]) { + let dev = devices[vif.device]; + let handler = handlers[vif.device]; + if (!dev || !handler) + continue; + + let config = parse_attribute_list(data, handler.vlan); + + let vlan = { + name, + config + }; + push(vif.vlan, vlan); + } + } + + for (let name, data in sections.station) { + if (!data.iface || !vifs[data.iface]) + continue; + + for (let vif in vifs[data.iface]) { + let dev = devices[vif.device]; + let handler = handlers[vif.device]; + if (!dev || !handler) + continue; + + let config = parse_attribute_list(data, handler.station); + + let sta = { + name, + config + }; + push(vif.sta, sta); + } + } + + update_config(devices); +} + +function config_start() +{ + for (let name, dev in wireless.devices) + if (dev.autostart) + dev.start(); + +} + +function check_interfaces() +{ + for (let name, dev in wireless.devices) + if (dev.autostart) + dev.check(); +} + +function hotplug(name, add) +{ + for (let name, dev in wireless.devices) + if (dev.autostart) + dev.hotplug(name, add); +} + +const network_config_attr = { + network: TYPE_ARRAY, + network_vlan: TYPE_ARRAY, + bridge_isolate: TYPE_BOOL, + isolate: TYPE_BOOL, + proxy_arp: TYPE_BOOL, + multicast_to_unicast: TYPE_BOOL, +}; + +const default_config_attr = { + device: { + disabled: TYPE_BOOL, + type: TYPE_STRING, + }, + iface: { + ...network_config_attr, + device: TYPE_STRING, + mode: TYPE_STRING, + }, + station: { + iface: TYPE_STRING, + + mac: TYPE_STRING, + key: TYPE_STRING, + vid: TYPE_STRING, + }, + vlan: { + ...network_config_attr, + iface: TYPE_STRING, + name: TYPE_STRING, + vid: TYPE_STRING, + }, +}; + +const wdev_args = { + device: "" +}; + +function wdev_call(req, cb) +{ + let dev = req.args.device; + if (dev) { + dev = wireless.devices[dev]; + if (!dev) + return libubus.STATUS_NOT_FOUND; + + return cb(dev); + } + + for (let name, dev in wireless.devices) + cb(dev); + + return 0; +} + +function attr_validate(attr_type, validate) +{ + if (validate) + return validate; + switch (attr_type) { + case TYPE_STRING: + return "string"; + case TYPE_ARRAY: + return "list(string)"; + case TYPE_INT: + return "uinteger"; + case TYPE_BOOL: + return "bool"; + } +} + +function get_validate_info(ret, handler) +{ + for (let kind in default_config_attr) { + let cur = ret[kind == "iface" ? "interface" : kind] = {}; + let validate = handler[kind + "_validate"]; + + for (let attr, attr_type in handler[kind]) { + let val = attr_validate(attr_type, validate[attr]); + if (val != null) + cur[attr] = val; + } + } + + return ret; +} + +const ubus_obj = { + up: { + args: wdev_args, + call: function(req) { + return wdev_call(req, (dev) => { + dev.start(); + return 0; + }); + } + }, + down: { + args: wdev_args, + call: function(req) { + return wdev_call(req, (dev) => { + dev.stop(); + return 0; + }); + } + }, + reconf: { + args: wdev_args, + call: function(req) { + return wdev_call(req, (dev) => { + dev.update(); + return 0; + }); + } + }, + status: { + args: wdev_args, + call: function(req) { + let ret = {}; + let err = wdev_call(req, (dev) => { + ret[dev.data.name] = dev.status(); + return 0; + }); + if (err != 0) + return err; + + return ret; + } + }, + notify: { + args: { + ...wdev_args, + command: 0, + interface: "", + vlan: "", + data: {}, + }, + call: function(req) { + let dev = req.args.device; + if (!dev) + return libubus.STATUS_INVALID_ARGUMENT; + + dev = wireless.devices[dev]; + if (!dev) + return libubus.STATUS_NOT_FOUND; + + return dev.notify(req); + } + }, + get_validate: { + args: wdev_args, + call: function(req) { + let ret = {}; + let err = wdev_call(req, (dev) => { + let dev_type = dev.data.config.type; + let cur = ret[dev.data.name] = {}; + get_validate_info(cur, wireless.handlers[dev_type]); + return 0; + }); + if (err != 0) + return err; + + return ret; + } + }, +}; + + +handler_load(wireless.path, (script, data) => { + if (!data.name) + return; + + let handler = wireless.handlers[data.name] = { + script, + }; + for (let kind, attr in default_config_attr) { + let validate = handler[kind + "_validate"] = {}; + handler[kind] = handler_attributes(data[kind], attr, validate); + } +}); + +wireless.obj = ubus.publish("network.wireless", ubus_obj); + +return { + hotplug, + config_init, + config_start, + check_interfaces, +}; diff --git a/interface.c b/interface.c index 60b1807..2ae94ae 100644 --- a/interface.c +++ b/interface.c @@ -23,9 +23,9 @@ #include "interface-ip.h" #include "proto.h" #include "ubus.h" +#include "ucode.h" #include "config.h" #include "system.h" -#include "wireless.h" struct vlist_tree interfaces; static LIST_HEAD(iface_all_users); @@ -1137,7 +1137,7 @@ interface_set_up(struct interface *iface) const char *error = NULL; iface->autostart = true; - wireless_check_network_enabled(); + netifd_ucode_check_network_enabled(); if (iface->state != IFS_DOWN) return; @@ -1170,7 +1170,7 @@ interface_set_down(struct interface *iface) __interface_set_down(iface, false); } else { iface->autostart = false; - wireless_check_network_enabled(); + netifd_ucode_check_network_enabled(); __interface_set_down(iface, false); } } diff --git a/main.c b/main.c index ac0fcfe..6655e00 100644 --- a/main.c +++ b/main.c @@ -24,9 +24,9 @@ #include "config.h" #include "system.h" #include "interface.h" -#include "wireless.h" #include "proto.h" #include "extdev.h" +#include "ucode.h" unsigned int debug_mask = 0; const char *main_path = DEFAULT_MAIN_PATH; @@ -190,6 +190,20 @@ netifd_process_cb(struct uloop_process *proc, int ret) return; } +void +netifd_add_process(struct netifd_process *proc, int fd, int pid) +{ + proc->uloop.cb = netifd_process_cb; + proc->uloop.pid = pid; + uloop_process_add(&proc->uloop); + list_add_tail(&proc->list, &process_list); + + system_fd_set_cloexec(fd); + proc->log.stream.string_data = true; + proc->log.stream.notify_read = netifd_process_log_read_cb; + ustream_fd_init(&proc->log, fd); +} + int netifd_start_process(const char **argv, char **env, struct netifd_process *proc) { @@ -233,15 +247,7 @@ netifd_start_process(const char **argv, char **env, struct netifd_process *proc) } close(pfds[1]); - proc->uloop.cb = netifd_process_cb; - proc->uloop.pid = pid; - uloop_process_add(&proc->uloop); - list_add_tail(&proc->list, &process_list); - - system_fd_set_cloexec(pfds[0]); - proc->log.stream.string_data = true; - proc->log.stream.notify_read = netifd_process_log_read_cb; - ustream_fd_init(&proc->log, pfds[0]); + netifd_add_process(proc, pfds[0], pid); return 0; @@ -391,7 +397,7 @@ int main(int argc, char **argv) proto_shell_init(); extdev_init(); - wireless_init(); + netifd_ucode_init(); if (system_init()) { fprintf(stderr, "Failed to initialize system control\n"); @@ -404,6 +410,7 @@ int main(int argc, char **argv) netifd_kill_processes(); netifd_ubus_done(); + netifd_ucode_free(); if (use_syslog) closelog(); diff --git a/netifd.h b/netifd.h index 0c0537f..25a1139 100644 --- a/netifd.h +++ b/netifd.h @@ -107,6 +107,7 @@ void netifd_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data, void netifd_log_message(int priority, const char *format, ...) __attribute__((format (printf, 2, 3))); +void netifd_add_process(struct netifd_process *proc, int fd, int pid); int netifd_start_process(const char **argv, char **env, struct netifd_process *proc); void netifd_kill_process(struct netifd_process *proc); diff --git a/scripts/netifd-wireless.sh b/scripts/netifd-wireless.sh index bb3e5c7..74661f0 100644 --- a/scripts/netifd-wireless.sh +++ b/scripts/netifd-wireless.sh @@ -187,12 +187,6 @@ _wireless_add_process() { _wdev_notify } -_wireless_process_kill_all() { - _wdev_notify_init $CMD_PROCESS_KILL_ALL - [ -n "$1" ] && json_add_int signal "$1" - _wdev_notify -} - _wireless_set_retry() { _wdev_notify_init $CMD_SET_RETRY json_add_int retry "$1" @@ -205,7 +199,6 @@ _wdev_wrapper \ wireless_set_up \ wireless_set_data \ wireless_add_process \ - wireless_process_kill_all \ wireless_set_retry \ wireless_vif_parse_encryption() { diff --git a/ubus.c b/ubus.c index 5ab1884..1416fa6 100644 --- a/ubus.c +++ b/ubus.c @@ -22,7 +22,6 @@ #include "proto.h" #include "ubus.h" #include "system.h" -#include "wireless.h" struct ubus_context *ubus_ctx = NULL; static struct blob_buf b; @@ -1187,180 +1186,6 @@ static void netifd_add_iface_object(void) netifd_add_object(&iface_object); } -static struct wireless_device * -get_wdev(struct blob_attr *msg, int *ret) -{ - struct blobmsg_policy wdev_policy = { - .name = "device", - .type = BLOBMSG_TYPE_STRING, - }; - struct blob_attr *dev_attr; - struct wireless_device *wdev = NULL; - - - blobmsg_parse(&wdev_policy, 1, &dev_attr, blob_data(msg), blob_len(msg)); - if (!dev_attr) { - *ret = UBUS_STATUS_INVALID_ARGUMENT; - return NULL; - } - - wdev = vlist_find(&wireless_devices, blobmsg_data(dev_attr), wdev, node); - if (!wdev) { - *ret = UBUS_STATUS_NOT_FOUND; - return NULL; - } - - *ret = 0; - return wdev; -} - -static int -netifd_handle_wdev_reconf(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct wireless_device *wdev; - int ret; - - wdev = get_wdev(msg, &ret); - if (ret == UBUS_STATUS_NOT_FOUND) - return ret; - - if (wdev) { - wireless_device_reconf(wdev); - } else { - vlist_for_each_element(&wireless_devices, wdev, node) - wireless_device_reconf(wdev); - } - - return 0; -} - -static int -netifd_handle_wdev_up(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct wireless_device *wdev; - int ret; - - wdev = get_wdev(msg, &ret); - if (ret == UBUS_STATUS_NOT_FOUND) - return ret; - - if (wdev) { - wireless_device_set_up(wdev); - } else { - vlist_for_each_element(&wireless_devices, wdev, node) - wireless_device_set_up(wdev); - } - - return 0; -} - -static int -netifd_handle_wdev_down(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct wireless_device *wdev; - int ret; - - wdev = get_wdev(msg, &ret); - if (ret == UBUS_STATUS_NOT_FOUND) - return ret; - - if (wdev) { - wireless_device_set_down(wdev); - } else { - vlist_for_each_element(&wireless_devices, wdev, node) - wireless_device_set_down(wdev); - } - - return 0; -} - -static int -netifd_handle_wdev_status(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct wireless_device *wdev; - int ret; - - wdev = get_wdev(msg, &ret); - if (ret == UBUS_STATUS_NOT_FOUND) - return ret; - - blob_buf_init(&b, 0); - if (wdev) { - wireless_device_status(wdev, &b); - } else { - vlist_for_each_element(&wireless_devices, wdev, node) - wireless_device_status(wdev, &b); - } - ubus_send_reply(ctx, req, b.head); - return 0; -} - -static int -netifd_handle_wdev_get_validate(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct wireless_device *wdev; - int ret; - - wdev = get_wdev(msg, &ret); - if (ret == UBUS_STATUS_NOT_FOUND) - return ret; - - blob_buf_init(&b, 0); - if (wdev) { - wireless_device_get_validate(wdev, &b); - } else { - vlist_for_each_element(&wireless_devices, wdev, node) - wireless_device_get_validate(wdev, &b); - } - ubus_send_reply(ctx, req, b.head); - return 0; -} - -static int -netifd_handle_wdev_notify(struct ubus_context *ctx, struct ubus_object *obj, - struct ubus_request_data *req, const char *method, - struct blob_attr *msg) -{ - struct wireless_device *wdev; - int ret; - - wdev = get_wdev(msg, &ret); - if (!wdev) - return ret; - - return wireless_device_notify(wdev, msg, req); -} - -static struct ubus_method wireless_object_methods[] = { - { .name = "up", .handler = netifd_handle_wdev_up }, - { .name = "down", .handler = netifd_handle_wdev_down }, - { .name = "reconf", .handler = netifd_handle_wdev_reconf }, - { .name = "status", .handler = netifd_handle_wdev_status }, - { .name = "notify", .handler = netifd_handle_wdev_notify }, - { .name = "get_validate", .handler = netifd_handle_wdev_get_validate }, -}; - -static struct ubus_object_type wireless_object_type = - UBUS_OBJECT_TYPE("netifd_iface", wireless_object_methods); - - -static struct ubus_object wireless_object = { - .name = "network.wireless", - .type = &wireless_object_type, - .methods = wireless_object_methods, - .n_methods = ARRAY_SIZE(wireless_object_methods), -}; - int netifd_extdev_invoke(uint32_t id, const char *method, struct blob_attr *msg, ubus_data_handler_t data_cb, void *data) @@ -1383,7 +1208,6 @@ netifd_ubus_init(const char *path) netifd_add_object(&main_object); netifd_add_object(&dev_object); - netifd_add_object(&wireless_object); netifd_add_iface_object(); udebug_ubus_init(&udebug, ubus_ctx, "netifd", netifd_udebug_config); @@ -1418,16 +1242,6 @@ netifd_ubus_interface_notify(struct interface *iface, bool up) ubus_notify(ubus_ctx, &iface->ubus, event, b.head, -1); } -void -netifd_ubus_wireless_notify(struct wireless_device *wdev, bool up) -{ - const char *event = (up) ? "wireless.update" : "wireless.down"; - - blob_buf_init(&b, 0); - wireless_device_status(wdev, &b); - ubus_notify(ubus_ctx, &wireless_object, event, b.head, -1); -} - void netifd_ubus_add_interface(struct interface *iface) { diff --git a/ucode.c b/ucode.c new file mode 100644 index 0000000..a1b67f5 --- /dev/null +++ b/ucode.c @@ -0,0 +1,582 @@ +/* + * netifd - network interface daemon + * Copyright (C) 2025 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include "netifd.h" +#include "device.h" +#include "interface.h" +#include "ucode.h" + +static uc_vm_t vm; +static uc_value_t *netifd_obj; +static struct blob_buf b; + +struct uc_netifd_process { + struct netifd_process proc; + uc_value_t *res; +}; + +static uc_value_t * +prop_get(uc_value_t *obj, const char *name, uc_type_t type) +{ + uc_value_t *data = ucv_object_get(obj, name, NULL); + + if (!type || ucv_type(data) != type) + return NULL; + + return data; +} + +static bool +prop_get_bool(uc_value_t *obj, const char *name, bool *val) +{ + uc_value_t *data = prop_get(obj, name, UC_BOOLEAN); + + if (data) + *val = ucv_boolean_get(data); + return !!data; +} + + +static bool +prop_get_int(uc_value_t *obj, const char *name, int *val) +{ + uc_value_t *data = prop_get(obj, name, UC_INTEGER); + + if (data) + *val = ucv_int64_get(data); + + return !!data; +} + +static uc_value_t * +uc_netifd_device_set(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *name = uc_fn_arg(0); + uc_value_t *data = uc_fn_arg(1); + struct device *dev; + bool check_vlan = true; + int external = 0; + bool bval; + int ival; + + if (ucv_type(name) != UC_STRING || ucv_type(data) != UC_OBJECT) + return NULL; + + prop_get_int(data, "external", &external); + prop_get_bool(data, "check_vlan", &check_vlan); + dev = __device_get(ucv_string_get(name), external, check_vlan); + if (!dev) + return NULL; + + if (prop_get_bool(data, "isolate", &bval)) { + dev->settings.flags |= DEV_OPT_ISOLATE; + dev->settings.isolate = bval; + } + + if (prop_get_int(data, "multicast_to_unicast", &ival)) { + if (ival < 0) { + dev->settings.flags &= ~DEV_OPT_MULTICAST_TO_UNICAST; + } else { + dev->settings.flags |= DEV_OPT_MULTICAST_TO_UNICAST; + dev->settings.multicast_to_unicast = !!ival; + } + } + + if (prop_get_bool(data, "wireless", &bval)) + dev->wireless = bval; + if (prop_get_bool(data, "wireless_isolate", &bval)) + dev->wireless_isolate = bval; + if (prop_get_bool(data, "wireless_proxyarp", &bval)) + dev->wireless_proxyarp = bval; + if (prop_get_bool(data, "wireless_ap", &bval)) { + dev->wireless_ap = bval; + if (bval) + dev->bpdu_filter = 1; + } + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_netifd_interface_get_bridge(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *name = uc_fn_arg(0); + uc_value_t *obj = uc_fn_arg(1); + struct device *dev, *orig_dev; + struct interface *iface; + + if (ucv_type(name) != UC_STRING) + return NULL; + + iface = vlist_find(&interfaces, ucv_string_get(name), iface, node); + if (!iface) + return NULL; + + dev = orig_dev = iface->main_dev.dev; + if (!dev) + return NULL; + + if (ucv_type(obj) == UC_OBJECT) + ucv_get(obj); + else + obj = ucv_object_new(vm); + + if (!dev->hotplug_ops) + return obj; + + if (dev->hotplug_ops && dev->hotplug_ops->prepare) + dev->hotplug_ops->prepare(dev, &dev); + + if (!dev || !dev->type->bridge_capability) + return obj; + + ucv_object_add(obj, "bridge", ucv_string_new(dev->ifname)); + ucv_object_add(obj, "bridge-ifname", ucv_string_new(orig_dev->ifname)); + if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST) + ucv_object_add(obj, "multicast_to_unicast", + ucv_boolean_new(dev->settings.multicast_to_unicast)); + + return obj; +} + +static uc_value_t * +uc_netifd_interface_handle_link(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *args = uc_fn_arg(0); + uc_value_t *name = ucv_object_get(args, "name", NULL); + uc_value_t *ifname = ucv_object_get(args, "ifname", NULL); + uc_value_t *vlan = ucv_object_get(args, "vlan", NULL); + bool up = ucv_is_truish(ucv_object_get(args, "up", NULL)); + bool link_ext = ucv_is_truish(ucv_object_get(args, "link_ext", NULL)); + struct blob_attr *vlan_attr = NULL; + struct interface *iface; + const char *net; + int ret; + + if (ucv_type(name) != UC_STRING || ucv_type(ifname) != UC_STRING || + (vlan && ucv_type(vlan) != UC_ARRAY)) + return NULL; + + net = ucv_string_get(name); + iface = vlist_find(&interfaces, net, iface, node); + if (!iface) + return NULL; + + if (vlan) { + size_t len = ucv_array_length(vlan); + + blob_buf_init(&b, 0); + for (size_t i = 0; i < len; i++) { + uc_value_t *val = ucv_array_get(vlan, i); + if (ucv_type(val) == UC_STRING) + blobmsg_add_string(&b, NULL, ucv_string_get(val)); + } + vlan_attr = b.head; + } + + ret = interface_handle_link(iface, ucv_string_get(ifname), vlan_attr, up, link_ext); + return ucv_boolean_new(ret == 0); +} + +static void +netifd_call_cb(const char *name, size_t nargs, ...) +{ + uc_value_t *val; + va_list ap; + + va_start(ap, nargs); + val = ucv_object_get(netifd_obj, "cb", NULL); + if (ucv_type(val) != UC_OBJECT) + goto out; + + val = ucv_object_get(val, name, NULL); + if (!ucv_is_callable(val)) + goto out; + + uc_vm_stack_push(&vm, ucv_get(val)); + for (size_t i = 0; i < nargs; i++) + uc_vm_stack_push(&vm, va_arg(ap, void *)); + va_end(ap); + + if (uc_vm_call(&vm, false, nargs) == EXCEPTION_NONE) + ucv_put(uc_vm_stack_pop(&vm)); + + return; + +out: + for (size_t i = 0; i < nargs; i++) + ucv_put(va_arg(ap, void *)); + va_end(ap); +} + +void netifd_ucode_config_load(bool start) +{ + netifd_call_cb(start ? "config_start" : "config_init", 0); +} + +void netifd_ucode_check_network_enabled(void) +{ + netifd_call_cb("check_interfaces", 0); +} + +void netifd_ucode_hotplug_event(const char *name, bool add) +{ + netifd_call_cb("hotplug", 2, ucv_string_new(name), ucv_boolean_new(add)); +} + +static uc_value_t * +uc_netifd_interface_get_enabled(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *name = uc_fn_arg(0); + struct interface *iface; + struct device *dev; + uc_value_t *val; + + if (ucv_type(name) != UC_STRING) + return NULL; + + iface = vlist_find(&interfaces, ucv_string_get(name), iface, node); + if (!iface) + return NULL; + + val = ucv_object_new(vm); + ucv_object_add(val, "enabled", ucv_boolean_new(!!iface->autostart)); + dev = iface->main_dev.dev; + if (dev && dev->hotplug_ops) + ucv_object_add(val, "ifindex", ucv_int64_new(dev->ifindex)); + + return val; +} + +static void +uc_netifd_process_cb(struct netifd_process *proc, int ret) +{ + struct uc_netifd_process *up = container_of(proc, struct uc_netifd_process, proc); + + uc_vm_stack_push(&vm, ucv_get(up->res)); + uc_vm_stack_push(&vm, ucv_get(ucv_resource_value_get(up->res, 0))); + uc_vm_stack_push(&vm, ucv_int64_new(ret)); + + if (uc_vm_call(&vm, true, 1) == EXCEPTION_NONE) + ucv_put(uc_vm_stack_pop(&vm)); +} + +static bool +fill_array(char **dest, uc_value_t *arr, size_t len) +{ + if (ucv_type(arr) != UC_ARRAY) + return false; + + for (size_t i = 0; i < len; i++) { + uc_value_t *str = ucv_array_get(arr, i); + if (ucv_type(str) != UC_STRING) + return false; + + dest[i] = strdup(ucv_string_get(str)); + } + dest[len] = NULL; + + return true; +} + +static int +uc_netifd_start_process(uc_value_t *dir, uc_value_t *arg, uc_value_t *env, int *fd) +{ + uc_value_t *fn; + char **argv; + size_t len; + int pfds[2]; + int pid; + + len = ucv_array_length(arg); + if (!len) + return -1; + + if (pipe(pfds) < 0) + return -1; + + if ((pid = fork()) < 0) + goto error; + + if (pid > 0) { + close(pfds[1]); + *fd = pfds[0]; + return pid; + } + + switch (ucv_type(dir)) { + case UC_OBJECT: + fn = ucv_property_get(dir, "fileno"); + if (!ucv_is_callable(fn)) + break; + + uc_vm_stack_push(&vm, ucv_get(dir)); + uc_vm_stack_push(&vm, ucv_get(fn)); + if (uc_vm_call(&vm, true, 0) != EXCEPTION_NONE) + break; + + dir = uc_vm_stack_pop(&vm); + if (ucv_type(dir) != UC_INTEGER) + break; + fallthrough; + case UC_INTEGER: + fchdir(ucv_int64_get(dir)); + break; + case UC_STRING: + chdir(ucv_string_get(dir)); + break; + default: + break; + } + + argv = calloc(len + 1, sizeof(*argv)); + if (!fill_array(argv, arg, len)) + exit(127); + + len = ucv_array_length(env); + for (size_t i = 0; i < len; i++) { + uc_value_t *strval = ucv_array_get(env, i); + char *str = ucv_string_get(strval); + + if (!str) + continue; + + putenv(strdup(str)); + } + + for (int i = 0; i <= 2; i++) { + if (pfds[1] == i) + continue; + + dup2(pfds[1], i); + } + + if (pfds[1] > 2) + close(pfds[1]); + + execvp(argv[0], (char **) argv); + exit(127); + +error: + close(pfds[0]); + close(pfds[1]); + return -1; +} + +static uc_value_t * +uc_netifd_log(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *prio = uc_fn_arg(0); + uc_value_t *msg = uc_fn_arg(1); + + if (ucv_type(prio) != UC_INTEGER || + ucv_type(msg) != UC_STRING) + return NULL; + + netifd_log_message(ucv_int64_get(prio), "%s", ucv_string_get(msg)); + return NULL; +} + +static uc_value_t * +uc_netifd_debug(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *msg = uc_fn_arg(0); + + if (ucv_type(msg) != UC_STRING) + return NULL; + + netifd_udebug_printf("%s", ucv_string_get(msg)); + return NULL; +} + +static uc_value_t * +uc_netifd_process(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *res, *cb, *arg, *env, *dir, *prefix; + uc_value_t *args = uc_fn_arg(0); + struct uc_netifd_process *up; + const char *prefix_str; + int pid, fd; + + if (ucv_type(args) != UC_OBJECT) + return NULL; + + arg = ucv_object_get(args, "argv", NULL); + if (!ucv_array_length(arg)) + return NULL; + + env = ucv_object_get(args, "envp", NULL); + if (env && ucv_type(env) != UC_ARRAY) + return NULL; + + dir = ucv_object_get(args, "dir", NULL); + + cb = ucv_object_get(args, "cb", NULL); + if (!ucv_is_callable(cb)) + return NULL; + + prefix = ucv_object_get(args, "log_prefix", NULL); + if (!prefix) + prefix = ucv_array_get(arg, 0); + if (ucv_type(prefix) != UC_STRING) + return NULL; + + prefix_str = ucv_string_get(prefix); + + res = ucv_resource_create_ex(vm, "netifd.process", (void **)&up, 1, sizeof(*up) + strlen(prefix_str + 1)); + if (!res) + return NULL; + + up->res = res; + + pid = uc_netifd_start_process(dir, arg, env, &fd); + if (pid < 0) { + ucv_put(res); + return NULL; + } + + up->proc.log_prefix = strcpy((char *)(up + 1), prefix_str); + up->proc.cb = uc_netifd_process_cb; + netifd_add_process(&up->proc, fd, pid); + ucv_resource_persistent_set(res, true); + ucv_resource_value_set(res, 0, ucv_get(cb)); + + return res; +} + +static uc_value_t * +uc_netifd_process_cancel(uc_vm_t *vm, size_t nargs) +{ + struct uc_netifd_process *up; + bool cancelled; + + up = uc_fn_thisval("netifd.process"); + if (!up) + return NULL; + + cancelled = up->proc.uloop.pending; + ucv_resource_persistent_set(up->res, false); + ucv_resource_value_set(up->res, 0, NULL); + netifd_kill_process(&up->proc); + + return ucv_boolean_new(cancelled); +} + +static void close_proc(void *ud) +{ + netifd_kill_process(ud); +} + +static uc_value_t * +uc_netifd_process_check(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *pid = uc_fn_arg(0); + uc_value_t *exe = uc_fn_arg(1); + bool ret; + + if (ucv_type(pid) != UC_INTEGER || ucv_type(exe) != UC_STRING) + return NULL; + + ret = check_pid_path(ucv_int64_get(pid), ucv_string_get(exe)); + + return ucv_boolean_new(ret); +} + +static const uc_function_list_t proc_fns[] = { + { "cancel", uc_netifd_process_cancel }, +}; + +static const uc_function_list_t netifd_fns[] = { + { "log", uc_netifd_log }, + { "debug", uc_netifd_debug }, + { "process", uc_netifd_process }, + { "process_check", uc_netifd_process_check }, + { "device_set", uc_netifd_device_set }, + { "interface_get_enabled", uc_netifd_interface_get_enabled }, + { "interface_handle_link", uc_netifd_interface_handle_link }, + { "interface_get_bridge", uc_netifd_interface_get_bridge }, +}; + + +void netifd_ucode_init(void) +{ + static uc_parse_config_t config = { + .strict_declarations = true, + .lstrip_blocks = true, + .trim_blocks = true, + .raw_mode = true + }; + uc_value_t *obj, *val; + uc_source_t *source; + uc_program_t *prog; + char *err; + + source = uc_source_new_file(DEFAULT_MAIN_PATH "/main.uc"); + if (!source) + return; + + uc_search_path_init(&config.module_search_path); + uc_search_path_add(&config.module_search_path, DEFAULT_MAIN_PATH "/*.so"); + uc_search_path_add(&config.module_search_path, DEFAULT_MAIN_PATH "/*.uc"); + + uc_vm_init(&vm, &config); + uc_stdlib_load(uc_vm_scope_get(&vm)); + uc_type_declare(&vm, "netifd.process", proc_fns, close_proc); + + obj = netifd_obj = ucv_object_new(&vm); + + uc_vm_registry_set(&vm, "netifd.obj", ucv_get(obj)); + ucv_object_add(uc_vm_scope_get(&vm), "netifd", obj); + ucv_object_add(obj, "cb", ucv_object_new(&vm)); + ucv_object_add(obj, "main_path", ucv_string_new(DEFAULT_MAIN_PATH)); + if (config_path) + ucv_object_add(obj, "config_path", ucv_string_new(config_path)); +#ifdef DUMMY_MODE + ucv_object_add(obj, "dummy_mode", ucv_boolean_new(true)); +#endif + +#define ADD_CONST(n) ucv_object_add(obj, #n, ucv_int64_new(n)) + ADD_CONST(L_CRIT); + ADD_CONST(L_WARNING); + ADD_CONST(L_NOTICE); + ADD_CONST(L_INFO); + ADD_CONST(L_DEBUG); +#undef ADD_CONST + + uc_function_list_register(obj, netifd_fns); + + prog = uc_compile(vm.config, source, &err); + uc_source_put(source); + + if (!prog) { + netifd_log_message(L_CRIT, "Error loading ucode script: %s\n", err); + netifd_ucode_free(); + return; + } + + uc_vm_execute(&vm, prog, &val); + uc_program_put(prog); + ucv_put(val); +} + +void netifd_ucode_free(void) +{ + if (!vm.config) + return; + + uc_search_path_free(&vm.config->module_search_path); + uc_vm_free(&vm); +} diff --git a/ucode.h b/ucode.h new file mode 100644 index 0000000..5147726 --- /dev/null +++ b/ucode.h @@ -0,0 +1,26 @@ +/* + * netifd - network interface daemon + * Copyright (C) 2025 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __NETIFD_UCODE_H +#define __NETIFD_UCODE_H + +#include + +void netifd_ucode_config_load(bool start); +void netifd_ucode_check_network_enabled(void); +void netifd_ucode_hotplug_event(const char *name, bool add); +void netifd_ucode_init(void); +void netifd_ucode_free(void); + +#endif diff --git a/wireless.c b/wireless.c deleted file mode 100644 index c05bf7d..0000000 --- a/wireless.c +++ /dev/null @@ -1,1704 +0,0 @@ -/* - * netifd - network interface daemon - * Copyright (C) 2013 Felix Fietkau - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -/* The wireless configuration is projected on the following objects - * - * 1. wireless device - * 2. wireless interface - * 3. wireless vlan - * 4. wireless station - * - * A wireless device is a phy or simplified a wireless card. - * A wireless interface is a SSID on a phy. - * A wireless vlan can be assigned to a wireless interface. A wireless interface can - * have multiple vlans. - * A wireless station is a client connected to an wireless interface. - */ - -#include -#include "netifd.h" -#include "wireless.h" -#include "handler.h" -#include "ubus.h" - -#define WIRELESS_SETUP_RETRY 3 - -struct vlist_tree wireless_devices; -struct avl_tree wireless_drivers; -static struct blob_buf b; -static int drv_fd; -static LIST_HEAD(handlers); -static bool handler_pending; - -enum { - WDEV_ATTR_DISABLED, - WDEV_ATTR_RECONF, - WDEV_ATTR_SERIALIZE, - __WDEV_ATTR_MAX, -}; - -static const struct blobmsg_policy wdev_policy[__WDEV_ATTR_MAX] = { - [WDEV_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, - [WDEV_ATTR_RECONF] = { .name = "reconf", .type = BLOBMSG_TYPE_BOOL }, - [WDEV_ATTR_SERIALIZE] = { .name = "serialize", .type = BLOBMSG_TYPE_BOOL }, -}; - -static const struct uci_blob_param_list wdev_param = { - .n_params = ARRAY_SIZE(wdev_policy), - .params = wdev_policy, -}; - -enum { - VIF_ATTR_DISABLED, - VIF_ATTR_NETWORK, - VIF_ATTR_NETWORK_VLAN, - VIF_ATTR_BRIDGE_ISOLATE, - VIF_ATTR_ISOLATE, - VIF_ATTR_MODE, - VIF_ATTR_PROXYARP, - VIF_ATTR_MCAST_TO_UCAST, - __VIF_ATTR_MAX, -}; - -static const struct blobmsg_policy vif_policy[__VIF_ATTR_MAX] = { - [VIF_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, - [VIF_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY }, - [VIF_ATTR_NETWORK_VLAN] = { .name = "network_vlan", .type = BLOBMSG_TYPE_ARRAY }, - [VIF_ATTR_BRIDGE_ISOLATE] = { .name = "bridge_isolate", .type = BLOBMSG_TYPE_BOOL }, - [VIF_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL }, - [VIF_ATTR_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_STRING }, - [VIF_ATTR_PROXYARP] = { .name = "proxy_arp", .type = BLOBMSG_TYPE_BOOL }, - [VIF_ATTR_MCAST_TO_UCAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL }, -}; - -static const struct uci_blob_param_list vif_param = { - .n_params = ARRAY_SIZE(vif_policy), - .params = vif_policy, -}; - -enum { - VLAN_ATTR_DISABLED, - VLAN_ATTR_NETWORK, - VLAN_ATTR_NETWORK_VLAN, - VLAN_ATTR_BRIDGE_ISOLATE, - VLAN_ATTR_ISOLATE, - VLAN_ATTR_MCAST_TO_UCAST, - __VLAN_ATTR_MAX, -}; - -static const struct blobmsg_policy vlan_policy[__VLAN_ATTR_MAX] = { - [VLAN_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, - [VLAN_ATTR_NETWORK] = { .name = "network", .type = BLOBMSG_TYPE_ARRAY }, - [VLAN_ATTR_NETWORK_VLAN] = { .name = "network_vlan", .type = BLOBMSG_TYPE_ARRAY }, - [VLAN_ATTR_BRIDGE_ISOLATE] = { .name = "bridge_isolate", .type = BLOBMSG_TYPE_BOOL }, - [VLAN_ATTR_ISOLATE] = { .name = "isolate", .type = BLOBMSG_TYPE_BOOL }, - [VLAN_ATTR_MCAST_TO_UCAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL }, -}; - -static const struct uci_blob_param_list vlan_param = { - .n_params = ARRAY_SIZE(vlan_policy), - .params = vlan_policy, -}; - -enum { - STA_ATTR_DISABLED, - __STA_ATTR_MAX, -}; - -static const struct blobmsg_policy sta_policy[__STA_ATTR_MAX] = { - [STA_ATTR_DISABLED] = { .name = "disabled", .type = BLOBMSG_TYPE_BOOL }, -}; - -static const struct uci_blob_param_list station_param = { - .n_params = ARRAY_SIZE(sta_policy), - .params = sta_policy, -}; - -static void -wireless_handler_stop(struct wireless_device *wdev) -{ - if (wdev->handler_pending) { - wdev->handler_pending = false; - list_del(&wdev->handler); - } -} - -static void -put_container(struct blob_buf *buf, struct blob_attr *attr, const char *name) -{ - void *c = blobmsg_open_table(buf, name); - blob_put_raw(buf, blobmsg_data(attr), blobmsg_len(attr)); - blobmsg_close_table(buf, c); -} - -static void -vif_config_add_bridge(struct blob_buf *buf, struct blob_attr *networks, bool prepare) -{ - struct interface *iface; - struct device *dev = NULL, *orig_dev; - struct blob_attr *cur; - const char *network; - size_t rem; - - if (!networks) - return; - - blobmsg_for_each_attr(cur, networks, rem) { - network = blobmsg_data(cur); - - iface = vlist_find(&interfaces, network, iface, node); - if (!iface) - continue; - - dev = iface->main_dev.dev; - if (!dev) - return; - - if (!dev->hotplug_ops) - return; - } - - if (!dev) - return; - - orig_dev = dev; - if (dev->hotplug_ops && dev->hotplug_ops->prepare) - dev->hotplug_ops->prepare(dev, &dev); - - if (!dev || !dev->type->bridge_capability) - return; - - blobmsg_add_string(buf, "bridge", dev->ifname); - blobmsg_add_string(buf, "bridge-ifname", orig_dev->ifname); - - if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST) - blobmsg_add_u8(buf, "multicast_to_unicast", - dev->settings.multicast_to_unicast); -} - -static void -prepare_config(struct wireless_device *wdev, struct blob_buf *buf, bool up) -{ - struct wireless_interface *vif; - struct wireless_vlan *vlan; - struct wireless_station *sta; - void *l, *i, *j, *k; - - blob_buf_init(&b, 0); - put_container(&b, wdev->config, "config"); - if (wdev->data) - blobmsg_add_blob(&b, wdev->data); - - l = blobmsg_open_table(&b, "interfaces"); - vlist_for_each_element(&wdev->interfaces, vif, node) { - if (vif->disabled) - continue; - - i = blobmsg_open_table(&b, vif->name); - vif_config_add_bridge(&b, vif->network, up); - put_container(&b, vif->config, "config"); - if (vif->data) - blobmsg_add_blob(&b, vif->data); - - j = blobmsg_open_table(&b, "vlans"); - vlist_for_each_element(&vif->vlans, vlan, node) { - k = blobmsg_open_table(&b, vlan->name); - vif_config_add_bridge(&b, vlan->network, up); - put_container(&b, vlan->config, "config"); - if (vlan->data) - blobmsg_add_blob(&b, vlan->data); - blobmsg_close_table(&b, k); - } - blobmsg_close_table(&b, j); - - j = blobmsg_open_table(&b, "stas"); - vlist_for_each_element(&vif->stations, sta, node) { - k = blobmsg_open_table(&b, sta->name); - put_container(&b, sta->config, "config"); - if (sta->data) - blobmsg_add_blob(&b, sta->data); - blobmsg_close_table(&b, k); - } - blobmsg_close_table(&b, j); - blobmsg_close_table(&b, i); - } - blobmsg_close_table(&b, l); - -} - -static bool -wireless_process_check(struct wireless_process *proc) -{ - return check_pid_path(proc->pid, proc->exe); -} - -static void -wireless_complete_kill_request(struct wireless_device *wdev) -{ - if (!wdev->kill_request) - return; - - ubus_complete_deferred_request(ubus_ctx, wdev->kill_request, 0); - free(wdev->kill_request); - wdev->kill_request = NULL; -} - -static void -wireless_process_free(struct wireless_device *wdev, struct wireless_process *proc) -{ - D(WIRELESS, "Wireless device '%s' free pid %d", wdev->name, proc->pid); - list_del(&proc->list); - free(proc); - - if (list_empty(&wdev->script_proc)) - wireless_complete_kill_request(wdev); -} - -static void -wireless_close_script_proc_fd(struct wireless_device *wdev) -{ - if (wdev->script_proc_fd.fd < 0) - return; - - uloop_fd_delete(&wdev->script_proc_fd); - close(wdev->script_proc_fd.fd); - wdev->script_proc_fd.fd = -1; -} - -static void -wireless_process_kill_all(struct wireless_device *wdev, int signal, bool free) -{ - struct wireless_process *proc, *tmp; - - list_for_each_entry_safe(proc, tmp, &wdev->script_proc, list) { - bool check = wireless_process_check(proc); - - if (check && !proc->keep) { - D(WIRELESS, "Wireless device '%s' kill pid %d", wdev->name, proc->pid); - kill(proc->pid, signal); - } - - if (free || !check) - wireless_process_free(wdev, proc); - } - - if (free) - wireless_close_script_proc_fd(wdev); -} - -static void -wireless_device_free_state(struct wireless_device *wdev) -{ - struct wireless_interface *vif; - struct wireless_vlan *vlan; - struct wireless_station *sta; - - wireless_handler_stop(wdev); - uloop_timeout_cancel(&wdev->script_check); - uloop_timeout_cancel(&wdev->timeout); - wireless_complete_kill_request(wdev); - free(wdev->data); - wdev->data = NULL; - vlist_for_each_element(&wdev->interfaces, vif, node) { - free(vif->data); - vif->data = NULL; - vif->ifname = NULL; - vlist_for_each_element(&vif->vlans, vlan, node) { - free(vlan->data); - vlan->data = NULL; - vlan->ifname = NULL; - } - vlist_for_each_element(&vif->stations, sta, node) { - free(sta->data); - sta->data = NULL; - } - } -} - -static void wireless_device_set_mcast_to_unicast(struct device *dev, int val) -{ - if (val < 0) { - dev->settings.flags &= ~DEV_OPT_MULTICAST_TO_UNICAST; - return; - } - - dev->settings.multicast_to_unicast = !!val; - dev->settings.flags |= DEV_OPT_MULTICAST_TO_UNICAST; -} - -static void wireless_check_interface(struct blob_attr *list, int *enabled, int *ifindex) -{ - struct interface *iface; - struct blob_attr *cur; - size_t rem; - - blobmsg_for_each_attr(cur, list, rem) { - struct device *mdev; - - iface = vlist_find(&interfaces, blobmsg_get_string(cur), iface, node); - if (!iface) - continue; - - if (iface->autostart) - *enabled = 1; - else if (*enabled != 1) - *enabled = 0; - - mdev = iface->main_dev.dev; - if (!mdev || !mdev->hotplug_ops) - continue; - - *ifindex = mdev->ifindex; - } -} - -static void wireless_interface_handle_link(struct wireless_interface *vif, const char *ifname, bool up) -{ - struct interface *iface; - struct blob_attr *cur; - const char *network; - struct device *dev; - int enabled = -1; - size_t rem; - - if (!vif->network || !vif->ifname) - return; - - if (!ifname) - ifname = vif->ifname; - - if (!up) - goto out; - - dev = __device_get(ifname, 2, false); - if (!dev) - goto out; - - dev->wireless = true; - dev->settings.flags |= DEV_OPT_ISOLATE; - dev->settings.isolate = vif->bridge_isolate; - - if (strcmp(ifname, vif->ifname) != 0) - goto out; - - dev->wireless_isolate = vif->isolate; - dev->wireless_proxyarp = vif->proxyarp; - dev->wireless_ap = vif->ap_mode; - wireless_device_set_mcast_to_unicast(dev, vif->multicast_to_unicast); - dev->bpdu_filter = dev->wireless_ap; - -out: - wireless_check_interface(vif->network, &enabled, &vif->network_ifindex); - blobmsg_for_each_attr(cur, vif->network, rem) { - network = blobmsg_data(cur); - - iface = vlist_find(&interfaces, network, iface, node); - if (!iface) - continue; - - interface_handle_link(iface, ifname, vif->network_vlan, up, true); - } -} - -static void wireless_vlan_handle_link(struct wireless_vlan *vlan, bool up) -{ - struct interface *iface; - struct blob_attr *cur; - const char *network; - int enabled = -1; - size_t rem; - - if (!vlan->network || !vlan->ifname) - return; - - if (up) { - struct device *dev = device_get(vlan->ifname, 2); - if (dev) { - dev->wireless_isolate = vlan->isolate; - dev->wireless = true; - dev->wireless_ap = true; - dev->bpdu_filter = true; - dev->settings.flags |= DEV_OPT_ISOLATE; - dev->settings.isolate = vlan->bridge_isolate; - wireless_device_set_mcast_to_unicast(dev, vlan->multicast_to_unicast); - } - } - - wireless_check_interface(vlan->network, &enabled, &vlan->network_ifindex); - blobmsg_for_each_attr(cur, vlan->network, rem) { - network = blobmsg_data(cur); - - iface = vlist_find(&interfaces, network, iface, node); - if (!iface) - continue; - - interface_handle_link(iface, vlan->ifname, vlan->network_vlan, up, true); - } -} - -static void -wireless_device_setup_cancel(struct wireless_device *wdev) -{ - if (wdev->cancel) - return; - - wireless_handler_stop(wdev); - D(WIRELESS, "Cancel wireless device '%s' setup", wdev->name); - wdev->cancel = true; - uloop_timeout_set(&wdev->timeout, 10 * 1000); -} - -static void -wireless_device_run_handler(struct wireless_device *wdev, bool up) -{ - const char *action = up ? "setup" : "teardown"; - const char *argv[6]; - char *config; - int i = 0; - int fds[2] = { -1, -1 }; - - wireless_handler_stop(wdev); - - if (handler_pending && wdev->serialize) { - wdev->handler_action = up; - wdev->handler_pending = true; - list_add_tail(&wdev->handler, &handlers); - return; - } - if (wdev->serialize) - handler_pending = true; - - D(WIRELESS, "Wireless device '%s' run %s handler", wdev->name, action); - if (!up && wdev->prev_config) { - config = blobmsg_format_json(wdev->prev_config, true); - free(wdev->prev_config); - wdev->prev_config = NULL; - } else { - prepare_config(wdev, &b, up); - free(wdev->prev_config); - wdev->prev_config = up ? blob_memdup(b.head) : NULL; - config = blobmsg_format_json(b.head, true); - } - - argv[i++] = wdev->drv->script; - argv[i++] = wdev->drv->name; - argv[i++] = action; - argv[i++] = wdev->name; - argv[i++] = config; - argv[i] = NULL; - - if (up && pipe(fds) == 0) { - if (wdev->script_proc_fd.fd >= 0) - wireless_close_script_proc_fd(wdev); - - wdev->script_proc_fd.fd = fds[0]; - uloop_fd_add(&wdev->script_proc_fd, - ULOOP_READ | ULOOP_EDGE_TRIGGER); - } - - netifd_start_process(argv, NULL, &wdev->script_task); - - if (fds[1] >= 0) - close(fds[1]); - - free(config); -} - -static void -wireless_handler_next(void) -{ - struct wireless_device *wdev; - - if (handler_pending) - return; - if (list_empty(&handlers)) - return; - wdev = list_first_entry(&handlers, struct wireless_device, handler); - list_del(&wdev->handler); - wdev->handler_pending = false; - wireless_device_run_handler(wdev, wdev->handler_action); -} - -static void -__wireless_device_set_up(struct wireless_device *wdev, int force) -{ - if (wdev->disabled) - return; - - if (wdev->retry_setup_failed) - return; - - if (!wdev->autostart) - return; - - if ((!force && wdev->state != IFS_DOWN) || config_init) - return; - - wdev->state = IFS_SETUP; - wireless_device_run_handler(wdev, true); -} - -static void -wireless_device_free(struct wireless_device *wdev) -{ - wireless_handler_stop(wdev); - vlist_flush_all(&wdev->interfaces); - avl_delete(&wireless_devices.avl, &wdev->node.avl); - free(wdev->config); - free(wdev->prev_config); - free(wdev); -} - -static void -wdev_handle_config_change(struct wireless_device *wdev) -{ - enum interface_config_state state = wdev->config_state; - - switch(state) { - case IFC_RELOAD: - wdev->retry = WIRELESS_SETUP_RETRY; - wdev->retry_setup_failed = false; - fallthrough; - case IFC_NORMAL: - __wireless_device_set_up(wdev, 0); - - wdev->config_state = IFC_NORMAL; - break; - case IFC_REMOVE: - wireless_device_free(wdev); - break; - } -} - -static void -wireless_device_mark_down(struct wireless_device *wdev) -{ - struct wireless_interface *vif; - struct wireless_vlan *vlan; - - netifd_log_message(L_NOTICE, "Wireless device '%s' is now down\n", wdev->name); - - - vlist_for_each_element(&wdev->interfaces, vif, node) { - wireless_interface_handle_link(vif, NULL, false); - vlist_for_each_element(&vif->vlans, vlan, node) - wireless_vlan_handle_link(vlan, false); - } - - wireless_process_kill_all(wdev, SIGTERM, true); - - wdev->cancel = false; - wdev->state = IFS_DOWN; - wireless_device_free_state(wdev); - wdev_handle_config_change(wdev); - netifd_ubus_wireless_notify(wdev, false); -} - -/* timeout callback to protect the tear down */ -static void -wireless_device_setup_timeout(struct uloop_timeout *timeout) -{ - struct wireless_device *wdev = container_of(timeout, struct wireless_device, timeout); - - if (wdev->handler_pending) { - wdev->handler_pending = false; - list_del(&wdev->handler); - } - netifd_kill_process(&wdev->script_task); - wdev->script_task.cb(&wdev->script_task, -1); - wireless_device_mark_down(wdev); -} - -void -wireless_device_set_up(struct wireless_device *wdev) -{ - wdev->retry = WIRELESS_SETUP_RETRY; - wdev->autostart = true; - __wireless_device_set_up(wdev, 0); -} - -void -wireless_device_reconf(struct wireless_device *wdev) -{ - wdev->retry = WIRELESS_SETUP_RETRY; - wdev->autostart = true; - __wireless_device_set_up(wdev, wdev->reconf && (wdev->state == IFS_UP)); -} - -static void -__wireless_device_set_down(struct wireless_device *wdev) -{ - if (wdev->state == IFS_TEARDOWN || wdev->state == IFS_DOWN) - return; - - if (wdev->script_task.uloop.pending) { - wireless_device_setup_cancel(wdev); - return; - } - - wdev->state = IFS_TEARDOWN; - wireless_device_run_handler(wdev, false); -} - -/* ubus callback network.wireless.notify, command = up */ -static void -wireless_device_mark_up(struct wireless_device *wdev) -{ - struct wireless_interface *vif; - struct wireless_vlan *vlan; - - if (wdev->cancel) { - wdev->cancel = false; - __wireless_device_set_down(wdev); - return; - } - - netifd_log_message(L_NOTICE, "Wireless device '%s' is now up\n", wdev->name); - wdev->retry = WIRELESS_SETUP_RETRY; - wdev->state = IFS_UP; - vlist_for_each_element(&wdev->interfaces, vif, node) { - wireless_interface_handle_link(vif, NULL, true); - vlist_for_each_element(&vif->vlans, vlan, node) - wireless_vlan_handle_link(vlan, true); - } - netifd_ubus_wireless_notify(wdev, true); -} - -static void -wireless_device_retry_setup(struct wireless_device *wdev) -{ - if (wdev->state == IFS_TEARDOWN || wdev->state == IFS_DOWN || wdev->cancel) - return; - - netifd_log_message(wdev->retry ? L_WARNING : L_CRIT, - "Wireless device '%s' setup failed, retry=%d\n", - wdev->name, wdev->retry); - if (--wdev->retry < 0) - wdev->retry_setup_failed = true; - - __wireless_device_set_down(wdev); -} - -static void -wireless_device_script_task_cb(struct netifd_process *proc, int ret) -{ - struct wireless_device *wdev = container_of(proc, struct wireless_device, script_task); - - switch (wdev->state) { - case IFS_SETUP: - wireless_device_retry_setup(wdev); - break; - case IFS_TEARDOWN: - wireless_device_mark_down(wdev); - break; - default: - break; - } - - if (wdev->serialize) { - handler_pending = false; - wireless_handler_next(); - } -} - -void -wireless_device_set_down(struct wireless_device *wdev) -{ - wdev->retry_setup_failed = false; - wdev->autostart = false; - __wireless_device_set_down(wdev); -} - -static void -wdev_set_config_state(struct wireless_device *wdev, enum interface_config_state s) -{ - if (wdev->config_state != IFC_NORMAL) - return; - - wdev->config_update = false; - if (!wdev->disabled && s == IFC_RELOAD && wdev->reconf && wdev->state == IFS_UP) { - wireless_device_reconf(wdev); - return; - } - - wdev->config_state = s; - if (wdev->state == IFS_DOWN) - wdev_handle_config_change(wdev); - else - __wireless_device_set_down(wdev); -} - -static void -wdev_prepare_prev_config(struct wireless_device *wdev) -{ - prepare_config(wdev, &b, false); - free(wdev->prev_config); - wdev->prev_config = blob_memdup(b.head); -} - -static void -wdev_change_config(struct wireless_device *wdev, struct wireless_device *wd_new) -{ - struct blob_attr *new_config = wd_new->config; - bool disabled = wd_new->disabled; - - wdev->reconf = wd_new->reconf; - wdev->serialize = wd_new->serialize; - free(wd_new); - - if (blob_attr_equal(wdev->config, new_config) && wdev->disabled == disabled) - return; - - D(WIRELESS, "Update configuration of wireless device '%s'", wdev->name); - free(wdev->config); - wdev->config = blob_memdup(new_config); - wdev->disabled = disabled; - wdev->config_update = true; -} - -static void -wdev_create(struct wireless_device *wdev) -{ - wdev->retry = WIRELESS_SETUP_RETRY; - wdev->config = blob_memdup(wdev->config); -} - -/* vlist update call for wireless device list */ -static void -wdev_update(struct vlist_tree *tree, struct vlist_node *node_new, - struct vlist_node *node_old) -{ - struct wireless_device *wd_old = container_of(node_old, struct wireless_device, node); - struct wireless_device *wd_new = container_of(node_new, struct wireless_device, node); - - if (wd_old && wd_new) { - D(WIRELESS, "Update wireless device '%s'", wd_old->name); - wdev_change_config(wd_old, wd_new); - } else if (wd_old) { - D(WIRELESS, "Delete wireless device '%s'", wd_old->name); - wdev_set_config_state(wd_old, IFC_REMOVE); - } else if (wd_new) { - D(WIRELESS, "Create wireless device '%s'", wd_new->name); - wdev_create(wd_new); - } -} - -/* wireless netifd script handler */ -static void -wireless_add_handler(const char *script, const char *name, json_object *obj) -{ - struct wireless_driver *drv; - char *name_str, *script_str; - json_object *dev_config_obj, *iface_config_obj, *vlan_config_obj, *station_config_obj; - struct uci_blob_param_list *dev_config, *iface_config, *vlan_config, *station_config; - - dev_config_obj = json_get_field(obj, "device", json_type_array); - iface_config_obj = json_get_field(obj, "iface", json_type_array); - vlan_config_obj = json_get_field(obj, "vlan", json_type_array); - station_config_obj = json_get_field(obj, "station", json_type_array); - - if (!dev_config_obj || !iface_config_obj || !vlan_config_obj || !station_config_obj) - return; - - drv = calloc_a(sizeof(*drv), - &dev_config, sizeof(*dev_config) + sizeof(void *), - &iface_config, sizeof(*iface_config) + sizeof(void *), - &vlan_config, sizeof(*vlan_config) + sizeof(void *), - &station_config, sizeof(*station_config) + sizeof(void *), - &name_str, strlen(name) + 1, - &script_str, strlen(script) + 1); - - drv->name = strcpy(name_str, name); - drv->script = strcpy(script_str, script); - - dev_config->n_next = 1; - dev_config->next[0] = &wdev_param; - drv->device.config = dev_config; - - iface_config->n_next = 1; - iface_config->next[0] = &vif_param; - drv->interface.config = iface_config; - - vlan_config->n_next = 1; - vlan_config->next[0] = &vlan_param; - drv->vlan.config = vlan_config; - - station_config->n_next = 1; - station_config->next[0] = &station_param; - drv->station.config = station_config; - - drv->device.buf = netifd_handler_parse_config(drv->device.config, dev_config_obj); - drv->interface.buf = netifd_handler_parse_config(drv->interface.config, iface_config_obj); - drv->vlan.buf = netifd_handler_parse_config(drv->vlan.config, vlan_config_obj); - drv->station.buf = netifd_handler_parse_config(drv->station.config, station_config_obj); - - drv->node.key = drv->name; - avl_insert(&wireless_drivers, &drv->node); - D(WIRELESS, "Add handler for script %s: %s", script, name); -} - -void wireless_init(void) -{ - vlist_init(&wireless_devices, avl_strcmp, wdev_update); - wireless_devices.keep_old = true; - wireless_devices.no_delete = true; - - avl_init(&wireless_drivers, avl_strcmp, false, NULL); - drv_fd = netifd_open_subdir("wireless"); - if (drv_fd < 0) - return; - - netifd_init_script_handlers(drv_fd, wireless_add_handler); -} - -/* parse blob config into the wireless interface object */ -static void -wireless_interface_init_config(struct wireless_interface *vif) -{ - struct blob_attr *tb[__VIF_ATTR_MAX]; - struct blob_attr *cur; - - vif->network = NULL; - blobmsg_parse_attr(vif_policy, __VIF_ATTR_MAX, tb, vif->config); - - if ((cur = tb[VIF_ATTR_NETWORK])) - vif->network = cur; - - if ((cur = tb[VIF_ATTR_NETWORK_VLAN])) - vif->network_vlan = cur; - - cur = tb[VIF_ATTR_MODE]; - vif->ap_mode = cur && !strcmp(blobmsg_get_string(cur), "ap"); - - cur = tb[VIF_ATTR_BRIDGE_ISOLATE]; - vif->bridge_isolate = cur && blobmsg_get_bool(cur); - - cur = tb[VIF_ATTR_ISOLATE]; - vif->isolate = cur && blobmsg_get_bool(cur); - - cur = tb[VIF_ATTR_PROXYARP]; - vif->proxyarp = vif->ap_mode && cur && blobmsg_get_bool(cur); - - cur = tb[VIF_ATTR_MCAST_TO_UCAST]; - vif->multicast_to_unicast = cur ? blobmsg_get_bool(cur) : -1; -} - -static void -vif_free(struct wireless_interface *vif) -{ - vlist_flush_all(&vif->vlans); - vlist_flush_all(&vif->stations); - free((void *) vif->section); - free(vif->config); - free(vif); -} - -/* vlist update call for wireless interface list */ -static void -vif_update(struct vlist_tree *tree, struct vlist_node *node_new, - struct vlist_node *node_old) -{ - struct wireless_interface *vif_old = container_of(node_old, struct wireless_interface, node); - struct wireless_interface *vif_new = container_of(node_new, struct wireless_interface, node); - struct wireless_device *wdev; - - if (vif_old) - wdev = vif_old->wdev; - else - wdev = vif_new->wdev; - - if (vif_old && vif_new) { - free((void *) vif_old->section); - vif_old->section = strdup(vif_new->section); - if (blob_attr_equal(vif_old->config, vif_new->config)) { - free(vif_new); - return; - } - - D(WIRELESS, "Update wireless interface %s on device %s", vif_new->name, wdev->name); - wireless_interface_handle_link(vif_old, NULL, false); - free(vif_old->config); - vif_old->config = blob_memdup(vif_new->config); - wireless_interface_init_config(vif_old); - free(vif_new); - } else if (vif_new) { - D(WIRELESS, "Create new wireless interface %s on device %s", vif_new->name, wdev->name); - vif_new->section = strdup(vif_new->section); - vif_new->config = blob_memdup(vif_new->config); - wireless_interface_init_config(vif_new); - } else if (vif_old) { - D(WIRELESS, "Delete wireless interface %s on device %s", vif_old->name, wdev->name); - wireless_interface_handle_link(vif_old, NULL, false); - vif_free(vif_old); - } - - wdev->config_update = true; -} - -/* parse blob config into the vlan object */ -static void -wireless_vlan_init_config(struct wireless_vlan *vlan) -{ - struct blob_attr *tb[__VLAN_ATTR_MAX]; - struct blob_attr *cur; - - vlan->network = NULL; - blobmsg_parse_attr(vlan_policy, __VLAN_ATTR_MAX, tb, vlan->config); - - if ((cur = tb[VLAN_ATTR_NETWORK])) - vlan->network = cur; - - if ((cur = tb[VLAN_ATTR_NETWORK_VLAN])) - vlan->network_vlan = cur; - - cur = tb[VLAN_ATTR_BRIDGE_ISOLATE]; - vlan->bridge_isolate = cur && blobmsg_get_bool(cur); - - cur = tb[VLAN_ATTR_ISOLATE]; - vlan->isolate = cur && blobmsg_get_bool(cur); - - cur = tb[VLAN_ATTR_MCAST_TO_UCAST]; - vlan->multicast_to_unicast = cur ? blobmsg_get_bool(cur) : -1; -} - -/* vlist update call for vlan list */ -static void -vlan_update(struct vlist_tree *tree, struct vlist_node *node_new, - struct vlist_node *node_old) -{ - struct wireless_vlan *vlan_old = container_of_safe(node_old, struct wireless_vlan, node); - struct wireless_vlan *vlan_new = container_of_safe(node_new, struct wireless_vlan, node); - struct wireless_interface *vif = container_of(tree, struct wireless_interface, vlans); - struct wireless_device *wdev = vif->wdev; - - if (vlan_old && vlan_new) { - free((void *) vlan_old->section); - vlan_old->section = strdup(vlan_new->section); - if (blob_attr_equal(vlan_old->config, vlan_new->config)) { - free(vlan_new); - return; - } - - D(WIRELESS, "Update wireless vlan %s on device %s", vlan_new->name, wdev->name); - wireless_vlan_handle_link(vlan_old, false); - free(vlan_old->config); - vlan_old->config = blob_memdup(vlan_new->config); - vlan_old->isolate = vlan_new->isolate; - wireless_vlan_init_config(vlan_old); - free(vlan_new); - } else if (vlan_new) { - D(WIRELESS, "Create new wireless vlan %s on device %s", vlan_new->name, wdev->name); - vlan_new->section = strdup(vlan_new->section); - vlan_new->config = blob_memdup(vlan_new->config); - wireless_vlan_init_config(vlan_new); - } else if (vlan_old) { - D(WIRELESS, "Delete wireless vlan %s on device %s", vlan_old->name, wdev->name); - wireless_vlan_handle_link(vlan_old, false); - free((void *) vlan_old->section); - free(vlan_old->config); - free(vlan_old); - } - - wdev->config_update = true; -} - -/* vlist update call for station list */ -static void -station_update(struct vlist_tree *tree, struct vlist_node *node_new, - struct vlist_node *node_old) -{ - struct wireless_station *sta_old = container_of_safe(node_old, struct wireless_station, node); - struct wireless_station *sta_new = container_of_safe(node_new, struct wireless_station, node); - struct wireless_interface *vif = container_of(tree, struct wireless_interface, stations); - struct wireless_device *wdev = vif->wdev; - - if (sta_old && sta_new) { - free((void *) sta_old->section); - sta_old->section = strdup(sta_new->section); - if (blob_attr_equal(sta_old->config, sta_new->config)) { - free(sta_new); - return; - } - - D(WIRELESS, "Update wireless station %s on device %s", sta_new->name, wdev->name); - free(sta_old->config); - sta_old->config = blob_memdup(sta_new->config); - free(sta_new); - } else if (sta_new) { - D(WIRELESS, "Create new wireless station %s on device %s", sta_new->name, wdev->name); - sta_new->section = strdup(sta_new->section); - sta_new->config = blob_memdup(sta_new->config); - } else if (sta_old) { - D(WIRELESS, "Delete wireless station %s on device %s", sta_old->name, wdev->name); - free((void *) sta_old->section); - free(sta_old->config); - free(sta_old); - } - - wdev->config_update = true; -} - -static void -wireless_proc_poll_fd(struct uloop_fd *fd, unsigned int events) -{ - struct wireless_device *wdev = container_of(fd, struct wireless_device, script_proc_fd); - char buf[128]; - - while (1) { - int b = read(fd->fd, buf, sizeof(buf)); - if (b < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return; - - goto done; - } - - if (!b) - goto done; - } - -done: - uloop_timeout_set(&wdev->script_check, 0); - wireless_close_script_proc_fd(wdev); -} - -/* watchdog and garbage collector for wireless processes. - * It cleans up terminated processes. If a process is a requirement for the wireless device, it retries the setup */ -static void -wireless_device_check_script_tasks(struct uloop_timeout *timeout) -{ - struct wireless_device *wdev = container_of(timeout, struct wireless_device, script_check); - struct wireless_process *proc, *tmp; - bool restart = false; - - list_for_each_entry_safe(proc, tmp, &wdev->script_proc, list) { - if (wireless_process_check(proc)) - continue; - - D(WIRELESS, "Wireless device '%s' pid %d has terminated", wdev->name, proc->pid); - if (proc->required) - restart = true; - - wireless_process_free(wdev, proc); - } - - if (restart) - wireless_device_retry_setup(wdev); - else - uloop_timeout_set(&wdev->script_check, 1000); -} - -/* creates a wireless device object. Called by config */ -void -wireless_device_create(struct wireless_driver *drv, const char *name, struct blob_attr *data) -{ - struct wireless_device *wdev; - char *name_buf; - struct blob_attr *tb[__WDEV_ATTR_MAX]; - struct blob_attr *cur; - - blobmsg_parse_attr(wdev_policy, __WDEV_ATTR_MAX, tb, data); - - wdev = calloc_a(sizeof(*wdev), &name_buf, strlen(name) + 1); - - cur = tb[WDEV_ATTR_DISABLED]; - wdev->disabled = cur && blobmsg_get_bool(cur); - - wdev->drv = drv; - wdev->state = IFS_DOWN; - wdev->config_state = IFC_NORMAL; - wdev->name = strcpy(name_buf, name); - wdev->config = data; - wdev->handler_pending = false; - - cur = tb[WDEV_ATTR_SERIALIZE]; - wdev->serialize = cur && blobmsg_get_bool(cur); - - cur = tb[WDEV_ATTR_RECONF]; - wdev->reconf = !cur || blobmsg_get_bool(cur); - - wdev->retry_setup_failed = false; - wdev->autostart = true; - INIT_LIST_HEAD(&wdev->script_proc); - vlist_init(&wdev->interfaces, avl_strcmp, vif_update); - wdev->interfaces.keep_old = true; - - wdev->timeout.cb = wireless_device_setup_timeout; - wdev->script_task.cb = wireless_device_script_task_cb; - wdev->script_task.dir_fd = drv_fd; - wdev->script_task.log_prefix = wdev->name; - - wdev->script_proc_fd.fd = -1; - wdev->script_proc_fd.cb = wireless_proc_poll_fd; - - wdev->script_check.cb = wireless_device_check_script_tasks; - - vlist_add(&wireless_devices, &wdev->node, wdev->name); -} - -/* creates a wireless station object. Called by config */ -void -wireless_station_create(struct wireless_interface *vif, struct blob_attr *data, const char *section) -{ - struct wireless_station *sta; - struct blob_attr *tb[__STA_ATTR_MAX]; - struct blob_attr *cur; - char *name_buf; - char name[8]; - - blobmsg_parse_attr(sta_policy, __STA_ATTR_MAX, tb, data); - - cur = tb[STA_ATTR_DISABLED]; - if (cur && blobmsg_get_bool(cur)) - return; - - sprintf(name, "%d", vif->sta_idx++); - - sta = calloc_a(sizeof(*sta), - &name_buf, strlen(name) + 1); - sta->name = strcpy(name_buf, name); - sta->config = data; - sta->section = section; - - vlist_add(&vif->stations, &sta->node, sta->name); -} - -/* ubus callback network.wireless.status, runs for every interface, encode the station */ -static void -wireless_station_status(struct wireless_station *sta, struct blob_buf *b) -{ - void *i; - - i = blobmsg_open_table(b, NULL); - if (sta->section) - blobmsg_add_string(b, "section", sta->section); - put_container(b, sta->config, "config"); - blobmsg_close_table(b, i); -} - -/* create a vlan object. Called by config */ -void -wireless_vlan_create(struct wireless_interface *vif, struct blob_attr *data, const char *section) -{ - struct wireless_vlan *vlan; - struct blob_attr *tb[__VLAN_ATTR_MAX]; - struct blob_attr *cur; - char *name_buf; - char name[8]; - - blobmsg_parse_attr(vlan_policy, __VLAN_ATTR_MAX, tb, data); - - cur = tb[VLAN_ATTR_DISABLED]; - if (cur && blobmsg_get_bool(cur)) - return; - - sprintf(name, "%d", vif->vlan_idx++); - - vlan = calloc_a(sizeof(*vlan), &name_buf, strlen(name) + 1); - vlan->name = strcpy(name_buf, name); - vlan->config = data; - vlan->section = section; - - vlist_add(&vif->vlans, &vlan->node, vlan->name); -} - -/* ubus callback network.wireless.status, runs for every interface, encode the vlan informations */ -static void -wireless_vlan_status(struct wireless_vlan *vlan, struct blob_buf *b) -{ - void *i; - - i = blobmsg_open_table(b, NULL); - if (vlan->section) - blobmsg_add_string(b, "section", vlan->section); - if (vlan->ifname) - blobmsg_add_string(b, "ifname", vlan->ifname); - put_container(b, vlan->config, "config"); - blobmsg_close_table(b, i); -} - -/* create a wireless interface object. Called by config */ -struct wireless_interface* wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section) -{ - struct wireless_interface *vif; - struct blob_attr *tb[__VIF_ATTR_MAX]; - struct blob_attr *cur; - char *name_buf; - char name[8]; - - blobmsg_parse_attr(vif_policy, __VIF_ATTR_MAX, tb, data); - - cur = tb[VIF_ATTR_DISABLED]; - if (cur && blobmsg_get_bool(cur)) - return NULL; - - sprintf(name, "%d", wdev->vif_idx++); - - vif = calloc_a(sizeof(*vif), - &name_buf, strlen(name) + 1); - vif->name = strcpy(name_buf, name); - vif->wdev = wdev; - vif->config = data; - vif->section = section; - vif->isolate = false; - - vlist_init(&vif->vlans, avl_strcmp, vlan_update); - vif->vlans.keep_old = true; - - vlist_init(&vif->stations, avl_strcmp, station_update); - vif->stations.keep_old = true; - - vlist_add(&wdev->interfaces, &vif->node, vif->name); - - vif = vlist_find(&wdev->interfaces, name, vif, node); - if (!vif) - return NULL; - - vif->vlan_idx = vif->sta_idx = 0; - vlist_update(&vif->vlans); - vlist_update(&vif->stations); - - return vif; -} - -/* ubus callback network.wireless.status, runs for every interface */ -static void -wireless_interface_status(struct wireless_interface *iface, struct blob_buf *b) -{ - struct wireless_vlan *vlan; - struct wireless_station *sta; - void *i, *j; - - i = blobmsg_open_table(b, NULL); - if (iface->section) - blobmsg_add_string(b, "section", iface->section); - if (iface->ifname) - blobmsg_add_string(b, "ifname", iface->ifname); - put_container(b, iface->config, "config"); - j = blobmsg_open_array(b, "vlans"); - vlist_for_each_element(&iface->vlans, vlan, node) - wireless_vlan_status(vlan, b); - blobmsg_close_array(b, j); - j = blobmsg_open_array(b, "stations"); - vlist_for_each_element(&iface->stations, sta, node) - wireless_station_status(sta, b); - blobmsg_close_array(b, j); - blobmsg_close_table(b, i); -} - -/* ubus callback network.wireless.status */ -void -wireless_device_status(struct wireless_device *wdev, struct blob_buf *b) -{ - struct wireless_interface *iface; - void *c, *i; - - c = blobmsg_open_table(b, wdev->name); - blobmsg_add_u8(b, "up", wdev->state == IFS_UP); - blobmsg_add_u8(b, "pending", wdev->state == IFS_SETUP || wdev->state == IFS_TEARDOWN); - blobmsg_add_u8(b, "autostart", wdev->autostart); - blobmsg_add_u8(b, "disabled", wdev->disabled); - blobmsg_add_u8(b, "retry_setup_failed", wdev->retry_setup_failed); - put_container(b, wdev->config, "config"); - - i = blobmsg_open_array(b, "interfaces"); - vlist_for_each_element(&wdev->interfaces, iface, node) - wireless_interface_status(iface, b); - blobmsg_close_array(b, i); - blobmsg_close_table(b, c); -} - -/* ubus callback network.wireless.get_validate */ -void -wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b) -{ - struct uci_blob_param_list *p; - void *c, *d; - int i; - - c = blobmsg_open_table(b, wdev->name); - - d = blobmsg_open_table(b, "device"); - p = wdev->drv->device.config; - for (i = 0; i < p->n_params; i++) - blobmsg_add_string(b, p->params[i].name, uci_get_validate_string(p, i)); - blobmsg_close_table(b, d); - - d = blobmsg_open_table(b, "interface"); - p = wdev->drv->interface.config; - for (i = 0; i < p->n_params; i++) - blobmsg_add_string(b, p->params[i].name, uci_get_validate_string(p, i)); - blobmsg_close_table(b, d); - - blobmsg_close_table(b, c); -} - -/* ubus callback network.wireless.notify, command = set data, for vif */ -static void -wireless_interface_set_data(struct wireless_interface *vif) -{ - enum { - VIF_DATA_IFNAME, - __VIF_DATA_MAX, - }; - static const struct blobmsg_policy data_policy[__VIF_DATA_MAX] = { - [VIF_DATA_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING }, - }; - struct blob_attr *tb[__VIF_DATA_MAX]; - struct blob_attr *cur; - - blobmsg_parse_attr(data_policy, __VIF_DATA_MAX, tb, vif->data); - - if ((cur = tb[VIF_DATA_IFNAME])) - vif->ifname = blobmsg_data(cur); -} - -/* ubus callback network.wireless.notify, command = set data, for vlan */ -static void -wireless_vlan_set_data(struct wireless_vlan *vlan) -{ - enum { - VLAN_DATA_IFNAME, - __VLAN_DATA_MAX, - }; - static const struct blobmsg_policy data_policy[__VLAN_DATA_MAX] = { - [VLAN_DATA_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING }, - }; - struct blob_attr *tb[__VLAN_DATA_MAX]; - struct blob_attr *cur; - - blobmsg_parse_attr(data_policy, __VLAN_DATA_MAX, tb, vlan->data); - - if ((cur = tb[VLAN_DATA_IFNAME])) - vlan->ifname = blobmsg_data(cur); -} - -/* ubus callback network.wireless.notify, command = process add */ -static int -wireless_device_add_process(struct wireless_device *wdev, struct blob_attr *data) -{ - enum { - PROC_ATTR_PID, - PROC_ATTR_EXE, - PROC_ATTR_REQUIRED, - PROC_ATTR_KEEP, - __PROC_ATTR_MAX - }; - static const struct blobmsg_policy proc_policy[__PROC_ATTR_MAX] = { - [PROC_ATTR_PID] = { .name = "pid", .type = BLOBMSG_TYPE_INT32 }, - [PROC_ATTR_EXE] = { .name = "exe", .type = BLOBMSG_TYPE_STRING }, - [PROC_ATTR_REQUIRED] = { .name = "required", .type = BLOBMSG_TYPE_BOOL }, - [PROC_ATTR_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL }, - }; - struct blob_attr *tb[__PROC_ATTR_MAX]; - struct wireless_process *proc; - char *name; - int pid; - - if (!data) - return UBUS_STATUS_INVALID_ARGUMENT; - - blobmsg_parse_attr(proc_policy, __PROC_ATTR_MAX, tb, data); - if (!tb[PROC_ATTR_PID] || !tb[PROC_ATTR_EXE]) - return UBUS_STATUS_INVALID_ARGUMENT; - - pid = blobmsg_get_u32(tb[PROC_ATTR_PID]); - if (pid < 2) - return UBUS_STATUS_INVALID_ARGUMENT; - - proc = calloc_a(sizeof(*proc), - &name, strlen(blobmsg_data(tb[PROC_ATTR_EXE])) + 1); - - proc->pid = pid; - proc->exe = strcpy(name, blobmsg_data(tb[PROC_ATTR_EXE])); - - if (tb[PROC_ATTR_REQUIRED]) - proc->required = blobmsg_get_bool(tb[PROC_ATTR_REQUIRED]); - - if (tb[PROC_ATTR_KEEP]) - proc->keep = blobmsg_get_bool(tb[PROC_ATTR_KEEP]); - - D(WIRELESS, "Wireless device '%s' add pid %d", wdev->name, proc->pid); - list_add(&proc->list, &wdev->script_proc); - uloop_timeout_set(&wdev->script_check, 0); - - return 0; -} - -/* ubus callback network.wireless.notify, command = process kill all */ -static int -wireless_device_process_kill_all(struct wireless_device *wdev, struct blob_attr *data, - struct ubus_request_data *req) -{ - enum { - KILL_ATTR_SIGNAL, - KILL_ATTR_IMMEDIATE, - __KILL_ATTR_MAX - }; - static const struct blobmsg_policy kill_policy[__KILL_ATTR_MAX] = { - [KILL_ATTR_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 }, - [KILL_ATTR_IMMEDIATE] = { .name = "immediate", .type = BLOBMSG_TYPE_BOOL }, - }; - struct blob_attr *tb[__KILL_ATTR_MAX]; - struct blob_attr *cur; - bool immediate = false; - int signal = SIGTERM; - - blobmsg_parse_attr(kill_policy, __KILL_ATTR_MAX, tb, data); - - if ((cur = tb[KILL_ATTR_SIGNAL])) - signal = blobmsg_get_u32(cur); - - if ((cur = tb[KILL_ATTR_IMMEDIATE])) - immediate = blobmsg_get_bool(cur); - - if (wdev->state != IFS_TEARDOWN || wdev->kill_request) - return UBUS_STATUS_PERMISSION_DENIED; - - wireless_process_kill_all(wdev, signal, immediate); - - if (list_empty(&wdev->script_proc)) - return 0; - - wdev->kill_request = calloc(1, sizeof(*wdev->kill_request)); - ubus_defer_request(ubus_ctx, req, wdev->kill_request); - - return 0; -} - -/* ubus callback network.wireless.notify, command = set_retry */ -static int -wireless_device_set_retry(struct wireless_device *wdev, struct blob_attr *data) -{ - static const struct blobmsg_policy retry_policy = { - .name = "retry", .type = BLOBMSG_TYPE_INT32 - }; - struct blob_attr *val; - - blobmsg_parse_attr(&retry_policy, 1, &val, data); - if (val) - wdev->retry = blobmsg_get_u32(val); - else - wdev->retry = WIRELESS_SETUP_RETRY; - __wireless_device_set_up(wdev, 0); - netifd_log_message(L_NOTICE, "Wireless device '%s' set retry=%d\n", wdev->name, wdev->retry); - return 0; -} - -enum { - NOTIFY_CMD_UP = 0, - NOTIFY_CMD_SET_DATA = 1, - NOTIFY_CMD_PROCESS_ADD = 2, - NOTIFY_CMD_PROCESS_KILL_ALL = 3, - NOTIFY_CMD_SET_RETRY = 4, -}; - -/* ubus callback network.wireless.notify */ -int -wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data, - struct ubus_request_data *req) -{ - enum { - NOTIFY_ATTR_COMMAND, - NOTIFY_ATTR_VIF, - NOTIFY_ATTR_VLAN, - NOTIFY_ATTR_DATA, - __NOTIFY_MAX, - }; - static const struct blobmsg_policy notify_policy[__NOTIFY_MAX] = { - [NOTIFY_ATTR_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_INT32 }, - [NOTIFY_ATTR_VIF] = { .name = "interface", .type = BLOBMSG_TYPE_STRING }, - [NOTIFY_ATTR_VLAN] = { .name = "vlan", .type = BLOBMSG_TYPE_STRING }, - [NOTIFY_ATTR_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, - }; - struct wireless_interface *vif = NULL; - struct wireless_vlan *vlan = NULL; - struct blob_attr *tb[__NOTIFY_MAX]; - struct blob_attr *cur, **pdata; - - blobmsg_parse_attr(notify_policy, __NOTIFY_MAX, tb, data); - - if (!tb[NOTIFY_ATTR_COMMAND]) - return UBUS_STATUS_INVALID_ARGUMENT; - - if ((cur = tb[NOTIFY_ATTR_VIF]) != NULL) { - vif = vlist_find(&wdev->interfaces, blobmsg_data(cur), vif, node); - if (!vif) - return UBUS_STATUS_NOT_FOUND; - } - - if ((cur = tb[NOTIFY_ATTR_VLAN]) != NULL) { - if (!vif) - return UBUS_STATUS_NOT_FOUND; - vlan = vlist_find(&vif->vlans, blobmsg_data(cur), vlan, node); - if (!vlan) - return UBUS_STATUS_NOT_FOUND; - } - - cur = tb[NOTIFY_ATTR_DATA]; - if (!cur) - return UBUS_STATUS_INVALID_ARGUMENT; - - switch (blobmsg_get_u32(tb[NOTIFY_ATTR_COMMAND])) { - case NOTIFY_CMD_UP: - if (vif || vlan) - return UBUS_STATUS_INVALID_ARGUMENT; - - if (wdev->state != IFS_SETUP) - return UBUS_STATUS_PERMISSION_DENIED; - - wireless_device_mark_up(wdev); - break; - case NOTIFY_CMD_SET_DATA: - if (vlan) - pdata = &vlan->data; - else if (vif) - pdata = &vif->data; - else - pdata = &wdev->data; - - free(*pdata); - *pdata = blob_memdup(cur); - if (vlan) - wireless_vlan_set_data(vlan); - else if (vif) - wireless_interface_set_data(vif); - wdev_prepare_prev_config(wdev); - break; - case NOTIFY_CMD_PROCESS_ADD: - return wireless_device_add_process(wdev, cur); - case NOTIFY_CMD_PROCESS_KILL_ALL: - return wireless_device_process_kill_all(wdev, cur, req); - case NOTIFY_CMD_SET_RETRY: - return wireless_device_set_retry(wdev, cur); - default: - return UBUS_STATUS_INVALID_ARGUMENT; - } - - return 0; -} - -static void -wdev_vlan_check_network_enabled(struct wireless_device *wdev, - struct wireless_interface *vif) -{ - struct wireless_vlan *vlan; - - vlist_for_each_element(&vif->vlans, vlan, node) { - int enabled = -1, ifindex = -1; - - wireless_check_interface(vlan->network, &enabled, &ifindex); - - if (wdev->state != IFS_UP || vlan->network_ifindex == ifindex) - continue; - - vlan->network_ifindex = ifindex; - wdev->config_update = true; - } -} - -static void -wdev_check_network_enabled(struct wireless_device *wdev) -{ - struct wireless_interface *vif; - - vlist_for_each_element(&wdev->interfaces, vif, node) { - int enabled = -1, ifindex = -1; - - wireless_check_interface(vif->network, &enabled, &ifindex); - wdev_vlan_check_network_enabled(wdev, vif); - - if (vif->disabled == !enabled && - (wdev->state != IFS_UP || vif->network_ifindex == ifindex)) - continue; - - vif->disabled = !enabled; - vif->network_ifindex = ifindex; - wdev->config_update = true; - } -} - -static void -__wireless_start_pending(struct uloop_timeout *t) -{ - struct wireless_device *wdev; - - vlist_for_each_element(&wireless_devices, wdev, node) { - wdev_check_network_enabled(wdev); - if (wdev->config_update) - wdev_set_config_state(wdev, IFC_RELOAD); - __wireless_device_set_up(wdev, 0); - } -} - -void wireless_start_pending(int timeout) -{ - static struct uloop_timeout timer = { - .cb = __wireless_start_pending - }; - - if (timeout) { - uloop_timeout_set(&timer, timeout); - return; - } - - uloop_timeout_cancel(&timer); - timer.cb(&timer); -} - -void wireless_check_network_enabled(void) -{ - struct wireless_device *wdev; - - vlist_for_each_element(&wireless_devices, wdev, node) { - wdev_check_network_enabled(wdev); - - if (wdev->config_update) - wireless_start_pending(1000); - } -} - -void wireless_device_hotplug_event(const char *name, bool add) -{ - struct wireless_interface *vif; - struct wireless_device *wdev; - const char *s; - size_t len; - - s = strstr(name, ".sta"); - if (s) { - if (strchr(s + 4, '.')) - return; - - len = s - name; - } else { - len = strlen(name); - } - - vlist_for_each_element(&wireless_devices, wdev, node) { - vlist_for_each_element(&wdev->interfaces, vif, node) { - if (!vif->ifname) - continue; - - if (strlen(vif->ifname) != len || - strncmp(vif->ifname, name, len) != 0) - continue; - - wireless_interface_handle_link(vif, name, add); - } - } -} diff --git a/wireless.h b/wireless.h deleted file mode 100644 index 7a981e7..0000000 --- a/wireless.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * netifd - network interface daemon - * Copyright (C) 2013 Felix Fietkau - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef __NETIFD_WIRELESS_H -#define __NETIFD_WIRELESS_H - -#include -#include -#include "interface.h" - -extern struct vlist_tree wireless_devices; -extern struct avl_tree wireless_drivers; - -struct wireless_driver { - struct avl_node node; - - const char *name; - const char *script; - - struct { - char *buf; - struct uci_blob_param_list *config; - } device, interface, vlan, station; -}; - -struct wireless_device { - struct vlist_node node; - - struct list_head handler; - bool handler_action; - bool handler_pending; - bool serialize; - bool config_update; - - struct wireless_driver *drv; - struct vlist_tree interfaces; - char *name; - - struct netifd_process script_task; - struct uloop_timeout timeout; - struct uloop_timeout poll; - - struct list_head script_proc; - struct uloop_fd script_proc_fd; - struct uloop_timeout script_check; - - struct ubus_request_data *kill_request; - - struct blob_attr *prev_config; - struct blob_attr *config; - struct blob_attr *data; - - bool autostart; - bool disabled; - bool retry_setup_failed; - - enum interface_state state; - enum interface_config_state config_state; - bool reconf; - bool cancel; - int retry; - - int vif_idx; -}; - -struct wireless_interface { - struct vlist_node node; - const char *section; - char *name; - - struct vlist_tree vlans; - struct vlist_tree stations; - struct wireless_device *wdev; - - struct blob_attr *config; - struct blob_attr *data; - - const char *ifname; - struct blob_attr *network; - struct blob_attr *network_vlan; - bool proxyarp; - bool isolate; - bool bridge_isolate; - bool ap_mode; - int multicast_to_unicast; - int vlan_idx; - int sta_idx; - bool disabled; - - int network_ifindex; -}; - -struct wireless_vlan { - struct vlist_node node; - const char *section; - char *name; - - struct blob_attr *config; - struct blob_attr *data; - - const char *ifname; - struct blob_attr *network; - struct blob_attr *network_vlan; - int multicast_to_unicast; - bool isolate; - bool bridge_isolate; - - int network_ifindex; -}; - -struct wireless_station { - struct vlist_node node; - const char *section; - char *name; - - struct blob_attr *config; - struct blob_attr *data; -}; - -struct wireless_process { - struct list_head list; - - const char *exe; - int pid; - - bool required; - bool keep; -}; - -void wireless_device_create(struct wireless_driver *drv, const char *name, struct blob_attr *data); -void wireless_device_set_up(struct wireless_device *wdev); -void wireless_device_set_down(struct wireless_device *wdev); -void wireless_device_reconf(struct wireless_device *wdev); -void wireless_device_status(struct wireless_device *wdev, struct blob_buf *b); -void wireless_device_get_validate(struct wireless_device *wdev, struct blob_buf *b); -struct wireless_interface* wireless_interface_create(struct wireless_device *wdev, struct blob_attr *data, const char *section); -void wireless_vlan_create(struct wireless_interface *vif, struct blob_attr *data, const char *section); -void wireless_station_create(struct wireless_interface *vif, struct blob_attr *data, const char *section); -int wireless_device_notify(struct wireless_device *wdev, struct blob_attr *data, - struct ubus_request_data *req); - -void wireless_check_network_enabled(void); -void wireless_start_pending(int timeout); -void wireless_init(void); -void wireless_device_hotplug_event(const char *name, bool add); - -#endif -- 2.30.2