luci-mod-status: status/routing support for pbr 5235/head
authorVladislav Grigoryev <vg.aetera@gmail.com>
Thu, 5 Aug 2021 00:50:37 +0000 (03:50 +0300)
committerVladislav Grigoryev <vg.aetera@gmail.com>
Sat, 7 Aug 2021 07:51:38 +0000 (10:51 +0300)
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>
modules/luci-mod-status/htdocs/luci-static/resources/view/status/routes.js
modules/luci-mod-status/root/usr/share/luci/menu.d/luci-mod-status.json
modules/luci-mod-status/root/usr/share/rpcd/acl.d/luci-mod-status.json

index 6051b74c3be2f360d4183b35a3d2d548e1ac915d..91adae2434e00b2561ee362df0d0a740b86fd001 100644 (file)
@@ -3,6 +3,7 @@
 'require fs';
 'require rpc';
 'require validation';
+'require ui';
 
 var callNetworkInterfaceDump = rpc.declare({
        object: 'network.interface',
@@ -32,8 +33,10 @@ return view.extend({
                        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', [ '-4', 'rule', '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;
        },
 
+       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) {
                var networks = data[0],
                    ip4neigh = data[1].stdout || '',
                    ip4route = data[2].stdout || '',
-                   ip6neigh = data[3].stdout || '',
-                   ip6route = data[4].stdout || '';
+                   ip4rule = data[3].stdout || '',
+                   ip6neigh = data[4].stdout || '',
+                   ip6route = data[5].stdout || '',
+                   ip6rule = data[6].stdout || '';
 
                var neigh4tbl = E('table', { 'class': 'table' }, [
                        E('tr', { 'class': 'tr table-titles' }, [
@@ -165,7 +188,14 @@ return view.extend({
                                E('th', { 'class': 'th' }, [ _('IPv4 gateway') ]),
                                E('th', { 'class': 'th' }, [ _('Metric') ]),
                                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' }, [ _('Metric') ]),
                                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(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(route6tbl, this.parseRoute(ip6route, networks, true));
-
-               return E([], [
-                       E('h2', {}, [ _('Routes') ]),
+               cbi_update_table(rule6tbl, this.parseRule(ip6rule, networks, false));
+
+               var view = E([], [
+                       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('div', {}, [
+                               E('div', { 'data-tab': 'ipv4routing', 'data-tab-title': _('IPv4 Routing') }, [
+                                       E('h3', {}, [ _('ARP') ]),
+                                       neigh4tbl,
+
+                                       E('h3', {}, _('Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes')),
+                                       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') ]),
+                                       neigh6tbl,
+
+                                       E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes')),
+                                       route6tbl,
+
+                                       E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Rules')),
+                                       rule6tbl
+                               ])
+                       ])
+               ]);
 
-                       E('h3', {}, [ _('ARP') ]),
-                       neigh4tbl,
-
-                       E('h3', {}, _('Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes')),
-                       route4tbl,
-
-                       E('h3', {}, [ _('IPv6 Neighbours') ]),
-                       neigh6tbl,
+               ui.tabs.initTabGroup(view.lastElementChild.childNodes);
 
-                       E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes')),
-                       route6tbl
-               ]);
+               return view;
        },
 
        handleSaveApply: null,
index 0f066e67ad15a0ad7af36ed6e6a3cddbbdda9199..1e6556bab2843951616ba25f565c0b968a802b5c 100644 (file)
                }
        },
 
-       "admin/status/iptables": {
-               "title": "Firewall",
+       "admin/status/routes": {
+               "title": "Routing",
                "order": 2,
                "action": {
                        "type": "view",
-                       "path": "status/iptables"
+                       "path": "status/routes"
                },
                "depends": {
-                       "acl": [ "luci-mod-status-firewall" ]
+                       "acl": [ "luci-mod-status-routes" ]
                }
        },
 
-       "admin/status/routes": {
-               "title": "Routes",
+       "admin/status/iptables": {
+               "title": "Firewall",
                "order": 3,
                "action": {
                        "type": "view",
-                       "path": "status/routes"
+                       "path": "status/iptables"
                },
                "depends": {
-                       "acl": [ "luci-mod-status-routes" ]
+                       "acl": [ "luci-mod-status-firewall" ]
                }
        },
 
index 3e7d823014361b97ad962c299f190a61947c7f5a..63ff83132d98b843907223b538a71dab0d2eec57 100644 (file)
        },
 
        "luci-mod-status-routes": {
-               "description": "Grant access to the system route status",
+               "description": "Grant access to routing status",
                "read": {
                        "file": {
                                "/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": {
                                "file": [ "exec" ]