(function() { var network_class = { deviceBlacklist: [ /^gre[0-9]+$/, /^gretap[0-9]+$/, /^ifb[0-9]+$/, /^ip6tnl[0-9]+$/, /^sit[0-9]+$/, /^wlan[0-9]+\.sta[0-9]+$/, /^tunl[0-9]+$/, /^ip6gre[0-9]+$/ ], rpcCacheFunctions: [ 'protolist', 0, L.rpc.declare({ object: 'network', method: 'get_proto_handlers', expect: { '': { } } }), 'ifstate', 1, L.rpc.declare({ object: 'network.interface', method: 'dump', expect: { 'interface': [ ] } }), 'devstate', 2, L.rpc.declare({ object: 'network.device', method: 'status', expect: { '': { } } }), 'wifistate', 0, L.rpc.declare({ object: 'network.wireless', method: 'status', expect: { '': { } } }), 'bwstate', 2, L.rpc.declare({ object: 'luci2.network.bwmon', method: 'statistics', expect: { 'statistics': { } } }), 'devlist', 2, L.rpc.declare({ object: 'luci2.network', method: 'device_list', expect: { 'devices': [ ] } }), 'swlist', 0, L.rpc.declare({ object: 'luci2.network', method: 'switch_list', expect: { 'switches': [ ] } }) ], loadProtocolHandler: function(proto) { var url = L.globals.resource + '/proto/' + proto + '.js'; var self = L.network; var def = $.Deferred(); $.ajax(url, { method: 'GET', cache: true, dataType: 'text' }).then(function(data) { try { var protoConstructorSource = ( '(function(L, $) { ' + 'return %s' + '})(L, $);\n\n' + '//@ sourceURL=%s/%s' ).format(data, window.location.origin, url); var protoClass = eval(protoConstructorSource); self.protocolHandlers[proto] = new protoClass(); } catch(e) { alert('Unable to instantiate proto "%s": %s'.format(url, e)); }; def.resolve(); }).fail(function() { def.resolve(); }); return def; }, loadProtocolHandlers: function() { var self = L.network; var deferreds = [ self.loadProtocolHandler('none') ]; for (var proto in self.rpcCache.protolist) deferreds.push(self.loadProtocolHandler(proto)); return $.when.apply($, deferreds); }, callSwitchInfo: L.rpc.declare({ object: 'luci2.network', method: 'switch_info', params: [ 'switch' ], expect: { 'info': { } } }), callSwitchInfoCallback: function(responses) { var self = L.network; var swlist = self.rpcCache.swlist; var swstate = self.rpcCache.swstate = { }; for (var i = 0; i < responses.length; i++) swstate[swlist[i]] = responses[i]; }, loadCacheCallback: function(level) { var self = L.network; var name = '_fetch_cache_cb_' + level; return self[name] || ( self[name] = function(responses) { for (var i = 0; i < self.rpcCacheFunctions.length; i += 3) if (!level || self.rpcCacheFunctions[i + 1] == level) self.rpcCache[self.rpcCacheFunctions[i]] = responses.shift(); if (!level) { L.rpc.batch(); for (var i = 0; i < self.rpcCache.swlist.length; i++) self.callSwitchInfo(self.rpcCache.swlist[i]); return L.rpc.flush().then(self.callSwitchInfoCallback); } return L.deferrable(); } ); }, loadCache: function(level) { var self = L.network; return L.uci.load(['network', 'wireless']).then(function() { L.rpc.batch(); for (var i = 0; i < self.rpcCacheFunctions.length; i += 3) if (!level || self.rpcCacheFunctions[i + 1] == level) self.rpcCacheFunctions[i + 2](); return L.rpc.flush().then(self.loadCacheCallback(level || 0)); }); }, isBlacklistedDevice: function(dev) { for (var i = 0; i < this.deviceBlacklist.length; i++) if (dev.match(this.deviceBlacklist[i])) return true; return false; }, sortDevicesCallback: function(a, b) { if (a.options.kind < b.options.kind) return -1; else if (a.options.kind > b.options.kind) return 1; if (a.options.name < b.options.name) return -1; else if (a.options.name > b.options.name) return 1; return 0; }, getDeviceObject: function(ifname) { var alias = (ifname.charAt(0) == '@'); return this.deviceObjects[ifname] || ( this.deviceObjects[ifname] = { ifname: ifname, kind: alias ? 'alias' : 'ethernet', type: alias ? 0 : 1, up: false, changed: { } } ); }, getInterfaceObject: function(name) { return this.interfaceObjects[name] || ( this.interfaceObjects[name] = { name: name, proto: this.protocolHandlers.none, changed: { } } ); }, loadDevicesCallback: function() { var self = L.network; var wificount = { }; for (var ifname in self.rpcCache.devstate) { if (self.isBlacklistedDevice(ifname)) continue; var dev = self.rpcCache.devstate[ifname]; var entry = self.getDeviceObject(ifname); entry.up = dev.up; switch (dev.type) { case 'IP tunnel': entry.kind = 'tunnel'; break; case 'Bridge': entry.kind = 'bridge'; //entry.ports = dev['bridge-members'].sort(); break; } } for (var i = 0; i < self.rpcCache.devlist.length; i++) { var dev = self.rpcCache.devlist[i]; if (self.isBlacklistedDevice(dev.device)) continue; var entry = self.getDeviceObject(dev.device); entry.up = dev.is_up; entry.type = dev.type; switch (dev.type) { case 1: /* Ethernet */ if (dev.is_bridge) entry.kind = 'bridge'; else if (dev.is_tuntap) entry.kind = 'tunnel'; else if (dev.is_wireless) entry.kind = 'wifi'; break; case 512: /* PPP */ case 768: /* IP-IP Tunnel */ case 769: /* IP6-IP6 Tunnel */ case 776: /* IPv6-in-IPv4 */ case 778: /* GRE over IP */ entry.kind = 'tunnel'; break; } } var net = L.uci.sections('network'); for (var i = 0; i < net.length; i++) { var s = net[i]; var sid = s['.name']; if (s['.type'] == 'device' && s.name) { var entry = self.getDeviceObject(s.name); switch (s.type) { case 'macvlan': case 'tunnel': entry.kind = 'tunnel'; break; } entry.sid = sid; } else if (s['.type'] == 'interface' && !s['.anonymous'] && s.ifname) { var ifnames = L.toArray(s.ifname); for (var j = 0; j < ifnames.length; j++) self.getDeviceObject(ifnames[j]); if (s['.name'] != 'loopback') { var entry = self.getDeviceObject('@%s'.format(s['.name'])); entry.type = 0; entry.kind = 'alias'; entry.sid = sid; } } else if (s['.type'] == 'switch_vlan' && s.device) { var sw = self.rpcCache.swstate[s.device]; var vid = parseInt(s.vid || s.vlan); var ports = L.toArray(s.ports); if (!sw || !ports.length || isNaN(vid)) continue; var ifname = undefined; for (var j = 0; j < ports.length; j++) { var port = parseInt(ports[j]); var tag = (ports[j].replace(/[^tu]/g, '') == 't'); if (port == sw.cpu_port) { // XXX: need a way to map switch to netdev if (tag) ifname = 'eth0.%d'.format(vid); else ifname = 'eth0'; break; } } if (!ifname) continue; var entry = self.getDeviceObject(ifname); entry.kind = 'vlan'; entry.sid = sid; entry.vsw = sw; entry.vid = vid; } } var wifi = L.uci.sections('wireless'); for (var i = 0, c = 0; i < wifi.length; i++) { var s = wifi[i]; if (s['.type'] == 'wifi-iface') { var sid = '@wifi-iface[%d]'.format(c++); if (!s.device) continue; var r = parseInt(s.device.replace(/^[^0-9]+/, '')); var n = wificount[s.device] = (wificount[s.device] || 0) + 1; var id = 'radio%d.network%d'.format(r, n); var ifname = id; if (self.rpcCache.wifistate[s.device]) { var ifcs = self.rpcCache.wifistate[s.device].interfaces; for (var ifc in ifcs) { if (ifcs[ifc].section == sid && ifcs[ifc].ifname) { ifname = ifcs[ifc].ifname; break; } } } var entry = self.getDeviceObject(ifname); entry.kind = 'wifi'; entry.sid = s['.name']; entry.wid = id; entry.wdev = s.device; entry.wmode = s.mode; entry.wssid = s.ssid; entry.wbssid = s.bssid; } } for (var i = 0; i < net.length; i++) { var s = net[i]; var sid = s['.name']; if (s['.type'] == 'interface' && !s['.anonymous'] && s.type == 'bridge') { var ifnames = L.toArray(s.ifname); for (var ifname in self.deviceObjects) { var dev = self.deviceObjects[ifname]; if (dev.kind != 'wifi') continue; var wnets = L.toArray(L.uci.get('wireless', dev.sid, 'network')); if ($.inArray(sid, wnets) > -1) ifnames.push(ifname); } entry = self.getDeviceObject('br-%s'.format(s['.name'])); entry.type = 1; entry.kind = 'bridge'; entry.sid = sid; entry.ports = ifnames.sort(); } } }, loadInterfacesCallback: function() { var self = L.network; var net = L.uci.sections('network'); for (var i = 0; i < net.length; i++) { var s = net[i]; var sid = s['.name']; if (s['.type'] == 'interface' && !s['.anonymous'] && s.proto) { var entry = self.getInterfaceObject(s['.name']); var proto = self.protocolHandlers[s.proto] || self.protocolHandlers.none; var l3dev = undefined; var l2dev = undefined; var ifnames = L.toArray(s.ifname); for (var ifname in self.deviceObjects) { var dev = self.deviceObjects[ifname]; if (dev.kind != 'wifi') continue; var wnets = L.toArray(L.uci.get('wireless', dev.sid, 'network')); if ($.inArray(entry.name, wnets) > -1) ifnames.push(ifname); } if (proto.virtual) l3dev = '%s-%s'.format(s.proto, entry.name); else if (s.type == 'bridge') l3dev = 'br-%s'.format(entry.name); else l3dev = ifnames[0]; if (!proto.virtual && s.type == 'bridge') l2dev = 'br-%s'.format(entry.name); else if (!proto.virtual) l2dev = ifnames[0]; entry.proto = proto; entry.sid = sid; entry.l3dev = l3dev; entry.l2dev = l2dev; } } for (var i = 0; i < self.rpcCache.ifstate.length; i++) { var iface = self.rpcCache.ifstate[i]; var entry = self.getInterfaceObject(iface['interface']); var proto = self.protocolHandlers[iface.proto] || self.protocolHandlers.none; /* this is a virtual interface, either deleted from config but not applied yet or set up from external tools (6rd) */ if (!entry.sid) { entry.proto = proto; entry.l2dev = iface.device; entry.l3dev = iface.l3_device; } } }, load: function() { var self = this; if (self.rpcCache) return L.deferrable(); self.rpcCache = { }; self.deviceObjects = { }; self.interfaceObjects = { }; self.protocolHandlers = { }; return self.loadCache() .then(self.loadProtocolHandlers) .then(self.loadDevicesCallback) .then(self.loadInterfacesCallback); }, update: function() { delete this.rpcCache; return this.load(); }, refreshInterfaceStatus: function() { return this.loadCache(1).then(this.loadInterfacesCallback); }, refreshDeviceStatus: function() { return this.loadCache(2).then(this.loadDevicesCallback); }, refreshStatus: function() { return this.loadCache(1) .then(this.loadCache(2)) .then(this.loadDevicesCallback) .then(this.loadInterfacesCallback); }, getDevices: function() { var devs = [ ]; for (var ifname in this.deviceObjects) if (ifname != 'lo') devs.push(new L.network.Device(this.deviceObjects[ifname])); return devs.sort(this.sortDevicesCallback); }, getDeviceByInterface: function(iface) { if (iface instanceof L.network.Interface) iface = iface.name(); if (this.interfaceObjects[iface]) return this.getDevice(this.interfaceObjects[iface].l3dev) || this.getDevice(this.interfaceObjects[iface].l2dev); return undefined; }, getDevice: function(ifname) { if (this.deviceObjects[ifname]) return new L.network.Device(this.deviceObjects[ifname]); return undefined; }, createDevice: function(name) { return new L.network.Device(this.getDeviceObject(name)); }, getInterfaces: function() { var ifaces = [ ]; for (var name in this.interfaceObjects) if (name != 'loopback') ifaces.push(this.getInterface(name)); ifaces.sort(function(a, b) { if (a.name() < b.name()) return -1; else if (a.name() > b.name()) return 1; else return 0; }); return ifaces; }, getInterfacesByDevice: function(dev) { var ifaces = [ ]; if (dev instanceof L.network.Device) dev = dev.name(); for (var name in this.interfaceObjects) { var iface = this.interfaceObjects[name]; if (iface.l2dev == dev || iface.l3dev == dev) ifaces.push(this.getInterface(name)); } ifaces.sort(function(a, b) { if (a.name() < b.name()) return -1; else if (a.name() > b.name()) return 1; else return 0; }); return ifaces; }, getInterface: function(iface) { if (this.interfaceObjects[iface]) return new L.network.Interface(this.interfaceObjects[iface]); return undefined; }, getProtocols: function() { var rv = [ ]; for (var proto in this.protocolHandlers) { var pr = this.protocolHandlers[proto]; rv.push({ name: proto, description: pr.description, virtual: pr.virtual, tunnel: pr.tunnel }); } return rv.sort(function(a, b) { if (a.name < b.name) return -1; else if (a.name > b.name) return 1; else return 0; }); }, findWANByAddr: function(ipaddr) { for (var i = 0; i < this.rpcCache.ifstate.length; i++) { var ifstate = this.rpcCache.ifstate[i]; if (!ifstate.route) continue; for (var j = 0; j < ifstate.route.length; j++) if (ifstate.route[j].mask == 0 && ifstate.route[j].target == ipaddr && typeof(ifstate.route[j].table) == 'undefined') { return this.getInterface(ifstate['interface']); } } return undefined; }, findWAN: function() { return this.findWANByAddr('0.0.0.0'); }, findWAN6: function() { return this.findWANByAddr('::'); }, resolveAlias: function(ifname) { if (ifname instanceof L.network.Device) ifname = ifname.name(); var dev = this.deviceObjects[ifname]; var seen = { }; while (dev && dev.kind == 'alias') { // loop if (seen[dev.ifname]) return undefined; var ifc = this.interfaceObjects[dev.sid]; seen[dev.ifname] = true; dev = ifc ? this.deviceObjects[ifc.l3dev] : undefined; } return dev ? this.getDevice(dev.ifname) : undefined; } }; network_class.Interface = Class.extend({ getStatus: function(key) { var s = L.network.rpcCache.ifstate; for (var i = 0; i < s.length; i++) if (s[i]['interface'] == this.options.name) return key ? s[i][key] : s[i]; return undefined; }, get: function(key) { return L.uci.get('network', this.options.name, key); }, set: function(key, val) { return L.uci.set('network', this.options.name, key, val); }, name: function() { return this.options.name; }, protocol: function() { return (this.get('proto') || 'none'); }, isUp: function() { return (this.getStatus('up') === true); }, isVirtual: function() { return (typeof(this.options.sid) != 'string'); }, getProtocol: function() { var prname = this.get('proto') || 'none'; return L.network.protocolHandlers[prname] || L.network.protocolHandlers.none; }, getUptime: function() { var uptime = this.getStatus('uptime'); return isNaN(uptime) ? 0 : uptime; }, getDevice: function(resolveAlias) { if (this.options.l3dev) return L.network.getDevice(this.options.l3dev); return undefined; }, getPhysdev: function() { if (this.options.l2dev) return L.network.getDevice(this.options.l2dev); return undefined; }, getSubdevices: function() { var rv = [ ]; var dev = this.options.l2dev ? L.network.deviceObjects[this.options.l2dev] : undefined; if (dev && dev.kind == 'bridge' && dev.ports && dev.ports.length) for (var i = 0; i < dev.ports.length; i++) rv.push(L.network.getDevice(dev.ports[i])); return rv; }, getIPv4Addrs: function(mask) { var rv = [ ]; var addrs = this.getStatus('ipv4-address'); if (addrs) for (var i = 0; i < addrs.length; i++) if (!mask) rv.push(addrs[i].address); else rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask)); return rv; }, getIPv6Addrs: function(mask) { var rv = [ ]; var addrs; addrs = this.getStatus('ipv6-address'); if (addrs) for (var i = 0; i < addrs.length; i++) if (!mask) rv.push(addrs[i].address); else rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask)); addrs = this.getStatus('ipv6-prefix-assignment'); if (addrs) for (var i = 0; i < addrs.length; i++) if (!mask) rv.push('%s1'.format(addrs[i].address)); else rv.push('%s1/%d'.format(addrs[i].address, addrs[i].mask)); return rv; }, getDNSAddrs: function() { var rv = [ ]; var addrs = this.getStatus('dns-server'); if (addrs) for (var i = 0; i < addrs.length; i++) rv.push(addrs[i]); return rv; }, getIPv4DNS: function() { var rv = [ ]; var dns = this.getStatus('dns-server'); if (dns) for (var i = 0; i < dns.length; i++) if (dns[i].indexOf(':') == -1) rv.push(dns[i]); return rv; }, getIPv6DNS: function() { var rv = [ ]; var dns = this.getStatus('dns-server'); if (dns) for (var i = 0; i < dns.length; i++) if (dns[i].indexOf(':') > -1) rv.push(dns[i]); return rv; }, getIPv4Gateway: function() { var rt = this.getStatus('route'); if (rt) for (var i = 0; i < rt.length; i++) if (rt[i].target == '0.0.0.0' && rt[i].mask == 0) return rt[i].nexthop; return undefined; }, getIPv6Gateway: function() { var rt = this.getStatus('route'); if (rt) for (var i = 0; i < rt.length; i++) if (rt[i].target == '::' && rt[i].mask == 0) return rt[i].nexthop; return undefined; }, getStatistics: function() { var dev = this.getDevice() || new L.network.Device({}); return dev.getStatistics(); }, getTrafficHistory: function() { var dev = this.getDevice() || new L.network.Device({}); return dev.getTrafficHistory(); }, renderBadge: function() { var badge = $('') .addClass('badge') .text('%s: '.format(this.name())); var dev = this.getDevice(); var subdevs = this.getSubdevices(); if (subdevs.length) for (var j = 0; j < subdevs.length; j++) badge.append($('') .attr('src', subdevs[j].icon()) .attr('title', '%s (%s)'.format(subdevs[j].description(), subdevs[j].name() || '?'))); else if (dev) badge.append($('') .attr('src', dev.icon()) .attr('title', '%s (%s)'.format(dev.description(), dev.name() || '?'))); else badge.append($('').text(L.tr('(No devices attached)'))); return badge; }, setDevices: function(devs) { var dev = this.getPhysdev(); var old_devs = [ ]; var changed = false; if (dev && dev.isBridge()) old_devs = this.getSubdevices(); else if (dev) old_devs = [ dev ]; if (old_devs.length != devs.length) changed = true; else for (var i = 0; i < old_devs.length; i++) { var dev = devs[i]; if (dev instanceof L.network.Device) dev = dev.name(); if (!dev || old_devs[i].name() != dev) { changed = true; break; } } if (changed) { for (var i = 0; i < old_devs.length; i++) old_devs[i].removeFromInterface(this); for (var i = 0; i < devs.length; i++) { var dev = devs[i]; if (!(dev instanceof L.network.Device)) dev = L.network.getDevice(dev); if (dev) dev.attachToInterface(this); } } }, changeProtocol: function(proto) { var pr = L.network.protocolHandlers[proto]; if (!pr) return; for (var opt in (this.get() || { })) { switch (opt) { case 'type': case 'ifname': case 'macaddr': if (pr.virtual) this.set(opt, undefined); break; case 'auto': case 'mtu': break; case 'proto': this.set(opt, pr.protocol); break; default: this.set(opt, undefined); break; } } }, createFormPrepareCallback: function() { var map = this; var iface = map.options.netIface; var proto = iface.getProtocol(); var device = iface.getDevice(); map.options.caption = L.tr('Configure "%s"').format(iface.name()); var section = map.section(L.cbi.SingleSection, iface.name(), { anonymous: true }); section.tab({ id: 'general', caption: L.tr('General Settings') }); section.tab({ id: 'advanced', caption: L.tr('Advanced Settings') }); section.tab({ id: 'ipv6', caption: L.tr('IPv6') }); section.tab({ id: 'physical', caption: L.tr('Physical Settings') }); section.taboption('general', L.cbi.CheckboxValue, 'auto', { caption: L.tr('Start on boot'), optional: true, initial: true }); var pr = section.taboption('general', L.cbi.ListValue, 'proto', { caption: L.tr('Protocol') }); pr.ucivalue = function(sid) { return iface.get('proto') || 'none'; }; var ok = section.taboption('general', L.cbi.ButtonValue, '_confirm', { caption: L.tr('Really switch?'), description: L.tr('Changing the protocol will clear all configuration for this interface!'), text: L.tr('Change protocol') }); ok.on('click', function(ev) { iface.changeProtocol(pr.formvalue(ev.data.sid)); iface.createForm(mapwidget).show(); }); var protos = L.network.getProtocols(); for (var i = 0; i < protos.length; i++) pr.value(protos[i].name, protos[i].description); proto.populateForm(section, iface); if (!proto.virtual) { var br = section.taboption('physical', L.cbi.CheckboxValue, 'type', { caption: L.tr('Network bridge'), description: L.tr('Merges multiple devices into one logical bridge'), optional: true, enabled: 'bridge', disabled: '', initial: '' }); section.taboption('physical', L.cbi.DeviceList, '__iface_multi', { caption: L.tr('Devices'), multiple: true, bridges: false }).depends('type', true); section.taboption('physical', L.cbi.DeviceList, '__iface_single', { caption: L.tr('Device'), multiple: false, bridges: true }).depends('type', false); var mac = section.taboption('physical', L.cbi.InputValue, 'macaddr', { caption: L.tr('Override MAC'), optional: true, placeholder: device ? device.getMACAddress() : undefined, datatype: 'macaddr' }) mac.ucivalue = function(sid) { if (device) return device.get('macaddr'); return this.callSuper('ucivalue', sid); }; mac.save = function(sid) { if (!this.changed(sid)) return false; if (device) device.set('macaddr', this.formvalue(sid)); else this.callSuper('set', sid); return true; }; } section.taboption('physical', L.cbi.InputValue, 'mtu', { caption: L.tr('Override MTU'), optional: true, placeholder: device ? device.getMTU() : undefined, datatype: 'range(1, 9000)' }); section.taboption('physical', L.cbi.InputValue, 'metric', { caption: L.tr('Override Metric'), optional: true, placeholder: 0, datatype: 'uinteger' }); for (var field in section.fields) { switch (field) { case 'proto': break; case '_confirm': for (var i = 0; i < protos.length; i++) if (protos[i].name != proto.protocol) section.fields[field].depends('proto', protos[i].name); break; default: section.fields[field].depends('proto', proto.protocol, true); break; } } }, createForm: function(mapwidget) { var self = this; if (!mapwidget) mapwidget = L.cbi.Map; var map = new mapwidget('network', { prepare: self.createFormPrepareCallback, netIface: self }); return map; } }); network_class.Device = Class.extend({ wifiModeStrings: { ap: L.tr('Master'), sta: L.tr('Client'), adhoc: L.tr('Ad-Hoc'), monitor: L.tr('Monitor'), wds: L.tr('Static WDS') }, getStatus: function(key) { var s = L.network.rpcCache.devstate[this.options.ifname]; if (s) return key ? s[key] : s; return undefined; }, get: function(key) { var sid = this.options.sid; var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network'; return L.uci.get(pkg, sid, key); }, set: function(key, val) { var sid = this.options.sid; var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network'; return L.uci.set(pkg, sid, key, val); }, init: function() { if (typeof(this.options.type) == 'undefined') this.options.type = 1; if (typeof(this.options.kind) == 'undefined') this.options.kind = 'ethernet'; if (typeof(this.options.networks) == 'undefined') this.options.networks = [ ]; }, name: function() { return this.options.ifname; }, description: function() { switch (this.options.kind) { case 'alias': return L.tr('Alias for network "%s"').format(this.options.ifname.substring(1)); case 'bridge': return L.tr('Network bridge'); case 'ethernet': return L.tr('Network device'); case 'tunnel': switch (this.options.type) { case 1: /* tuntap */ return L.tr('TAP device'); case 512: /* PPP */ return L.tr('PPP tunnel'); case 768: /* IP-IP Tunnel */ return L.tr('IP-in-IP tunnel'); case 769: /* IP6-IP6 Tunnel */ return L.tr('IPv6-in-IPv6 tunnel'); case 776: /* IPv6-in-IPv4 */ return L.tr('IPv6-over-IPv4 tunnel'); break; case 778: /* GRE over IP */ return L.tr('GRE-over-IP tunnel'); default: return L.tr('Tunnel device'); } case 'vlan': return L.tr('VLAN %d on %s').format(this.options.vid, this.options.vsw.model); case 'wifi': var o = this.options; return L.trc('(Wifi-Mode) "(SSID)" on (radioX)', '%s "%h" on %s').format( o.wmode ? this.wifiModeStrings[o.wmode] : L.tr('Unknown mode'), o.wssid || '?', o.wdev ); } return L.tr('Unknown device'); }, icon: function(up) { var kind = this.options.kind; if (kind == 'alias') kind = 'ethernet'; if (typeof(up) == 'undefined') up = this.isUp(); return L.globals.resource + '/icons/%s%s.png'.format(kind, up ? '' : '_disabled'); }, isUp: function() { var l = L.network.rpcCache.devlist; for (var i = 0; i < l.length; i++) if (l[i].device == this.options.ifname) return (l[i].is_up === true); return false; }, isAlias: function() { return (this.options.kind == 'alias'); }, isBridge: function() { return (this.options.kind == 'bridge'); }, isBridgeable: function() { return (this.options.type == 1 && this.options.kind != 'bridge'); }, isWireless: function() { return (this.options.kind == 'wifi'); }, isInNetwork: function(net) { if (!(net instanceof L.network.Interface)) net = L.network.getInterface(net); if (net) { if (net.options.l3dev == this.options.ifname || net.options.l2dev == this.options.ifname) return true; var dev = L.network.deviceObjects[net.options.l2dev]; if (dev && dev.kind == 'bridge' && dev.ports) return ($.inArray(this.options.ifname, dev.ports) > -1); } return false; }, getMTU: function() { var dev = L.network.rpcCache.devstate[this.options.ifname]; if (dev && !isNaN(dev.mtu)) return dev.mtu; return undefined; }, getMACAddress: function() { if (this.options.type != 1) return undefined; var dev = L.network.rpcCache.devstate[this.options.ifname]; if (dev && dev.macaddr) return dev.macaddr.toUpperCase(); return undefined; }, getInterfaces: function() { return L.network.getInterfacesByDevice(this.options.name); }, getStatistics: function() { var s = this.getStatus('statistics') || { }; return { rx_bytes: (s.rx_bytes || 0), tx_bytes: (s.tx_bytes || 0), rx_packets: (s.rx_packets || 0), tx_packets: (s.tx_packets || 0) }; }, getTrafficHistory: function() { var def = new Array(120); for (var i = 0; i < 120; i++) def[i] = 0; var h = L.network.rpcCache.bwstate[this.options.ifname] || { }; return { rx_bytes: (h.rx_bytes || def), tx_bytes: (h.tx_bytes || def), rx_packets: (h.rx_packets || def), tx_packets: (h.tx_packets || def) }; }, removeFromInterface: function(iface) { if (!(iface instanceof L.network.Interface)) iface = L.network.getInterface(iface); if (!iface) return; var ifnames = L.toArray(iface.get('ifname')); if ($.inArray(this.options.ifname, ifnames) > -1) iface.set('ifname', L.filterArray(ifnames, this.options.ifname)); if (this.options.kind != 'wifi') return; var networks = L.toArray(this.get('network')); if ($.inArray(iface.name(), networks) > -1) this.set('network', L.filterArray(networks, iface.name())); }, attachToInterface: function(iface) { if (!(iface instanceof L.network.Interface)) iface = L.network.getInterface(iface); if (!iface) return; if (this.options.kind != 'wifi') { var ifnames = L.toArray(iface.get('ifname')); if ($.inArray(this.options.ifname, ifnames) < 0) { ifnames.push(this.options.ifname); iface.set('ifname', (ifnames.length > 1) ? ifnames : ifnames[0]); } } else { var networks = L.toArray(this.get('network')); if ($.inArray(iface.name(), networks) < 0) { networks.push(iface.name()); this.set('network', (networks.length > 1) ? networks : networks[0]); } } } }); network_class.Protocol = network_class.Interface.extend({ description: '__unknown__', tunnel: false, virtual: false, populateForm: function(section, iface) { } }); return Class.extend(network_class); })();