luci-mod-status: reimplement route status page as client side view
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
(backported from commit 16d049f7cd
)
This commit is contained in:
parent
4306c1859a
commit
01d8af7eb6
4 changed files with 221 additions and 157 deletions
|
@ -44,6 +44,10 @@
|
|||
"/bin/ping6 *": [ "exec" ],
|
||||
"/bin/traceroute *": [ "exec" ],
|
||||
"/bin/traceroute6 *": [ "exec" ],
|
||||
"/sbin/ip -4 neigh show": [ "exec" ],
|
||||
"/sbin/ip -4 route show table all": [ "exec" ],
|
||||
"/sbin/ip -6 neigh show": [ "exec" ],
|
||||
"/sbin/ip -6 route show table all": [ "exec" ],
|
||||
"/sbin/logread -e ^": [ "exec" ],
|
||||
"/usr/bin/ping *": [ "exec" ],
|
||||
"/usr/bin/ping6 *": [ "exec" ],
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
'use strict';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
'require validation';
|
||||
|
||||
var callNetworkInterfaceDump = rpc.declare({
|
||||
object: 'network.interface',
|
||||
method: 'dump',
|
||||
expect: { interface: [] }
|
||||
});
|
||||
|
||||
function applyMask(addr, mask, v6) {
|
||||
var words = v6 ? validation.parseIPv6(addr) : validation.parseIPv4(addr);
|
||||
|
||||
if (!words || mask < 0 || mask > (v6 ? 128 : 32))
|
||||
return null;
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var b = Math.min(mask, v6 ? 16 : 8);
|
||||
words[i] &= ((1 << b) - 1);
|
||||
mask -= b;
|
||||
}
|
||||
|
||||
return String.prototype.format.apply(
|
||||
v6 ? '%x:%x:%x:%x:%x:%x:%x:%x' : '%d.%d.%d.%d', words);
|
||||
}
|
||||
|
||||
return L.view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
callNetworkInterfaceDump(),
|
||||
L.resolveDefault(fs.exec('/sbin/ip', [ '-4', 'neigh', 'show' ]), {}),
|
||||
L.resolveDefault(fs.exec('/sbin/ip', [ '-4', 'route', 'show', 'table', 'all' ]), {}),
|
||||
L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'neigh', 'show' ]), {}),
|
||||
L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'route', 'show', 'table', 'all' ]), {})
|
||||
]);
|
||||
},
|
||||
|
||||
getNetworkByDevice(networks, dev, addr, mask, v6) {
|
||||
var addr_arrays = [ 'ipv4-address', 'ipv6-address', 'ipv6-prefix', 'ipv6-prefix-assignment', 'route' ],
|
||||
matching_iface = null,
|
||||
matching_prefix = -1;
|
||||
|
||||
for (var i = 0; i < networks.length; i++) {
|
||||
if (!L.isObject(networks[i]))
|
||||
continue;
|
||||
|
||||
if (networks[i].l3_device != dev && networks[i].device != dev)
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < addr_arrays.length; j++) {
|
||||
var addr_list = networks[i][addr_arrays[j]];
|
||||
|
||||
if (!Array.isArray(addr_list) || addr_list.length == 0)
|
||||
continue;
|
||||
|
||||
for (var k = 0; k < addr_list.length; k++) {
|
||||
var cmp_addr = addr_list[k].address || addr_list[k].target,
|
||||
cmp_mask = addr_list[k].mask;
|
||||
|
||||
if (cmp_addr == null)
|
||||
continue;
|
||||
|
||||
var addr1 = applyMask(cmp_addr, cmp_mask, v6),
|
||||
addr2 = applyMask(addr, cmp_mask, v6);
|
||||
|
||||
if (addr1 != addr2 || mask < cmp_mask)
|
||||
continue;
|
||||
|
||||
if (cmp_mask > matching_prefix) {
|
||||
matching_iface = networks[i].interface;
|
||||
matching_prefix = cmp_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matching_iface;
|
||||
},
|
||||
|
||||
parseNeigh: function(s, networks, v6) {
|
||||
var lines = s.trim().split(/\n/),
|
||||
res = [];
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var m = lines[i].match(/^([0-9a-f:.]+) (.+) (\S+)$/),
|
||||
addr = m ? m[1] : null,
|
||||
flags = m ? m[2].trim().split(/\s+/) : [],
|
||||
state = (m ? m[3] : null) || 'FAILED';
|
||||
|
||||
if (!addr || state == 'FAILED' || addr.match(/^fe[89a-f][0-9a-f]:/))
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < flags.length; j += 2)
|
||||
flags[flags[j]] = flags[j + 1];
|
||||
|
||||
if (!flags.lladdr)
|
||||
continue;
|
||||
|
||||
var net = this.getNetworkByDevice(networks, flags.dev, addr, v6 ? 128 : 32, v6);
|
||||
|
||||
res.push([
|
||||
addr,
|
||||
flags.lladdr.toUpperCase(),
|
||||
E('span', { 'class': 'ifacebadge' }, [ net ? net : '(%s)'.format(flags.dev) ])
|
||||
]);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
parseRoute: function(s, networks, v6) {
|
||||
var lines = s.trim().split(/\n/),
|
||||
res = [];
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var m = lines[i].match(/^(?:([a-z_]+|\d+) )?(default|[0-9a-f:.\/]+) (.+)$/),
|
||||
type = (m ? m[1] : null) || 'unicast',
|
||||
dest = m ? (m[2] == 'default' ? (v6 ? '::/0' : '0.0.0.0/0') : m[2]) : null,
|
||||
flags = m ? m[3].trim().split(/\s+/) : [];
|
||||
|
||||
console.debug(lines[i], m);
|
||||
|
||||
if (!dest || type != 'unicast' || dest == 'fe80::/64' || dest == 'ff00::/8')
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < flags.length; j += 2)
|
||||
flags[flags[j]] = flags[j + 1];
|
||||
|
||||
var addr = dest.split('/'),
|
||||
bits = (addr[1] != null) ? +addr[1] : (v6 ? 128 : 32),
|
||||
net = this.getNetworkByDevice(networks, flags.dev, addr[0], bits, v6);
|
||||
|
||||
res.push([
|
||||
E('span', { 'class': 'ifacebadge' }, [ net ? net : '(%s)'.format(flags.dev) ]),
|
||||
dest,
|
||||
(v6 ? flags.from : flags.via) || '-',
|
||||
String(flags.metric || 0),
|
||||
flags.table || 'main'
|
||||
]);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var networks = data[0],
|
||||
ip4neigh = data[1].stdout || '',
|
||||
ip4route = data[2].stdout || '',
|
||||
ip6neigh = data[3].stdout || '',
|
||||
ip6route = data[4].stdout || '';
|
||||
|
||||
var neigh4tbl = E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', { 'class': 'th' }, [ _('IPv4-Address') ]),
|
||||
E('div', { 'class': 'th' }, [ _('MAC-Address') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Interface') ])
|
||||
])
|
||||
]);
|
||||
|
||||
var route4tbl = E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', { 'class': 'th' }, [ _('Network') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Target') ]),
|
||||
E('div', { 'class': 'th' }, [ _('IPv4-Gateway') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Metric') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Table') ])
|
||||
])
|
||||
]);
|
||||
|
||||
var neigh6tbl = E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', { 'class': 'th' }, [ _('IPv6-Address') ]),
|
||||
E('div', { 'class': 'th' }, [ _('MAC-Address') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Interface') ])
|
||||
])
|
||||
]);
|
||||
|
||||
var route6tbl = E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', { 'class': 'th' }, [ _('Network') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Target') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Source') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Metric') ]),
|
||||
E('div', { 'class': 'th' }, [ _('Table') ])
|
||||
])
|
||||
]);
|
||||
|
||||
cbi_update_table(neigh4tbl, this.parseNeigh(ip4neigh, networks, false));
|
||||
cbi_update_table(route4tbl, this.parseRoute(ip4route, networks, false));
|
||||
cbi_update_table(neigh6tbl, this.parseNeigh(ip6neigh, networks, true));
|
||||
cbi_update_table(route6tbl, this.parseRoute(ip6route, networks, true));
|
||||
|
||||
return E([], [
|
||||
E('h2', {}, [ _('Routes') ]),
|
||||
E('p', {}, [ _('The following rules are currently active on this system.') ]),
|
||||
|
||||
E('h3', {}, [ _('ARP') ]),
|
||||
neigh4tbl,
|
||||
|
||||
E('h3', {}, _('Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes')),
|
||||
route4tbl,
|
||||
|
||||
E('h3', {}, [ _('IPv6 Neighbours') ]),
|
||||
neigh6tbl,
|
||||
|
||||
E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes')),
|
||||
route6tbl
|
||||
]);
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
|
|
@ -11,7 +11,7 @@ function index()
|
|||
|
||||
entry({"admin", "status", "iptables"}, view("status/iptables"), _("Firewall"), 2).leaf = true
|
||||
|
||||
entry({"admin", "status", "routes"}, template("admin_status/routes"), _("Routes"), 3)
|
||||
entry({"admin", "status", "routes"}, view("status/routes"), _("Routes"), 3)
|
||||
entry({"admin", "status", "syslog"}, view("status/syslog"), _("System Log"), 4)
|
||||
entry({"admin", "status", "dmesg"}, view("status/dmesg"), _("Kernel Log"), 5)
|
||||
entry({"admin", "status", "processes"}, view("status/processes"), _("Processes"), 6)
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
<%#
|
||||
Copyright 2008-2009 Steven Barth <steven@midlink.org>
|
||||
Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%-
|
||||
require "luci.tools.webadmin"
|
||||
require "nixio.fs"
|
||||
|
||||
local ip = require "luci.ip"
|
||||
local style = true
|
||||
local _, v
|
||||
|
||||
local rtn = {
|
||||
[255] = "local",
|
||||
[254] = "main",
|
||||
[253] = "default",
|
||||
[0] = "unspec"
|
||||
}
|
||||
|
||||
if nixio.fs.access("/etc/iproute2/rt_tables") then
|
||||
local ln
|
||||
for ln in io.lines("/etc/iproute2/rt_tables") do
|
||||
local i, n = ln:match("^(%d+)%s+(%S+)")
|
||||
if i and n then
|
||||
rtn[tonumber(i)] = n
|
||||
end
|
||||
end
|
||||
end
|
||||
-%>
|
||||
|
||||
<%+header%>
|
||||
|
||||
|
||||
<div class="cbi-map" id="cbi-network">
|
||||
<h2 name="content"><%:Routes%></h2>
|
||||
<div class="cbi-map-descr"><%:The following rules are currently active on this system.%></div>
|
||||
|
||||
<div class="cbi-section">
|
||||
<legend>ARP</legend>
|
||||
<div class="cbi-section-node">
|
||||
<div class="table">
|
||||
<div class="tr table-titles">
|
||||
<div class="th"><%_<abbr title="Internet Protocol Version 4">IPv4</abbr>-Address%></div>
|
||||
<div class="th"><%_<abbr title="Media Access Control">MAC</abbr>-Address%></div>
|
||||
<div class="th"><%:Interface%></div>
|
||||
</div>
|
||||
|
||||
<%
|
||||
for _, v in ipairs(ip.neighbors({ family = 4 })) do
|
||||
if v.mac then
|
||||
%>
|
||||
<div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
|
||||
<div class="td"><%=v.dest%></div>
|
||||
<div class="td"><%=v.mac%></div>
|
||||
<div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></div>
|
||||
</div>
|
||||
<%
|
||||
style = not style
|
||||
end
|
||||
end
|
||||
%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbi-section">
|
||||
<legend><%_Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes%></legend>
|
||||
<div class="cbi-section-node">
|
||||
<div class="table">
|
||||
<div class="tr table-titles">
|
||||
<div class="th"><%:Network%></div>
|
||||
<div class="th"><%:Target%></div>
|
||||
<div class="th"><%_<abbr title="Internet Protocol Version 4">IPv4</abbr>-Gateway%></div>
|
||||
<div class="th"><%:Metric%></div>
|
||||
<div class="th"><%:Table%></div>
|
||||
</div>
|
||||
<% for _, v in ipairs(ip.routes({ family = 4, type = 1 })) do %>
|
||||
<div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
|
||||
<div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or v.dev%></div>
|
||||
<div class="td"><%=v.dest%></div>
|
||||
<div class="td"><%=v.gw or "-"%></div>
|
||||
<div class="td"><%=v.metric or 0%></div>
|
||||
<div class="td"><%=rtn[v.table] or v.table%></div>
|
||||
</div>
|
||||
<% style = not style end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%
|
||||
if nixio.fs.access("/proc/net/ipv6_route") then
|
||||
style = true
|
||||
%>
|
||||
<div class="cbi-section">
|
||||
<legend><%_Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes%></legend>
|
||||
<div class="cbi-section-node">
|
||||
<div class="table">
|
||||
<div class="tr table-titles">
|
||||
<div class="th"><%:Network%></div>
|
||||
<div class="th"><%:Target%></div>
|
||||
<div class="th"><%:Source%></div>
|
||||
<div class="th"><%:Metric%></div>
|
||||
<div class="th"><%:Table%></div>
|
||||
</div>
|
||||
<%
|
||||
for _, v in ipairs(ip.routes({ family = 6, type = 1 })) do
|
||||
if v.dest and not v.dest:is6linklocal() then
|
||||
%>
|
||||
<div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
|
||||
<div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></div>
|
||||
<div class="td"><%=v.dest%></div>
|
||||
<div class="td"><%=v.from%></div>
|
||||
<div class="td"><%=v.metric or 0%></div>
|
||||
<div class="td"><%=rtn[v.table] or v.table%></div>
|
||||
</div>
|
||||
<%
|
||||
style = not style
|
||||
end
|
||||
end
|
||||
%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbi-section">
|
||||
<legend><%:IPv6 Neighbours%></legend>
|
||||
<div class="cbi-section-node">
|
||||
<div class="table">
|
||||
<div class="tr table-titles">
|
||||
<div class="th"><%:IPv6-Address%></div>
|
||||
<div class="th"><%:MAC-Address%></div>
|
||||
<div class="th"><%:Interface%></div>
|
||||
</div>
|
||||
<%
|
||||
for _, v in ipairs(ip.neighbors({ family = 6 })) do
|
||||
if v.dest and not v.dest:is6linklocal() and v.mac then
|
||||
%>
|
||||
<div class="tr cbi-rowstyle-<%=(style and 1 or 2)%>">
|
||||
<div class="td"><%=v.dest%></div>
|
||||
<div class="td"><%=v.mac%></div>
|
||||
<div class="td"><%=luci.tools.webadmin.iface_get_network(v.dev) or '(' .. v.dev .. ')'%></div>
|
||||
</div>
|
||||
<%
|
||||
style = not style
|
||||
end
|
||||
end
|
||||
%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%+footer%>
|
Loading…
Reference in a new issue