From 091bbb9c22fa0df4658afa02742aebc32f223b1b Mon Sep 17 00:00:00 2001 From: Tobias Waldvogel Date: Sun, 5 Jan 2025 18:14:06 +0100 Subject: [PATCH] luci: show wifi vlan in the associated wireless stations list This patch adds a badge to the associated wireless stations with the vlan id and name. It is displayed in the same color as the network, to which it is bridged, so the color corresponds to the color in the network configuration page. Signed-off-by: Tobias Waldvogel --- .../htdocs/luci-static/resources/network.js | 107 +++++++++++++++++- .../resources/view/network/wireless.js | 32 +++++- .../resources/view/status/include/60_wifi.js | 23 +++- 3 files changed, 153 insertions(+), 9 deletions(-) diff --git a/modules/luci-base/htdocs/luci-static/resources/network.js b/modules/luci-base/htdocs/luci-static/resources/network.js index d71aa69fa361..79191603f1ac 100644 --- a/modules/luci-base/htdocs/luci-static/resources/network.js +++ b/modules/luci-base/htdocs/luci-static/resources/network.js @@ -639,7 +639,7 @@ function enumerateNetworks() { } -var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork; +var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork, WifiVlan; /** * @class network @@ -4150,16 +4150,42 @@ WifiNetwork = baseclass.extend(/** @lends LuCI.network.WifiNetwork.prototype */ */ getAssocList: function() { var tasks = []; - var ifnames = [ this.getIfname() ].concat(this.getVlanIfnames()); + var station; - for (var i = 0; i < ifnames.length; i++) - tasks.push(callIwinfoAssoclist(ifnames[i])); + for (let vlan of this.getVlans()) + tasks.push(callIwinfoAssoclist(vlan.getIfname()).then( + function(stations) { + for (station of stations) + station.vlan = vlan; + + return stations; + }) + ); + + tasks.push(callIwinfoAssoclist(this.getIfname())); return Promise.all(tasks).then(function(values) { return Array.prototype.concat.apply([], values); }); }, + /** + * Fetch the vlans for this network. + * + * @returns {Array} + * Returns an array of vlans for this network. + */ + getVlans: function() { + var vlans = []; + var vlans_ubus = this.ubus('net', 'vlans'); + + if (vlans_ubus) + for (let vlan of vlans_ubus) + vlans.push(new WifiVlan(vlan)); + + return vlans; + }, + /** * Query the current operating frequency of the wireless network. * @@ -4436,4 +4462,77 @@ WifiNetwork = baseclass.extend(/** @lends LuCI.network.WifiNetwork.prototype */ } }); +/** + * @class + * @memberof LuCI.network + * @hideconstructor + * @classdesc + * + * A `Network.WifiVlan` class instance represents a vlan on a WifiNetwork. + */ +WifiVlan = baseclass.extend(/** @lends LuCI.network.WifiVlan.prototype */ { + __init__: function(vlan) { + this.ifname = vlan.ifname; + if (L.isObject(vlan.config)) { + this.vid = vlan.config.vid; + this.name = vlan.config.name; + + if (Array.isArray(vlan.config.network) && vlan.config.network.length) + this.network = vlan.config.network[0]; + } + }, + + /** + * Get the name of the wifi vlan. + * + * @returns {string} + * Returns the name. + */ + getName: function() { + return this.name; + }, + + /** + * Get the vlan id of the wifi vlan. + * + * @returns {number} + * Returns the vlan id. + */ + getVlanId: function() { + return this.vid; + }, + + /** + * Get the network of the wifi vlan. + * + * @returns {string} + * Returns the network. + */ + getNetwork: function() { + return this.network; + }, + + /** + * Get the Linux network device name of the wifi vlan. + * + * @returns {string} + * Returns the current network device name for this wifi vlan. + */ + getIfname: function() { + return this.ifname; + }, + + /** + * Get a long description string for the wifi vlan. + * + * @returns {string} + * Returns a string containing the vlan id and the vlan name, + * if it is different than the vlan id + */ + getI18n: function() { + var name = this.name && this.name != this.vid ? ' (' + this.name + ')' : ''; + return 'vlan %d%s'.format(this.vid, name); + }, +}); + return Network; diff --git a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js index 4b12042b721c..64dad5cfbcbb 100644 --- a/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js +++ b/modules/luci-mod-network/htdocs/luci-static/resources/view/network/wireless.js @@ -765,6 +765,25 @@ return view.extend({ ]) ]; + var zones = data[4]; + if (bss.vlan) { + var desc = bss.vlan.getI18n(); + var vlan_network = bss.vlan.getNetwork(); + var vlan_zone; + + if (vlan_network && zones) + for (let zone of zones) + if (zone.getNetworks().includes(vlan_network)) + vlan_zone = zone; + + row[0].insertBefore( + E('div', { + 'class' : 'zonebadge', + 'title' : desc, + 'style' : firewall.getZoneColorStyle(vlan_zone) + }, [ desc ]), row[0].firstChild); + } + if (bss.network.isClientDisconnectSupported()) { if (table.firstElementChild.childNodes.length < 6) table.firstElementChild.appendChild(E('th', { 'class': 'th cbi-section-actions'})); @@ -803,7 +822,8 @@ return view.extend({ return Promise.all([ uci.changes(), uci.load('wireless'), - uci.load('system') + uci.load('system'), + firewall.getZones(), ]); }, @@ -823,11 +843,11 @@ return view.extend({ params: [ 'config', 'section', 'name' ] }), - render: function() { + render: function(data) { if (this.checkAnonymousSections()) return this.renderMigration(); else - return this.renderOverview(); + return this.renderOverview(data[3]); }, handleMigration: function(ev) { @@ -862,7 +882,7 @@ return view.extend({ ]); }, - renderOverview: function() { + renderOverview: function(zones) { var m, s, o; m = new form.Map('wireless'); @@ -2402,6 +2422,10 @@ return view.extend({ return hosts_radios_wifis; }); }, network)) + .then(L.bind(function(zones, data) { + data.push(zones); + return data; + }, network, zones)) .then(L.bind(this.poll_status, this, nodes)); }, this), 5); diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js index 805423053076..c77e88fb76f3 100644 --- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js +++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/60_wifi.js @@ -5,6 +5,7 @@ 'require uci'; 'require fs'; 'require rpc'; +'require firewall'; return baseclass.extend({ title: _('Wireless'), @@ -184,6 +185,7 @@ return baseclass.extend({ network.getHostHints(), this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'read'), this.callSessionAccess('access-group', 'luci-mod-status-index-wifi', 'write'), + firewall.getZones(), L.hasSystemFeature('wifi') ? L.resolveDefault(uci.load('wireless')) : L.resolveDefault(), ]).then(L.bind(function(data) { var tasks = [], @@ -216,7 +218,8 @@ return baseclass.extend({ networks = data[1], hosthints = data[2], hasReadPermission = data[3], - hasWritePermission = data[4]; + hasWritePermission = data[4], + zones = data[5]; var table = E('div', { 'class': 'network-status-table' }); @@ -326,6 +329,24 @@ return baseclass.extend({ ]) ]; + if (bss.vlan) { + var desc = bss.vlan.getI18n(); + var vlan_network = bss.vlan.getNetwork(); + var vlan_zone; + + if (vlan_network) + for (let zone of zones) + if (zone.getNetworks().includes(vlan_network)) + vlan_zone = zone; + + row[0].insertBefore( + E('div', { + 'class' : 'zonebadge', + 'title' : desc, + 'style' : firewall.getZoneColorStyle(vlan_zone) + }, [ desc ]), row[0].firstChild); + } + if (networks[i].isClientDisconnectSupported() && hasWritePermission) { if (assoclist.firstElementChild.childNodes.length < 6) assoclist.firstElementChild.appendChild(E('th', { 'class': 'th cbi-section-actions' }));