netifd: update to Git HEAD (2025-08-02)
3a7878065829 system-dummy: add missing vrf functions 471d9d6abb6d CMakeLists.txt: bump minimum required version c3a0255e2150 scripts: fix dummy mode on systems where libubox is in /usr/local 7a3b281230e4 update example mac80211 script and wireless config d9f2dd2614f2 wireless: replace with ucode scripts 74c22601baad wireless: add MLO support to example scripts Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		@@ -5,9 +5,9 @@ PKG_RELEASE:=1
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
PKG_SOURCE_PROTO:=git
 | 
					PKG_SOURCE_PROTO:=git
 | 
				
			||||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git
 | 
					PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git
 | 
				
			||||||
PKG_SOURCE_DATE:=2025-05-23
 | 
					PKG_SOURCE_DATE:=2025-08-02
 | 
				
			||||||
PKG_SOURCE_VERSION:=7901e66c5f273bceee8981bc8a0c8b0e60945f60
 | 
					PKG_SOURCE_VERSION:=74c22601baad83cc9bc0fddb98f15d7abaa52c67
 | 
				
			||||||
PKG_MIRROR_HASH:=8b85ec64e446ae065b1466c520b2d3aae329b6167221e425af903777278f557e
 | 
					PKG_MIRROR_HASH:=3841d17a0e59bab8dbcf0433bbc326f5b7b9b54dbb79b8759c20bdb5f0340227
 | 
				
			||||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
 | 
					PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKG_LICENSE:=GPL-2.0
 | 
					PKG_LICENSE:=GPL-2.0
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										80
									
								
								package/network/config/netifd/files/lib/netifd/main.uc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								package/network/config/netifd/files/lib/netifd/main.uc
									
									
									
									
									
										Normal file
									
								
							@@ -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.guard(ex_handler);
 | 
				
			||||||
 | 
					libubus.guard(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`);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										131
									
								
								package/network/config/netifd/files/lib/netifd/utils.uc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								package/network/config/netifd/files/lib/netifd/utils.uc
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,637 @@
 | 
				
			|||||||
 | 
					'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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const mlo_name = "#mlo";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let mlo_wdev;
 | 
				
			||||||
 | 
					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_mlo_fixup(config)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!mlo_wdev)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (let name, iface in config.interfaces) {
 | 
				
			||||||
 | 
							let config = iface.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (config.mode != "link")
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let mlo_config = mlo_wdev.handler_data[iface.name];
 | 
				
			||||||
 | 
							if (mlo_config && mlo_config.ifname)
 | 
				
			||||||
 | 
								config.ifname = mlo_config.ifname;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (wdev_handler[mlo_name])
 | 
				
			||||||
 | 
							return mlo_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);
 | 
				
			||||||
 | 
						if (name != mlo_name)
 | 
				
			||||||
 | 
							wdev_mlo_fixup(wdev.handler_config);
 | 
				
			||||||
 | 
						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();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 || 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.name == mlo_name)
 | 
				
			||||||
 | 
							mlo_wdev = wdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										450
									
								
								package/network/config/wifi-scripts/files/lib/netifd/wireless.uc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										450
									
								
								package/network/config/wifi-scripts/files/lib/netifd/wireless.uc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,450 @@
 | 
				
			|||||||
 | 
					'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 mlo_device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let sections = {
 | 
				
			||||||
 | 
							device: {},
 | 
				
			||||||
 | 
							iface: {},
 | 
				
			||||||
 | 
							vlan: {},
 | 
				
			||||||
 | 
							station: {},
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						let radio_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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (type == "wifi-iface" && parse_bool(data.mlo))
 | 
				
			||||||
 | 
								mlo_device = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mlo_device) {
 | 
				
			||||||
 | 
							devices[wdev.mlo_name] = {
 | 
				
			||||||
 | 
								name: wdev.mlo_name,
 | 
				
			||||||
 | 
								config: {
 | 
				
			||||||
 | 
									type: "mac80211",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								vif: [],
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							handlers[wdev.mlo_name] = wireless.handlers.mac80211;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
							if (mlo_vif)
 | 
				
			||||||
 | 
								dev_names = [ wdev.mlo_name, ...dev_names ];
 | 
				
			||||||
 | 
							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);
 | 
				
			||||||
 | 
								if (mlo_vif && dev_name != wdev.mlo_name)
 | 
				
			||||||
 | 
									config.mode = "link";
 | 
				
			||||||
 | 
								config.radios = radios;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								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);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let udata = ubus.call({
 | 
				
			||||||
 | 
							object: "service",
 | 
				
			||||||
 | 
							method: "get_data",
 | 
				
			||||||
 | 
							data: {
 | 
				
			||||||
 | 
								type: "wifi-iface"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (let svcname, svc in udata) {
 | 
				
			||||||
 | 
							for (let typename, data in svc) {
 | 
				
			||||||
 | 
								for (let radio, vifs in data) {
 | 
				
			||||||
 | 
									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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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) {
 | 
				
			||||||
 | 
							if (name == wdev.mlo_name)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							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) {
 | 
				
			||||||
 | 
								let mlo_dev = wireless.devices[wdev.mlo_name];
 | 
				
			||||||
 | 
								if (mlo_dev)
 | 
				
			||||||
 | 
									mlo_dev.start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return wdev_call(req, (dev) => {
 | 
				
			||||||
 | 
									dev.start();
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						down: {
 | 
				
			||||||
 | 
							args: wdev_args,
 | 
				
			||||||
 | 
							call: function(req) {
 | 
				
			||||||
 | 
								let mlo_dev = wireless.devices[wdev.mlo_name];
 | 
				
			||||||
 | 
								if (mlo_dev)
 | 
				
			||||||
 | 
									mlo_dev.config_change = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return wdev_call(req, (dev) => {
 | 
				
			||||||
 | 
									dev.stop();
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						reconf: {
 | 
				
			||||||
 | 
							args: wdev_args,
 | 
				
			||||||
 | 
							call: function(req) {
 | 
				
			||||||
 | 
								let mlo_dev = wireless.devices[wdev.mlo_name];
 | 
				
			||||||
 | 
								if (mlo_dev)
 | 
				
			||||||
 | 
									mlo_dev.update();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								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,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Reference in New Issue
	
	Block a user