'use strict';'require fs';'require rpc';'require tools.network as tn';'require ui';'require validation';'require view';const callNetworkInterfaceDump=rpc.declare({object:'network.interface',method:'dump',expect:{interface:[]}});const callUfpList=rpc.declare({object:'fingerprint',method:'fingerprint',expect:{'':{}}});function applyMask(addr,mask,v6){const words=v6?validation.parseIPv6(addr):validation.parseIPv4(addr);const bword=v6?0xffff:0xff;const bwlen=v6?16:8;if(!words||mask<0||mask>(v6?128:32))
return null;for(let i=0;i<words.length;i++){const b=Math.min(mask,bwlen);words[i]&=(bword<<(bwlen-b))&bword;mask-=b;}
return String.prototype.format.apply(v6?'%x:%x:%x:%x:%x:%x:%x:%x':'%d.%d.%d.%d',words);}
return view.extend({load(){return Promise.all([callNetworkInterfaceDump(),L.resolveDefault(fs.exec('/sbin/ip',['-4','-j','neigh','show']),{}),L.resolveDefault(fs.exec('/sbin/ip',['-4','-j','route','show','table','all']),{}),L.resolveDefault(fs.exec('/sbin/ip',['-4','-j','rule','show']),{}),L.resolveDefault(fs.exec('/sbin/ip',['-6','-j','neigh','show']),{}),L.resolveDefault(fs.exec('/sbin/ip',['-6','-j','route','show','table','all']),{}),L.resolveDefault(fs.exec('/sbin/ip',['-6','-j','rule','show']),{}),L.hasSystemFeature('ufpd')?callUfpList():null,]);},getNetworkByDevice(networks,dev,addr,mask,v6){const addr_arrays=['ipv4-address','ipv6-address','ipv6-prefix','ipv6-prefix-assignment','route'];let matching_iface=null;let matching_prefix=-1;for(const net of networks){if(!L.isObject(net)||(net.l3_device!==dev&&net.device!==dev))
continue;for(const key of addr_arrays){const list=net[key];if(!Array.isArray(list))continue;for(const{address,target,mask:cmp_mask}of list){const cmp_addr=address||target;if(!cmp_addr)continue;if(applyMask(cmp_addr,cmp_mask,v6)!==applyMask(addr,cmp_mask,v6)||mask<cmp_mask)
continue;if(cmp_mask>matching_prefix){matching_iface=net.interface;matching_prefix=cmp_mask;}}}}
return matching_iface;},parseJSON(string){try{return JSON.parse(string);}catch(e){return[];}},parseNeighbs(nbs,macs,networks,v6){const res=[];for(const n of this.parseJSON(nbs)){let vendor;if(n.dst.match(/^fe[89a-f][0-9a-f]:/))
continue;if(n.state.find(f=>{return f=='FAILED'}))
continue;for(let mac in macs){if(n?.lladdr===mac)
vendor=macs[mac].vendor;}
const net=this.getNetworkByDevice(networks,n?.dev,n?.dst,v6?128:32,v6);res.push([E('div',{'data-tooltip':JSON.stringify(n)},['#',n?.nud?`; ${_('NUD')}: ${n?.nud}`:'',n?.proxy===null?`; ${_('Proxy')}: ✅`:'',n?.nomaster===null?`;  ${_('No master')} : ✅`:'',n?.vrf?`; ${_('VRF')}: ${n?.vrf}`:'',]),n?.dst,n?.lladdr?.toUpperCase()+(vendor?` (${vendor})`:''),E('span',{'class':'ifacebadge'},[net?net:'(%s)'.format(n?.dev)]),]);}
return res;},parseRoutes(routes,networks,v6){const res=[];for(const rt of this.parseJSON(routes)){const dest=rt.dst=='default'?(v6?'::/0':'0.0.0.0/0'):rt.dst;if(dest=='fe80::/64'||dest=='ff00::/8')
continue;const[addr,bits=(v6?128:32)]=dest.split('/');const net=this.getNetworkByDevice(networks,rt.dev,addr,bits,v6);res.push([E('span',{'class':'ifacebadge'},[net?net:'(%s)'.format(rt.dev)]),dest,rt?.gateway||'-',rt?.prefsrc||rt?.from||'-',String(rt?.metric||'-'),rt?.table||'main',rt?.protocol,]);}
return res;},parseRules(rules){const r=[];for(const rl of this.parseJSON(rules)){r.push([E('div',{'data-tooltip':JSON.stringify(rl)},['#',rl?.not===null?`; ${_('Not')}: ✅`:'',rl?.nop===null?`; ${_('No-op')}: ✅`:'',rl?.l3mdev===null?`; ${_('L3Mdev')}: ✅`:'',rl?.fwmark?`; ${_('Fwmark')}:${rl?.fwmark}`:'',rl?.from?`; ${_('From')}:${rl?.from}`:'',rl?.to?`; ${_('To')}:${rl?.to}`:'',rl?.tos?`; ${_('ToS')}:${rl?.tos}`:'',rl?.dsfield?`; ${_('DSCP')}:${rl?.dsfield}`:'',rl?.uidrange?`; ${_('UID-range')}:${rl?.uidrange}`:'',rl?.goto?`; ${_('goto')}:${rl?.goto}`:'',rl?.nat?`; ${_('NAT')}:${rl?.nat}`:'',]),rl?.priority,rl?.iif?E('span',{'class':'ifacebadge'},[rl?.iif]):'-',rl?.src?(rl?.srclen?rl?.src+'/'+rl?.srclen:rl?.src):_('any'),rl?.sport||'-',rl?.action||'-',tn.protocols.find(f=>{return f.i==rl?.ipproto?.split?.('-')[1]})?.d||'-',rl?.oif?E('span',{'class':'ifacebadge'},[rl?.oif]):'-',rl?.dst?(rl?.dstlen?rl?.dst+'/'+rl?.dstlen:rl?.dst):_('any'),rl?.dport||'-',rl?.table||'-',]);}
return r;},render([networks,{stdout:ip4neighbs=''}={},{stdout:ip4routes=''}={},{stdout:ip4rules=''}={},{stdout:ip6neighbs=''}={},{stdout:ip6routes=''}={},{stdout:ip6rules=''}={},macdata,]){const device_title=_('Which is used to access this %s').format(_('Target'));const target_title=_('Network and its mask that define the size of the destination');const gateway_title=_('The address through which this %s is reachable').format(_('Target'));const metric_title=_('Quantifies the cost or distance to a destination in a way that allows routers to make informed decisions about the optimal path to forward data packets');const table_title=_('Common name or numeric ID of the %s in which this route is found').format(_('Table'));const proto_title=_('The routing protocol identifier of this route');const source_title=_('Network and its mask that define which source addresses use this route');const neigh4tbl=E('table',{'class':'table'},[E('tr',{'class':'tr table-titles'},[E('th',{'class':'th'},[_('Entry')]),E('th',{'class':'th'},[_('IP address')]),E('th',{'class':'th'},[_('MAC address')]),E('th',{'class':'th'},[_('Interface')]),])]);const route4tbl=E('table',{'class':'table'},[E('tr',{'class':'tr table-titles'},[E('th',{'class':'th','title':device_title},[_('Device')]),E('th',{'class':'th','title':target_title},[_('Target')]),E('th',{'class':'th','title':gateway_title},[_('Gateway')]),E('th',{'class':'th','title':source_title},[_('Source')]),E('th',{'class':'th','title':metric_title},[_('Metric')]),E('th',{'class':'th','title':table_title},[_('Table')]),E('th',{'class':'th','title':proto_title},[_('Protocol')]),])]);const rule4tbl=E('table',{'class':'table'},[E('tr',{'class':'tr table-titles'},[E('th',{'class':'th'},[_('Rule')]),E('th',{'class':'th'},[_('Priority')]),E('th',{'class':'th'},[_('Ingress')]),E('th',{'class':'th'},[_('Source')]),E('th',{'class':'th'},[_('Src Port')]),E('th',{'class':'th'},[_('Action')]),E('th',{'class':'th'},[_('IP Protocol')]),E('th',{'class':'th'},[_('Egress')]),E('th',{'class':'th'},[_('Destination')]),E('th',{'class':'th'},[_('Dest Port')]),E('th',{'class':'th'},[_('Table')]),])]);const neigh6tbl=E('table',{'class':'table'},[E('tr',{'class':'tr table-titles'},[E('th',{'class':'th'},[_('Entry')]),E('th',{'class':'th'},[_('IP address')]),E('th',{'class':'th'},[_('MAC address')]),E('th',{'class':'th'},[_('Interface')]),])]);const route6tbl=E('table',{'class':'table'},[E('tr',{'class':'tr table-titles'},[E('th',{'class':'th','title':device_title},[_('Device')]),E('th',{'class':'th','title':target_title},[_('Target')]),E('th',{'class':'th','title':gateway_title},[_('Gateway')]),E('th',{'class':'th','title':source_title},[_('Source')]),E('th',{'class':'th','title':metric_title},[_('Metric')]),E('th',{'class':'th','title':table_title},[_('Table')]),E('th',{'class':'th','title':proto_title},[_('Protocol')]),])]);const rule6tbl=E('table',{'class':'table'},[E('tr',{'class':'tr table-titles'},[E('th',{'class':'th'},[_('Rule')]),E('th',{'class':'th'},[_('Priority')]),E('th',{'class':'th'},[_('Ingress')]),E('th',{'class':'th'},[_('Source')]),E('th',{'class':'th'},[_('Src Port')]),E('th',{'class':'th'},[_('Action')]),E('th',{'class':'th'},[_('IP Protocol')]),E('th',{'class':'th'},[_('Egress')]),E('th',{'class':'th'},[_('Destination')]),E('th',{'class':'th'},[_('Dest Port')]),E('th',{'class':'th'},[_('Table')]),])]);cbi_update_table(neigh4tbl,this.parseNeighbs(ip4neighbs,macdata,networks,false),E('em',_('No entries available')));cbi_update_table(route4tbl,this.parseRoutes(ip4routes,networks,false),E('em',_('No entries available')));cbi_update_table(rule4tbl,this.parseRules(ip4rules),E('em',_('No entries available')));cbi_update_table(neigh6tbl,this.parseNeighbs(ip6neighbs,macdata,networks,true),E('em',_('No entries available')));cbi_update_table(route6tbl,this.parseRoutes(ip6routes,networks,true),E('em',_('No entries available')));cbi_update_table(rule6tbl,this.parseRules(ip6rules),E('em',_('No entries available')));const view=E([],[E('h2',{},[_('Routing')]),E('p',{},[_('The following rules are currently active on this system.')]),E('div',{},[E('div',{'class':'cbi-section','data-tab':'ipv4routing','data-tab-title':_('IPv4 Routing')},[E('h3',{},[_('IPv4 Neighbours')]),neigh4tbl,E('h3',{},[_('Active IPv4 Routes')]),route4tbl,E('h3',{},[_('Active IPv4 Rules')]),rule4tbl,]),E('div',{'class':'cbi-section','data-tab':'ipv6routing','data-tab-title':_('IPv6 Routing')},[E('h3',{},[_('IPv6 Neighbours')]),neigh6tbl,E('h3',{},[_('Active IPv6 Routes')]),route6tbl,E('h3',{},[_('Active IPv6 Rules')]),rule6tbl,])])]);ui.tabs.initTabGroup(view.lastElementChild.childNodes);return view;},handleSaveApply:null,handleSave:null,handleReset:null});