'use strict';'require fs';'require rpc';'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','neigh','show']),{stdout:''}),L.resolveDefault(fs.exec('/sbin/ip',['-4','route','show','table','all']),{stdout:''}),L.resolveDefault(fs.exec('/sbin/ip',['-4','rule','show']),{stdout:''}),L.resolveDefault(fs.exec('/sbin/ip',['-6','neigh','show']),{stdout:''}),L.resolveDefault(fs.exec('/sbin/ip',['-6','route','show','table','all']),{stdout:''}),L.resolveDefault(fs.exec('/sbin/ip',['-6','rule','show']),{stdout:''}),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;},parseNeighbs(nbs,macs,networks,v6){if(!nbs)return[];const res=[];for(const line of nbs.trim().split(/\n/)){const[,addr=null,f=[],state=null]=line.match(/^([0-9a-f:.]+) (.+) (\S+) *$/);const flags=f?.trim?.().split?.(/\s+/);let vendor;if(!addr||!state||addr.match(/^fe[89a-f][0-9a-f]:/))
continue;for(let j=0;j<flags.length;j+=2)
flags[flags[j]]=flags[j+1];if(!flags.lladdr)
continue;for(let mac in macs){if(flags.lladdr===mac)
vendor=macs[mac].vendor;}
const net=this.getNetworkByDevice(networks,flags.dev,addr,v6?128:32,v6);res.push([addr,vendor?flags.lladdr.toUpperCase()+` (${vendor})`:flags.lladdr.toUpperCase(),E('span',{'class':'ifacebadge'},[net?net:'(%s)'.format(flags.dev)]),]);}
return res;},parseRoutes(routes,networks,v6){if(!routes)return[];const res=[];for(const line of routes.trim().split(/\n/)){const[,type='unicast',d,f=[]]=line.match(/^(?:([a-z_]+|\d+) )?(default|[0-9a-f:./]+) (.+)$/);const dest=d=='default'?(v6?'::/0':'0.0.0.0/0'):d;const flags=f?.trim?.().split?.(/\s+/);if(!dest||type!='unicast'||dest=='fe80::/64'||dest=='ff00::/8')
continue;for(let j=0;j<flags.length;j+=2)
flags[flags[j]]=flags[j+1];const[addr,bits=(v6?128:32)]=dest.split('/');const net=this.getNetworkByDevice(networks,flags.dev,addr,bits,v6);res.push([E('span',{'class':'ifacebadge'},[net?net:'(%s)'.format(flags.dev)]),dest,flags.via||'-',flags.src||flags.from||'-',String(flags.metric||0),flags.table||'main',flags.proto,]);}
return res;},parseRules:rules=>rules?.trim()?.split('\n')?.map(l=>{const[,prio=null,rule=null]=l.match(/^(\d+):\s+(.+)$/)||[];return[prio,rule];}),render([networks,{stdout:ip4neigh=''}={},{stdout:ip4route=''}={},{stdout:ip4rule=''}={},{stdout:ip6neigh=''}={},{stdout:ip6route=''}={},{stdout:ip6rule=''}={},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'},[_('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'},[_('Priority')]),E('th',{'class':'th'},[_('Rule')]),])]);const neigh6tbl=E('table',{'class':'table'},[E('tr',{'class':'tr table-titles'},[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'},[_('Priority')]),E('th',{'class':'th'},[_('Rule')]),])]);cbi_update_table(neigh4tbl,this.parseNeighbs(ip4neigh,macdata,networks,false),E('em',_('No entries available')));cbi_update_table(route4tbl,this.parseRoutes(ip4route,networks,false),E('em',_('No entries available')));cbi_update_table(rule4tbl,this.parseRules(ip4rule),E('em',_('No entries available')));cbi_update_table(neigh6tbl,this.parseNeighbs(ip6neigh,macdata,networks,true),E('em',_('No entries available')));cbi_update_table(route6tbl,this.parseRoutes(ip6route,networks,true),E('em',_('No entries available')));cbi_update_table(rule6tbl,this.parseRules(ip6rule),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});