summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-mod-status
diff options
context:
space:
mode:
Diffstat (limited to 'modules/luci-mod-status')
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/view/status/channel_analysis.js129
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/29_ports.js386
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js4
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js8
-rw-r--r--modules/luci-mod-status/htdocs/luci-static/resources/view/status/nftables.js2
-rw-r--r--modules/luci-mod-status/root/usr/share/rpcd/acl.d/luci-mod-status-index.json10
6 files changed, 477 insertions, 62 deletions
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/channel_analysis.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/channel_analysis.js
index 692c67e3cd..2ce22d5838 100644
--- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/channel_analysis.js
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/channel_analysis.js
@@ -97,21 +97,20 @@ return view.extend({
})
},
- create_channel_graph: function(chan_analysis, freq_tbl, freq) {
- var is5GHz = freq == '5GHz',
- columns = is5GHz ? freq_tbl.length * 4 : freq_tbl.length + 3,
+ create_channel_graph: function(chan_analysis, freq_tbl, band) {
+ var columns = (band != 2) ? freq_tbl.length * 4 : freq_tbl.length + 3,
chan_graph = chan_analysis.graph,
G = chan_graph.firstElementChild,
step = (chan_graph.offsetWidth - 2) / columns,
curr_offset = step;
- function createGraphHLine(graph, pos) {
+ function createGraphHLine(graph, pos, width, dash) {
var elem = document.createElementNS('http://www.w3.org/2000/svg', 'line');
elem.setAttribute('x1', pos);
elem.setAttribute('y1', 0);
elem.setAttribute('x2', pos);
elem.setAttribute('y2', '100%');
- elem.setAttribute('style', 'stroke:black;stroke-width:0.1');
+ elem.setAttribute('style', 'stroke:black;stroke-width:'+width+';stroke-dasharray:'+dash);
graph.appendChild(elem);
}
@@ -126,40 +125,51 @@ return view.extend({
chan_analysis.col_width = step;
- createGraphHLine(G,curr_offset);
+ createGraphHLine(G,curr_offset, 0.1, 1);
for (var i=0; i< freq_tbl.length;i++) {
var channel = freq_tbl[i]
chan_analysis.offset_tbl[channel] = curr_offset+step;
- createGraphHLine(G,curr_offset+step);
- createGraphText(G,curr_offset+step, channel);
+ if (band != 2) {
+ createGraphHLine(G,curr_offset+step, 0.1, 3);
+ if (channel < 100)
+ createGraphText(G,curr_offset-(step/2), channel);
+ else
+ createGraphText(G,curr_offset-step, channel);
+ } else {
+ createGraphHLine(G,curr_offset+step, 0.1, 0);
+ createGraphText(G,curr_offset+step, channel);
+ }
curr_offset += step;
- if (is5GHz && freq_tbl[i+1]) {
+ if ((band != 2) && freq_tbl[i+1]) {
var next_channel = freq_tbl[i+1];
- /* Check if we are transitioning to another 5Ghz band range */
+ /* Check if we are transitioning to another 5/6Ghz band range */
if ((next_channel - channel) == 4) {
for (var j=1; j < 4; j++) {
chan_analysis.offset_tbl[channel+j] = curr_offset+step;
- createGraphHLine(G,curr_offset+step);
+ if (j == 2)
+ createGraphHLine(G,curr_offset+step, 0.1, 0);
+ else
+ createGraphHLine(G,curr_offset+step, 0.1, 1);
curr_offset += step;
}
} else {
chan_analysis.offset_tbl[channel+1] = curr_offset+step;
- createGraphHLine(G,curr_offset+step);
+ createGraphHLine(G,curr_offset+step, 0.1, 1);
curr_offset += step;
chan_analysis.offset_tbl[next_channel-2] = curr_offset+step;
- createGraphHLine(G,curr_offset+step);
+ createGraphHLine(G,curr_offset+step, 0.5, 0);
curr_offset += step;
chan_analysis.offset_tbl[next_channel-1] = curr_offset+step;
- createGraphHLine(G,curr_offset+step);
+ createGraphHLine(G,curr_offset+step, 0.1, 1);
curr_offset += step;
}
}
}
- createGraphHLine(G,curr_offset+step);
+ createGraphHLine(G,curr_offset+step, 0.1, 1);
chan_analysis.tab.addEventListener('cbi-tab-active', L.bind(function(ev) {
this.active_tab = ev.detail.tab;
@@ -182,7 +192,8 @@ return view.extend({
local_wifi = data[1],
table = radio.table,
chan_analysis = radio.graph,
- scanCache = radio.scanCache;
+ scanCache = radio.scanCache,
+ band = radio.band;
var rows = [];
@@ -191,42 +202,45 @@ return view.extend({
scanCache[results[i].bssid] = {};
scanCache[results[i].bssid].data = results[i];
+ scanCache[results[i].bssid].data.stale = false;
}
- if (scanCache[local_wifi.bssid] == null)
- scanCache[local_wifi.bssid] = {};
-
- scanCache[local_wifi.bssid].data = local_wifi;
+ if (band + 'g' == radio.dev.get('band')) {
+ if (scanCache[local_wifi.bssid] == null)
+ scanCache[local_wifi.bssid] = {};
- if (chan_analysis.offset_tbl[local_wifi.channel] != null && local_wifi.center_chan1) {
- var center_channels = [local_wifi.center_chan1],
- chan_width_text = local_wifi.htmode.replace(/(V)*H[TE]/,''), /* Handle HT VHT HE */
- chan_width = parseInt(chan_width_text)/10;
+ scanCache[local_wifi.bssid].data = local_wifi;
- if (local_wifi.center_chan2) {
- center_channels.push(local_wifi.center_chan2);
- chan_width = 8;
- }
+ if (chan_analysis.offset_tbl[local_wifi.channel] != null && local_wifi.center_chan1) {
+ var center_channels = [local_wifi.center_chan1],
+ chan_width_text = local_wifi.htmode.replace(/(V)*H[TE]/,''), /* Handle HT VHT HE */
+ chan_width = parseInt(chan_width_text)/10;
- local_wifi.signal = -10;
- local_wifi.ssid = 'Local Interface';
+ if (local_wifi.center_chan2) {
+ center_channels.push(local_wifi.center_chan2);
+ chan_width = 8;
+ }
- this.add_wifi_to_graph(chan_analysis, local_wifi, scanCache, center_channels, chan_width);
- rows.push([
- this.render_signal_badge(q, local_wifi.signal),
- [
- E('span', { 'style': 'color:'+scanCache[local_wifi.bssid].color }, '⬤ '),
- local_wifi.ssid
- ],
- '%d'.format(local_wifi.channel),
- '%h MHz'.format(chan_width_text),
- '%h'.format(local_wifi.mode),
- '%h'.format(local_wifi.bssid)
- ]);
+ local_wifi.signal = -10;
+ local_wifi.ssid = 'Local Interface';
+
+ this.add_wifi_to_graph(chan_analysis, local_wifi, scanCache, center_channels, chan_width);
+ rows.push([
+ this.render_signal_badge(q, local_wifi.signal),
+ [
+ E('span', { 'style': 'color:'+scanCache[local_wifi.bssid].color }, '⬤ '),
+ local_wifi.ssid
+ ],
+ '%d'.format(local_wifi.channel),
+ '%h MHz'.format(chan_width_text),
+ '%h'.format(local_wifi.mode),
+ '%h'.format(local_wifi.bssid)
+ ]);
+ }
}
for (var k in scanCache)
- if (scanCache[k].stale)
+ if (scanCache[k].data.stale)
results.push(scanCache[k].data);
results.sort(function(a, b) {
@@ -254,6 +268,8 @@ return view.extend({
chan_width = 2;
/* Skip WiFi not supported by the current band */
+ if (band != res.band)
+ continue;
if (chan_analysis.offset_tbl[res.channel] == null)
continue;
@@ -302,7 +318,7 @@ return view.extend({
E('span', { 'style': s }, '%h'.format(res.bssid))
]);
- res.stale = true;
+ scanCache[results[i].bssid].data.stale = true;
}
cbi_update_table(table, rows);
@@ -364,22 +380,20 @@ return view.extend({
var tabs = E('div', {}, E('div'));
for (var ifname in wifiDevs) {
- var freq_tbl = {
- ['2.4GHz'] : [],
- ['5GHz'] : [],
+ var bands = {
+ [2] : { title: '2.4GHz', channels: [] },
+ [5] : { title: '5GHz', channels: [] },
+ [6] : { title: '6GHz', channels: [] },
};
/* Split FrequencyList in Bands */
wifiDevs[ifname].freq.forEach(function(freq) {
- if (freq.mhz >= 5000) {
- freq_tbl['5GHz'].push(freq.channel);
- } else {
- freq_tbl['2.4GHz'].push(freq.channel);
- }
+ if (bands[freq.band])
+ bands[freq.band].channels.push(freq.channel);
});
- for (var freq in freq_tbl) {
- if (freq_tbl[freq].length == 0)
+ for (var band in bands) {
+ if (bands[band].channels.length == 0)
continue;
var csvg = svg.cloneNode(true),
@@ -393,7 +407,7 @@ return view.extend({
E('th', { 'class': 'th col-3 middle left hide-xs' }, _('BSSID'))
])
]),
- tab = E('div', { 'data-tab': ifname+freq, 'data-tab-title': ifname+' ('+freq+')' },
+ tab = E('div', { 'data-tab': ifname+band, 'data-tab-title': ifname+' ('+bands[band].title+')' },
[E('br'),csvg,E('br'),table,E('br')]),
graph_data = {
graph: csvg,
@@ -402,8 +416,9 @@ return view.extend({
tab: tab,
};
- this.radios[ifname+freq] = {
+ this.radios[ifname+band] = {
dev: wifiDevs[ifname].dev,
+ band: band,
graph: graph_data,
table: table,
scanCache: {},
@@ -414,7 +429,7 @@ return view.extend({
tabs.firstElementChild.appendChild(tab)
- requestAnimationFrame(L.bind(this.create_channel_graph, this, graph_data, freq_tbl[freq], freq));
+ requestAnimationFrame(L.bind(this.create_channel_graph, this, graph_data, bands[band].channels, band));
}
}
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/29_ports.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/29_ports.js
new file mode 100644
index 0000000000..b511897921
--- /dev/null
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/29_ports.js
@@ -0,0 +1,386 @@
+'use strict';
+'require baseclass';
+'require fs';
+'require ui';
+'require uci';
+'require rpc';
+'require network';
+'require firewall';
+
+var callGetBuiltinEthernetPorts = rpc.declare({
+ object: 'luci',
+ method: 'getBuiltinEthernetPorts',
+ expect: { result: [] }
+});
+
+function isString(v)
+{
+ return typeof(v) === 'string' && v !== '';
+}
+
+function resolveVLANChain(ifname, bridges, mapping)
+{
+ while (!mapping[ifname]) {
+ var m = ifname.match(/^(.+)\.([^.]+)$/);
+
+ if (!m)
+ break;
+
+ if (bridges[m[1]]) {
+ if (bridges[m[1]].vlan_filtering)
+ mapping[ifname] = bridges[m[1]].vlans[m[2]];
+ else
+ mapping[ifname] = bridges[m[1]].ports;
+ }
+ else if (/^[0-9]{1,4}$/.test(m[2]) && m[2] <= 4095) {
+ mapping[ifname] = [ m[1] ];
+ }
+ else {
+ break;
+ }
+
+ ifname = m[1];
+ }
+}
+
+function buildVLANMappings(mapping)
+{
+ var bridge_vlans = uci.sections('network', 'bridge-vlan'),
+ vlan_devices = uci.sections('network', 'device'),
+ interfaces = uci.sections('network', 'interface'),
+ bridges = {};
+
+ /* find bridge VLANs */
+ for (var i = 0, s; (s = bridge_vlans[i]) != null; i++) {
+ if (!isString(s.device) || !/^[0-9]{1,4}$/.test(s.vlan) || +s.vlan > 4095)
+ continue;
+
+ var aliases = L.toArray(s.alias),
+ ports = L.toArray(s.ports),
+ br = bridges[s.device] = (bridges[s.device] || { ports: [], vlans: {}, vlan_filtering: true });
+
+ br.vlans[s.vlan] = [];
+
+ for (var j = 0; j < ports.length; j++) {
+ var port = ports[j].replace(/:[ut*]+$/, '');
+
+ if (br.ports.indexOf(port) === -1)
+ br.ports.push(port);
+
+ br.vlans[s.vlan].push(port);
+ }
+
+ for (var j = 0; j < aliases.length; j++)
+ if (aliases[j] != s.vlan)
+ br.vlans[aliases[j]] = br.vlans[s.vlan];
+ }
+
+ /* find bridges, VLAN devices */
+ for (var i = 0, s; (s = vlan_devices[i]) != null; i++) {
+ if (s.type == 'bridge') {
+ if (!isString(s.name))
+ continue;
+
+ var ports = L.toArray(s.ports),
+ br = bridges[s.name] || (bridges[s.name] = { ports: [], vlans: {}, vlan_filtering: false });
+
+ if (s.vlan_filtering == '0')
+ br.vlan_filtering = false;
+ else if (s.vlan_filtering == '1')
+ br.vlan_filtering = true;
+
+ for (var j = 0; j < ports.length; j++)
+ if (br.ports.indexOf(ports[j]) === -1)
+ br.ports.push(ports[j]);
+
+ mapping[s.name] = br.ports;
+ }
+ else if (s.type == '8021q' || s.type == '8021ad') {
+ if (!isString(s.name) || !isString(s.vid) || !isString(s.ifname))
+ continue;
+
+ /* parent device is a bridge */
+ if (bridges[s.ifname]) {
+ /* parent bridge is VLAN enabled, device refers to VLAN ports */
+ if (bridges[s.ifname].vlan_filtering)
+ mapping[s.name] = bridges[s.ifname].vlans[s.vid];
+
+ /* parent bridge is not VLAN enabled, device refers to all bridge ports */
+ else
+ mapping[s.name] = bridges[s.ifname].ports;
+ }
+
+ /* parent is a simple netdev */
+ else {
+ mapping[s.name] = [ s.ifname ];
+ }
+
+ resolveVLANChain(s.ifname, bridges, mapping);
+ }
+ }
+
+ /* resolve VLAN tagged interfaces in bridge ports */
+ for (var brname in bridges) {
+ for (var i = 0; i < bridges[brname].ports.length; i++)
+ resolveVLANChain(bridges[brname].ports[i], bridges, mapping);
+
+ for (var vid in bridges[brname].vlans)
+ for (var i = 0; i < bridges[brname].vlans[vid].length; i++)
+ resolveVLANChain(bridges[brname].vlans[vid][i], bridges, mapping);
+ }
+
+ /* find implicit VLAN devices */
+ for (var i = 0, s; (s = interfaces[i]) != null; i++) {
+ if (!isString(s.device))
+ continue;
+
+ resolveVLANChain(s.device, bridges, mapping);
+ }
+}
+
+function resolveVLANPorts(ifname, mapping, seen)
+{
+ var ports = [];
+
+ if (!seen)
+ seen = {};
+
+ if (mapping[ifname]) {
+ for (var i = 0; i < mapping[ifname].length; i++) {
+ if (!seen[mapping[ifname][i]]) {
+ seen[mapping[ifname][i]] = true;
+ ports.push.apply(ports, resolveVLANPorts(mapping[ifname][i], mapping, seen));
+ }
+ }
+ }
+ else {
+ ports.push(ifname);
+ }
+
+ return ports.sort(L.naturalCompare);
+}
+
+function buildInterfaceMapping(zones, networks) {
+ var vlanmap = {},
+ portmap = {},
+ netmap = {};
+
+ buildVLANMappings(vlanmap);
+
+ for (var i = 0; i < networks.length; i++) {
+ var l3dev = networks[i].getDevice();
+
+ if (!l3dev)
+ continue;
+
+ var ports = resolveVLANPorts(l3dev.getName(), vlanmap);
+
+ for (var j = 0; j < ports.length; j++) {
+ portmap[ports[j]] = portmap[ports[j]] || { networks: [], zones: [] };
+ portmap[ports[j]].networks.push(networks[i]);
+ }
+
+ netmap[networks[i].getName()] = networks[i];
+ }
+
+ for (var i = 0; i < zones.length; i++) {
+ var networknames = zones[i].getNetworks();
+
+ for (var j = 0; j < networknames.length; j++) {
+ if (!netmap[networknames[j]])
+ continue;
+
+ var l3dev = netmap[networknames[j]].getDevice();
+
+ if (!l3dev)
+ continue;
+
+ var ports = resolveVLANPorts(l3dev.getName(), vlanmap);
+
+ for (var k = 0; k < ports.length; k++) {
+ portmap[ports[k]] = portmap[ports[k]] || { networks: [], zones: [] };
+
+ if (portmap[ports[k]].zones.indexOf(zones[i]) === -1)
+ portmap[ports[k]].zones.push(zones[i]);
+ }
+ }
+ }
+
+ return portmap;
+}
+
+function formatSpeed(carrier, speed, duplex) {
+ if (speed && duplex) {
+ var d = (duplex == 'half') ? '\u202f(H)' : '',
+ e = E('span', { 'title': _('Speed: %d Mibit/s, Duplex: %s').format(speed, duplex) });
+
+ switch (speed) {
+ case 10: e.innerText = '10\u202fM' + d; break;
+ case 100: e.innerText = '100\u202fM' + d; break;
+ case 1000: e.innerText = '1\u202fGbE' + d; break;
+ case 2500: e.innerText = '2.5\u202fGbE'; break;
+ case 5000: e.innerText = '5\u202fGbE'; break;
+ case 10000: e.innerText = '10\u202fGbE'; break;
+ case 25000: e.innerText = '25\u202fGbE'; break;
+ case 40000: e.innerText = '40\u202fGbE'; break;
+ default: e.innerText = '%d\u202fMbE%s'.format(speed, d);
+ }
+
+ return e;
+ }
+
+ return carrier ? _('Connected') : _('no link');
+}
+
+function formatStats(portdev) {
+ var stats = portdev._devstate('stats') || {};
+
+ return ui.itemlist(E('span'), [
+ _('Received bytes'), '%1024mB'.format(stats.rx_bytes),
+ _('Received packets'), '%1000mPkts.'.format(stats.rx_packets),
+ _('Received multicast'), '%1000mPkts.'.format(stats.multicast),
+ _('Receive errors'), '%1000mPkts.'.format(stats.rx_errors),
+ _('Receive dropped'), '%1000mPkts.'.format(stats.rx_dropped),
+
+ _('Transmitted bytes'), '%1024mB'.format(stats.tx_bytes),
+ _('Transmitted packets'), '%1000mPkts.'.format(stats.tx_packets),
+ _('Transmit errors'), '%1000mPkts.'.format(stats.tx_errors),
+ _('Transmit dropped'), '%1000mPkts.'.format(stats.tx_dropped),
+
+ _('Collisions seen'), stats.collisions
+ ]);
+}
+
+function renderNetworkBadge(network, zonename) {
+ var l3dev = network.getDevice();
+ var span = E('span', { 'class': 'ifacebadge', 'style': 'margin:.125em 0' }, [
+ E('span', {
+ 'class': 'zonebadge',
+ 'title': zonename ? _('Part of zone %q').format(zonename) : _('No zone assigned'),
+ 'style': firewall.getZoneColorStyle(zonename)
+ }, '\u202f'),
+ '\u202f', network.getName(), ': '
+ ]);
+
+ if (l3dev)
+ span.appendChild(E('img', {
+ 'title': l3dev.getI18n(),
+ 'src': L.resource('icons/%s%s.png'.format(l3dev.getType(), l3dev.isUp() ? '' : '_disabled'))
+ }));
+ else
+ span.appendChild(E('em', _('(no interfaces attached)')));
+
+ return span;
+}
+
+function renderNetworksTooltip(pmap) {
+ var res = [ null ],
+ zmap = {};
+
+ for (var i = 0; pmap && i < pmap.zones.length; i++) {
+ var networknames = pmap.zones[i].getNetworks();
+
+ for (var k = 0; k < networknames.length; k++)
+ zmap[networknames[k]] = pmap.zones[i].getName();
+ }
+
+ for (var i = 0; pmap && i < pmap.networks.length; i++)
+ res.push(E('br'), renderNetworkBadge(pmap.networks[i], zmap[pmap.networks[i].getName()]));
+
+ if (res.length > 1)
+ res[0] = N_((res.length - 1) / 2, 'Part of network:', 'Part of networks:');
+ else
+ res[0] = _('Port is not part of any network');
+
+ return E([], res);
+}
+
+return baseclass.extend({
+ title: _('Port status'),
+
+ load: function() {
+ return Promise.all([
+ L.resolveDefault(callGetBuiltinEthernetPorts(), []),
+ L.resolveDefault(fs.read('/etc/board.json'), '{}'),
+ firewall.getZones(),
+ network.getNetworks(),
+ uci.load('network')
+ ]);
+ },
+
+ render: function(data) {
+ if (L.hasSystemFeature('swconfig'))
+ return null;
+
+ var board = JSON.parse(data[1]),
+ known_ports = [],
+ port_map = buildInterfaceMapping(data[2], data[3]);
+
+ if (Array.isArray(data[0]) && data[0].length > 0) {
+ known_ports = data[0].map(port => ({
+ ...port,
+ netdev: network.instantiateDevice(port.device)
+ }));
+ }
+ else {
+ if (L.isObject(board) && L.isObject(board.network)) {
+ for (var k = 'lan'; k != null; k = (k == 'lan') ? 'wan' : null) {
+ if (!L.isObject(board.network[k]))
+ continue;
+
+ if (Array.isArray(board.network[k].ports))
+ for (let i = 0; i < board.network[k].ports.length; i++)
+ known_ports.push({
+ role: k,
+ device: board.network[k].ports[i],
+ netdev: network.instantiateDevice(board.network[k].ports[i])
+ });
+ else if (typeof(board.network[k].device) == 'string')
+ known_ports.push({
+ role: k,
+ device: board.network[k].device,
+ netdev: network.instantiateDevice(board.network[k].device)
+ });
+ }
+ }
+ }
+
+ known_ports.sort(function(a, b) {
+ return L.naturalCompare(a.device, b.device);
+ });
+
+ return E('div', { 'style': 'display:grid;grid-template-columns:repeat(auto-fit, minmax(70px, 1fr));margin-bottom:1em' }, known_ports.map(function(port) {
+ var speed = port.netdev.getSpeed(),
+ duplex = port.netdev.getDuplex(),
+ carrier = port.netdev.getCarrier(),
+ pmap = port_map[port.netdev.getName()],
+ pzones = (pmap && pmap.zones.length) ? pmap.zones.sort(function(a, b) { return L.naturalCompare(a.getName(), b.getName()) }) : [ null ];
+
+ return E('div', { 'class': 'ifacebox', 'style': 'margin:.25em;min-width:70px;max-width:100px' }, [
+ E('div', { 'class': 'ifacebox-head', 'style': 'font-weight:bold' }, [ port.netdev.getName() ]),
+ E('div', { 'class': 'ifacebox-body' }, [
+ E('img', { 'src': L.resource('icons/port_%s.png').format(carrier ? 'up' : 'down') }),
+ E('br'),
+ formatSpeed(carrier, speed, duplex)
+ ]),
+ E('div', { 'class': 'ifacebox-head cbi-tooltip-container', 'style': 'display:flex' }, [
+ E([], pzones.map(function(zone) {
+ return E('div', {
+ 'class': 'zonebadge',
+ 'style': 'cursor:help;flex:1;height:3px;opacity:' + (carrier ? 1 : 0.25) + ';' + firewall.getZoneColorStyle(zone)
+ });
+ })),
+ E('span', { 'class': 'cbi-tooltip left' }, [ renderNetworksTooltip(pmap) ])
+ ]),
+ E('div', { 'class': 'ifacebox-body' }, [
+ E('div', { 'class': 'cbi-tooltip-container', 'style': 'text-align:left;font-size:80%' }, [
+ '\u25b2\u202f%1024.1mB'.format(port.netdev.getTXBytes()),
+ E('br'),
+ '\u25bc\u202f%1024.1mB'.format(port.netdev.getRXBytes()),
+ E('span', { 'class': 'cbi-tooltip' }, formatStats(port.netdev))
+ ]),
+ ])
+ ]);
+ }));
+ }
+});
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js
index 956c1b20f9..4c52916423 100644
--- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/include/40_dhcp.js
@@ -82,7 +82,7 @@ return baseclass.extend({
}
};
- var table = E('table', { 'class': 'table lases' }, [
+ var table = E('table', { 'id': 'status_leases', 'class': 'table lases' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, _('Hostname')),
E('th', { 'class': 'th' }, _('IPv4 address')),
@@ -129,7 +129,7 @@ return baseclass.extend({
return rows;
}, this)), E('em', _('There are no active leases')));
- var table6 = E('table', { 'class': 'table leases6' }, [
+ var table6 = E('table', { 'id': 'status_leases6', 'class': 'table leases6' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, _('Host')),
E('th', { 'class': 'th' }, _('IPv6 address')),
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js
index 951c31218d..917dada57a 100644
--- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/iptables.js
@@ -173,7 +173,7 @@ return view.extend({
srcnet,
dstnet,
options,
- [ comment ]
+ [ comment, '%h'.format(comment) ]
]);
if (target) {
@@ -252,7 +252,11 @@ return view.extend({
elem = document.getElementById('rule_%s_%s'.format(table.toLowerCase(), chain));
if (elem) {
- (document.documentElement || document.body.parentNode || document.body).scrollTop = elem.offsetTop - 40;
+ if (elem.scrollIntoView) {
+ elem.scrollIntoView();
+ } else {
+ (document.documentElement || document.body.parentNode || document.body).scrollTop = elem.offsetTop - 40;
+ }
elem.classList.remove('flash');
void elem.offsetWidth;
elem.classList.add('flash');
diff --git a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/nftables.js b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/nftables.js
index d891526d04..be62d91350 100644
--- a/modules/luci-mod-status/htdocs/luci-static/resources/view/status/nftables.js
+++ b/modules/luci-mod-status/htdocs/luci-static/resources/view/status/nftables.js
@@ -9,7 +9,7 @@ var expr_translations = {
'meta.iifname': _('Ingress device name', 'nft meta iifname'),
'meta.oifname': _('Egress device name', 'nft meta oifname'),
'meta.iif': _('Ingress device id', 'nft meta iif'),
- 'meta.iif': _('Egress device id', 'nft meta oif'),
+ 'meta.oif': _('Egress device id', 'nft meta oif'),
'meta.l4proto': _('IP protocol', 'nft meta l4proto'),
'meta.l4proto.tcp': 'TCP',
diff --git a/modules/luci-mod-status/root/usr/share/rpcd/acl.d/luci-mod-status-index.json b/modules/luci-mod-status/root/usr/share/rpcd/acl.d/luci-mod-status-index.json
index 7c2cd0998f..20cd23bb6c 100644
--- a/modules/luci-mod-status/root/usr/share/rpcd/acl.d/luci-mod-status-index.json
+++ b/modules/luci-mod-status/root/usr/share/rpcd/acl.d/luci-mod-status-index.json
@@ -3,6 +3,7 @@
"description": "Grant access to main status display",
"read": {
"file": {
+ "/etc/board.json": [ "read" ],
"/proc/sys/net/netfilter/nf_conntrack_count": [ "read" ],
"/proc/sys/net/netfilter/nf_conntrack_max": [ "read" ],
"/www/luci-static/resources/view/status/include": [ "list" ]
@@ -36,6 +37,15 @@
}
},
+ "luci-mod-status-index-ports": {
+ "description": "Grant access to port status display",
+ "read": {
+ "ubus": {
+ "luci": [ "getBuiltinEthernetPorts" ]
+ }
+ }
+ },
+
"luci-mod-status-index-wifi": {
"description": "Grant access to wireless status display",
"read": {