31af6f460c0315955d33c6477c8ba0968ebb146e
11 return typeof(v
) === 'string' && v
!== '';
14 function resolveVLANChain(ifname
, bridges
, mapping
)
16 while (!mapping
[ifname
]) {
17 var m
= ifname
.match(/^(.+)\.([^.]+)$/);
23 if (bridges
[m
[1]].vlan_filtering
)
24 mapping
[ifname
] = bridges
[m
[1]].vlans
[m
[2]];
26 mapping
[ifname
] = bridges
[m
[1]].ports
;
28 else if (/^[0-9]{1,4}$/.test(m
[2]) && m
[2] <= 4095) {
29 mapping
[ifname
] = [ m
[1] ];
39 function buildVLANMappings(mapping
)
41 var bridge_vlans
= uci
.sections('network', 'bridge-vlan'),
42 vlan_devices
= uci
.sections('network', 'device'),
43 interfaces
= uci
.sections('network', 'interface'),
46 /* find bridge VLANs */
47 for (var i
= 0, s
; (s
= bridge_vlans
[i
]) != null; i
++) {
48 if (!isString(s
.device
) || !/^[0-9]{1,4}$/.test(s
.vlan
) || +s
.vlan
> 4095)
51 var aliases
= L
.toArray(s
.alias
),
52 ports
= L
.toArray(s
.ports
),
53 br
= bridges
[s
.device
] = (bridges
[s
.device
] || { ports
: [], vlans
: {}, vlan_filtering
: true });
55 br
.vlans
[s
.vlan
] = [];
57 for (var j
= 0; j
< ports
.length
; j
++) {
58 var port
= ports
[j
].replace(/:[ut*]+$/, '');
60 if (br
.ports
.indexOf(port
) === -1)
63 br
.vlans
[s
.vlan
].push(port
);
66 for (var j
= 0; j
< aliases
.length
; j
++)
67 if (aliases
[j
] != s
.vlan
)
68 br
.vlans
[aliases
[j
]] = br
.vlans
[s
.vlan
];
71 /* find bridges, VLAN devices */
72 for (var i
= 0, s
; (s
= vlan_devices
[i
]) != null; i
++) {
73 if (s
.type
== 'bridge') {
74 if (!isString(s
.name
))
77 var ports
= L
.toArray(s
.ports
),
78 br
= bridges
[s
.name
] || (bridges
[s
.name
] = { ports
: [], vlans
: {}, vlan_filtering
: false });
80 if (s
.vlan_filtering
== '0')
81 br
.vlan_filtering
= false;
82 else if (s
.vlan_filtering
== '1')
83 br
.vlan_filtering
= true;
85 for (var j
= 0; j
< ports
.length
; j
++)
86 if (br
.ports
.indexOf(ports
[j
]) === -1)
87 br
.ports
.push(ports
[j
]);
89 mapping
[s
.name
] = br
.ports
;
91 else if (s
.type
== '8021q' || s
.type
== '8021ad') {
92 if (!isString(s
.name
) || !isString(s
.vid
) || !isString(s
.ifname
))
95 /* parent device is a bridge */
96 if (bridges
[s
.ifname
]) {
97 /* parent bridge is VLAN enabled, device refers to VLAN ports */
98 if (bridges
[s
.ifname
].vlan_filtering
)
99 mapping
[s
.name
] = bridges
[s
.ifname
].vlans
[s
.vid
];
101 /* parent bridge is not VLAN enabled, device refers to all bridge ports */
103 mapping
[s
.name
] = bridges
[s
.ifname
].ports
;
106 /* parent is a simple netdev */
108 mapping
[s
.name
] = [ s
.ifname
];
111 resolveVLANChain(s
.ifname
, bridges
, mapping
);
115 /* resolve VLAN tagged interfaces in bridge ports */
116 for (var brname
in bridges
) {
117 for (var i
= 0; i
< bridges
[brname
].ports
.length
; i
++)
118 resolveVLANChain(bridges
[brname
].ports
[i
], bridges
, mapping
);
120 for (var vid
in bridges
[brname
].vlans
)
121 for (var i
= 0; i
< bridges
[brname
].vlans
[vid
].length
; i
++)
122 resolveVLANChain(bridges
[brname
].vlans
[vid
][i
], bridges
, mapping
);
125 /* find implicit VLAN devices */
126 for (var i
= 0, s
; (s
= interfaces
[i
]) != null; i
++) {
127 if (!isString(s
.device
))
130 resolveVLANChain(s
.device
, bridges
, mapping
);
134 function resolveVLANPorts(ifname
, mapping
)
139 for (var i
= 0; i
< mapping
[ifname
].length
; i
++)
140 ports
.push
.apply(ports
, resolveVLANPorts(mapping
[ifname
][i
], mapping
));
144 return ports
.sort(L
.naturalCompare
);
147 function buildInterfaceMapping(zones
, networks
) {
152 buildVLANMappings(vlanmap
);
154 for (var i
= 0; i
< networks
.length
; i
++) {
155 var l3dev
= networks
[i
].getDevice();
160 var ports
= resolveVLANPorts(l3dev
.getName(), vlanmap
);
162 for (var j
= 0; j
< ports
.length
; j
++) {
163 portmap
[ports
[j
]] = portmap
[ports
[j
]] || { networks
: [], zones
: [] };
164 portmap
[ports
[j
]].networks
.push(networks
[i
]);
167 netmap
[networks
[i
].getName()] = networks
[i
];
170 for (var i
= 0; i
< zones
.length
; i
++) {
171 var networknames
= zones
[i
].getNetworks();
173 for (var j
= 0; j
< networknames
.length
; j
++) {
174 if (!netmap
[networknames
[j
]])
177 var l3dev
= netmap
[networknames
[j
]].getDevice();
182 var ports
= resolveVLANPorts(l3dev
.getName(), vlanmap
);
184 for (var k
= 0; k
< ports
.length
; k
++) {
185 portmap
[ports
[k
]] = portmap
[ports
[k
]] || { networks
: [], zones
: [] };
187 if (portmap
[ports
[k
]].zones
.indexOf(zones
[i
]) === -1)
188 portmap
[ports
[k
]].zones
.push(zones
[i
]);
196 function formatSpeed(speed
, duplex
) {
197 if (speed
&& duplex
) {
198 var d
= (duplex
== 'half') ? '\u202f(H)' : '',
199 e
= E('span', { 'title': _('Speed: %d Mibit/s, Duplex: %s').format(speed
, duplex
) });
202 case 10: e
.innerText
= '10\u202fM' + d
; break;
203 case 100: e
.innerText
= '100\u202fM' + d
; break;
204 case 1000: e
.innerText
= '1\u202fGbE' + d
; break;
205 case 2500: e
.innerText
= '2.5\u202fGbE'; break;
206 case 5000: e
.innerText
= '5\u202fGbE'; break;
207 case 10000: e
.innerText
= '10\u202fGbE'; break;
208 case 25000: e
.innerText
= '25\u202fGbE'; break;
209 case 40000: e
.innerText
= '40\u202fGbE'; break;
210 default: e
.innerText
= '%d\u202fMbE%s'.format(speed
, d
);
219 function formatStats(portdev
) {
220 var stats
= portdev
._devstate('stats');
222 return ui
.itemlist(E('span'), [
223 _('Received bytes'), '%1024mB'.format(stats
.rx_bytes
),
224 _('Received packets'), '%1000mPkts.'.format(stats
.rx_packets
),
225 _('Received multicast'), '%1000mPkts.'.format(stats
.multicast
),
226 _('Receive errors'), '%1000mPkts.'.format(stats
.rx_errors
),
227 _('Receive dropped'), '%1000mPkts.'.format(stats
.rx_dropped
),
229 _('Transmitted bytes'), '%1024mB'.format(stats
.tx_bytes
),
230 _('Transmitted packets'), '%1000mPkts.'.format(stats
.tx_packets
),
231 _('Transmit errors'), '%1000mPkts.'.format(stats
.tx_errors
),
232 _('Transmit dropped'), '%1000mPkts.'.format(stats
.tx_dropped
),
234 _('Collisions seen'), stats
.collisions
238 function renderNetworkBadge(network
, zonename
) {
239 var l3dev
= network
.getDevice();
240 var span
= E('span', { 'class': 'ifacebadge', 'style': 'margin:.125em 0' }, [
242 'class': 'zonebadge',
243 'title': zonename
? _('Part of zone %q').format(zonename
) : _('No zone assigned'),
244 'style': firewall
.getZoneColorStyle(zonename
)
246 '\u202f', network
.getName(), ': '
250 span
.appendChild(E('img', {
251 'title': l3dev
.getI18n(),
252 'src': L
.resource('icons/%s%s.png'.format(l3dev
.getType(), l3dev
.isUp() ? '' : '_disabled'))
255 span
.appendChild(E('em', _('(no interfaces attached)')));
260 function renderNetworksTooltip(pmap
) {
264 for (var i
= 0; pmap
&& i
< pmap
.zones
.length
; i
++) {
265 var networknames
= pmap
.zones
[i
].getNetworks();
267 for (var k
= 0; k
< networknames
.length
; k
++)
268 zmap
[networknames
[k
]] = pmap
.zones
[i
].getName();
271 for (var i
= 0; pmap
&& i
< pmap
.networks
.length
; i
++)
272 res
.push(E('br'), renderNetworkBadge(pmap
.networks
[i
], zmap
[pmap
.networks
[i
].getName()]));
275 res
[0] = N_((res
.length
- 1) / 2, 'Part of network:', 'Part of networks:');
277 res
[0] = _('Port is not part of any network');
282 return baseclass
.extend({
283 title
: _('Port status'),
287 L
.resolveDefault(fs
.read('/etc/board.json'), '{}'),
289 network
.getNetworks(),
294 render: function(data
) {
295 if (L
.hasSystemFeature('swconfig'))
298 var board
= JSON
.parse(data
[0]),
300 port_map
= buildInterfaceMapping(data
[1], data
[2]);
302 if (L
.isObject(board
) && L
.isObject(board
.network
)) {
303 for (var k
= 'lan'; k
!= null; k
= (k
== 'lan') ? 'wan' : null) {
304 if (!L
.isObject(board
.network
[k
]))
307 if (Array
.isArray(board
.network
[k
].ports
))
308 for (let i
= 0; i
< board
.network
[k
].ports
.length
; i
++)
311 device
: board
.network
[k
].ports
[i
],
312 netdev
: network
.instantiateDevice(board
.network
[k
].ports
[i
])
314 else if (typeof(board
.network
[k
].device
) == 'string')
317 device
: board
.network
[k
].device
,
318 netdev
: network
.instantiateDevice(board
.network
[k
].device
)
323 known_ports
.sort(function(a
, b
) {
324 return L
.naturalCompare(a
.device
, b
.device
);
327 return E('div', { 'style': 'display:grid;grid-template-columns:repeat(auto-fit, minmax(70px, 1fr));margin-bottom:1em' }, known_ports
.map(function(port
) {
328 var speed
= port
.netdev
.getSpeed(),
329 duplex
= port
.netdev
.getDuplex(),
330 pmap
= port_map
[port
.netdev
.getName()],
331 pzones
= (pmap
&& pmap
.zones
.length
) ? pmap
.zones
.sort(function(a
, b
) { return L
.naturalCompare(a
.getName(), b
.getName()) }) : [ null ];
333 return E('div', { 'class': 'ifacebox', 'style': 'margin:.25em;min-width:70px;max-width:100px' }, [
334 E('div', { 'class': 'ifacebox-head', 'style': 'font-weight:bold' }, [ port
.netdev
.getName() ]),
335 E('div', { 'class': 'ifacebox-body' }, [
336 E('img', { 'src': L
.resource('icons/port_%s.png').format((speed
&& duplex
) ? 'up' : 'down') }),
338 formatSpeed(speed
, duplex
)
340 E('div', { 'class': 'ifacebox-head cbi-tooltip-container', 'style': 'display:flex' }, [
341 E([], pzones
.map(function(zone
) {
343 'class': 'zonebadge',
344 'style': 'cursor:help;flex:1;height:3px;' + firewall
.getZoneColorStyle(zone
)
347 E('span', { 'class': 'cbi-tooltip left' }, [ renderNetworksTooltip(pmap
) ])
349 E('div', { 'class': 'ifacebox-body' }, [
350 E('div', { 'class': 'cbi-tooltip-container', 'style': 'text-align:left;font-size:80%' }, [
351 '\u25b2\u202f%1024.1mB'.format(port
.netdev
.getTXBytes()),
353 '\u25bc\u202f%1024.1mB'.format(port
.netdev
.getRXBytes()),
354 E('span', { 'class': 'cbi-tooltip' }, formatStats(port
.netdev
))