luci-base: network.js: overhaul code
authorJo-Philipp Wich <jo@mein.io>
Wed, 14 Aug 2019 16:17:10 +0000 (18:17 +0200)
committerJo-Philipp Wich <jo@mein.io>
Wed, 14 Aug 2019 20:58:15 +0000 (22:58 +0200)
 - Rework internal state management
 - Implement new utility functions such as getL3Device() or getModemType()
 - Fix bugs in various functions

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/htdocs/luci-static/resources/network.js

index 487adabb45dd34f2cc1c058f1d5cb65461b49581..d6a97408a253706c81ff998454a9493d7af724d5 100644 (file)
@@ -90,119 +90,98 @@ var callGetProtoHandlers = rpc.declare({
        expect: { '': {} }
 });
 
-var _cache = {},
-    _flush = true,
+var _init = null,
     _state = null,
-    _protocols = {};
-
-function getWifiState(flush) {
-       if (_cache.wifi == null || flush)
-               return callNetworkWirelessStatus().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.wifi = state);
-               }).catch(function() {
-                       return (_cache.wifi = {});
-               });
-
-       return Promise.resolve(_cache.wifi);
+    _protocols = {},
+    _protospecs = {};
+
+function getWifiState(cache) {
+       return callNetworkWirelessStatus().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
 }
 
-function getInterfaceState(flush) {
-       if (_cache.interfacedump == null || flush)
-               return callNetworkInterfaceStatus().then(function(state) {
-                       if (!Array.isArray(state))
-                               throw !1;
-                       return (_cache.interfacedump = state);
-               }).catch(function() {
-                       return (_cache.interfacedump = []);
-               });
-
-       return Promise.resolve(_cache.interfacedump);
+function getInterfaceState(cache) {
+       return callNetworkInterfaceStatus().then(function(state) {
+               if (!Array.isArray(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return [];
+       });
 }
 
-function getDeviceState(flush) {
-       if (_cache.devicedump == null || flush)
-               return callNetworkDeviceStatus().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.devicedump = state);
-               }).catch(function() {
-                       return (_cache.devicedump = {});
-               });
-
-       return Promise.resolve(_cache.devicedump);
+function getDeviceState(cache) {
+       return callNetworkDeviceStatus().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
 }
 
-function getIfaddrState(flush) {
-       if (_cache.ifaddrs == null || flush)
-               return callLuciIfaddrs().then(function(addrs) {
-                       if (!Array.isArray(addrs))
-                               throw !1;
-                       return (_cache.ifaddrs = addrs);
-               }).catch(function() {
-                       return (_cache.ifaddrs = []);
-               });
-
-       return Promise.resolve(_cache.ifaddrs);
+function getIfaddrState(cache) {
+       return callLuciIfaddrs().then(function(addrs) {
+               if (!Array.isArray(addrs))
+                       throw !1;
+               return addrs;
+       }).catch(function() {
+               return [];
+       });
 }
 
-function getNetdevState(flush) {
-       if (_cache.devices == null || flush)
-               return callLuciNetdevs().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.devices = state);
-               }).catch(function() {
-                       return (_cache.devices = {});
-               });
-
-       return Promise.resolve(_cache.devices);
+function getNetdevState(cache) {
+       return callLuciNetdevs().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
 }
 
-function getBoardState(flush) {
-       if (_cache.board == null || flush)
-               return callLuciBoardjson().then(function(state) {
-                       if (!L.isObject(state))
-                               throw !1;
-                       return (_cache.board = state);
-               }).catch(function() {
-                       return (_cache.board = {});
-               });
-
-       return Promise.resolve(_cache.board);
+function getBoardState(cache) {
+       return callLuciBoardjson().then(function(state) {
+               if (!L.isObject(state))
+                       throw !1;
+               return state;
+       }).catch(function() {
+               return {};
+       });
 }
 
-function getProtocolHandlers(flush) {
-       if (_cache.protocols == null || flush)
-               return callGetProtoHandlers().then(function(protos) {
-                       if (!L.isObject(protos))
-                               throw !1;
+function getProtocolHandlers(cache) {
+       return callGetProtoHandlers().then(function(protos) {
+               if (!L.isObject(protos))
+                       throw !1;
 
-                       _cache.protocols = protos;
+               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 _cache.protocols;
+               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);
                        });
-               }).catch(function() {
-                       return (_cache.protocols = {});
+               })).then(function() {
+                       return protos;
                });
-
-       return Promise.resolve(_cache.protocols);
+       }).catch(function() {
+               return {};
+       });
 }
 
 function getWifiStateBySid(sid) {
        var s = uci.get('wireless', sid);
 
        if (s != null && s['.type'] == 'wifi-iface') {
-               for (var radioname in _cache.wifi) {
-                       for (var i = 0; i < _cache.wifi[radioname].interfaces.length; i++) {
-                               var netstate = _cache.wifi[radioname].interfaces[i];
+               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;
@@ -210,7 +189,7 @@ function getWifiStateBySid(sid) {
                                var s2 = uci.get('wireless', netstate.section);
 
                                if (s2 != null && s['.type'] == s2['.type'] && s['.name'] == s2['.name'])
-                                       return [ radioname, _cache.wifi[radioname], netstate ];
+                                       return [ radioname, _state.radios[radioname], netstate ];
                        }
                }
        }
@@ -219,15 +198,15 @@ function getWifiStateBySid(sid) {
 }
 
 function getWifiStateByIfname(ifname) {
-       for (var radioname in _cache.wifi) {
-               for (var i = 0; i < _cache.wifi[radioname].interfaces.length; i++) {
-                       var netstate = _cache.wifi[radioname].interfaces[i];
+       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, _cache.wifi[radioname], netstate ];
+                               return [ radioname, _state.radios[radioname], netstate ];
                }
        }
 
@@ -367,7 +346,7 @@ function appendValue(config, section, option, value) {
            rv = false;
 
        if (isArray == false)
-               values = String(values || '').split(/\s+/);
+               values = L.toArray(values);
 
        if (values.indexOf(value) == -1) {
                values.push(value);
@@ -385,7 +364,7 @@ function removeValue(config, section, option, value) {
            rv = false;
 
        if (isArray == false)
-               values = String(values || '').split(/\s+/);
+               values = L.toArray(values);
 
        for (var i = values.length - 1; i >= 0; i--) {
                if (values[i] == value) {
@@ -444,159 +423,160 @@ function maskToPrefix(mask, v6) {
        return bits;
 }
 
-function initNetworkState() {
-       var flush = _flush;
-
-       _flush = false;
-
-       if (_state != null && !flush)
-               return Promise.resolve(_state);
-
-       if (_cache.pendingInit != null)
-               return Promise.resolve(_cache.pendingInit);
-
-       return (_cache.pendingInit = Promise.all([
-               getInterfaceState(flush), getDeviceState(flush), getBoardState(flush),
-               getWifiState(flush), getIfaddrState(flush), getNetdevState(flush), getProtocolHandlers(flush),
-               uci.load('network'), uci.load('wireless'), uci.load('luci')
-       ]).finally(function() {
-               var ifaddrs = _cache.ifaddrs,
-                   devices = _cache.devices,
-                   board = _cache.board,
-                   s = { isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {}, interfaces: {}, bridges: {}, switches: {} };
-
-               for (var i = 0, a; (a = ifaddrs[i]) != null; i++) {
-                       var name = a.name.replace(/:.+$/, '');
-
-                       if (isVirtualIfname(name))
-                               s.isTunnel[name] = true;
-
-                       if (s.isTunnel[name] || !(isIgnoredIfname(name) || isVirtualIfname(name))) {
-                               s.interfaces[name] = s.interfaces[name] || {
-                                       idx:      a.ifindex || i,
-                                       name:     name,
-                                       rawname:  a.name,
-                                       flags:    [],
-                                       ipaddrs:  [],
-                                       ip6addrs: []
-                               };
-
-                               if (a.family == 'packet') {
-                                       s.interfaces[name].flags   = a.flags;
-                                       s.interfaces[name].stats   = a.data;
-                                       s.interfaces[name].macaddr = a.addr;
-                               }
-                               else if (a.family == 'inet') {
-                                       s.interfaces[name].ipaddrs.push(a.addr + '/' + a.netmask);
-                               }
-                               else if (a.family == 'inet6') {
-                                       s.interfaces[name].ip6addrs.push(a.addr + '/' + a.netmask);
+function initNetworkState(refresh) {
+       if (_state == null || refresh) {
+               _init = _init || Promise.all([
+                       getInterfaceState(), getDeviceState(), getBoardState(),
+                       getWifiState(), getIfaddrState(), getNetdevState(), getProtocolHandlers(),
+                       uci.load('network'), uci.load('wireless'), uci.load('luci')
+               ]).then(function(data) {
+                       var board = data[2], ifaddrs = data[4], devices = data[5];
+                       var s = {
+                               isTunnel: {}, isBridge: {}, isSwitch: {}, isWifi: {},
+                               ifaces: data[0], devices: data[1], radios: data[3],
+                               netdevs: {}, bridges: {}, switches: {}
+                       };
+
+                       for (var i = 0, a; (a = ifaddrs[i]) != null; i++) {
+                               var name = a.name.replace(/:.+$/, '');
+
+                               if (isVirtualIfname(name))
+                                       s.isTunnel[name] = true;
+
+                               if (s.isTunnel[name] || !(isIgnoredIfname(name) || isVirtualIfname(name))) {
+                                       s.netdevs[name] = s.netdevs[name] || {
+                                               idx:      a.ifindex || i,
+                                               name:     name,
+                                               rawname:  a.name,
+                                               flags:    [],
+                                               ipaddrs:  [],
+                                               ip6addrs: []
+                                       };
+
+                                       if (a.family == 'packet') {
+                                               s.netdevs[name].flags   = a.flags;
+                                               s.netdevs[name].stats   = a.data;
+                                               s.netdevs[name].macaddr = a.addr;
+                                       }
+                                       else if (a.family == 'inet') {
+                                               s.netdevs[name].ipaddrs.push(a.addr + '/' + a.netmask);
+                                       }
+                                       else if (a.family == 'inet6') {
+                                               s.netdevs[name].ip6addrs.push(a.addr + '/' + a.netmask);
+                                       }
                                }
                        }
-               }
 
-               for (var devname in devices) {
-                       var dev = devices[devname];
+                       for (var devname in devices) {
+                               var dev = devices[devname];
+
+                               if (dev.bridge) {
+                                       var b = {
+                                               name:    devname,
+                                               id:      dev.id,
+                                               stp:     dev.stp,
+                                               ifnames: []
+                                       };
 
-                       if (dev.bridge) {
-                               var b = {
-                                       name:    devname,
-                                       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]];
 
-                               for (var i = 0; dev.ports && i < dev.ports.length; i++) {
-                                       var subdev = s.interfaces[dev.ports[i]];
+                                               if (subdev == null)
+                                                       continue;
 
-                                       if (subdev == null)
-                                               continue;
+                                               b.ifnames.push(subdev);
+                                               subdev.bridge = b;
+                                       }
 
-                                       b.ifnames.push(subdev);
-                                       subdev.bridge = b;
+                                       s.bridges[devname] = b;
+                                       s.isBridge[devname] = true;
                                }
 
-                               s.bridges[devname] = b;
-                       }
-
-                       if (s.interfaces.hasOwnProperty(devname)) {
-                               Object.assign(s.interfaces[devname], {
-                                       macaddr: dev.mac,
-                                       type:    dev.type,
-                                       mtu:     dev.mtu,
-                                       qlen:    dev.qlen
-                               });
+                               if (s.netdevs.hasOwnProperty(devname)) {
+                                       Object.assign(s.netdevs[devname], {
+                                               macaddr: dev.mac,
+                                               type:    dev.type,
+                                               mtu:     dev.mtu,
+                                               qlen:    dev.qlen
+                                       });
+                               }
                        }
-               }
 
-               if (L.isObject(board.switch)) {
-                       for (var switchname in board.switch) {
-                               var layout = board.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;
+                       if (L.isObject(board.switch)) {
+                               for (var switchname in board.switch) {
+                                       var layout = board.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.push(spec);
+                                               ports.sort(function(a, b) {
+                                                       if (a.role != b.role)
+                                                               return (a.role < b.role) ? -1 : 1;
 
-                                                       if (port.role != null)
-                                                               nports[port.role] = (nports[port.role] || 0) + 1;
-                                               }
-                                       }
+                                                       return (a.index - b.index);
+                                               });
 
-                                       ports.sort(function(a, b) {
-                                               if (a.role != b.role)
-                                                       return (a.role < b.role) ? -1 : 1;
+                                               for (var i = 0, port; (port = ports[i]) != null; i++) {
+                                                       if (port.role != role) {
+                                                               role = port.role;
+                                                               pnum = 1;
+                                                       }
 
-                                               return (a.index - b.index);
-                                       });
+                                                       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();
 
-                                       for (var i = 0, port; (port = ports[i]) != null; i++) {
-                                               if (port.role != role) {
-                                                       role = port.role;
-                                                       pnum = 1;
+                                                       delete port.role;
+                                                       delete port.index;
                                                }
 
-                                               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
+                                               };
                                        }
-
-                                       s.switches[switchname] = {
-                                               ports: ports,
-                                               netdevs: netdevs
-                                       };
                                }
                        }
-               }
 
-               delete _cache.pendingInit;
+                       if (L.isObject(board.dsl) && L.isObject(board.dsl.modem)) {
+                               s.hasDSLModem = board.dsl.modem;
+                       }
 
-               return (_state = s);
-       }));
+                       _init = null;
+
+                       return (_state = s);
+               });
+       }
+
+       return (_state != null ? Promise.resolve(_state) : _init);
 }
 
 function ifnameOf(obj) {
@@ -637,9 +617,8 @@ Network = L.Class.extend({
        maskToPrefix: maskToPrefix,
 
        flushCache: function() {
-               return Promise.resolve(_state).then(function() {
-                       _flush = true;
-               });
+               initNetworkState(true);
+               return _init;
        },
 
        getProtocol: function(protoname, netname) {
@@ -660,7 +639,7 @@ Network = L.Class.extend({
        },
 
        registerProtocol: function(protoname, methods) {
-               var spec = L.isObject(_cache.protocols) ? _cache.protocols[protoname] : null;
+               var spec = L.isObject(_protospecs) ? _protospecs[protoname] : null;
                var proto = Protocol.extend(Object.assign({
                        getI18n: function() {
                                return protoname;
@@ -740,9 +719,9 @@ Network = L.Class.extend({
                                return this.instantiateNetwork(name);
                        }
                        else if (name != null) {
-                               for (var i = 0; i < _cache.interfacedump.length; i++)
-                                       if (_cache.interfacedump[i].interface == name)
-                                               return this.instantiateNetwork(name, _cache.interfacedump[i].proto);
+                               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;
@@ -757,10 +736,10 @@ Network = L.Class.extend({
                        for (var i = 0; i < uciInterfaces.length; i++)
                                networks[uciInterfaces[i]['.name']] = this.instantiateNetwork(uciInterfaces[i]['.name']);
 
-                       for (var i = 0; i < _cache.interfacedump.length; i++)
-                               if (networks[_cache.interfacedump[i].interface] == null)
-                                       networks[_cache.interfacedump[i].interface] =
-                                               this.instantiateNetwork(_cache.interfacedump[i].interface, _cache.interfacedump[i].proto);
+                       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 = [];
 
@@ -874,7 +853,7 @@ Network = L.Class.extend({
                        if (name == null)
                                return null;
 
-                       if (_state.interfaces.hasOwnProperty(name) || isWifiIfname(name))
+                       if (_state.netdevs.hasOwnProperty(name) || isWifiIfname(name))
                                return this.instantiateDevice(name);
 
                        var netid = getWifiNetidBySid(name);
@@ -905,7 +884,7 @@ Network = L.Class.extend({
                                }
                        }
 
-                       for (var ifname in _state.interfaces) {
+                       for (var ifname in _state.netdevs) {
                                if (devices.hasOwnProperty(ifname))
                                        continue;
 
@@ -1096,7 +1075,7 @@ Network = L.Class.extend({
                        var radioname = existingDevice['.name'],
                            netid = getWifiNetidBySid(sid) || [];
 
-                       return this.instantiateWifiNetwork(sid, radioname, _cache.wifi[radioname], netid[0], null, { ifname: netid });
+                       return this.instantiateWifiNetwork(sid, radioname, _state.radios[radioname], netid[0], null, { ifname: netid });
                }, this));
        },
 
@@ -1116,20 +1095,24 @@ Network = L.Class.extend({
                return initNetworkState().then(L.bind(function() {
                        var rv = [];
 
-                       for (var i = 0; i < _state.interfacedump.length; i++) {
-                               if (!Array.isArray(_state.interfacedump[i].route))
+                       for (var i = 0; i < _state.ifaces.length; i++) {
+                               if (!Array.isArray(_state.ifaces[i].route))
                                        continue;
 
-                               for (var j = 0; j < _state.interfacedump[i].route.length; j++) {
-                                       if (typeof(_state.interfacedump[i].route[j]) != 'object' ||
-                                           typeof(_state.interfacedump[i].route[j].target) != 'string' ||
-                                           typeof(_state.interfacedump[i].route[j].mask) != 'number')
+                               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.interfacedump[i].route[j].table)
+                                       if (_state.ifaces[i].route[j].table)
                                                continue;
 
-                                       rv.push(_state.interfacedump[i]);
+                                       if (_state.ifaces[i].route[j].target != addr ||
+                                           _state.ifaces[i].route[j].mask != mask)
+                                           continue;
+
+                                       rv.push(_state.ifaces[i]);
                                }
                        }
 
@@ -1141,25 +1124,25 @@ Network = L.Class.extend({
                return initNetworkState().then(L.bind(function() {
                        var rv = [];
 
-                       for (var i = 0; i < _state.interfacedump.length; i++) {
-                               if (Array.isArray(_state.interfacedump[i]['ipv4-address']))
-                                       for (var j = 0; j < _state.interfacedump[i]['ipv4-address'].length; j++)
-                                               if (typeof(_state.interfacedump[i]['ipv4-address'][j]) == 'object' &&
-                                                   _state.interfacedump[i]['ipv4-address'][j].address == addr)
-                                                       return _state.interfacedump[i];
-
-                               if (Array.isArray(_state.interfacedump[i]['ipv6-address']))
-                                       for (var j = 0; j < _state.interfacedump[i]['ipv6-address'].length; j++)
-                                               if (typeof(_state.interfacedump[i]['ipv6-address'][j]) == 'object' &&
-                                                   _state.interfacedump[i]['ipv6-address'][j].address == addr)
-                                                       return _state.interfacedump[i];
-
-                               if (Array.isArray(_state.interfacedump[i]['ipv6-prefix-assignment']))
-                                       for (var j = 0; j < _state.interfacedump[i]['ipv6-prefix-assignment'].length; j++)
-                                               if (typeof(_state.interfacedump[i]['ipv6-prefix-assignment'][j]) == 'object' &&
-                                                       typeof(_state.interfacedump[i]['ipv6-prefix-assignment'][j]['local-address']) == 'object' &&
-                                                   _state.interfacedump[i]['ipv6-prefix-assignment'][j]['local-address'].address == addr)
-                                                       return _state.interfacedump[i];
+                       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;
@@ -1218,6 +1201,12 @@ Network = L.Class.extend({
 
        getIfnameOf: function(obj) {
                return ifnameOf(obj);
+       },
+
+       getDSLModemType: function() {
+               return initNetworkState().then(function() {
+                       return _state.hasDSLModem ? _state.hasDSLModem.type : null;
+               });
        }
 });
 
@@ -1236,11 +1225,11 @@ Protocol = L.Class.extend({
        },
 
        _ubus: function(field) {
-               for (var i = 0; i < _cache.interfacedump.length; i++) {
-                       if (_cache.interfacedump[i].interface != this.sid)
+               for (var i = 0; i < _state.ifaces.length; i++) {
+                       if (_state.ifaces[i].interface != this.sid)
                                continue;
 
-                       return (field != null ? _cache.interfacedump[i][field] : _cache.interfacedump[i]);
+                       return (field != null ? _state.ifaces[i][field] : _state.ifaces[i]);
                }
        },
 
@@ -1538,11 +1527,6 @@ Protocol = L.Class.extend({
                        return new Device(ifname, this);
                }
                else {
-                       var ifname = this._ubus('l3_device') || this._ubus('device');
-
-                       if (ifname != null)
-                               return L.network.instantiateDevice(ifname, this);
-
                        var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
 
                        for (var i = 0; i < ifnames.length; i++) {
@@ -1561,6 +1545,11 @@ Protocol = L.Class.extend({
                return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
        },
 
+       getL3Device: function() {
+               var ifname = this._ubus('l3_device');
+               return (ifname != null ? L.network.instantiateDevice(ifname, this) : null);
+       },
+
        getDevices: function() {
                var rv = [];
 
@@ -1647,12 +1636,12 @@ Device = L.Class.extend({
                }
 
                this.ifname  = this.ifname || ifname;
-               this.dev     = _state.interfaces[this.ifname];
+               this.dev     = _state.netdevs[this.ifname];
                this.network = network;
        },
 
        _ubus: function(field) {
-               var dump = _cache.devicedump[this.ifname] || {};
+               var dump = _state.devices[this.ifname] || {};
 
                return (field != null ? dump[field] : dump);
        },
@@ -1751,7 +1740,9 @@ Device = L.Class.extend({
                        return null;
 
                for (var i = 0; i < br.ifnames.length; i++)
-                       rv.push(L.network.instantiateDevice(br.ifnames[i]));
+                       rv.push(L.network.instantiateDevice(br.ifnames[i].name));
+
+               rv.sort(deviceSort);
 
                return rv;
        },
@@ -1882,8 +1873,8 @@ WifiDevice = L.Class.extend({
        },
 
        isUp: function() {
-               if (L.isObject(_cache.wifi[this.sid]))
-                       return (_cache.wifi[this.sid].up == true);
+               if (L.isObject(_state.radios[this.sid]))
+                       return (_state.radios[this.sid].up == true);
 
                return false;
        },