'require uci';
'require rpc';
'require validation';
+'require baseclass';
+'require firewall';
var proto_errors = {
CONNECT_FAILED: _('Connection attempt failed'),
Object.assign(protos, { none: { no_device: false } });
/* Hack: emulate relayd protocol */
- if (!protos.hasOwnProperty('relay'))
+ if (!protos.hasOwnProperty('relay') && L.hasSystemFeature('relayd'))
Object.assign(protos, { relay: { no_device: true } });
Object.assign(_protospecs, protos);
L.resolveDefault(callLuciWirelessDevices(), {}),
L.resolveDefault(callLuciHostHints(), {}),
getProtocolHandlers(),
- uci.load(['network', 'wireless', 'luci'])
+ L.resolveDefault(uci.load('network')),
+ L.resolveDefault(uci.load('wireless')),
+ L.resolveDefault(uci.load('luci'))
]).then(function(data) {
var netifd_ifaces = data[0],
board_json = data[1],
stats: dev.stats,
macaddr: dev.mac,
type: dev.type,
+ devtype: dev.devtype,
mtu: dev.mtu,
qlen: dev.qlen,
wireless: dev.wireless,
+ parent: dev.parent,
ipaddrs: [],
ip6addrs: []
};
s.isBridge[name] = true;
}
+ for (var name in luci_devs) {
+ var dev = luci_devs[name];
+
+ if (!dev.parent || dev.devtype != 'dsa')
+ continue;
+
+ s.isSwitch[dev.parent] = true;
+ s.isSwitch[name] = true;
+ }
+
if (L.isObject(board_json.switch)) {
for (var switchname in board_json.switch) {
var layout = board_json.switch[switchname],
var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork;
/**
- * @class
+ * @class network
* @memberof LuCI
* @hideconstructor
* @classdesc
*
- * The `LuCI.Network` class combines data from multiple `ubus` apis to
+ * The `LuCI.network` class combines data from multiple `ubus` apis to
* provide an abstraction of the current network configuration state.
*
* It provides methods to enumerate interfaces and devices, to query
* current configuration details and to manipulate settings.
*/
-Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
+Network = baseclass.extend(/** @lends LuCI.network.prototype */ {
/**
* Converts the given prefix size in bits to a netmask.
*
* such as the used key management protocols, active ciphers and
* protocol versions.
*
- * @typedef {Object<string, boolean|Array<number|string>>} LuCI.Network.WifiEncryption
- * @memberof LuCI.Network
+ * @typedef {Object<string, boolean|Array<number|string>>} LuCI.network.WifiEncryption
+ * @memberof LuCI.network
*
* @property {boolean} enabled
* Specifies whether any kind of encryption, such as `WEP` or `WPA` is
*/
/**
- * Converts a given {@link LuCI.Network.WifiEncryption encryption entry}
+ * Converts a given {@link LuCI.network.WifiEncryption encryption entry}
* into a human readable string such as `mixed WPA/WPA2 PSK (TKIP, CCMP)`
* or `WPA3 SAE (CCMP)`.
*
* @method
*
- * @param {LuCI.Network.WifiEncryption} encryption
+ * @param {LuCI.network.WifiEncryption} encryption
* The wireless encryption entry to convert.
*
* @returns {null|string}
},
/**
- * Instantiates the given {@link LuCI.Network.Protocol Protocol} backend,
+ * Instantiates the given {@link LuCI.network.Protocol Protocol} backend,
* optionally using the given network name.
*
* @param {string} protoname
* but it is allowed to omit it, e.g. to query protocol capabilities
* without the need for an existing interface.
*
- * @returns {null|LuCI.Network.Protocol}
+ * @returns {null|LuCI.network.Protocol}
* Returns the instantiated protocol backend class or `null` if the given
* protocol isn't known.
*/
},
/**
- * Obtains instances of all known {@link LuCI.Network.Protocol Protocol}
+ * Obtains instances of all known {@link LuCI.network.Protocol Protocol}
* backend classes.
*
- * @returns {Array<LuCI.Network.Protocol>}
+ * @returns {Array<LuCI.network.Protocol>}
* Returns an array of protocol class instances.
*/
getProtocols: function() {
},
/**
- * Registers a new {@link LuCI.Network.Protocol Protocol} subclass
+ * Registers a new {@link LuCI.network.Protocol Protocol} subclass
* with the given methods and returns the resulting subclass value.
*
* This functions internally calls
* The member methods and values of the new `Protocol` subclass to
* be passed to {@link LuCI.Class.extend Class.extend()}.
*
- * @returns {LuCI.Network.Protocol}
+ * @returns {LuCI.network.Protocol}
* Returns the new `Protocol` subclass.
*/
registerProtocol: function(protoname, methods) {
* An object of uci option values to set on the new network or to
* update in an existing, empty network.
*
- * @returns {Promise<null|LuCI.Network.Protocol>}
+ * @returns {Promise<null|LuCI.network.Protocol>}
* Returns a promise resolving to the `Protocol` subclass instance
* describing the added network or resolving to `null` if the name
* was invalid or if a non-empty network of the given name already
},
/**
- * Get a {@link LuCI.Network.Protocol Protocol} instance describing
+ * Get a {@link LuCI.network.Protocol Protocol} instance describing
* the network with the given name.
*
* @param {string} name
* The logical interface name of the network get, e.g. `lan` or `wan`.
*
- * @returns {Promise<null|LuCI.Network.Protocol>}
+ * @returns {Promise<null|LuCI.network.Protocol>}
* Returns a promise resolving to a
- * {@link LuCI.Network.Protocol Protocol} subclass instance describing
+ * {@link LuCI.network.Protocol Protocol} subclass instance describing
* the network or `null` if the network did not exist.
*/
getNetwork: function(name) {
/**
* Gets an array containing all known networks.
*
- * @returns {Promise<Array<LuCI.Network.Protocol>>}
+ * @returns {Promise<Array<LuCI.network.Protocol>>}
* Returns a promise resolving to a name-sorted array of
- * {@link LuCI.Network.Protocol Protocol} subclass instances
+ * {@link LuCI.network.Protocol Protocol} subclass instances
* describing all known networks.
*/
getNetworks: function() {
* `false` if the given network could not be found.
*/
deleteNetwork: function(name) {
- var requireFirewall = Promise.resolve(L.require('firewall')).catch(function() {});
+ var requireFirewall = Promise.resolve(L.require('firewall')).catch(function() {}),
+ network = this.instantiateNetwork(name);
- return Promise.all([ requireFirewall, initNetworkState() ]).then(function() {
- var uciInterface = uci.get('network', name);
+ return Promise.all([ requireFirewall, initNetworkState() ]).then(function(res) {
+ var uciInterface = uci.get('network', name),
+ firewall = res[0];
if (uciInterface != null && uciInterface['.type'] == 'interface') {
- uci.remove('network', name);
+ return Promise.resolve(network ? network.deleteConfiguration() : null).then(function() {
+ uci.remove('network', name);
- uci.sections('luci', 'ifstate', function(s) {
- if (s.interface == name)
- uci.remove('luci', s['.name']);
- });
+ uci.sections('luci', 'ifstate', function(s) {
+ if (s.interface == name)
+ uci.remove('luci', s['.name']);
+ });
- uci.sections('network', 'alias', function(s) {
- if (s.interface == name)
- uci.remove('network', s['.name']);
- });
+ uci.sections('network', 'alias', function(s) {
+ if (s.interface == name)
+ uci.remove('network', s['.name']);
+ });
- uci.sections('network', 'route', function(s) {
- if (s.interface == name)
- uci.remove('network', s['.name']);
- });
+ uci.sections('network', 'route', function(s) {
+ if (s.interface == name)
+ uci.remove('network', s['.name']);
+ });
- uci.sections('network', 'route6', function(s) {
- if (s.interface == name)
- uci.remove('network', s['.name']);
- });
+ uci.sections('network', 'route6', function(s) {
+ if (s.interface == name)
+ uci.remove('network', s['.name']);
+ });
- uci.sections('wireless', 'wifi-iface', function(s) {
- var networks = L.toArray(s.network).filter(function(network) { return network != name });
+ uci.sections('wireless', 'wifi-iface', function(s) {
+ var networks = L.toArray(s.network).filter(function(network) { return network != name });
- if (networks.length > 0)
- uci.set('wireless', s['.name'], 'network', networks.join(' '));
- else
- uci.unset('wireless', s['.name'], 'network');
- });
+ if (networks.length > 0)
+ uci.set('wireless', s['.name'], 'network', networks.join(' '));
+ else
+ uci.unset('wireless', s['.name'], 'network');
+ });
- if (L.firewall)
- return L.firewall.deleteNetwork(name).then(function() { return true });
+ if (firewall)
+ return firewall.deleteNetwork(name).then(function() { return true });
- return true;
+ return true;
+ }).catch(function() {
+ return false;
+ });
}
return false;
},
/**
- * Get a {@link LuCI.Network.Device Device} instance describing the
+ * Get a {@link LuCI.network.Device Device} instance describing the
* given network device.
*
* @param {string} name
* The name of the network device to get, e.g. `eth0` or `br-lan`.
*
- * @returns {Promise<null|LuCI.Network.Device>}
+ * @returns {Promise<null|LuCI.network.Device>}
* Returns a promise resolving to the `Device` instance describing
* the network device or `null` if the given device name could not
* be found.
/**
* Get a sorted list of all found network devices.
*
- * @returns {Promise<Array<LuCI.Network.Device>>}
+ * @returns {Promise<Array<LuCI.network.Device>>}
* Returns a promise resolving to a sorted array of `Device` class
* instances describing the network devices found on the system.
*/
}
}
+ /* find bridge VLAN devices */
+ var uciBridgeVLANs = uci.sections('network', 'bridge-vlan');
+ for (var i = 0; i < uciBridgeVLANs.length; i++) {
+ var basedev = uciBridgeVLANs[i].device,
+ local = uciBridgeVLANs[i].local,
+ alias = uciBridgeVLANs[i].alias,
+ vid = +uciBridgeVLANs[i].vlan,
+ ports = L.toArray(uciBridgeVLANs[i].ports);
+
+ if (local == '0')
+ continue;
+
+ if (isNaN(vid) || vid < 0 || vid > 4095)
+ continue;
+
+ var vlandev = '%s.%s'.format(basedev, alias || vid);
+
+ _state.isBridge[basedev] = true;
+
+ if (!_state.bridges.hasOwnProperty(basedev))
+ _state.bridges[basedev] = {
+ name: basedev,
+ ifnames: []
+ };
+
+ if (!devices.hasOwnProperty(vlandev))
+ devices[vlandev] = this.instantiateDevice(vlandev);
+
+ ports.forEach(function(port_name) {
+ var m = port_name.match(/^([^:]+)(?::[ut*]+)?$/),
+ p = m ? m[1] : null;
+
+ if (!p)
+ return;
+
+ if (_state.bridges[basedev].ifnames.filter(function(sd) { return sd.name == p }).length)
+ return;
+
+ _state.netdevs[p] = _state.netdevs[p] || {
+ name: p,
+ ipaddrs: [],
+ ip6addrs: [],
+ type: 1,
+ devtype: 'ethernet',
+ stats: {},
+ flags: {}
+ };
+
+ _state.bridges[basedev].ifnames.push(_state.netdevs[p]);
+ _state.netdevs[p].bridge = _state.bridges[basedev];
+ });
+ }
+
/* find wireless interfaces */
var uciWifiIfaces = uci.sections('wireless', 'wifi-iface'),
networkCount = {};
devices[netid] = this.instantiateDevice(netid);
}
+ /* find uci declared devices */
+ var uciDevices = uci.sections('network', 'device');
+
+ for (var i = 0; i < uciDevices.length; i++) {
+ var type = uciDevices[i].type,
+ name = uciDevices[i].name;
+
+ if (!type || !name || devices.hasOwnProperty(name))
+ continue;
+
+ if (type == 'bridge')
+ _state.isBridge[name] = true;
+
+ devices[name] = this.instantiateDevice(name);
+ }
+
var rv = [];
for (var netdev in devices)
},
/**
- * Get a {@link LuCI.Network.WifiDevice WifiDevice} instance describing
+ * Get a {@link LuCI.network.WifiDevice WifiDevice} instance describing
* the given wireless radio.
*
* @param {string} devname
* The configuration name of the wireless radio to lookup, e.g. `radio0`
* for the first mac80211 phy on the system.
*
- * @returns {Promise<null|LuCI.Network.WifiDevice>}
+ * @returns {Promise<null|LuCI.network.WifiDevice>}
* Returns a promise resolving to the `WifiDevice` instance describing
* the underlying radio device or `null` if the wireless radio could not
* be found.
/**
* Obtain a list of all configured radio devices.
*
- * @returns {Promise<Array<LuCI.Network.WifiDevice>>}
+ * @returns {Promise<Array<LuCI.network.WifiDevice>>}
* Returns a promise resolving to an array of `WifiDevice` instances
* describing the wireless radios configured in the system.
* The order of the array corresponds to the order of the radios in
},
/**
- * Get a {@link LuCI.Network.WifiNetwork WifiNetwork} instance describing
+ * Get a {@link LuCI.network.WifiNetwork WifiNetwork} instance describing
* the given wireless network.
*
* @param {string} netname
* or a Linux network device name like `wlan0` which is resolved to the
* corresponding configuration section through `ubus` runtime information.
*
- * @returns {Promise<null|LuCI.Network.WifiNetwork>}
+ * @returns {Promise<null|LuCI.network.WifiNetwork>}
* Returns a promise resolving to the `WifiNetwork` instance describing
* the wireless network or `null` if the corresponding network could not
* be found.
},
/**
- * Get an array of all {@link LuCI.Network.WifiNetwork WifiNetwork}
+ * Get an array of all {@link LuCI.network.WifiNetwork WifiNetwork}
* instances describing the wireless networks present on the system.
*
- * @returns {Promise<Array<LuCI.Network.WifiNetwork>>}
+ * @returns {Promise<Array<LuCI.network.WifiNetwork>>}
* Returns a promise resolving to an array of `WifiNetwork` instances
* describing the wireless networks. The array will be empty if no networks
* are found.
* must at least contain a `device` property which is set to the radio
* name the new network belongs to.
*
- * @returns {Promise<null|LuCI.Network.WifiNetwork>}
+ * @returns {Promise<null|LuCI.network.WifiNetwork>}
* Returns a promise resolving to a `WifiNetwork` instance describing
* the newly added wireless network or `null` if the given options
* were invalid or if the associated radio device could not be found.
}
}
+ rv.sort(function(a, b) {
+ if (a.metric != b.metric)
+ return (a.metric - b.metric);
+
+ if (a.interface < b.interface)
+ return -1;
+ else if (a.interface > b.interface)
+ return 1;
+
+ return 0;
+ });
+
return rv;
}, this));
},
* This function looks up all networks having a default `0.0.0.0/0` route
* and returns them as array.
*
- * @returns {Promise<Array<LuCI.Network.Protocol>>}
+ * @returns {Promise<Array<LuCI.network.Protocol>>}
* Returns a promise resolving to an array of `Protocol` subclass
* instances describing the found default route interfaces.
*/
* This function looks up all networks having a default `::/0` route
* and returns them as array.
*
- * @returns {Promise<Array<LuCI.Network.Protocol>>}
+ * @returns {Promise<Array<LuCI.network.Protocol>>}
* Returns a promise resolving to an array of `Protocol` subclass
* instances describing the found IPv6 default route interfaces.
*/
* connections and external port labels of a switch.
*
* @typedef {Object<string, Object|Array>} SwitchTopology
- * @memberof LuCI.Network
+ * @memberof LuCI.network
*
* @property {Object<number, string>} netdevs
* The `netdevs` property points to an object describing the CPU port
/**
* Returns the topologies of all swconfig switches found on the system.
*
- * @returns {Promise<Object<string, LuCI.Network.SwitchTopology>>}
+ * @returns {Promise<Object<string, LuCI.network.SwitchTopology>>}
* Returns a promise resolving to an object containing the topologies
* of each switch. The object keys correspond to the name of the switches
* such as `switch0`, the values are
- * {@link LuCI.Network.SwitchTopology SwitchTopology} objects describing
+ * {@link LuCI.network.SwitchTopology SwitchTopology} objects describing
* the layout.
*/
getSwitchTopologies: function() {
/**
* Obtains the the network device name of the given object.
*
- * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} obj
+ * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} obj
* The object to get the device name from.
*
* @returns {null|string}
*
* This function aggregates information from various sources such as
* DHCP lease databases, ARP and IPv6 neighbour entries, wireless
- * association list etc. and returns a {@link LuCI.Network.Hosts Hosts}
+ * association list etc. and returns a {@link LuCI.network.Hosts Hosts}
* class instance describing the found hosts.
*
- * @returns {Promise<LuCI.Network.Hosts>}
+ * @returns {Promise<LuCI.network.Hosts>}
* Returns a `Hosts` instance describing host known on the system.
*/
getHostHints: function() {
/**
* @class
- * @memberof LuCI.Network
+ * @memberof LuCI.network
* @hideconstructor
* @classdesc
*
- * The `LuCI.Network.Hosts` class encapsulates host information aggregated
+ * The `LuCI.network.Hosts` class encapsulates host information aggregated
* from multiple sources and provides convenience functions to access the
* host information by different criteria.
*/
-Hosts = L.Class.extend(/** @lends LuCI.Network.Hosts.prototype */ {
+Hosts = baseclass.extend(/** @lends LuCI.network.Hosts.prototype */ {
__init__: function(hosts) {
this.hosts = hosts;
},
/**
* @class
- * @memberof LuCI.Network
+ * @memberof LuCI.network
* @hideconstructor
* @classdesc
*
* subclasses which describe logical UCI networks defined by `config
* interface` sections in `/etc/config/network`.
*/
-Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
+Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
__init__: function(name) {
this.sid = name;
},
* Get the name of this network protocol class.
*
* This function will be overwritten by subclasses created by
- * {@link LuCI.Network#registerProtocol Network.registerProtocol()}.
+ * {@link LuCI.network#registerProtocol Network.registerProtocol()}.
*
* @abstract
* @returns {string}
* Get the type of the underlying interface.
*
* This function actually is a convenience wrapper around
- * `proto.get("type")` and is mainly used by other `LuCI.Network` code
+ * `proto.get("type")` and is mainly used by other `LuCI.network` code
* to check whether the interface is declared as bridge in UCI.
*
* @returns {null|string}
*
* This function will translate the found error codes to human readable
* messages using the descriptions registered by
- * {@link LuCI.Network#registerErrorCode Network.registerErrorCode()}
+ * {@link LuCI.network#registerErrorCode Network.registerErrorCode()}
* and fall back to `"Unknown error (%s)"` where `%s` is replaced by the
* error code in case no translation can be found.
*
return null;
},
+ /**
+ * Check function for the protocol handler if a new interface is createable.
+ *
+ * This function should be overwritten by protocol specific subclasses.
+ *
+ * @abstract
+ *
+ * @param {string} ifname
+ * The name of the interface to be created.
+ *
+ * @returns {Promise<void>}
+ * Returns a promise resolving if new interface is createable, else
+ * rejects with an error message string.
+ */
+ isCreateable: function(ifname) {
+ return Promise.resolve(null);
+ },
+
/**
* Checks whether the protocol functionality is installed.
*
/**
* Add the given network device to the logical interface.
*
- * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device
+ * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} device
* The object or device name to add to the logical interface. In case the
* given argument is not a string, it is resolved though the
- * {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function.
+ * {@link LuCI.network#getIfnameOf Network.getIfnameOf()} function.
*
* @returns {boolean}
* Returns `true` if the device name has been added or `false` if any
/**
* Remove the given network device from the logical interface.
*
- * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device
+ * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} device
* The object or device name to remove from the logical interface. In case
* the given argument is not a string, it is resolved though the
- * {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function.
+ * {@link LuCI.network#getIfnameOf Network.getIfnameOf()} function.
*
* @returns {boolean}
* Returns `true` if the device name has been added or `false` if any
* Returns the Linux network device associated with this logical
* interface.
*
- * @returns {LuCI.Network.Device}
+ * @returns {LuCI.network.Device}
* Returns a `Network.Device` class instance representing the
* expected Linux network device according to the configuration.
*/
if (this.isVirtual()) {
var ifname = '%s-%s'.format(this.getProtocol(), this.sid);
_state.isTunnel[this.getProtocol() + '-' + this.sid] = true;
- return L.network.instantiateDevice(ifname, this);
+ return Network.prototype.instantiateDevice(ifname, this);
}
else if (this.isBridge()) {
var ifname = 'br-%s'.format(this.sid);
for (var i = 0; i < ifnames.length; i++) {
var m = ifnames[i].match(/^([^:/]+)/);
- return ((m && m[1]) ? L.network.instantiateDevice(m[1], this) : null);
+ return ((m && m[1]) ? Network.prototype.instantiateDevice(m[1], this) : null);
}
ifname = getWifiNetidByNetname(this.sid);
- return (ifname != null ? L.network.instantiateDevice(ifname[0], this) : null);
+ return (ifname != null ? Network.prototype.instantiateDevice(ifname[0], this) : null);
}
},
* Returns the layer 2 linux network device currently associated
* with this logical interface.
*
- * @returns {LuCI.Network.Device}
+ * @returns {LuCI.network.Device}
* Returns a `Network.Device` class instance representing the Linux
* network device currently associated with the logical interface.
*/
getL2Device: function() {
var ifname = this._ubus('device');
- return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
+ return (ifname != null ? Network.prototype.instantiateDevice(ifname, this) : null);
},
/**
* Returns the layer 3 linux network device currently associated
* with this logical interface.
*
- * @returns {LuCI.Network.Device}
+ * @returns {LuCI.network.Device}
* Returns a `Network.Device` class instance representing the Linux
* network device currently associated with the logical interface.
*/
getL3Device: function() {
var ifname = this._ubus('l3_device');
- return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
+ return (ifname != null ? Network.prototype.instantiateDevice(ifname, this) : null);
},
/**
* Returns a list of network sub-devices associated with this logical
* interface.
*
- * @returns {null|Array<LuCI.Network.Device>}
+ * @returns {null|Array<LuCI.network.Device>}
* Returns an array of of `Network.Device` class instances representing
* the sub-devices attached to this logical interface or `null` if the
* logical interface does not support sub-devices, e.g. because it is
var m = ifnames[i].match(/^([^:/]+)/);
if (m != null)
- rv.push(L.network.instantiateDevice(m[1], this));
+ rv.push(Network.prototype.instantiateDevice(m[1], this));
}
var uciWifiIfaces = uci.sections('wireless', 'wifi-iface');
var netid = getWifiNetidBySid(uciWifiIfaces[i]['.name']);
if (netid != null)
- rv.push(L.network.instantiateDevice(netid[0], this));
+ rv.push(Network.prototype.instantiateDevice(netid[0], this));
}
}
* Checks whether this logical interface contains the given device
* object.
*
- * @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device
+ * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} device
* The object or device name to check. In case the given argument is not
* a string, it is resolved though the
- * {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function.
+ * {@link LuCI.network#getIfnameOf Network.getIfnameOf()} function.
*
* @returns {boolean}
* Returns `true` when this logical interface contains the given network
}
return false;
- }
+ },
+
+ /**
+ * Cleanup related configuration entries.
+ *
+ * This function will be invoked if an interface is about to be removed
+ * from the configuration and is responsible for performing any required
+ * cleanup tasks, such as unsetting uci entries in related configurations.
+ *
+ * It should be overwritten by protocol specific subclasses.
+ *
+ * @abstract
+ *
+ * @returns {*|Promise<*>}
+ * This function may return a promise which is awaited before the rest of
+ * the configuration is removed. Any non-promise return value and any
+ * resolved promise value is ignored. If the returned promise is rejected,
+ * the interface removal will be aborted.
+ */
+ deleteConfiguration: function() {}
});
/**
* @class
- * @memberof LuCI.Network
+ * @memberof LuCI.network
* @hideconstructor
* @classdesc
*
* A `Network.Device` class instance represents an underlying Linux network
* device and allows querying device details such as packet statistics or MTU.
*/
-Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
+Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
__init__: function(ifname, network) {
var wif = getWifiSidByIfname(ifname);
}
this.ifname = this.ifname || ifname;
- this.dev = _state.netdevs[this.ifname];
+ this.dev = Object.assign({}, _state.netdevs[this.ifname]);
this.network = network;
},
},
/**
- * Get the type of the device..
+ * Get the type of the device.
*
* @returns {string}
* Returns a string describing the type of the network device:
getType: function() {
if (this.ifname != null && this.ifname.charAt(0) == '@')
return 'alias';
- else if (this.wif != null || isWifiIfname(this.ifname))
+ else if (this.dev.devtype == 'wlan' || this.wif != null || isWifiIfname(this.ifname))
return 'wifi';
- else if (_state.isBridge[this.ifname])
+ else if (this.dev.devtype == 'bridge' || _state.isBridge[this.ifname])
return 'bridge';
else if (_state.isTunnel[this.ifname])
return 'tunnel';
- else if (this.ifname.indexOf('.') > -1)
+ else if (this.dev.devtype == 'vlan' || this.ifname.indexOf('.') > -1)
return 'vlan';
- else if (_state.isSwitch[this.ifname])
+ else if (this.dev.devtype == 'dsa' || _state.isSwitch[this.ifname])
return 'switch';
else
return 'ethernet';
return _('Bridge');
case 'switch':
- return _('Ethernet Switch');
+ return (_state.netdevs[this.ifname] && _state.netdevs[this.ifname].devtype == 'dsa')
+ ? _('Switch port') : _('Ethernet Switch');
case 'vlan':
return (_state.isSwitch[this.ifname] ? _('Switch VLAN') : _('Software VLAN'));
/**
* Get the associated bridge ports of the device.
*
- * @returns {null|Array<LuCI.Network.Device>}
+ * @returns {null|Array<LuCI.network.Device>}
* Returns an array of `Network.Device` instances representing the ports
* (slave interfaces) of the bridge or `null` when this device isn't
* a Linux bridge.
return null;
for (var i = 0; i < br.ifnames.length; i++)
- rv.push(L.network.instantiateDevice(br.ifnames[i].name));
+ rv.push(Network.prototype.instantiateDevice(br.ifnames[i].name));
rv.sort(deviceSort);
/**
* Get the primary logical interface this device is assigned to.
*
- * @returns {null|LuCI.Network.Protocol}
+ * @returns {null|LuCI.network.Protocol}
* Returns a `Network.Protocol` instance representing the logical
* interface this device is attached to or `null` if it is not
* assigned to any logical interface.
/**
* Get the logical interfaces this device is assigned to.
*
- * @returns {Array<LuCI.Network.Protocol>}
+ * @returns {Array<LuCI.network.Protocol>}
* Returns an array of `Network.Protocol` instances representing the
* logical interfaces this device is assigned to.
*/
/**
* Get the related wireless network this device is related to.
*
- * @returns {null|LuCI.Network.WifiNetwork}
+ * @returns {null|LuCI.network.WifiNetwork}
* Returns a `Network.WifiNetwork` instance representing the wireless
* network corresponding to this network device or `null` if this device
* is not a wireless device.
*/
getWifiNetwork: function() {
return (this.wif != null ? this.wif : null);
+ },
+
+ /**
+ * Get the logical parent device of this device.
+ *
+ * In case of DSA switch ports, the parent device will be the DSA switch
+ * device itself, for VLAN devices, the parent refers to the base device
+ * etc.
+ *
+ * @returns {null|LuCI.network.Device}
+ * Returns a `Network.Device` instance representing the parent device or
+ * `null` when this device has no parent, as it is the case for e.g.
+ * ordinary ethernet interfaces.
+ */
+ getParent: function() {
+ return this.dev.parent ? Network.prototype.instantiateDevice(this.dev.parent) : null;
}
});
/**
* @class
- * @memberof LuCI.Network
+ * @memberof LuCI.network
* @hideconstructor
* @classdesc
*
* present on the system and provides wireless capability information as
* well as methods for enumerating related wireless networks.
*/
-WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
+WifiDevice = baseclass.extend(/** @lends LuCI.network.WifiDevice.prototype */ {
__init__: function(name, radiostate) {
var uciWifiDevice = uci.get('wireless', name);
* - `g` - Legacy 802.11g mode, 2.4 GHz, up to 54 Mbit/s
* - `n` - IEEE 802.11n mode, 2.4 or 5 GHz, up to 600 Mbit/s
* - `ac` - IEEE 802.11ac mode, 5 GHz, up to 6770 Mbit/s
+ * - `ax` - IEEE 802.11ax mode, 2.4 or 5 GHz
*/
getHWModes: function() {
var hwmodes = this.ubus('dev', 'iwinfo', 'hwmodes');
* - `VHT40` - applicable to IEEE 802.11ac, 40 MHz wide channels
* - `VHT80` - applicable to IEEE 802.11ac, 80 MHz wide channels
* - `VHT160` - applicable to IEEE 802.11ac, 160 MHz wide channels
+ * - `HE20` - applicable to IEEE 802.11ax, 20 MHz wide channels
+ * - `HE40` - applicable to IEEE 802.11ax, 40 MHz wide channels
+ * - `HE80` - applicable to IEEE 802.11ax, 80 MHz wide channels
+ * - `HE160` - applicable to IEEE 802.11ax, 160 MHz wide channels
*/
getHTModes: function() {
var htmodes = this.ubus('dev', 'iwinfo', 'htmodes');
* A wireless scan result object describes a neighbouring wireless
* network found in the vincinity.
*
- * @typedef {Object<string, number|string|LuCI.Network.WifiEncryption>} WifiScanResult
- * @memberof LuCI.Network
+ * @typedef {Object<string, number|string|LuCI.network.WifiEncryption>} WifiScanResult
+ * @memberof LuCI.network
*
* @property {string} ssid
* The SSID / Mesh ID of the network.
* The maximum possible quality level of the signal, can be used in
* conjunction with `quality` to calculate a quality percentage.
*
- * @property {LuCI.Network.WifiEncryption} encryption
+ * @property {LuCI.network.WifiEncryption} encryption
* The encryption used by the wireless network.
*/
* Trigger a wireless scan on this radio device and obtain a list of
* nearby networks.
*
- * @returns {Promise<Array<LuCI.Network.WifiScanResult>>}
+ * @returns {Promise<Array<LuCI.network.WifiScanResult>>}
* Returns a promise resolving to an array of scan result objects
* describing the networks found in the vincinity.
*/
* or a Linux network device name like `wlan0` which is resolved to the
* corresponding configuration section through `ubus` runtime information.
*
- * @returns {Promise<LuCI.Network.WifiNetwork>}
+ * @returns {Promise<LuCI.network.WifiNetwork>}
* Returns a promise resolving to a `Network.WifiNetwork` instance
* representing the wireless network and rejecting with `null` if
* the given network could not be found or is not associated with
* this radio device.
*/
getWifiNetwork: function(network) {
- return L.network.getWifiNetwork(network).then(L.bind(function(networkInstance) {
+ return Network.prototype.getWifiNetwork(network).then(L.bind(function(networkInstance) {
var uciWifiIface = (networkInstance.sid ? uci.get('wireless', networkInstance.sid) : null);
if (uciWifiIface == null || uciWifiIface['.type'] != 'wifi-iface' || uciWifiIface.device != this.sid)
/**
* Get all wireless networks associated with this wireless radio device.
*
- * @returns {Promise<Array<LuCI.Network.WifiNetwork>>}
+ * @returns {Promise<Array<LuCI.network.WifiNetwork>>}
* Returns a promise resolving to an array of `Network.WifiNetwork`
* instances respresenting the wireless networks associated with this
* radio device.
*/
getWifiNetworks: function() {
- return L.network.getWifiNetworks().then(L.bind(function(networks) {
+ return Network.prototype.getWifiNetworks().then(L.bind(function(networks) {
var rv = [];
for (var i = 0; i < networks.length; i++)
* @param {Object<string, string|string[]>} [options]
* The options to set for the newly added wireless network.
*
- * @returns {Promise<null|LuCI.Network.WifiNetwork>}
+ * @returns {Promise<null|LuCI.network.WifiNetwork>}
* Returns a promise resolving to a `WifiNetwork` instance describing
* the newly added wireless network or `null` if the given options
* were invalid.
options.device = this.sid;
- return L.network.addWifiNetwork(options);
+ return Network.prototype.addWifiNetwork(options);
},
/**
/**
* @class
- * @memberof LuCI.Network
+ * @memberof LuCI.network
* @hideconstructor
* @classdesc
*
* the runtime state of the network. Most radio devices support multiple
* such networks in parallel.
*/
-WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
+WifiNetwork = baseclass.extend(/** @lends LuCI.network.WifiNetwork.prototype */ {
__init__: function(sid, radioname, radiostate, netid, netstate, hostapd) {
this.sid = sid;
this.netid = netid;
return ifname;
},
+ /**
+ * Get the Linux VLAN network device names.
+ *
+ * @returns {string[]}
+ * Returns the current Linux VLAN network device name as resolved
+ * from `ubus` runtime information or empty array if this network
+ * has no associated VLAN network devices.
+ */
+ getVlanIfnames: function() {
+ var vlans = L.toArray(this.ubus('net', 'vlans')),
+ ifnames = [];
+
+ for (var i = 0; i < vlans.length; i++)
+ ifnames.push(vlans[i]['ifname']);
+
+ return ifnames;
+ },
+
/**
* Get the name of the corresponding wifi radio device.
*
/**
* Get the corresponding wifi radio device.
*
- * @returns {null|LuCI.Network.WifiDevice}
+ * @returns {null|LuCI.network.WifiDevice}
* Returns a `Network.WifiDevice` instance representing the corresponding
* wifi radio device or `null` if the related radio device could not be
* found.
if (radioname == null)
return Promise.reject();
- return L.network.getWifiDevice(radioname);
+ return Network.prototype.getWifiDevice(radioname);
},
/**
* A wireless peer entry describes the properties of a remote wireless
* peer associated with a local network.
*
- * @typedef {Object<string, boolean|number|string|LuCI.Network.WifiRateEntry>} WifiPeerEntry
- * @memberof LuCI.Network
+ * @typedef {Object<string, boolean|number|string|LuCI.network.WifiRateEntry>} WifiPeerEntry
+ * @memberof LuCI.network
*
* @property {string} mac
* The MAC address (BSSID).
* - `DEEP SLEEP`
* - `UNKNOWN`
*
- * @property {LuCI.Network.WifiRateEntry} rx
+ * @property {LuCI.network.WifiRateEntry} rx
* Describes the receiving wireless rate from the peer.
*
- * @property {LuCI.Network.WifiRateEntry} tx
+ * @property {LuCI.network.WifiRateEntry} tx
* Describes the transmitting wireless rate to the peer.
*/
* transmission rate to or from a peer.
*
* @typedef {Object<string, boolean|number>} WifiRateEntry
- * @memberof LuCI.Network
+ * @memberof LuCI.network
*
* @property {number} [drop_misc]
* The amount of received misc. packages that have been dropped, e.g.
* @property {number} [nss]
* Specifies the number of spatial streams used by the transmission.
* Only applicable to VHT rates.
+ *
+ * @property {boolean} [he]
+ * Specifies whether this rate is an HE (IEEE 802.11ax) rate.
+ *
+ * @property {number} [he_gi]
+ * Specifies whether the guard interval used for the transmission.
+ * Only applicable to HE rates.
+ *
+ * @property {number} [he_dcm]
+ * Specifies whether dual concurrent modulation is used for the transmission.
+ * Only applicable to HE rates.
*/
/**
* Fetch the list of associated peers.
*
- * @returns {Promise<Array<LuCI.Network.WifiPeerEntry>>}
+ * @returns {Promise<Array<LuCI.network.WifiPeerEntry>>}
* Returns a promise resolving to an array of wireless peers associated
* with this network.
*/
getAssocList: function() {
- return callIwinfoAssoclist(this.getIfname());
+ var tasks = [];
+ var ifnames = [ this.getIfname() ].concat(this.getVlanIfnames());
+
+ for (var i = 0; i < ifnames.length; i++)
+ tasks.push(callIwinfoAssoclist(ifnames[i]));
+
+ return Promise.all(tasks).then(function(values) {
+ return Array.prototype.concat.apply([], values);
+ });
},
/**
/**
* Get the primary logical interface this wireless network is attached to.
*
- * @returns {null|LuCI.Network.Protocol}
+ * @returns {null|LuCI.network.Protocol}
* Returns a `Network.Protocol` instance representing the logical
* interface or `null` if this network is not attached to any logical
* interface.
/**
* Get the logical interfaces this wireless network is attached to.
*
- * @returns {Array<LuCI.Network.Protocol>}
+ * @returns {Array<LuCI.network.Protocol>}
* Returns an array of `Network.Protocol` instances representing the
* logical interfaces this wireless network is attached to.
*/
if (uciInterface == null || uciInterface['.type'] != 'interface')
continue;
- networks.push(L.network.instantiateNetwork(networkNames[i]));
+ networks.push(Network.prototype.instantiateNetwork(networkNames[i]));
}
networks.sort(networkSort);
/**
* Get the associated Linux network device.
*
- * @returns {LuCI.Network.Device}
+ * @returns {LuCI.network.Device}
* Returns a `Network.Device` instance representing the Linux network
* device associted with this wireless network.
*/
getDevice: function() {
- return L.network.instantiateDevice(this.getIfname());
+ return Network.prototype.instantiateDevice(this.getIfname());
},
/**