Merge pull request #3713 from TDT-AG/pr/20200303-luci-mod-network
[project/luci.git] / modules / luci-mod-status / htdocs / luci-static / resources / view / status / routes.js
1 'use strict';
2 'require fs';
3 'require rpc';
4 'require validation';
5
6 var callNetworkInterfaceDump = rpc.declare({
7 object: 'network.interface',
8 method: 'dump',
9 expect: { interface: [] }
10 });
11
12 function applyMask(addr, mask, v6) {
13 var words = v6 ? validation.parseIPv6(addr) : validation.parseIPv4(addr);
14
15 if (!words || mask < 0 || mask > (v6 ? 128 : 32))
16 return null;
17
18 for (var i = 0; i < words.length; i++) {
19 var b = Math.min(mask, v6 ? 16 : 8);
20 words[i] &= ((1 << b) - 1);
21 mask -= b;
22 }
23
24 return String.prototype.format.apply(
25 v6 ? '%x:%x:%x:%x:%x:%x:%x:%x' : '%d.%d.%d.%d', words);
26 }
27
28 return L.view.extend({
29 load: function() {
30 return Promise.all([
31 callNetworkInterfaceDump(),
32 L.resolveDefault(fs.exec('/sbin/ip', [ '-4', 'neigh', 'show' ]), {}),
33 L.resolveDefault(fs.exec('/sbin/ip', [ '-4', 'route', 'show', 'table', 'all' ]), {}),
34 L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'neigh', 'show' ]), {}),
35 L.resolveDefault(fs.exec('/sbin/ip', [ '-6', 'route', 'show', 'table', 'all' ]), {})
36 ]);
37 },
38
39 getNetworkByDevice(networks, dev, addr, mask, v6) {
40 var addr_arrays = [ 'ipv4-address', 'ipv6-address', 'ipv6-prefix', 'ipv6-prefix-assignment', 'route' ],
41 matching_iface = null,
42 matching_prefix = -1;
43
44 for (var i = 0; i < networks.length; i++) {
45 if (!L.isObject(networks[i]))
46 continue;
47
48 if (networks[i].l3_device != dev && networks[i].device != dev)
49 continue;
50
51 for (var j = 0; j < addr_arrays.length; j++) {
52 var addr_list = networks[i][addr_arrays[j]];
53
54 if (!Array.isArray(addr_list) || addr_list.length == 0)
55 continue;
56
57 for (var k = 0; k < addr_list.length; k++) {
58 var cmp_addr = addr_list[k].address || addr_list[k].target,
59 cmp_mask = addr_list[k].mask;
60
61 if (cmp_addr == null)
62 continue;
63
64 var addr1 = applyMask(cmp_addr, cmp_mask, v6),
65 addr2 = applyMask(addr, cmp_mask, v6);
66
67 if (addr1 != addr2 || mask < cmp_mask)
68 continue;
69
70 if (cmp_mask > matching_prefix) {
71 matching_iface = networks[i].interface;
72 matching_prefix = cmp_mask;
73 }
74 }
75 }
76 }
77
78 return matching_iface;
79 },
80
81 parseNeigh: function(s, networks, v6) {
82 var lines = s.trim().split(/\n/),
83 res = [];
84
85 for (var i = 0; i < lines.length; i++) {
86 var m = lines[i].match(/^([0-9a-f:.]+) (.+) (\S+)$/),
87 addr = m ? m[1] : null,
88 flags = m ? m[2].trim().split(/\s+/) : [],
89 state = (m ? m[3] : null) || 'FAILED';
90
91 if (!addr || state == 'FAILED' || addr.match(/^fe[89a-f][0-9a-f]:/))
92 continue;
93
94 for (var j = 0; j < flags.length; j += 2)
95 flags[flags[j]] = flags[j + 1];
96
97 if (!flags.lladdr)
98 continue;
99
100 var net = this.getNetworkByDevice(networks, flags.dev, addr, v6 ? 128 : 32, v6);
101
102 res.push([
103 addr,
104 flags.lladdr.toUpperCase(),
105 E('span', { 'class': 'ifacebadge' }, [ net ? net : '(%s)'.format(flags.dev) ])
106 ]);
107 }
108
109 return res;
110 },
111
112 parseRoute: function(s, networks, v6) {
113 var lines = s.trim().split(/\n/),
114 res = [];
115
116 for (var i = 0; i < lines.length; i++) {
117 var m = lines[i].match(/^(?:([a-z_]+|\d+) )?(default|[0-9a-f:.\/]+) (.+)$/),
118 type = (m ? m[1] : null) || 'unicast',
119 dest = m ? (m[2] == 'default' ? (v6 ? '::/0' : '0.0.0.0/0') : m[2]) : null,
120 flags = m ? m[3].trim().split(/\s+/) : [];
121
122 if (!dest || type != 'unicast' || dest == 'fe80::/64' || dest == 'ff00::/8')
123 continue;
124
125 for (var j = 0; j < flags.length; j += 2)
126 flags[flags[j]] = flags[j + 1];
127
128 var addr = dest.split('/'),
129 bits = (addr[1] != null) ? +addr[1] : (v6 ? 128 : 32),
130 net = this.getNetworkByDevice(networks, flags.dev, addr[0], bits, v6);
131
132 res.push([
133 E('span', { 'class': 'ifacebadge' }, [ net ? net : '(%s)'.format(flags.dev) ]),
134 dest,
135 (v6 ? flags.from : flags.via) || '-',
136 String(flags.metric || 0),
137 flags.table || 'main'
138 ]);
139 }
140
141 return res;
142 },
143
144 render: function(data) {
145 var networks = data[0],
146 ip4neigh = data[1].stdout || '',
147 ip4route = data[2].stdout || '',
148 ip6neigh = data[3].stdout || '',
149 ip6route = data[4].stdout || '';
150
151 var neigh4tbl = E('div', { 'class': 'table' }, [
152 E('div', { 'class': 'tr table-titles' }, [
153 E('div', { 'class': 'th' }, [ _('IPv4-Address') ]),
154 E('div', { 'class': 'th' }, [ _('MAC-Address') ]),
155 E('div', { 'class': 'th' }, [ _('Interface') ])
156 ])
157 ]);
158
159 var route4tbl = E('div', { 'class': 'table' }, [
160 E('div', { 'class': 'tr table-titles' }, [
161 E('div', { 'class': 'th' }, [ _('Network') ]),
162 E('div', { 'class': 'th' }, [ _('Target') ]),
163 E('div', { 'class': 'th' }, [ _('IPv4-Gateway') ]),
164 E('div', { 'class': 'th' }, [ _('Metric') ]),
165 E('div', { 'class': 'th' }, [ _('Table') ])
166 ])
167 ]);
168
169 var neigh6tbl = E('div', { 'class': 'table' }, [
170 E('div', { 'class': 'tr table-titles' }, [
171 E('div', { 'class': 'th' }, [ _('IPv6-Address') ]),
172 E('div', { 'class': 'th' }, [ _('MAC-Address') ]),
173 E('div', { 'class': 'th' }, [ _('Interface') ])
174 ])
175 ]);
176
177 var route6tbl = E('div', { 'class': 'table' }, [
178 E('div', { 'class': 'tr table-titles' }, [
179 E('div', { 'class': 'th' }, [ _('Network') ]),
180 E('div', { 'class': 'th' }, [ _('Target') ]),
181 E('div', { 'class': 'th' }, [ _('Source') ]),
182 E('div', { 'class': 'th' }, [ _('Metric') ]),
183 E('div', { 'class': 'th' }, [ _('Table') ])
184 ])
185 ]);
186
187 cbi_update_table(neigh4tbl, this.parseNeigh(ip4neigh, networks, false));
188 cbi_update_table(route4tbl, this.parseRoute(ip4route, networks, false));
189 cbi_update_table(neigh6tbl, this.parseNeigh(ip6neigh, networks, true));
190 cbi_update_table(route6tbl, this.parseRoute(ip6route, networks, true));
191
192 return E([], [
193 E('h2', {}, [ _('Routes') ]),
194 E('p', {}, [ _('The following rules are currently active on this system.') ]),
195
196 E('h3', {}, [ _('ARP') ]),
197 neigh4tbl,
198
199 E('h3', {}, _('Active <abbr title="Internet Protocol Version 4">IPv4</abbr>-Routes')),
200 route4tbl,
201
202 E('h3', {}, [ _('IPv6 Neighbours') ]),
203 neigh6tbl,
204
205 E('h3', {}, _('Active <abbr title="Internet Protocol Version 6">IPv6</abbr>-Routes')),
206 route6tbl
207 ]);
208 },
209
210 handleSaveApply: null,
211 handleSave: null,
212 handleReset: null
213 });
214