luci-mod-status: add odhcp6c stats to network include

Provides odhcp6c statistics of DHCPv6 request and response traffic,
for dhcpv6 client interfaces only.

This appends to IPv6 Upstream network interface boxes:

DHCPv6 solicit: x pkts
DHCPv6 advertise: x pkts
DHCPv6 request: x pkts
DHCPv6 confirm: x pkts
...

Signed-off-by: Paul Donald <newtwen+github@gmail.com>
This commit is contained in:
Paul Donald
2025-12-08 15:02:53 +01:00
parent 2decd522de
commit dd1ede16a0
2 changed files with 26 additions and 4 deletions

View File

@@ -2,6 +2,16 @@
'require baseclass'; 'require baseclass';
'require fs'; 'require fs';
'require network'; 'require network';
'require rpc';
/* returns per odhcp6c active interface JSON like:
{"result":{"eth1":{"dhcp_solicit":3,"dhcp_advertise":3,"dhcp_request":3,...}}} */
const callOdhcp6cStats = rpc.declare({
object: 'luci',
method: 'getOdhcp6cStats',
expect: { '': {} },
});
function progressbar(value, max, byte) { function progressbar(value, max, byte) {
const vn = parseInt(value) || 0; const vn = parseInt(value) || 0;
@@ -16,7 +26,7 @@ function progressbar(value, max, byte) {
}, E('div', { 'style': 'width:%.2f%%'.format(pc) })); }, E('div', { 'style': 'width:%.2f%%'.format(pc) }));
} }
function renderbox(ifc, ipv6) { function renderbox(ifc, ipv6, dhcpv6_stats) {
const dev = ifc.getL3Device(); const dev = ifc.getL3Device();
const active = (dev && ifc.getProtocol() != 'none'); const active = (dev && ifc.getProtocol() != 'none');
const addrs = (ipv6 ? ifc.getIP6Addrs() : ifc.getIPAddrs()) || []; const addrs = (ipv6 ? ifc.getIP6Addrs() : ifc.getIPAddrs()) || [];
@@ -28,6 +38,16 @@ function renderbox(ifc, ipv6) {
return Array.isArray(array) ? array.flatMap((item) => [label, item]) : [label, null]; return Array.isArray(array) ? array.flatMap((item) => [label, item]) : [label, null];
} }
function addDhcpv6Stats() {
if (ipv6 && ifc.getProtocol() === 'dhcpv6' && dhcpv6_stats && dhcpv6_stats[dev.device]) {
const arr = [];
for (const [pkt_type, count] of Object.entries(dhcpv6_stats[dev.device]))
arr.push(pkt_type.replace('dhcp_', _('DHCPv6') + ' '), `${count} ${_('pkts', 'packets, abbreviated')}`);
return arr;
}
return ['', null];
}
return E('div', { class: 'ifacebox' }, [ return E('div', { class: 'ifacebox' }, [
E('div', { class: 'ifacebox-head center ' + (active ? 'active' : '') }, E('div', { class: 'ifacebox-head center ' + (active ? 'active' : '') },
E('strong', ipv6 ? _('IPv6 Upstream') : _('IPv4 Upstream'))), E('strong', ipv6 ? _('IPv6 Upstream') : _('IPv4 Upstream'))),
@@ -40,6 +60,7 @@ function renderbox(ifc, ipv6) {
...addEntries(_('DNS'), dnssrv), ...addEntries(_('DNS'), dnssrv),
_('Expires'), (expires != null && expires > -1) ? '%t'.format(expires) : null, _('Expires'), (expires != null && expires > -1) ? '%t'.format(expires) : null,
_('Connected'), (uptime > 0) ? '%t'.format(uptime) : null, _('Connected'), (uptime > 0) ? '%t'.format(uptime) : null,
...addDhcpv6Stats(),
]), ]),
E('div', {}, renderBadge( E('div', {}, renderBadge(
L.resource('icons/%s.svg').format(dev ? dev.getType() : 'ethernet_disabled'), null, L.resource('icons/%s.svg').format(dev ? dev.getType() : 'ethernet_disabled'), null,
@@ -59,10 +80,11 @@ return baseclass.extend({
fs.trimmed('/proc/sys/net/netfilter/nf_conntrack_max'), fs.trimmed('/proc/sys/net/netfilter/nf_conntrack_max'),
network.getWANNetworks(), network.getWANNetworks(),
network.getWAN6Networks(), network.getWAN6Networks(),
callOdhcp6cStats(),
]); ]);
}, },
render([ct_count, ct_max, wan_nets, wan6_nets]) { render([ct_count, ct_max, wan_nets, wan6_nets, dhcpv6_stats]) {
const fields = [ const fields = [
{ label: _('Active Connections'), value: ct_max ? ct_count : null } { label: _('Active Connections'), value: ct_max ? ct_count : null }
@@ -85,7 +107,7 @@ return baseclass.extend({
netstatus.appendChild(renderbox(wan_net, false)); netstatus.appendChild(renderbox(wan_net, false));
for (const wan6_net of wan6_nets) for (const wan6_net of wan6_nets)
netstatus.appendChild(renderbox(wan6_net, true)); netstatus.appendChild(renderbox(wan6_net, true, dhcpv6_stats?.result));
return E([ return E([
netstatus, netstatus,

View File

@@ -9,7 +9,7 @@
"/www/luci-static/resources/view/status/include": [ "list" ] "/www/luci-static/resources/view/status/include": [ "list" ]
}, },
"ubus": { "ubus": {
"luci": [ "getVersion", "getUnixtime" ], "luci": [ "getVersion", "getUnixtime", "getOdhcp6cStats" ],
"file": [ "list", "read" ], "file": [ "list", "read" ],
"system": [ "board", "info" ] "system": [ "board", "info" ]
} }