luci-base: network.js: introduce further API functions
- Add Network.getWifiNetworks() to simplify fetching *all* networks instead of building a list by iterating each radio first - Add Network.WifiNetwork.isClientDisconnectSupported() to check whether a wireless network supports client disconnections - Add Network.WifiNetwork.disconnectClient() to trigger client disconnections via hostapd Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
861ec9e821
commit
a2dc6c8216
1 changed files with 154 additions and 54 deletions
|
@ -363,7 +363,7 @@ function initNetworkState(refresh) {
|
||||||
var s = {
|
var s = {
|
||||||
isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {},
|
isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {},
|
||||||
ifaces: netifd_ifaces, radios: data[3], hosts: data[4],
|
ifaces: netifd_ifaces, radios: data[3], hosts: data[4],
|
||||||
netdevs: {}, bridges: {}, switches: {}
|
netdevs: {}, bridges: {}, switches: {}, hostapd: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var name in luci_devs) {
|
for (var name in luci_devs) {
|
||||||
|
@ -495,8 +495,25 @@ function initNetworkState(refresh) {
|
||||||
|
|
||||||
_init = null;
|
_init = null;
|
||||||
|
|
||||||
|
var objects = [];
|
||||||
|
|
||||||
|
if (L.isObject(s.radios))
|
||||||
|
for (var radio in s.radios)
|
||||||
|
if (L.isObject(s.radios[radio]) && Array.isArray(s.radios[radio].interfaces))
|
||||||
|
for (var i = 0; i < s.radios[radio].interfaces.length; i++)
|
||||||
|
if (L.isObject(s.radios[radio].interfaces[i]) && s.radios[radio].interfaces[i].ifname)
|
||||||
|
objects.push('hostapd.%s'.format(s.radios[radio].interfaces[i].ifname));
|
||||||
|
|
||||||
|
return (objects.length ? L.resolveDefault(rpc.list.apply(rpc, objects), {}) : Promise.resolve({})).then(function(res) {
|
||||||
|
for (var k in res) {
|
||||||
|
var m = k.match(/^hostapd\.(.+)$/);
|
||||||
|
if (m)
|
||||||
|
s.hostapd[m[1]] = res[k];
|
||||||
|
}
|
||||||
|
|
||||||
return (_state = s);
|
return (_state = s);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (_state != null ? Promise.resolve(_state) : _init);
|
return (_state != null ? Promise.resolve(_state) : _init);
|
||||||
|
@ -1287,51 +1304,32 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
|
||||||
* be found.
|
* be found.
|
||||||
*/
|
*/
|
||||||
getWifiNetwork: function(netname) {
|
getWifiNetwork: function(netname) {
|
||||||
var sid, res, netid, radioname, radiostate, netstate;
|
return initNetworkState()
|
||||||
|
.then(L.bind(this.lookupWifiNetwork, this, netname));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 a promise resolving to an array of `WifiNetwork` instances
|
||||||
|
* describing the wireless networks. The array will be empty if no networks
|
||||||
|
* are found.
|
||||||
|
*/
|
||||||
|
getWifiNetworks: function() {
|
||||||
return initNetworkState().then(L.bind(function() {
|
return initNetworkState().then(L.bind(function() {
|
||||||
sid = getWifiSidByNetid(netname);
|
var wifiIfaces = uci.sections('wireless', 'wifi-iface'),
|
||||||
|
rv = [];
|
||||||
|
|
||||||
if (sid != null) {
|
for (var i = 0; i < wifiIfaces.length; i++)
|
||||||
res = getWifiStateBySid(sid);
|
rv.push(this.lookupWifiNetwork(wifiIfaces[i]['.name']));
|
||||||
netid = netname;
|
|
||||||
radioname = res ? res[0] : null;
|
|
||||||
radiostate = res ? res[1] : null;
|
|
||||||
netstate = res ? res[2] : null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res = getWifiStateByIfname(netname);
|
|
||||||
|
|
||||||
if (res != null) {
|
rv.sort(function(a, b) {
|
||||||
radioname = res[0];
|
return (a.getID() > b.getID());
|
||||||
radiostate = res[1];
|
});
|
||||||
netstate = res[2];
|
|
||||||
sid = netstate.section;
|
|
||||||
netid = L.toArray(getWifiNetidBySid(sid))[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res = getWifiStateBySid(netname);
|
|
||||||
|
|
||||||
if (res != null) {
|
return rv;
|
||||||
radioname = res[0];
|
|
||||||
radiostate = res[1];
|
|
||||||
netstate = res[2];
|
|
||||||
sid = netname;
|
|
||||||
netid = L.toArray(getWifiNetidBySid(sid))[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res = getWifiNetidBySid(netname);
|
|
||||||
|
|
||||||
if (res != null) {
|
|
||||||
netid = res[0];
|
|
||||||
radioname = res[1];
|
|
||||||
sid = netname;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.instantiateWifiNetwork(sid || netname, radioname, radiostate, netid, netstate);
|
|
||||||
}, this));
|
}, this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1574,8 +1572,58 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
|
||||||
},
|
},
|
||||||
|
|
||||||
/* private */
|
/* private */
|
||||||
instantiateWifiNetwork: function(sid, radioname, radiostate, netid, netstate) {
|
instantiateWifiNetwork: function(sid, radioname, radiostate, netid, netstate, hostapd) {
|
||||||
return new WifiNetwork(sid, radioname, radiostate, netid, netstate);
|
return new WifiNetwork(sid, radioname, radiostate, netid, netstate, hostapd);
|
||||||
|
},
|
||||||
|
|
||||||
|
/* private */
|
||||||
|
lookupWifiNetwork: function(netname) {
|
||||||
|
var sid, res, netid, radioname, radiostate, netstate;
|
||||||
|
|
||||||
|
sid = getWifiSidByNetid(netname);
|
||||||
|
|
||||||
|
if (sid != null) {
|
||||||
|
res = getWifiStateBySid(sid);
|
||||||
|
netid = netname;
|
||||||
|
radioname = res ? res[0] : null;
|
||||||
|
radiostate = res ? res[1] : null;
|
||||||
|
netstate = res ? res[2] : null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = getWifiStateByIfname(netname);
|
||||||
|
|
||||||
|
if (res != null) {
|
||||||
|
radioname = res[0];
|
||||||
|
radiostate = res[1];
|
||||||
|
netstate = res[2];
|
||||||
|
sid = netstate.section;
|
||||||
|
netid = L.toArray(getWifiNetidBySid(sid))[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = getWifiStateBySid(netname);
|
||||||
|
|
||||||
|
if (res != null) {
|
||||||
|
radioname = res[0];
|
||||||
|
radiostate = res[1];
|
||||||
|
netstate = res[2];
|
||||||
|
sid = netname;
|
||||||
|
netid = L.toArray(getWifiNetidBySid(sid))[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = getWifiNetidBySid(netname);
|
||||||
|
|
||||||
|
if (res != null) {
|
||||||
|
netid = res[0];
|
||||||
|
radioname = res[1];
|
||||||
|
sid = netname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.instantiateWifiNetwork(sid || netname, radioname,
|
||||||
|
radiostate, netid, netstate,
|
||||||
|
netstate ? _state.hostapd[netstate.ifname] : null);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3205,14 +3253,15 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
|
||||||
* radio device.
|
* radio device.
|
||||||
*/
|
*/
|
||||||
getWifiNetworks: function() {
|
getWifiNetworks: function() {
|
||||||
var uciWifiIfaces = uci.sections('wireless', 'wifi-iface'),
|
return L.network.getWifiNetworks().then(L.bind(function(networks) {
|
||||||
tasks = [];
|
var rv = [];
|
||||||
|
|
||||||
for (var i = 0; i < uciWifiIfaces.length; i++)
|
for (var i = 0; i < networks.length; i++)
|
||||||
if (uciWifiIfaces[i].device == this.sid)
|
if (networks[i].getWifiDeviceName() == this.getName())
|
||||||
tasks.push(L.network.getWifiNetwork(uciWifiIfaces[i]['.name']));
|
rv.push(networks[i]);
|
||||||
|
|
||||||
return Promise.all(tasks);
|
return rv;
|
||||||
|
}, this));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3286,10 +3335,11 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
|
||||||
* such networks in parallel.
|
* such networks in parallel.
|
||||||
*/
|
*/
|
||||||
WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
|
WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
|
||||||
__init__: function(sid, radioname, radiostate, netid, netstate) {
|
__init__: function(sid, radioname, radiostate, netid, netstate, hostapd) {
|
||||||
this.sid = sid;
|
this.sid = sid;
|
||||||
this.netid = netid;
|
this.netid = netid;
|
||||||
this._ubusdata = {
|
this._ubusdata = {
|
||||||
|
hostapd: hostapd,
|
||||||
radio: radioname,
|
radio: radioname,
|
||||||
dev: radiostate,
|
dev: radiostate,
|
||||||
net: netstate
|
net: netstate
|
||||||
|
@ -3989,6 +4039,56 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
|
||||||
*/
|
*/
|
||||||
getDevice: function() {
|
getDevice: function() {
|
||||||
return L.network.instantiateDevice(this.getIfname());
|
return L.network.instantiateDevice(this.getIfname());
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this wifi network supports deauthenticating clients.
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
* Returns `true` when this wifi network instance supports forcibly
|
||||||
|
* deauthenticating clients, otherwise `false`.
|
||||||
|
*/
|
||||||
|
isClientDisconnectSupported: function() {
|
||||||
|
return L.isObject(this.ubus('hostapd', 'del_client'));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcibly disconnect the given client from the wireless network.
|
||||||
|
*
|
||||||
|
* @param {string} mac
|
||||||
|
* The MAC address of the client to disconnect.
|
||||||
|
*
|
||||||
|
* @param {boolean} [deauth=false]
|
||||||
|
* Specifies whether to deauthenticate (`true`) or disassociate (`false`)
|
||||||
|
* the client.
|
||||||
|
*
|
||||||
|
* @param {number} [reason=1]
|
||||||
|
* Specifies the IEEE 802.11 reason code to disassoc/deauth the client
|
||||||
|
* with. Default is `1` which corresponds to `Unspecified reason`.
|
||||||
|
*
|
||||||
|
* @param {number} [ban_time=0]
|
||||||
|
* Specifies the amount of milliseconds to ban the client from
|
||||||
|
* reconnecting. By default, no ban time is set which allows the client
|
||||||
|
* to reassociate / reauthenticate immediately.
|
||||||
|
*
|
||||||
|
* @returns {Promise<number>}
|
||||||
|
* Returns a promise resolving to the underlying ubus call result code
|
||||||
|
* which is typically `0`, even for not existing MAC addresses.
|
||||||
|
* The promise might reject with an error in case invalid arguments
|
||||||
|
* are passed.
|
||||||
|
*/
|
||||||
|
disconnectClient: function(mac, deauth, reason, ban_time) {
|
||||||
|
if (reason == null || reason == 0)
|
||||||
|
reason = 1;
|
||||||
|
|
||||||
|
if (ban_time == 0)
|
||||||
|
ban_time = null;
|
||||||
|
|
||||||
|
return rpc.declare({
|
||||||
|
object: 'hostapd.%s'.format(this.getIfname()),
|
||||||
|
method: 'del_client',
|
||||||
|
params: [ 'addr', 'deauth', 'reason', 'ban_time' ]
|
||||||
|
})(mac, deauth, reason, ban_time);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue