'use strict';'require uci';'require rpc';'require validation';'require baseclass';'require firewall';var proto_errors={CONNECT_FAILED:_('Connection attempt failed'),INVALID_ADDRESS:_('IP address is invalid'),INVALID_GATEWAY:_('Gateway address is invalid'),INVALID_LOCAL_ADDRESS:_('Local IP address is invalid'),MISSING_ADDRESS:_('IP address is missing'),MISSING_PEER_ADDRESS:_('Peer address is missing'),NO_DEVICE:_('Network device is not present'),NO_IFACE:_('Unable to determine device name'),NO_IFNAME:_('Unable to determine device name'),NO_WAN_ADDRESS:_('Unable to determine external IP address'),NO_WAN_LINK:_('Unable to determine upstream interface'),PEER_RESOLVE_FAIL:_('Unable to resolve peer host name'),PIN_FAILED:_('PIN code rejected')};var iface_patterns_ignore=[/^wmaster\d+/,/^wifi\d+/,/^hwsim\d+/,/^imq\d+/,/^ifb\d+/,/^mon\.wlan\d+/,/^sit\d+/,/^gre\d+/,/^gretap\d+/,/^ip6gre\d+/,/^ip6tnl\d+/,/^tunl\d+/,/^lo$/];var iface_patterns_wireless=[/^wlan\d+/,/^wl\d+/,/^ath\d+/,/^\w+\.network\d+/];var iface_patterns_virtual=[];var callLuciNetworkDevices=rpc.declare({object:'luci-rpc',method:'getNetworkDevices',expect:{'':{}}});var callLuciWirelessDevices=rpc.declare({object:'luci-rpc',method:'getWirelessDevices',expect:{'':{}}});var callLuciBoardJSON=rpc.declare({object:'luci-rpc',method:'getBoardJSON'});var callLuciHostHints=rpc.declare({object:'luci-rpc',method:'getHostHints',expect:{'':{}}});var callIwinfoAssoclist=rpc.declare({object:'iwinfo',method:'assoclist',params:['device','mac'],expect:{results:[]}});var callIwinfoScan=rpc.declare({object:'iwinfo',method:'scan',params:['device'],nobatch:true,expect:{results:[]}});var callNetworkInterfaceDump=rpc.declare({object:'network.interface',method:'dump',expect:{'interface':[]}});var callNetworkProtoHandlers=rpc.declare({object:'network',method:'get_proto_handlers',expect:{'':{}}});var _init=null,_state=null,_protocols={},_protospecs={};function getProtocolHandlers(cache){return callNetworkProtoHandlers().then(function(protos){delete protos.bonding;if(!protos.hasOwnProperty('none'))
Object.assign(protos,{none:{no_device:false}});if(!protos.hasOwnProperty('relay')&&L.hasSystemFeature('relayd'))
Object.assign(protos,{relay:{no_device:true}});Object.assign(_protospecs,protos);return Promise.all(Object.keys(protos).map(function(p){return Promise.resolve(L.require('protocol.%s'.format(p))).catch(function(err){if(L.isObject(err)&&err.name!='NetworkError')
L.error(err);});})).then(function(){return protos;});}).catch(function(){return{};});}
function getWifiStateBySid(sid){var s=uci.get('wireless',sid);if(s!=null&&s['.type']=='wifi-iface'){for(var radioname in _state.radios){for(var i=0;i<_state.radios[radioname].interfaces.length;i++){var netstate=_state.radios[radioname].interfaces[i];if(typeof(netstate.section)!='string')
continue;var s2=uci.get('wireless',netstate.section);if(s2!=null&&s['.type']==s2['.type']&&s['.name']==s2['.name']){if(s2['.anonymous']==false&&netstate.section.charAt(0)=='@')
return null;return[radioname,_state.radios[radioname],netstate];}}}}
return null;}
function getWifiStateByIfname(ifname){for(var radioname in _state.radios){for(var i=0;i<_state.radios[radioname].interfaces.length;i++){var netstate=_state.radios[radioname].interfaces[i];if(typeof(netstate.ifname)!='string')
continue;if(netstate.ifname==ifname)
return[radioname,_state.radios[radioname],netstate];}}
return null;}
function isWifiIfname(ifname){for(var i=0;i<iface_patterns_wireless.length;i++)
if(iface_patterns_wireless[i].test(ifname))
return true;return false;}
function getWifiSidByNetid(netid){var m=/^(\w+)\.network(\d+)$/.exec(netid);if(m){var sections=uci.sections('wireless','wifi-iface');for(var i=0,n=0;i<sections.length;i++){if(sections[i].device!=m[1])
continue;if(++n==+m[2])
return sections[i]['.name'];}}
return null;}
function getWifiSidByIfname(ifname){var sid=getWifiSidByNetid(ifname);if(sid!=null)
return sid;var res=getWifiStateByIfname(ifname);if(res!=null&&L.isObject(res[2])&&typeof(res[2].section)=='string')
return res[2].section;return null;}
function getWifiNetidBySid(sid){var s=uci.get('wireless',sid);if(s!=null&&s['.type']=='wifi-iface'){var radioname=s.device;if(typeof(radioname)=='string'){var sections=uci.sections('wireless','wifi-iface');for(var i=0,n=0;i<sections.length;i++){if(sections[i].device!=s.device)
continue;n++;if(sections[i]['.name']!=s['.name'])
continue;return['%s.network%d'.format(s.device,n),s.device];}}}
return null;}
function getWifiNetidByNetname(name){var sections=uci.sections('wireless','wifi-iface');for(var i=0;i<sections.length;i++){if(typeof(sections[i].network)!='string')
continue;var nets=sections[i].network.split(/\s+/);for(var j=0;j<nets.length;j++){if(nets[j]!=name)
continue;return getWifiNetidBySid(sections[i]['.name']);}}
return null;}
function isVirtualIfname(ifname){for(var i=0;i<iface_patterns_virtual.length;i++)
if(iface_patterns_virtual[i].test(ifname))
return true;return false;}
function isIgnoredIfname(ifname){for(var i=0;i<iface_patterns_ignore.length;i++)
if(iface_patterns_ignore[i].test(ifname))
return true;return false;}
function appendValue(config,section,option,value){var values=uci.get(config,section,option),isArray=Array.isArray(values),rv=false;if(isArray==false)
values=L.toArray(values);if(values.indexOf(value)==-1){values.push(value);rv=true;}
uci.set(config,section,option,isArray?values:values.join(' '));return rv;}
function removeValue(config,section,option,value){var values=uci.get(config,section,option),isArray=Array.isArray(values),rv=false;if(isArray==false)
values=L.toArray(values);for(var i=values.length-1;i>=0;i--){if(values[i]==value){values.splice(i,1);rv=true;}}
if(values.length>0)
uci.set(config,section,option,isArray?values:values.join(' '));else
uci.unset(config,section,option);return rv;}
function prefixToMask(bits,v6){var w=v6?128:32,m=[];if(bits>w)
return null;for(var i=0;i<w/16;i++){var b=Math.min(16,bits);m.push((0xffff<<(16-b))&0xffff);bits-=b;}
if(v6)
return String.prototype.format.apply('%x:%x:%x:%x:%x:%x:%x:%x',m).replace(/:0(?::0)+$/,'::');else
return'%d.%d.%d.%d'.format(m[0]>>>8,m[0]&0xff,m[1]>>>8,m[1]&0xff);}
function maskToPrefix(mask,v6){var m=v6?validation.parseIPv6(mask):validation.parseIPv4(mask);if(!m)
return null;var bits=0;for(var i=0,z=false;i<m.length;i++){z=z||!m[i];while(!z&&(m[i]&(v6?0x8000:0x80))){m[i]=(m[i]<<1)&(v6?0xffff:0xff);bits++;}
if(m[i])
return null;}
return bits;}
function initNetworkState(refresh){if(_state==null||refresh){const hasWifi=L.hasSystemFeature('wifi');_init=_init||Promise.all([L.resolveDefault(callNetworkInterfaceDump(),[]),L.resolveDefault(callLuciBoardJSON(),{}),L.resolveDefault(callLuciNetworkDevices(),{}),L.resolveDefault(callLuciWirelessDevices(),{}),L.resolveDefault(callLuciHostHints(),{}),getProtocolHandlers(),L.resolveDefault(uci.load('network')),hasWifi?L.resolveDefault(uci.load('wireless')):L.resolveDefault(),L.resolveDefault(uci.load('luci'))]).then(function(data){var netifd_ifaces=data[0],board_json=data[1],luci_devs=data[2];var s={isTunnel:{},isBridge:{},isSwitch:{},isWifi:{},ifaces:netifd_ifaces,radios:data[3],hosts:data[4],netdevs:{},bridges:{},switches:{},hostapd:{}};for(var name in luci_devs){var dev=luci_devs[name];if(isVirtualIfname(name))
s.isTunnel[name]=true;if(!s.isTunnel[name]&&isIgnoredIfname(name))
continue;s.netdevs[name]=s.netdevs[name]||{idx:dev.ifindex,name:name,rawname:name,flags:dev.flags,link:dev.link,stats:dev.stats,macaddr:dev.mac,type:dev.type,devtype:dev.devtype,mtu:dev.mtu,qlen:dev.qlen,wireless:dev.wireless,parent:dev.parent,ipaddrs:[],ip6addrs:[]};if(Array.isArray(dev.ipaddrs))
for(var i=0;i<dev.ipaddrs.length;i++)
s.netdevs[name].ipaddrs.push(dev.ipaddrs[i].address+'/'+dev.ipaddrs[i].netmask);if(Array.isArray(dev.ip6addrs))
for(var i=0;i<dev.ip6addrs.length;i++)
s.netdevs[name].ip6addrs.push(dev.ip6addrs[i].address+'/'+dev.ip6addrs[i].netmask);}
for(var name in luci_devs){var dev=luci_devs[name];if(!dev.bridge)
continue;var b={name:name,id:dev.id,stp:dev.stp,ifnames:[]};for(var i=0;dev.ports&&i<dev.ports.length;i++){var subdev=s.netdevs[dev.ports[i]];if(subdev==null)
continue;b.ifnames.push(subdev);subdev.bridge=b;}
s.bridges[name]=b;s.isBridge[name]=true;}
for(var name in luci_devs){var dev=luci_devs[name];if(!dev.parent||dev.devtype!='dsa')
continue;s.isSwitch[dev.parent]=true;s.isSwitch[name]=true;}
if(L.isObject(board_json.switch)){for(var switchname in board_json.switch){var layout=board_json.switch[switchname],netdevs={},nports={},ports=[],pnum=null,role=null;if(L.isObject(layout)&&Array.isArray(layout.ports)){for(var i=0,port;(port=layout.ports[i])!=null;i++){if(typeof(port)=='object'&&typeof(port.num)=='number'&&(typeof(port.role)=='string'||typeof(port.device)=='string')){var spec={num:port.num,role:port.role||'cpu',index:(port.index!=null)?port.index:port.num};if(port.device!=null){spec.device=port.device;spec.tagged=spec.need_tag;netdevs[port.num]=port.device;}
ports.push(spec);if(port.role!=null)
nports[port.role]=(nports[port.role]||0)+1;}}
ports.sort(function(a,b){return L.naturalCompare(a.role,b.role)||L.naturalCompare(a.index,b.index);});for(var i=0,port;(port=ports[i])!=null;i++){if(port.role!=role){role=port.role;pnum=1;}
if(role=='cpu')
port.label='CPU (%s)'.format(port.device);else if(nports[role]>1)
port.label='%s %d'.format(role.toUpperCase(),pnum++);else
port.label=role.toUpperCase();delete port.role;delete port.index;}
s.switches[switchname]={ports:ports,netdevs:netdevs};}}}
if(L.isObject(board_json.dsl)&&L.isObject(board_json.dsl.modem)){s.hasDSLModem=board_json.dsl.modem;}
_init=null;var objects=[];if(L.isObject(s.radios))
for(var radio in s.radios)
if(L.isObject(s.radios[radio])&&Array.isArray(s.radios[radio].interfaces))
for(var i=0;i<s.radios[radio].interfaces.length;i++)
if(L.isObject(s.radios[radio].interfaces[i])&&s.radios[radio].interfaces[i].ifname)
objects.push('hostapd.%s'.format(s.radios[radio].interfaces[i].ifname));return(objects.length?L.resolveDefault(rpc.list.apply(rpc,objects),{}):Promise.resolve({})).then(function(res){for(var k in res){var m=k.match(/^hostapd\.(.+)$/);if(m)
s.hostapd[m[1]]=res[k];}
return(_state=s);});});}
return(_state!=null?Promise.resolve(_state):_init);}
function ifnameOf(obj){if(obj instanceof Protocol)
return obj.getIfname();else if(obj instanceof Device)
return obj.getName();else if(obj instanceof WifiDevice)
return obj.getName();else if(obj instanceof WifiNetwork)
return obj.getIfname();else if(typeof(obj)=='string')
return obj.replace(/:.+$/,'');return null;}
function networkSort(a,b){return L.naturalCompare(a.getName(),b.getName());}
function deviceSort(a,b){var typeWeigth={wifi:2,alias:3};return L.naturalCompare(typeWeigth[a.getType()]||1,typeWeigth[b.getType()]||1)||L.naturalCompare(a.getName(),b.getName());}
function formatWifiEncryption(enc){if(!L.isObject(enc))
return null;if(!enc.enabled)
return'None';var ciphers=Array.isArray(enc.ciphers)?enc.ciphers.map(function(c){return c.toUpperCase()}):['NONE'];if(Array.isArray(enc.wep)){var has_open=false,has_shared=false;for(var i=0;i<enc.wep.length;i++)
if(enc.wep[i]=='open')
has_open=true;else if(enc.wep[i]=='shared')
has_shared=true;if(has_open&&has_shared)
return'WEP Open/Shared (%s)'.format(ciphers.join(', '));else if(has_open)
return'WEP Open System (%s)'.format(ciphers.join(', '));else if(has_shared)
return'WEP Shared Auth (%s)'.format(ciphers.join(', '));return'WEP';}
if(Array.isArray(enc.wpa)){var versions=[],suites=Array.isArray(enc.authentication)?enc.authentication.map(function(a){return a.toUpperCase()}):['NONE'];for(var i=0;i<enc.wpa.length;i++)
switch(enc.wpa[i]){case 1:versions.push('WPA');break;default:versions.push('WPA%d'.format(enc.wpa[i]));break;}
if(versions.length>1)
return'mixed %s %s (%s)'.format(versions.join('/'),suites.join(', '),ciphers.join(', '));return'%s %s (%s)'.format(versions[0],suites.join(', '),ciphers.join(', '));}
return'Unknown';}
function enumerateNetworks(){var uciInterfaces=uci.sections('network','interface'),networks={};for(var i=0;i<uciInterfaces.length;i++)
networks[uciInterfaces[i]['.name']]=this.instantiateNetwork(uciInterfaces[i]['.name']);for(var i=0;i<_state.ifaces.length;i++)
if(networks[_state.ifaces[i].interface]==null)
networks[_state.ifaces[i].interface]=this.instantiateNetwork(_state.ifaces[i].interface,_state.ifaces[i].proto);var rv=[];for(var network in networks)
if(networks.hasOwnProperty(network))
rv.push(networks[network]);rv.sort(networkSort);return rv;}
var Hosts,Network,Protocol,Device,WifiDevice,WifiNetwork,WifiVlan;Network=baseclass.extend({prefixToMask:prefixToMask,maskToPrefix:maskToPrefix,formatWifiEncryption:formatWifiEncryption,flushCache:function(){initNetworkState(true);return _init;},getProtocol:function(protoname,netname){var v=_protocols[protoname];if(v!=null)
return new v(netname||'__dummy__');return null;},getProtocols:function(){var rv=[];for(var protoname in _protocols)
rv.push(new _protocols[protoname]('__dummy__'));return rv;},registerProtocol:function(protoname,methods){var spec=L.isObject(_protospecs)?_protospecs[protoname]:null;var proto=Protocol.extend(Object.assign({getI18n:function(){return protoname;},isFloating:function(){return false;},isVirtual:function(){return(L.isObject(spec)&&spec.no_device==true);},renderFormOptions:function(section){}},methods,{__init__:function(name){this.sid=name;},getProtocol:function(){return protoname;}}));_protocols[protoname]=proto;return proto;},registerPatternVirtual:function(pat){iface_patterns_virtual.push(pat);},registerErrorCode:function(code,message){if(typeof(code)=='string'&&typeof(message)=='string'&&!proto_errors.hasOwnProperty(code)){proto_errors[code]=message;return true;}
return false;},addNetwork:function(name,options){return this.getNetwork(name).then(L.bind(function(existingNetwork){if(name!=null&&/^[a-zA-Z0-9_]+$/.test(name)&&existingNetwork==null){var sid=uci.add('network','interface',name);if(sid!=null){if(L.isObject(options))
for(var key in options)
if(options.hasOwnProperty(key))
uci.set('network',sid,key,options[key]);return this.instantiateNetwork(sid);}}
else if(existingNetwork!=null&&existingNetwork.isEmpty()){if(L.isObject(options))
for(var key in options)
if(options.hasOwnProperty(key))
existingNetwork.set(key,options[key]);return existingNetwork;}},this));},getNetwork:function(name){return initNetworkState().then(L.bind(function(){var section=(name!=null)?uci.get('network',name):null;if(section!=null&&section['.type']=='interface'){return this.instantiateNetwork(name);}
else if(name!=null){for(var i=0;i<_state.ifaces.length;i++)
if(_state.ifaces[i].interface==name)
return this.instantiateNetwork(name,_state.ifaces[i].proto);}
return null;},this));},getNetworks:function(){return initNetworkState().then(L.bind(enumerateNetworks,this));},deleteNetwork:function(name){var requireFirewall=Promise.resolve(L.require('firewall')).catch(function(){}),loadDHCP=L.resolveDefault(uci.load('dhcp')),network=this.instantiateNetwork(name);return Promise.all([requireFirewall,loadDHCP,initNetworkState()]).then(function(res){var uciInterface=uci.get('network',name),firewall=res[0];if(uciInterface!=null&&uciInterface['.type']=='interface'){return Promise.resolve(network?network.deleteConfiguration():null).then(function(){uci.remove('network',name);uci.sections('luci','ifstate',function(s){if(s.interface==name)
uci.remove('luci',s['.name']);});uci.sections('network',null,function(s){switch(s['.type']){case'alias':case'route':case'route6':if(s.interface==name)
uci.remove('network',s['.name']);break;case'rule':case'rule6':if(s.in==name||s.out==name)
uci.remove('network',s['.name']);break;}});uci.sections('wireless','wifi-iface',function(s){var networks=L.toArray(s.network).filter(function(network){return network!=name});if(networks.length>0)
uci.set('wireless',s['.name'],'network',networks.join(' '));else
uci.unset('wireless',s['.name'],'network');});uci.sections('dhcp','dhcp',function(s){if(s.interface==name)
uci.remove('dhcp',s['.name']);});if(firewall)
return firewall.deleteNetwork(name).then(function(){return true});return true;}).catch(function(){return false;});}
return false;});},renameNetwork:function(oldName,newName){return initNetworkState().then(function(){if(newName==null||!/^[a-zA-Z0-9_]+$/.test(newName)||uci.get('network',newName)!=null)
return false;var oldNetwork=uci.get('network',oldName);if(oldNetwork==null||oldNetwork['.type']!='interface')
return false;var sid=uci.add('network','interface',newName);for(var key in oldNetwork)
if(oldNetwork.hasOwnProperty(key)&&key.charAt(0)!='.')
uci.set('network',sid,key,oldNetwork[key]);uci.sections('luci','ifstate',function(s){if(s.interface==oldName)
uci.set('luci',s['.name'],'interface',newName);});uci.sections('network','alias',function(s){if(s.interface==oldName)
uci.set('network',s['.name'],'interface',newName);});uci.sections('network','route',function(s){if(s.interface==oldName)
uci.set('network',s['.name'],'interface',newName);});uci.sections('network','route6',function(s){if(s.interface==oldName)
uci.set('network',s['.name'],'interface',newName);});uci.sections('wireless','wifi-iface',function(s){var networks=L.toArray(s.network).map(function(network){return(network==oldName?newName:network)});if(networks.length>0)
uci.set('wireless',s['.name'],'network',networks.join(' '));});uci.remove('network',oldName);return true;});},getDevice:function(name){return initNetworkState().then(L.bind(function(){if(name==null)
return null;if(_state.netdevs.hasOwnProperty(name))
return this.instantiateDevice(name);var netid=getWifiNetidBySid(name);if(netid!=null)
return this.instantiateDevice(netid[0]);return null;},this));},getDevices:function(){return initNetworkState().then(L.bind(function(){var devices={};var uciInterfaces=uci.sections('network','interface');for(var i=0;i<uciInterfaces.length;i++){var ifnames=L.toArray(uciInterfaces[i].ifname);for(var j=0;j<ifnames.length;j++){if(ifnames[j].charAt(0)=='@')
continue;if(isIgnoredIfname(ifnames[j])||isVirtualIfname(ifnames[j])||isWifiIfname(ifnames[j]))
continue;devices[ifnames[j]]=this.instantiateDevice(ifnames[j]);}}
for(var ifname in _state.netdevs){if(devices.hasOwnProperty(ifname))
continue;if(isIgnoredIfname(ifname)||isWifiIfname(ifname))
continue;if(_state.netdevs[ifname].wireless)
continue;devices[ifname]=this.instantiateDevice(ifname);}
var uciSwitchVLANs=uci.sections('network','switch_vlan');for(var i=0;i<uciSwitchVLANs.length;i++){if(typeof(uciSwitchVLANs[i].ports)!='string'||typeof(uciSwitchVLANs[i].device)!='string'||!_state.switches.hasOwnProperty(uciSwitchVLANs[i].device))
continue;var ports=uciSwitchVLANs[i].ports.split(/\s+/);for(var j=0;j<ports.length;j++){var m=ports[j].match(/^(\d+)([tu]?)$/);if(m==null)
continue;var netdev=_state.switches[uciSwitchVLANs[i].device].netdevs[m[1]];if(netdev==null)
continue;if(!devices.hasOwnProperty(netdev))
devices[netdev]=this.instantiateDevice(netdev);_state.isSwitch[netdev]=true;if(m[2]!='t')
continue;var vid=uciSwitchVLANs[i].vid||uciSwitchVLANs[i].vlan;vid=(vid!=null?+vid:null);if(vid==null||vid<0||vid>4095)
continue;var vlandev='%s.%d'.format(netdev,vid);if(!devices.hasOwnProperty(vlandev))
devices[vlandev]=this.instantiateDevice(vlandev);_state.isSwitch[vlandev]=true;}}
var uciBridgeVLANs=uci.sections('network','bridge-vlan');for(var i=0;i<uciBridgeVLANs.length;i++){var basedev=uciBridgeVLANs[i].device,local=uciBridgeVLANs[i].local,alias=uciBridgeVLANs[i].alias,vid=+uciBridgeVLANs[i].vlan,ports=L.toArray(uciBridgeVLANs[i].ports);if(local=='0')
continue;if(isNaN(vid)||vid<0||vid>4095)
continue;var vlandev='%s.%s'.format(basedev,alias||vid);_state.isBridge[basedev]=true;if(!_state.bridges.hasOwnProperty(basedev))
_state.bridges[basedev]={name:basedev,ifnames:[]};if(!devices.hasOwnProperty(vlandev))
devices[vlandev]=this.instantiateDevice(vlandev);ports.forEach(function(port_name){var m=port_name.match(/^([^:]+)(?::[ut*]+)?$/),p=m?m[1]:null;if(!p)
return;if(_state.bridges[basedev].ifnames.filter(function(sd){return sd.name==p}).length)
return;_state.netdevs[p]=_state.netdevs[p]||{name:p,ipaddrs:[],ip6addrs:[],type:1,devtype:'ethernet',stats:{},flags:{}};_state.bridges[basedev].ifnames.push(_state.netdevs[p]);_state.netdevs[p].bridge=_state.bridges[basedev];});}
var uciWifiIfaces=uci.sections('wireless','wifi-iface'),networkCount={};for(var i=0;i<uciWifiIfaces.length;i++){if(typeof(uciWifiIfaces[i].device)!='string')
continue;networkCount[uciWifiIfaces[i].device]=(networkCount[uciWifiIfaces[i].device]||0)+1;var netid='%s.network%d'.format(uciWifiIfaces[i].device,networkCount[uciWifiIfaces[i].device]);devices[netid]=this.instantiateDevice(netid);}
var uciDevices=uci.sections('network','device');for(var i=0;i<uciDevices.length;i++){var type=uciDevices[i].type,name=uciDevices[i].name;if(!type||!name||devices.hasOwnProperty(name))
continue;if(type=='bridge')
_state.isBridge[name]=true;devices[name]=this.instantiateDevice(name);}
var rv=[];for(var netdev in devices)
if(devices.hasOwnProperty(netdev))
rv.push(devices[netdev]);rv.sort(deviceSort);return rv;},this));},isIgnoredDevice:function(name){return isIgnoredIfname(name);},getWifiDevice:function(devname){return initNetworkState().then(L.bind(function(){var existingDevice=uci.get('wireless',devname);if(existingDevice==null||existingDevice['.type']!='wifi-device')
return null;return this.instantiateWifiDevice(devname,_state.radios[devname]||{});},this));},getWifiDevices:function(){return initNetworkState().then(L.bind(function(){var uciWifiDevices=uci.sections('wireless','wifi-device'),rv=[];for(var i=0;i<uciWifiDevices.length;i++){var devname=uciWifiDevices[i]['.name'];rv.push(this.instantiateWifiDevice(devname,_state.radios[devname]||{}));}
return rv;},this));},getWifiNetwork:function(netname){return initNetworkState().then(L.bind(this.lookupWifiNetwork,this,netname));},getWifiNetworks:function(){return initNetworkState().then(L.bind(function(){var wifiIfaces=uci.sections('wireless','wifi-iface'),rv=[];for(var i=0;i<wifiIfaces.length;i++)
rv.push(this.lookupWifiNetwork(wifiIfaces[i]['.name']));rv.sort(function(a,b){return L.naturalCompare(a.getID(),b.getID());});return rv;},this));},addWifiNetwork:function(options){return initNetworkState().then(L.bind(function(){if(options==null||typeof(options)!='object'||typeof(options.device)!='string')
return null;var existingDevice=uci.get('wireless',options.device);if(existingDevice==null||existingDevice['.type']!='wifi-device')
return null;var sid=uci.add('wireless','wifi-iface');for(var key in options)
if(options.hasOwnProperty(key))
uci.set('wireless',sid,key,options[key]);var radioname=existingDevice['.name'],netid=getWifiNetidBySid(sid)||[];return this.instantiateWifiNetwork(sid,radioname,_state.radios[radioname],netid[0],null);},this));},deleteWifiNetwork:function(netname){return initNetworkState().then(L.bind(function(){var sid=getWifiSidByIfname(netname);if(sid==null)
return false;uci.remove('wireless',sid);return true;},this));},getStatusByRoute:function(addr,mask){return initNetworkState().then(L.bind(function(){var rv=[];for(var i=0;i<_state.ifaces.length;i++){if(!Array.isArray(_state.ifaces[i].route))
continue;for(var j=0;j<_state.ifaces[i].route.length;j++){if(typeof(_state.ifaces[i].route[j])!='object'||typeof(_state.ifaces[i].route[j].target)!='string'||typeof(_state.ifaces[i].route[j].mask)!='number')
continue;if(_state.ifaces[i].route[j].table)
continue;if(_state.ifaces[i].route[j].target!=addr||_state.ifaces[i].route[j].mask!=mask)
continue;rv.push(_state.ifaces[i]);}}
rv.sort(function(a,b){return L.naturalCompare(a.metric,b.metric)||L.naturalCompare(a.interface,b.interface);});return rv;},this));},getStatusByAddress:function(addr){return initNetworkState().then(L.bind(function(){var rv=[];for(var i=0;i<_state.ifaces.length;i++){if(Array.isArray(_state.ifaces[i]['ipv4-address']))
for(var j=0;j<_state.ifaces[i]['ipv4-address'].length;j++)
if(typeof(_state.ifaces[i]['ipv4-address'][j])=='object'&&_state.ifaces[i]['ipv4-address'][j].address==addr)
return _state.ifaces[i];if(Array.isArray(_state.ifaces[i]['ipv6-address']))
for(var j=0;j<_state.ifaces[i]['ipv6-address'].length;j++)
if(typeof(_state.ifaces[i]['ipv6-address'][j])=='object'&&_state.ifaces[i]['ipv6-address'][j].address==addr)
return _state.ifaces[i];if(Array.isArray(_state.ifaces[i]['ipv6-prefix-assignment']))
for(var j=0;j<_state.ifaces[i]['ipv6-prefix-assignment'].length;j++)
if(typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j])=='object'&&typeof(_state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address'])=='object'&&_state.ifaces[i]['ipv6-prefix-assignment'][j]['local-address'].address==addr)
return _state.ifaces[i];}
return null;},this));},getWANNetworks:function(){return this.getStatusByRoute('0.0.0.0',0).then(L.bind(function(statuses){var rv=[],seen={};for(var i=0;i<statuses.length;i++){if(!seen.hasOwnProperty(statuses[i].interface)){rv.push(this.instantiateNetwork(statuses[i].interface,statuses[i].proto));seen[statuses[i].interface]=true;}}
return rv;},this));},getWAN6Networks:function(){return this.getStatusByRoute('::',0).then(L.bind(function(statuses){var rv=[],seen={};for(var i=0;i<statuses.length;i++){if(!seen.hasOwnProperty(statuses[i].interface)){rv.push(this.instantiateNetwork(statuses[i].interface,statuses[i].proto));seen[statuses[i].interface]=true;}}
return rv;},this));},getSwitchTopologies:function(){return initNetworkState().then(function(){return _state.switches;});},instantiateNetwork:function(name,proto){if(name==null)
return null;proto=(proto==null?(uci.get('network',name,'proto')||'none'):proto);var protoClass=_protocols[proto]||Protocol;return new protoClass(name);},instantiateDevice:function(name,network,extend){if(extend!=null)
return new(Device.extend(extend))(name,network);return new Device(name,network);},instantiateWifiDevice:function(radioname,radiostate){return new WifiDevice(radioname,radiostate);},instantiateWifiNetwork:function(sid,radioname,radiostate,netid,netstate,hostapd){return new WifiNetwork(sid,radioname,radiostate,netid,netstate,hostapd);},lookupWifiNetwork:function(netname){var sid,res,netid,radioname,radiostate,netstate;sid=getWifiSidByNetid(netname);if(sid!=null){res=getWifiStateBySid(sid);netid=netname;radioname=res?res[0]:null;radiostate=res?res[1]:null;netstate=res?res[2]:null;}
else{res=getWifiStateByIfname(netname);if(res!=null){radioname=res[0];radiostate=res[1];netstate=res[2];sid=netstate.section;netid=L.toArray(getWifiNetidBySid(sid))[0];}
else{res=getWifiStateBySid(netname);if(res!=null){radioname=res[0];radiostate=res[1];netstate=res[2];sid=netname;netid=L.toArray(getWifiNetidBySid(sid))[0];}
else{res=getWifiNetidBySid(netname);if(res!=null){netid=res[0];radioname=res[1];sid=netname;}}}}
return this.instantiateWifiNetwork(sid||netname,radioname,radiostate,netid,netstate,netstate?_state.hostapd[netstate.ifname]:null);},getIfnameOf:function(obj){return ifnameOf(obj);},getDSLModemType:function(){return initNetworkState().then(function(){return _state.hasDSLModem?_state.hasDSLModem.type:null;});},getHostHints:function(){return initNetworkState().then(function(){return new Hosts(_state.hosts);});}});Hosts=baseclass.extend({__init__:function(hosts){this.hosts=hosts;},getHostnameByMACAddr:function(mac){return this.hosts[mac]?(this.hosts[mac].name||null):null;},getIPAddrByMACAddr:function(mac){return this.hosts[mac]?(L.toArray(this.hosts[mac].ipaddrs||this.hosts[mac].ipv4)[0]||null):null;},getIP6AddrByMACAddr:function(mac){return this.hosts[mac]?(L.toArray(this.hosts[mac].ip6addrs||this.hosts[mac].ipv6)[0]||null):null;},getHostnameByIPAddr:function(ipaddr){for(var mac in this.hosts){if(this.hosts[mac].name==null)
continue;var addrs=L.toArray(this.hosts[mac].ipaddrs||this.hosts[mac].ipv4);for(var i=0;i<addrs.length;i++)
if(addrs[i]==ipaddr)
return this.hosts[mac].name;}
return null;},getMACAddrByIPAddr:function(ipaddr){for(var mac in this.hosts){var addrs=L.toArray(this.hosts[mac].ipaddrs||this.hosts[mac].ipv4);for(var i=0;i<addrs.length;i++)
if(addrs[i]==ipaddr)
return mac;}
return null;},getHostnameByIP6Addr:function(ip6addr){for(var mac in this.hosts){if(this.hosts[mac].name==null)
continue;var addrs=L.toArray(this.hosts[mac].ip6addrs||this.hosts[mac].ipv6);for(var i=0;i<addrs.length;i++)
if(addrs[i]==ip6addr)
return this.hosts[mac].name;}
return null;},getMACAddrByIP6Addr:function(ip6addr){for(var mac in this.hosts){var addrs=L.toArray(this.hosts[mac].ip6addrs||this.hosts[mac].ipv6);for(var i=0;i<addrs.length;i++)
if(addrs[i]==ip6addr)
return mac;}
return null;},getMACHints:function(preferIp6){var rv=[];for(var mac in this.hosts){var hint=this.hosts[mac].name||L.toArray(this.hosts[mac][preferIp6?'ip6addrs':'ipaddrs']||this.hosts[mac][preferIp6?'ipv6':'ipv4'])[0]||L.toArray(this.hosts[mac][preferIp6?'ipaddrs':'ip6addrs']||this.hosts[mac][preferIp6?'ipv4':'ipv6'])[0];rv.push([mac,hint]);}
return rv.sort(function(a,b){return L.naturalCompare(a[0],b[0]);});}});Protocol=baseclass.extend({__init__:function(name){this.sid=name;},_get:function(opt){var val=uci.get('network',this.sid,opt);if(Array.isArray(val))
return val.join(' ');return val||'';},_ubus:function(field){for(var i=0;i<_state.ifaces.length;i++){if(_state.ifaces[i].interface!=this.sid)
continue;return(field!=null?_state.ifaces[i][field]:_state.ifaces[i]);}},get:function(opt){return uci.get('network',this.sid,opt);},set:function(opt,val){return uci.set('network',this.sid,opt,val);},getIfname:function(){var ifname;if(this.isFloating())
ifname=this._ubus('l3_device');else
ifname=this._ubus('device')||this._ubus('l3_device');if(ifname!=null)
return ifname;var res=getWifiNetidByNetname(this.sid);return(res!=null?res[0]:null);},getProtocol:function(){return null;},getI18n:function(){switch(this.getProtocol()){case'none':return _('Unmanaged');case'static':return _('Static address');case'dhcp':return _('DHCP client');default:return _('Unknown');}},getType:function(){return this._get('type');},getName:function(){return this.sid;},getUptime:function(){return this._ubus('uptime')||0;},getExpiry:function(){var u=this._ubus('uptime'),d=this._ubus('data'),v6_prefixes=this._ubus('ipv6-prefix'),v6_addresses=this._ubus('ipv6-address');if(typeof(u)=='number'&&d!=null){if(typeof(d)=='object'&&typeof(d.leasetime)=='number'){var r=d.leasetime-(u%d.leasetime);return(r>0?r:0);}
if(Array.isArray(v6_prefixes)||Array.isArray(v6_addresses)){var prefixes=[...v6_prefixes,...v6_addresses];if(prefixes.length&&typeof(prefixes[0].valid)=='number'){var r=prefixes[0].valid;return(r>0?r:0);}}}
return-1;},getMetric:function(){return this._ubus('metric')||0;},getZoneName:function(){var d=this._ubus('data');if(L.isObject(d)&&typeof(d.zone)=='string')
return d.zone;return null;},getIPAddr:function(){var addrs=this._ubus('ipv4-address');return((Array.isArray(addrs)&&addrs.length)?addrs[0].address:null);},getIPAddrs:function(){var addrs=this._ubus('ipv4-address'),rv=[];if(Array.isArray(addrs))
for(var i=0;i<addrs.length;i++)
rv.push('%s/%d'.format(addrs[i].address,addrs[i].mask));return rv;},getNetmask:function(){var addrs=this._ubus('ipv4-address');if(Array.isArray(addrs)&&addrs.length)
return prefixToMask(addrs[0].mask,false);},getGatewayAddr:function(){var routes=this._ubus('route');if(Array.isArray(routes))
for(var i=0;i<routes.length;i++)
if(typeof(routes[i])=='object'&&routes[i].target=='0.0.0.0'&&routes[i].mask==0)
return routes[i].nexthop;return null;},getDNSAddrs:function(){var addrs=this._ubus('dns-server'),rv=[];if(Array.isArray(addrs))
for(var i=0;i<addrs.length;i++)
if(!/:/.test(addrs[i]))
rv.push(addrs[i]);return rv;},getIP6Addr:function(){var addrs=this._ubus('ipv6-address');if(Array.isArray(addrs)&&L.isObject(addrs[0]))
return'%s/%d'.format(addrs[0].address,addrs[0].mask);addrs=this._ubus('ipv6-prefix-assignment');if(Array.isArray(addrs)&&L.isObject(addrs[0])&&L.isObject(addrs[0]['local-address']))
return'%s/%d'.format(addrs[0]['local-address'].address,addrs[0]['local-address'].mask);return null;},getIP6Addrs:function(){var addrs=this._ubus('ipv6-address'),rv=[];if(Array.isArray(addrs))
for(var i=0;i<addrs.length;i++)
if(L.isObject(addrs[i]))
rv.push('%s/%d'.format(addrs[i].address,addrs[i].mask));addrs=this._ubus('ipv6-prefix-assignment');if(Array.isArray(addrs))
for(var i=0;i<addrs.length;i++)
if(L.isObject(addrs[i])&&L.isObject(addrs[i]['local-address']))
rv.push('%s/%d'.format(addrs[i]['local-address'].address,addrs[i]['local-address'].mask));return rv;},getGateway6Addr:function(){var routes=this._ubus('route');if(Array.isArray(routes))
for(var i=0;i<routes.length;i++)
if(typeof(routes[i])=='object'&&routes[i].target=='::'&&routes[i].mask==0)
return routes[i].nexthop;return null;},getDNS6Addrs:function(){var addrs=this._ubus('dns-server'),rv=[];if(Array.isArray(addrs))
for(var i=0;i<addrs.length;i++)
if(/:/.test(addrs[i]))
rv.push(addrs[i]);return rv;},getIP6Prefix:function(){var prefixes=this._ubus('ipv6-prefix');if(Array.isArray(prefixes)&&L.isObject(prefixes[0]))
return'%s/%d'.format(prefixes[0].address,prefixes[0].mask);return null;},getIP6Prefixes:function(){var prefixes=this._ubus('ipv6-prefix');var rv=[];if(Array.isArray(prefixes))
for(var i=0;i<prefixes.length;i++)
if(L.isObject(prefixes[i]))
rv.push('%s/%d'.format(prefixes[i].address,prefixes[i].mask));return rv.length>0?rv:null;},getErrors:function(){var errors=this._ubus('errors'),rv=null;if(Array.isArray(errors)){for(var i=0;i<errors.length;i++){if(!L.isObject(errors[i])||typeof(errors[i].code)!='string')
continue;rv=rv||[];rv.push(proto_errors[errors[i].code]||_('Unknown error (%s)').format(errors[i].code));}}
return rv;},isBridge:function(){return(!this.isVirtual()&&this.getType()=='bridge');},getPackageName:function(){return null;},isCreateable:function(ifname){return Promise.resolve(null);},isInstalled:function(){return true;},isVirtual:function(){return false;},isFloating:function(){return false;},isDynamic:function(){return(this._ubus('dynamic')==true);},isPending:function(){return(this._ubus('pending')==true);},isAlias:function(){var ifnames=L.toArray(uci.get('network',this.sid,'device')),parent=null;for(var i=0;i<ifnames.length;i++)
if(ifnames[i].charAt(0)=='@')
parent=ifnames[i].substr(1);else if(parent!=null)
parent=null;return parent;},isEmpty:function(){if(this.isFloating())
return false;var empty=true,device=this._get('device');if(device!=null&&device.match(/\S+/))
empty=false;if(empty==true&&getWifiNetidBySid(this.sid)!=null)
empty=false;return empty;},isUp:function(){return(this._ubus('up')==true);},addDevice:function(device){device=ifnameOf(device);if(device==null||this.isFloating())
return false;var wif=getWifiSidByIfname(device);if(wif!=null)
return appendValue('wireless',wif,'network',this.sid);return appendValue('network',this.sid,'device',device);},deleteDevice:function(device){var rv=false;device=ifnameOf(device);if(device==null||this.isFloating())
return false;var wif=getWifiSidByIfname(device);if(wif!=null)
rv=removeValue('wireless',wif,'network',this.sid);if(removeValue('network',this.sid,'device',device))
rv=true;return rv;},getDevice:function(){if(this.isVirtual()){var ifname='%s-%s'.format(this.getProtocol(),this.sid);_state.isTunnel[this.getProtocol()+'-'+this.sid]=true;return Network.prototype.instantiateDevice(ifname,this);}
else if(this.isBridge()){var ifname='br-%s'.format(this.sid);_state.isBridge[ifname]=true;return new Device(ifname,this);}
else{var ifnames=L.toArray(uci.get('network',this.sid,'device'));for(var i=0;i<ifnames.length;i++){var m=ifnames[i].match(/^([^:/]+)/);return((m&&m[1])?Network.prototype.instantiateDevice(m[1],this):null);}
ifname=getWifiNetidByNetname(this.sid);return(ifname!=null?Network.prototype.instantiateDevice(ifname[0],this):null);}},getL2Device:function(){var ifname=this._ubus('device');return(ifname!=null?Network.prototype.instantiateDevice(ifname,this):null);},getL3Device:function(){var ifname=this._ubus('l3_device');return(ifname!=null?Network.prototype.instantiateDevice(ifname,this):null);},getDevices:function(){var rv=[];if(!this.isBridge()&&!(this.isVirtual()&&!this.isFloating()))
return null;var device=uci.get('network',this.sid,'device');if(device&&device.charAt(0)!='@'){var m=device.match(/^([^:/]+)/);if(m!=null)
rv.push(Network.prototype.instantiateDevice(m[1],this));}
var uciWifiIfaces=uci.sections('wireless','wifi-iface');for(var i=0;i<uciWifiIfaces.length;i++){if(typeof(uciWifiIfaces[i].device)!='string')
continue;var networks=L.toArray(uciWifiIfaces[i].network);for(var j=0;j<networks.length;j++){if(networks[j]!=this.sid)
continue;var netid=getWifiNetidBySid(uciWifiIfaces[i]['.name']);if(netid!=null)
rv.push(Network.prototype.instantiateDevice(netid[0],this));}}
rv.sort(deviceSort);return rv;},containsDevice:function(device){device=ifnameOf(device);if(device==null)
return false;else if(this.isVirtual()&&'%s-%s'.format(this.getProtocol(),this.sid)==device)
return true;else if(this.isBridge()&&'br-%s'.format(this.sid)==device)
return true;var name=uci.get('network',this.sid,'device');if(name){var m=name.match(/^([^:/]+)/);if(m!=null&&m[1]==device)
return true;}
var wif=getWifiSidByIfname(device);if(wif!=null){var networks=L.toArray(uci.get('wireless',wif,'network'));for(var i=0;i<networks.length;i++)
if(networks[i]==this.sid)
return true;}
return false;},deleteConfiguration:function(){}});Device=baseclass.extend({__init__:function(device,network){var wif=getWifiSidByIfname(device);if(wif!=null){var res=getWifiStateBySid(wif)||[],netid=getWifiNetidBySid(wif)||[];this.wif=new WifiNetwork(wif,res[0],res[1],netid[0],res[2],{ifname:device});this.device=this.wif.getIfname();}
this.device=this.device||device;this.dev=Object.assign({},_state.netdevs[this.device]);this.network=network;var conf;uci.sections('network','device',function(s){if(s.name==device)
conf=s;});this.config=Object.assign({},conf);},_devstate:function(){var rv=this.dev;for(var i=0;i<arguments.length;i++)
if(L.isObject(rv))
rv=rv[arguments[i]];else
return null;return rv;},getName:function(){return(this.wif!=null?this.wif.getIfname():this.device);},getMAC:function(){var mac=this._devstate('macaddr');return mac?mac.toUpperCase():null;},getMTU:function(){return this._devstate('mtu');},getIPAddrs:function(){var addrs=this._devstate('ipaddrs');return(Array.isArray(addrs)?addrs:[]);},getIP6Addrs:function(){var addrs=this._devstate('ip6addrs');return(Array.isArray(addrs)?addrs:[]);},getType:function(){if(this.device!=null&&this.device.charAt(0)=='@')
return'alias';else if(this.dev.devtype=='wlan'||this.wif!=null||isWifiIfname(this.device))
return'wifi';else if(this.dev.devtype=='bridge'||_state.isBridge[this.device])
return'bridge';else if(this.dev.devtype=='wireguard')
return'wireguard';else if(_state.isTunnel[this.device])
return'tunnel';else if(this.dev.devtype=='vlan'||this.device.indexOf('.')>-1)
return'vlan';else if(this.dev.devtype=='dsa'||_state.isSwitch[this.device])
return'switch';else if(this.config.type=='8021q'||this.config.type=='8021ad')
return'vlan';else if(this.config.type=='bridge')
return'bridge';else if(this.config.type=='vrf')
return'vrf';else
return'ethernet';},getShortName:function(){if(this.wif!=null)
return this.wif.getShortName();return this.device;},getI18n:function(){if(this.wif!=null){return'%s: %s "%s"'.format(_('Wireless Network'),this.wif.getActiveMode(),this.wif.getActiveSSID()||this.wif.getActiveBSSID()||this.wif.getID()||'?');}
return'%s: "%s"'.format(this.getTypeI18n(),this.getName());},getTypeI18n:function(){switch(this.getType()){case'alias':return _('Alias Interface');case'wifi':return _('Wireless Adapter');case'bridge':return _('Bridge');case'vrf':return _('Virtual Routing and Forwarding (VRF)');case'switch':return(_state.netdevs[this.device]&&_state.netdevs[this.device].devtype=='dsa')?_('Switch port'):_('Ethernet Switch');case'vlan':return(_state.isSwitch[this.device]?_('Switch VLAN'):_('Software VLAN'));case'wireguard':return _('WireGuard Interface');case'tunnel':return _('Tunnel Interface');default:return _('Ethernet Adapter');}},getPorts:function(){var br=_state.bridges[this.device],rv=[];if(br==null||!Array.isArray(br.ifnames))
return null;for(var i=0;i<br.ifnames.length;i++)
rv.push(Network.prototype.instantiateDevice(br.ifnames[i].name));rv.sort(deviceSort);return rv;},getBridgeID:function(){var br=_state.bridges[this.device];return(br!=null?br.id:null);},getBridgeSTP:function(){var br=_state.bridges[this.device];return(br!=null?!!br.stp:false);},isUp:function(){var up=this._devstate('flags','up');if(up==null)
up=(this.getType()=='alias');return up;},isBridge:function(){return(this.getType()=='bridge');},isBridgePort:function(){return(this._devstate('bridge')!=null);},getTXBytes:function(){var stat=this._devstate('stats');return(stat!=null?stat.tx_bytes||0:0);},getRXBytes:function(){var stat=this._devstate('stats');return(stat!=null?stat.rx_bytes||0:0);},getTXPackets:function(){var stat=this._devstate('stats');return(stat!=null?stat.tx_packets||0:0);},getRXPackets:function(){var stat=this._devstate('stats');return(stat!=null?stat.rx_packets||0:0);},getCarrier:function(){var link=this._devstate('link');return(link!=null?link.carrier||false:false);},getSpeed:function(){var link=this._devstate('link');return(link!=null?link.speed||null:null);},getDuplex:function(){var link=this._devstate('link'),duplex=link?link.duplex:null;return(duplex!='unknown')?duplex:null;},getNetwork:function(){return this.getNetworks()[0];},getNetworks:function(){if(this.networks==null){this.networks=[];var networks=enumerateNetworks.apply(L.network);for(var i=0;i<networks.length;i++)
if(networks[i].containsDevice(this.device)||networks[i].getIfname()==this.device)
this.networks.push(networks[i]);this.networks.sort(networkSort);}
return this.networks;},getWifiNetwork:function(){return(this.wif!=null?this.wif:null);},getParent:function(){if(this.dev.parent)
return Network.prototype.instantiateDevice(this.dev.parent);if((this.config.type=='8021q'||this.config.type=='802ad')&&typeof(this.config.ifname)=='string')
return Network.prototype.instantiateDevice(this.config.ifname);return null;}});WifiDevice=baseclass.extend({__init__:function(name,radiostate){var uciWifiDevice=uci.get('wireless',name);if(uciWifiDevice!=null&&uciWifiDevice['.type']=='wifi-device'&&uciWifiDevice['.name']!=null){this.sid=uciWifiDevice['.name'];}
this.sid=this.sid||name;this._ubusdata={radio:name,dev:radiostate};},ubus:function(){var v=this._ubusdata;for(var i=0;i<arguments.length;i++)
if(L.isObject(v))
v=v[arguments[i]];else
return null;return v;},get:function(opt){return uci.get('wireless',this.sid,opt);},set:function(opt,value){return uci.set('wireless',this.sid,opt,value);},isDisabled:function(){return this.ubus('dev','disabled')||this.get('disabled')=='1';},getName:function(){return this.sid;},getHWModes:function(){var hwmodes=this.ubus('dev','iwinfo','hwmodes');return Array.isArray(hwmodes)?hwmodes:['b','g'];},getHTModes:function(){var htmodes=this.ubus('dev','iwinfo','htmodes');return(Array.isArray(htmodes)&&htmodes.length)?htmodes:null;},getI18n:function(){var hw=this.ubus('dev','iwinfo','hardware'),type=L.isObject(hw)?hw.name:null;var modes=this.ubus('dev','iwinfo','hwmodes_text');if(this.ubus('dev','iwinfo','type')=='wl')
type='Broadcom';return'%s %s Wireless Controller (%s)'.format(type||'Generic',modes?'802.11'+modes:'unknown',this.getName());},getScanList:function(){return callIwinfoScan(this.sid);},isUp:function(){if(L.isObject(_state.radios[this.sid]))
return(_state.radios[this.sid].up==true);return false;},getWifiNetwork:function(network){return Network.prototype.getWifiNetwork(network).then(L.bind(function(networkInstance){var uciWifiIface=(networkInstance.sid?uci.get('wireless',networkInstance.sid):null);if(uciWifiIface==null||uciWifiIface['.type']!='wifi-iface'||uciWifiIface.device!=this.sid)
return Promise.reject();return networkInstance;},this));},getWifiNetworks:function(){return Network.prototype.getWifiNetworks().then(L.bind(function(networks){var rv=[];for(var i=0;i<networks.length;i++)
if(networks[i].getWifiDeviceName()==this.getName())
rv.push(networks[i]);return rv;},this));},addWifiNetwork:function(options){if(!L.isObject(options))
options={};options.device=this.sid;return Network.prototype.addWifiNetwork(options);},deleteWifiNetwork:function(network){var sid=null;if(network instanceof WifiNetwork){sid=network.sid;}
else{var uciWifiIface=uci.get('wireless',network);if(uciWifiIface==null||uciWifiIface['.type']!='wifi-iface')
sid=getWifiSidByIfname(network);}
if(sid==null||uci.get('wireless',sid,'device')!=this.sid)
return Promise.resolve(false);uci.delete('wireless',network);return Promise.resolve(true);}});WifiNetwork=baseclass.extend({__init__:function(sid,radioname,radiostate,netid,netstate,hostapd){this.sid=sid;this.netid=netid;this._ubusdata={hostapd:hostapd,radio:radioname,dev:radiostate,net:netstate};},ubus:function(){var v=this._ubusdata;for(var i=0;i<arguments.length;i++)
if(L.isObject(v))
v=v[arguments[i]];else
return null;return v;},get:function(opt){return uci.get('wireless',this.sid,opt);},set:function(opt,value){return uci.set('wireless',this.sid,opt,value);},isDisabled:function(){return this.ubus('dev','disabled')||this.get('disabled')=='1';},getMode:function(){return this.ubus('net','config','mode')||this.get('mode')||'ap';},getSSID:function(){if(this.getMode()=='mesh')
return null;return this.ubus('net','config','ssid')||this.get('ssid');},getMeshID:function(){if(this.getMode()!='mesh')
return null;return this.ubus('net','config','mesh_id')||this.get('mesh_id');},getBSSID:function(){return this.ubus('net','config','bssid')||this.get('bssid');},getNetworkNames:function(){return L.toArray(this.ubus('net','config','network')||this.get('network'));},getID:function(){return this.netid;},getName:function(){return this.sid;},getIfname:function(){var ifname=this.ubus('net','ifname')||this.ubus('net','iwinfo','ifname');if(ifname==null||ifname.match(/^(wifi|radio)\d/))
ifname=this.netid;return ifname;},getVlanIfnames:function(){var vlans=L.toArray(this.ubus('net','vlans')),ifnames=[];for(var i=0;i<vlans.length;i++)
ifnames.push(vlans[i]['ifname']);return ifnames;},getWifiDeviceName:function(){return this.ubus('radio')||this.get('device');},getWifiDevice:function(){var radioname=this.getWifiDeviceName();if(radioname==null)
return Promise.reject();return Network.prototype.getWifiDevice(radioname);},isUp:function(){var device=this.getDevice();if(device==null)
return false;return device.isUp();},getActiveMode:function(){var mode=this.ubus('net','iwinfo','mode')||this.getMode();switch(mode){case'ap':return'Master';case'sta':return'Client';case'adhoc':return'Ad-Hoc';case'mesh':return'Mesh Point';case'monitor':return'Monitor';default:return mode;}},getActiveModeI18n:function(){var mode=this.getActiveMode();switch(mode){case'Master':return _('Access Point');case'Ad-Hoc':return _('Ad-Hoc');case'Client':return _('Client');case'Monitor':return _('Monitor');case'Master(VLAN)':return _('Master (VLAN)');case'WDS':return _('WDS');case'Mesh Point':return _('Mesh Point');case'P2P Client':return _('P2P Client');case'P2P Go':return _('P2P Go');case'Unknown':return _('Unknown');default:return mode;}},getActiveSSID:function(){return this.ubus('net','iwinfo','ssid')||this.ubus('net','config','ssid')||this.get('ssid');},getActiveBSSID:function(){return this.ubus('net','iwinfo','bssid')||this.ubus('net','config','bssid')||this.get('bssid');},getActiveEncryption:function(){return formatWifiEncryption(this.ubus('net','iwinfo','encryption'))||'-';},getAssocList:function(){var tasks=[];var station;for(let vlan of this.getVlans())
tasks.push(callIwinfoAssoclist(vlan.getIfname()).then(function(stations){for(station of stations)
station.vlan=vlan;return stations;}));tasks.push(callIwinfoAssoclist(this.getIfname()));return Promise.all(tasks).then(function(values){return Array.prototype.concat.apply([],values);});},getVlans:function(){var vlans=[];var vlans_ubus=this.ubus('net','vlans');if(vlans_ubus)
for(let vlan of vlans_ubus)
vlans.push(new WifiVlan(vlan));return vlans;},getFrequency:function(){var freq=this.ubus('net','iwinfo','frequency');if(freq!=null&&freq>0)
return'%.03f'.format(freq/1000);return null;},getBitRate:function(){var rate=this.ubus('net','iwinfo','bitrate');if(rate!=null&&rate>0)
return(rate/1000);return null;},getChannel:function(){return this.ubus('net','iwinfo','channel')||this.ubus('dev','config','channel')||this.get('channel');},getSignal:function(){return this.ubus('net','iwinfo','signal')||0;},getNoise:function(){return this.ubus('net','iwinfo','noise')||0;},getCountryCode:function(){return this.ubus('net','iwinfo','country')||this.ubus('dev','config','country')||'00';},getTXPower:function(){return this.ubus('net','iwinfo','txpower');},getTXPowerOffset:function(){return this.ubus('net','iwinfo','txpower_offset')||0;},getSignalLevel:function(signal,noise){if(this.getActiveBSSID()=='00:00:00:00:00:00')
return-1;signal=signal||this.getSignal();noise=noise||this.getNoise();if(signal<0&&noise<0){var snr=-1*(noise-signal);return Math.floor(snr/5);}
return 0;},getSignalPercent:function(){var qc=this.ubus('net','iwinfo','quality')||0,qm=this.ubus('net','iwinfo','quality_max')||0;if(qc>0&&qm>0)
return Math.floor((100/qm)*qc);return 0;},getShortName:function(){return'%s "%s"'.format(this.getActiveModeI18n(),this.getActiveSSID()||this.getActiveBSSID()||this.getID());},getI18n:function(){return'%s: %s "%s" (%s)'.format(_('Wireless Network'),this.getActiveModeI18n(),this.getActiveSSID()||this.getActiveBSSID()||this.getID(),this.getIfname());},getNetwork:function(){return this.getNetworks()[0];},getNetworks:function(){var networkNames=this.getNetworkNames(),networks=[];for(var i=0;i<networkNames.length;i++){var uciInterface=uci.get('network',networkNames[i]);if(uciInterface==null||uciInterface['.type']!='interface')
continue;networks.push(Network.prototype.instantiateNetwork(networkNames[i]));}
networks.sort(networkSort);return networks;},getDevice:function(){return Network.prototype.instantiateDevice(this.getIfname());},isClientDisconnectSupported:function(){return L.isObject(this.ubus('hostapd','del_client'));},disconnectClient:function(mac,deauth,reason,ban_time){if(reason==null||reason==0)
reason=1;if(ban_time==0)
ban_time=null;return rpc.declare({object:'hostapd.%s'.format(this.getIfname()),method:'del_client',params:['addr','deauth','reason','ban_time']})(mac,deauth,reason,ban_time);}});WifiVlan=baseclass.extend({__init__:function(vlan){this.ifname=vlan.ifname;if(L.isObject(vlan.config)){this.vid=vlan.config.vid;this.name=vlan.config.name;if(Array.isArray(vlan.config.network)&&vlan.config.network.length)
this.network=vlan.config.network[0];}},getName:function(){return this.name;},getVlanId:function(){return this.vid;},getNetwork:function(){return this.network;},getIfname:function(){return this.ifname;},getI18n:function(){var name=this.name&&this.name!=this.vid?' ('+this.name+')':'';return'vlan %d%s'.format(this.vid,name);},});return Network;