| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657 |
- 'use strict';
- import * as ubus 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 wireless_config_done()
- {
- ubus.call({
- object: "wpa_supplicant",
- method: "mld_start",
- return: "ignore",
- data: { },
- });
- ubus.call({
- object: "service",
- method: "event",
- return: "ignore",
- data: {
- type: "netifd.wireless.done",
- data: {},
- },
- });
- }
- 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 = {
- external: 2,
- check_vlan: false,
- 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.handler_config.data = wdev.handler_data[wdev.name] ?? {};
- 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();
- if (!wdev_cur && !length(wdev_handler))
- wireless_config_done();
- }
- function run_handler(wdev, op, cb)
- {
- wdev.dbg("queue " + op);
- 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)
- 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)
- 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 retry_setup()
- {
- if (this.delete)
- return;
- if (this.state != "down" || !this.autostart)
- return;
- this.start();
- }
- 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 ubus.STATUS_INVALID_ARGUMENT;
- if (vif) {
- cur = vif = config.interfaces[vif];
- if (!vif)
- return ubus.STATUS_NOT_FOUND;
- cur_type = "vif";
- }
- if (vlan) {
- if (!vif)
- return ubus.STATUS_INVALID_ARGUMENT;
- cur = vlan = vif.vlans[vlan];
- if (!vlan)
- return ubus.STATUS_NOT_FOUND;
- cur_type = "vlan";
- }
- let key = cur.name;
- if (cur_type == "vlan")
- key = vif.name + "/" + vlan.name;
- wdev.handler_data[key] = {
- ...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 ubus.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 ubus.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, add);
- }
- }
- function get_status_data(wdev, vif, parent_vif)
- {
- let key = vif.name;
- if (parent_vif)
- key = parent_vif.name + "/" + vif.name;
- let hdata = wdev.handler_data[key];
- 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, vif));
- 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,
- retry_setup,
- 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);
- };
|