| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- 'use strict';
- import * as ubus 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 { find_phy } from "wifi.utils";
- import * as wdev from "./wireless-device.uc";
- let wireless = netifd.wireless = {
- handlers: {},
- devices: {},
- mlo: {},
- path: realpath(netifd.main_path + "/wireless"),
- };
- function wpad_update_mlo(service, mode)
- {
- let config = {};
- for (let ifname, data in wireless.mlo) {
- if (data.mode != mode)
- continue;
- data.phy = find_phy(data.radio_config[0], true);
- if (!data.phy)
- continue;
- config[ifname] = data;
- }
- ubus.call({
- object: service,
- method: "mld_set",
- return: "ignore",
- data: { config },
- });
- }
- function hostapd_update_mlo()
- {
- wpad_update_mlo("hostapd", "ap");
- }
- function supplicant_update_mlo()
- {
- wpad_update_mlo("wpa_supplicant", "sta");
- }
- function update_config(new_devices, mlo_vifs)
- {
- wireless.mlo = mlo_vifs;
- hostapd_update_mlo();
- supplicant_update_mlo();
- 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 mlo_vifs = {};
- let sections = {
- device: {},
- iface: {},
- vlan: {},
- station: {},
- };
- let radio_idx = {};
- let vif_idx = {};
- 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;
- if (data.radio != null)
- radio_idx[name] = +data.radio;
- 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);
- let mlo_vif = parse_bool(data.mlo);
- let radios = map(dev_names, (v) => radio_idx[v]);
- radios = filter(radios, (v) => v != null);
- let radio_config = map(dev_names, (v) => devices[v].config);
- let ifname;
- let mlo_created = false;
- for (let dev_name in dev_names) {
- let dev = devices[dev_name];
- if (!dev || dev.config.disabled)
- continue;
- let handler = handlers[dev_name];
- if (!handler)
- continue;
- let config = parse_attribute_list(data, handler.iface);
- config.radios = radios;
- if (mlo_vif && !mlo_created) {
- let mlo_config = { ...config };
- if (config.wds)
- mlo_config['4addr'] = config.wds;
- mlo_config.radio_config = radio_config;
- ifname = config.ifname;
- if (!ifname) {
- let idx = vif_idx[config.mode] ?? 0;
- vif_idx[config.mode] = idx + 1;
- ifname = config.mode + "-mld" + idx;
- }
- mlo_vifs[ifname] = mlo_config;
- mlo_created = true;
- }
- if (ifname)
- config.ifname = ifname;
- if (dev_name != dev_names[0])
- delete config.macaddr;
- if (config.radio_macaddr) {
- let idx = index(dev_names, dev_name);
- let macaddr = idx >= 0 ? config.radio_macaddr[idx] : null;
- if (macaddr)
- config.macaddr = macaddr;
- }
- 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) {
- let ifaces = parse_array(data.iface);
- for (let iface, iface_vifs in vifs) {
- if (length(ifaces) && index(ifaces, iface) < 0)
- continue;
- for (let vif in iface_vifs) {
- 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) {
- let ifaces = parse_array(data.iface);
- for (let iface, iface_vifs in vifs) {
- if (length(ifaces) && index(ifaces, iface) < 0)
- continue;
- for (let vif in iface_vifs) {
- 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);
- }
- }
- }
- let udata = ubus.call({
- object: "service",
- method: "get_data",
- data: {
- type: "wifi-device"
- },
- });
- for (let svcname, svc in udata) {
- for (let insname, ins in svc) {
- for (let typename, data in ins) {
- for (let radio, config in data) {
- if (type(config) != "object")
- continue;
- let dev = devices[radio];
- if (dev) {
- dev.config = { ...dev.config, ...config };
- continue;
- }
- let handler = wireless.handlers[config.type];
- if (!handler)
- continue;
- dev = devices[radio] = {
- name,
- config,
- vif: [],
- };
- handlers[radio] = handler;
- }
- }
- }
- }
- udata = ubus.call({
- object: "service",
- method: "get_data",
- data: {
- type: "wifi-iface"
- },
- });
- for (let svcname, svc in udata) {
- for (let insname, ins in svc) {
- for (let typename, data in ins) {
- for (let radio, vifs in data) {
- if (type(vifs) != "object")
- continue;
- for (let name, vif in vifs) {
- let devs = vif.device;
- if (type(devs) != "array")
- devs = [ devs ];
- let config = vif.config;
- if (!config)
- continue;
- for (let device in devs) {
- let dev = devices[device];
- if (!dev)
- continue;
- let vif_data = {
- name, device, config,
- vlan: [],
- sta: []
- };
- if (vif.vlans)
- vif_data.vlans = vif.vlans;
- if (vif.stations)
- vif_data.sta = vif.stations;
- vifs[name] ??= [];
- push(vifs[name], vif_data);
- push(dev.vif, vif_data);
- }
- }
- }
- }
- }
- }
- update_config(devices, mlo_vifs);
- }
- 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(ifname, add)
- {
- for (let name, dev in wireless.devices)
- if (dev.autostart)
- dev.hotplug(ifname, 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,
- radio_macaddr: TYPE_ARRAY,
- },
- 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 ubus.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) {
- hostapd_update_mlo();
- 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;
- });
- }
- },
- retry: {
- args: wdev_args,
- call: function(req) {
- hostapd_update_mlo();
- return wdev_call(req, (dev) => {
- dev.retry_setup();
- return 0;
- });
- }
- },
- reconf: {
- args: wdev_args,
- call: function(req) {
- hostapd_update_mlo();
- 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 ubus.STATUS_INVALID_ARGUMENT;
- dev = wireless.devices[dev];
- if (!dev)
- return ubus.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);
- wireless.listener = ubus.listener("ubus.object.add", (event, msg) => {
- if (msg.path == "hostapd")
- hostapd_update_mlo();
- else if (msg.path == "wpa_supplicant")
- supplicant_update_mlo();
- });
- return {
- hotplug,
- config_init,
- config_start,
- check_interfaces,
- };
|