luci-mod-status: status/routing support for pbr

Provide comprehensive status information for routing.
Rename the "Status > Routes" page to "Status > Routing".
Unify sorting for the "Status" and "Network" menus.
Add tabs for IPv4 and IPv6 and reorganize the contents.
Display routing rules and their priorities for each protocol.

Policy-based routing is an increasingly popular problem.
Netifd natively supports policy-based routing:
* The interface-specific options "ip4table" and "ip6table".
* The routing rules using the "rule" and "rule6" sections.
LuCI is missing the information about routing rules.

Signed-off-by: Vladislav Grigoryev <vg.aetera@gmail.com>
This commit is contained in:
Vladislav Grigoryev 2021-08-05 03:50:37 +03:00
parent 39cc16d2eb
commit 5cd9209636
3 changed files with 93 additions and 31 deletions

View file

@ -3,6 +3,7 @@
'require fs'; 'require fs';
'require rpc'; 'require rpc';
'require validation'; 'require validation';
'require ui';
var callNetworkInterfaceDump = rpc.declare({ var callNetworkInterfaceDump = rpc.declare({
object: 'network.interface', object: 'network.interface',
@ -32,8 +33,10 @@ return view.extend({
callNetworkInterfaceDump(), callNetworkInterfaceDump(),
L.resolveDefault(fs.exec('/sbin/ip', [ '-4', 'neigh', 'show' ]), {}), 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', [ '-4', 'route', 'show', 'table', 'all' ]), {}),
L.resolveDefault(fs.exec('/sbin/ip', [ '-4', 'rule', 'show' ]), {}),
L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'neigh', 'show' ]), {}), L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'neigh', 'show' ]), {}),
L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'route', 'show', 'table', 'all' ]), {}) L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'route', 'show', 'table', 'all' ]), {}),
L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'rule', 'show' ]), {})
]); ]);
}, },
@ -143,12 +146,32 @@ return view.extend({
return res; return res;
}, },
parseRule: function(s) {
var lines = s.trim().split(/\n/),
res = [];
for (var i = 0; i < lines.length; i++) {
var m = lines[i].match(/^(\d+):\s+(.+)$/),
prio = m ? m[1] : null,
rule = m ? m[2] : null;
res.push([
prio,
rule
]);
}
return res;
},
render: function(data) { render: function(data) {
var networks = data[0], var networks = data[0],
ip4neigh = data[1].stdout || '', ip4neigh = data[1].stdout || '',
ip4route = data[2].stdout || '', ip4route = data[2].stdout || '',
ip6neigh = data[3].stdout || '', ip4rule = data[3].stdout || '',
ip6route = data[4].stdout || ''; ip6neigh = data[4].stdout || '',
ip6route = data[5].stdout || '',
ip6rule = data[6].stdout || '';
var neigh4tbl = E('table', { 'class': 'table' }, [ var neigh4tbl = E('table', { 'class': 'table' }, [
E('tr', { 'class': 'tr table-titles' }, [ E('tr', { 'class': 'tr table-titles' }, [
@ -165,7 +188,14 @@ return view.extend({
E('th', { 'class': 'th' }, [ _('IPv4 gateway') ]), E('th', { 'class': 'th' }, [ _('IPv4 gateway') ]),
E('th', { 'class': 'th' }, [ _('Metric') ]), E('th', { 'class': 'th' }, [ _('Metric') ]),
E('th', { 'class': 'th' }, [ _('Table') ]), E('th', { 'class': 'th' }, [ _('Table') ]),
E('th', { 'class': 'th' }, [ _('Protocol') ]), E('th', { 'class': 'th' }, [ _('Protocol') ])
])
]);
var rule4tbl = E('table', { 'class': 'table' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, [ _('Priority') ]),
E('th', { 'class': 'th' }, [ _('Rule') ])
]) ])
]); ]);
@ -184,31 +214,62 @@ return view.extend({
E('th', { 'class': 'th' }, [ _('Source') ]), E('th', { 'class': 'th' }, [ _('Source') ]),
E('th', { 'class': 'th' }, [ _('Metric') ]), E('th', { 'class': 'th' }, [ _('Metric') ]),
E('th', { 'class': 'th' }, [ _('Table') ]), E('th', { 'class': 'th' }, [ _('Table') ]),
E('th', { 'class': 'th' }, [ _('Protocol') ]), E('th', { 'class': 'th' }, [ _('Protocol') ])
])
]);
var rule6tbl = E('table', { 'class': 'table' }, [
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th' }, [ _('Priority') ]),
E('th', { 'class': 'th' }, [ _('Rule') ])
]) ])
]); ]);
cbi_update_table(neigh4tbl, this.parseNeigh(ip4neigh, networks, false)); cbi_update_table(neigh4tbl, this.parseNeigh(ip4neigh, networks, false));
cbi_update_table(route4tbl, this.parseRoute(ip4route, networks, false)); cbi_update_table(route4tbl, this.parseRoute(ip4route, networks, false));
cbi_update_table(rule4tbl, this.parseRule(ip4rule, networks, false));
cbi_update_table(neigh6tbl, this.parseNeigh(ip6neigh, networks, true)); cbi_update_table(neigh6tbl, this.parseNeigh(ip6neigh, networks, true));
cbi_update_table(route6tbl, this.parseRoute(ip6route, networks, true)); cbi_update_table(route6tbl, this.parseRoute(ip6route, networks, true));
cbi_update_table(rule6tbl, this.parseRule(ip6rule, networks, false));
return E([], [ var view = E([], [
E('h2', {}, [ _('Routes') ]), E('style', { 'type': 'text/css' }, [
'.cbi-tooltip-container, span.jump { border-bottom:1px dotted #00f;cursor:pointer }',
'ul { list-style:none }',
'.references { position:relative }',
'.references .cbi-tooltip { left:0!important;top:1.5em!important }',
'h4>span { font-size:90% }'
]),
E('h2', {}, [ _('Routing') ]),
E('p', {}, [ _('The following rules are currently active on this system.') ]), E('p', {}, [ _('The following rules are currently active on this system.') ]),
E('div', {}, [
E('div', { 'data-tab': 'ipv4routing', 'data-tab-title': _('IPv4 Routing') }, [
E('h3', {}, [ _('ARP') ]), E('h3', {}, [ _('ARP') ]),
neigh4tbl, neigh4tbl,
E('h3', {}, _('Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes')), E('h3', {}, _('Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes')),
route4tbl, route4tbl,
E('h3', {}, _('Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Rules')),
rule4tbl
]),
E('div', { 'data-tab': 'ipv6routing', 'data-tab-title': _('IPv6 Routing') }, [
E('h3', {}, [ _('IPv6 Neighbours') ]), E('h3', {}, [ _('IPv6 Neighbours') ]),
neigh6tbl, neigh6tbl,
E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes')), E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes')),
route6tbl route6tbl,
E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Rules')),
rule6tbl
])
])
]); ]);
ui.tabs.initTabGroup(view.lastElementChild.childNodes);
return view;
}, },
handleSaveApply: null, handleSaveApply: null,

View file

@ -11,21 +11,9 @@
} }
}, },
"admin/status/iptables": {
"title": "Firewall",
"order": 2,
"action": {
"type": "view",
"path": "status/iptables"
},
"depends": {
"acl": [ "luci-mod-status-firewall" ]
}
},
"admin/status/routes": { "admin/status/routes": {
"title": "Routes", "title": "Routing",
"order": 3, "order": 2,
"action": { "action": {
"type": "view", "type": "view",
"path": "status/routes" "path": "status/routes"
@ -35,6 +23,18 @@
} }
}, },
"admin/status/iptables": {
"title": "Firewall",
"order": 3,
"action": {
"type": "view",
"path": "status/iptables"
},
"depends": {
"acl": [ "luci-mod-status-firewall" ]
}
},
"admin/status/syslog": { "admin/status/syslog": {
"title": "System Log", "title": "System Log",
"order": 4, "order": 4,

View file

@ -44,11 +44,12 @@
}, },
"luci-mod-status-routes": { "luci-mod-status-routes": {
"description": "Grant access to the system route status", "description": "Grant access to routing status",
"read": { "read": {
"file": { "file": {
"/sbin/ip -[46] neigh show": [ "exec" ], "/sbin/ip -[46] neigh show": [ "exec" ],
"/sbin/ip -[46] route show table all": [ "exec" ] "/sbin/ip -[46] route show table all": [ "exec" ],
"/sbin/ip -[46] rule show": [ "exec" ]
}, },
"ubus": { "ubus": {
"file": [ "exec" ] "file": [ "exec" ]