Merge pull request #3063 from TDT-AG/pr/20190908-luci-app-statistics
[oweals/luci.git] / modules / luci-base / luasrc / model / network.lua
index 48a03393eb4ae90651bd134fb97cc9c5a4d620fc..a36a23f321b25375cbb496c35133e6d65815b3d3 100644 (file)
@@ -23,6 +23,22 @@ IFACE_PATTERNS_VIRTUAL  = { }
 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$" }
 IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
 
+IFACE_ERRORS = {
+       CONNECT_FAILED                  = lng.translate("Connection attempt failed"),
+       INVALID_ADDRESS                 = lng.translate("IP address in invalid"),
+       INVALID_GATEWAY                 = lng.translate("Gateway address is invalid"),
+       INVALID_LOCAL_ADDRESS   = lng.translate("Local IP address is invalid"),
+       MISSING_ADDRESS                 = lng.translate("IP address is missing"),
+       MISSING_PEER_ADDRESS    = lng.translate("Peer address is missing"),
+       NO_DEVICE                               = lng.translate("Network device is not present"),
+       NO_IFACE                                = lng.translate("Unable to determine device name"),
+       NO_IFNAME                               = lng.translate("Unable to determine device name"),
+       NO_WAN_ADDRESS                  = lng.translate("Unable to determine external IP address"),
+       NO_WAN_LINK                             = lng.translate("Unable to determine upstream interface"),
+       PEER_RESOLVE_FAIL               = lng.translate("Unable to resolve peer host name"),
+       PIN_FAILED                              = lng.translate("PIN code rejected")
+}
+
 
 protocol = utl.class()
 
@@ -113,6 +129,51 @@ local function _wifi_state()
        return _ubuswificache
 end
 
+local function _wifi_state_by_sid(sid)
+       local t1, n1 = _uci:get("wireless", sid)
+       if t1 == "wifi-iface" and n1 ~= nil then
+               local radioname, radiostate
+               for radioname, radiostate in pairs(_wifi_state()) do
+                       if type(radiostate) == "table" and
+                          type(radiostate.interfaces) == "table"
+                       then
+                               local netidx, netstate
+                               for netidx, netstate in ipairs(radiostate.interfaces) do
+                                       if type(netstate) == "table" and
+                                          type(netstate.section) == "string"
+                                       then
+                                               local t2, n2 = _uci:get("wireless", netstate.section)
+                                               if t1 == t2 and n1 == n2 then
+                                                       return radioname, radiostate, netstate
+                                               end
+                                       end
+                               end
+                       end
+               end
+       end
+end
+
+local function _wifi_state_by_ifname(ifname)
+       if type(ifname) == "string" then
+               local radioname, radiostate
+               for radioname, radiostate in pairs(_wifi_state()) do
+                       if type(radiostate) == "table" and
+                          type(radiostate.interfaces) == "table"
+                       then
+                               local netidx, netstate
+                               for netidx, netstate in ipairs(radiostate.interfaces) do
+                                       if type(netstate) == "table" and
+                                          type(netstate.ifname) == "string" and
+                                          netstate.ifname == ifname
+                                       then
+                                               return radioname, radiostate, netstate
+                                       end
+                               end
+                       end
+               end
+       end
+end
+
 function _wifi_iface(x)
        local _, p
        for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
@@ -230,51 +291,6 @@ local function _wifi_netid_by_netname(name)
        return netid
 end
 
-local function _wifi_state_by_sid(sid)
-       local t1, n1 = _uci:get("wireless", sid)
-       if t1 == "wifi-iface" and n1 ~= nil then
-               local radioname, radiostate
-               for radioname, radiostate in pairs(_wifi_state()) do
-                       if type(radiostate) == "table" and
-                          type(radiostate.interfaces) == "table"
-                       then
-                               local netidx, netstate
-                               for netidx, netstate in ipairs(radiostate.interfaces) do
-                                       if type(netstate) == "table" and
-                                          type(netstate.section) == "string"
-                                       then
-                                               local t2, n2 = _uci:get("wireless", netstate.section)
-                                               if t1 == t2 and n1 == n2 then
-                                                       return radioname, radiostate, netstate
-                                               end
-                                       end
-                               end
-                       end
-               end
-       end
-end
-
-local function _wifi_state_by_ifname(ifname)
-       if type(ifname) == "string" then
-               local radioname, radiostate
-               for radioname, radiostate in pairs(_wifi_state()) do
-                       if type(radiostate) == "table" and
-                          type(radiostate.interfaces) == "table"
-                       then
-                               local netidx, netstate
-                               for netidx, netstate in ipairs(radiostate.interfaces) do
-                                       if type(netstate) == "table" and
-                                          type(netstate.ifname) == "string" and
-                                          netstate.ifname == ifname
-                                       then
-                                               return radioname, radiostate, netstate
-                                       end
-                               end
-                       end
-               end
-       end
-end
-
 function _iface_virtual(x)
        local _, p
        for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do
@@ -330,7 +346,7 @@ function init(cursor)
                        if i.family == "packet" then
                                _interfaces[name].flags   = i.flags
                                _interfaces[name].stats   = i.data
-                               _interfaces[name].macaddr = i.addr
+                               _interfaces[name].macaddr = ipc.checkmac(i.addr)
                        elseif i.family == "inet" then
                                _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
                        elseif i.family == "inet6" then
@@ -495,6 +511,17 @@ function register_pattern_virtual(self, pat)
        IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
 end
 
+function register_error_code(self, code, message)
+       if type(code) == "string" and
+          type(message) == "string" and
+          not IFACE_ERRORS[code]
+       then
+               IFACE_ERRORS[code] = message
+               return true
+       end
+
+       return false
+end
 
 function has_ipv6(self)
        return nfs.access("/proc/net/ipv6_route")
@@ -520,6 +547,13 @@ end
 function get_network(self, n)
        if n and _uci:get("network", n) == "interface" then
                return network(n)
+       elseif n then
+               local stat = utl.ubus("network.interface", "status", { interface = n })
+               if type(stat) == "table" and
+                  type(stat.proto) == "string"
+               then
+                       return network(n, stat.proto)
+               end
        end
 end
 
@@ -532,6 +566,23 @@ function get_networks(self)
                        nls[s['.name']] = network(s['.name'])
                end)
 
+       local dump = utl.ubus("network.interface", "dump", { })
+       if type(dump) == "table" and
+          type(dump.interface) == "table"
+       then
+               local _, net
+               for _, net in ipairs(dump.interface) do
+                       if type(net) == "table" and
+                          type(net.proto) == "string" and
+                          type(net.interface) == "string"
+                       then
+                               if not nls[net.interface] then
+                                       nls[net.interface] = network(net.interface, net.proto)
+                               end
+                       end
+               end
+       end
+
        local n
        for n in utl.kspairs(nls) do
                nets[#nets+1] = nls[n]
@@ -543,6 +594,9 @@ end
 function del_network(self, n)
        local r = _uci:delete("network", n)
        if r then
+               _uci:delete_all("luci", "ifstate",
+                       function(s) return (s.interface == n) end)
+
                _uci:delete_all("network", "alias",
                        function(s) return (s.interface == n) end)
 
@@ -568,6 +622,12 @@ function del_network(self, n)
                                        _uci:delete("wireless", s['.name'], "network")
                                end
                        end)
+
+               local ok, fw = pcall(require, "luci.model.firewall")
+               if ok then
+                       fw.init()
+                       fw:del_network(n)
+               end
        end
        return r
 end
@@ -626,7 +686,7 @@ function get_interface(self, i)
        if _interfaces[i] or _wifi_iface(i) then
                return interface(i)
        else
-               local netid = _wifi_netid_by_netname(i)
+               local netid = _wifi_netid_by_sid(i)
                return netid and interface(netid)
        end
 end
@@ -759,6 +819,7 @@ function del_wifinet(self, net)
 end
 
 function get_status_by_route(self, addr, mask)
+       local route_statuses = { }
        local _, object
        for _, object in ipairs(utl.ubus()) do
                local net = object:match("^network%.interface%.(.+)")
@@ -768,12 +829,14 @@ function get_status_by_route(self, addr, mask)
                                local rt
                                for _, rt in ipairs(s.route) do
                                        if not rt.table and rt.target == addr and rt.mask == mask then
-                                               return net, s
+                                               route_statuses[net] = s
                                        end
                                end
                        end
                end
        end
+
+       return route_statuses
 end
 
 function get_status_by_address(self, addr)
@@ -798,28 +861,40 @@ function get_status_by_address(self, addr)
                                        end
                                end
                        end
+                       if s and s['ipv6-prefix-assignment'] then
+                               local a
+                               for _, a in ipairs(s['ipv6-prefix-assignment']) do
+                                       if a and a['local-address'] and a['local-address'].address == addr then
+                                               return net, s
+                                       end
+                               end
+                       end
                end
        end
 end
 
-function get_wannet(self)
-       local net, stat = self:get_status_by_route("0.0.0.0", 0)
-       return net and network(net, stat.proto)
-end
+function get_wan_networks(self)
+       local k, v
+       local wan_nets = { }
+       local route_statuses = self:get_status_by_route("0.0.0.0", 0)
 
-function get_wandev(self)
-       local _, stat = self:get_status_by_route("0.0.0.0", 0)
-       return stat and interface(stat.l3_device or stat.device)
-end
+       for k, v in pairs(route_statuses) do
+               wan_nets[#wan_nets+1] = network(k, v.proto)
+       end
 
-function get_wan6net(self)
-       local net, stat = self:get_status_by_route("::", 0)
-       return net and network(net, stat.proto)
+       return wan_nets
 end
 
-function get_wan6dev(self)
-       local _, stat = self:get_status_by_route("::", 0)
-       return stat and interface(stat.l3_device or stat.device)
+function get_wan6_networks(self)
+       local k, v
+       local wan6_nets = { }
+       local route_statuses = self:get_status_by_route("::", 0)
+
+       for k, v in pairs(route_statuses) do
+               wan6_nets[#wan6_nets+1] = network(k, v.proto)
+       end
+
+       return wan6_nets
 end
 
 function get_switch_topologies(self)
@@ -926,6 +1001,16 @@ function protocol.metric(self)
        return self:_ubus("metric") or 0
 end
 
+function protocol.zonename(self)
+       local d = self:_ubus("data")
+
+       if type(d) == "table" and type(d.zone) == "string" then
+               return d.zone
+       end
+
+       return nil
+end
+
 function protocol.ipaddr(self)
        local addrs = self:_ubus("ipv4-address")
        return addrs and #addrs > 0 and addrs[1].address
@@ -998,7 +1083,15 @@ function protocol.ip6addrs(self)
 
        if type(addrs) == "table" then
                for n, addr in ipairs(addrs) do
-                       rv[#rv+1] = "%s1/%d" %{ addr.address, addr.mask }
+                       if type(addr["local-address"]) == "table" and
+                          type(addr["local-address"].mask) == "number" and
+                          type(addr["local-address"].address) == "string"
+                       then
+                               rv[#rv+1] = "%s/%d" %{
+                                       addr["local-address"].address,
+                                       addr["local-address"].mask
+                               }
+                       end
                end
        end
 
@@ -1032,6 +1125,22 @@ function protocol.ip6prefix(self)
        end
 end
 
+function protocol.errors(self)
+       local _, err, rv
+       local errors = self:_ubus("errors")
+       if type(errors) == "table" then
+               for _, err in ipairs(errors) do
+                       if type(err) == "table" and
+                          type(err.code) == "string"
+                       then
+                               rv = rv or { }
+                               rv[#rv+1] = IFACE_ERRORS[err.code] or lng.translatef("Unknown error (%s)", err.code)
+                       end
+               end
+       end
+       return rv
+end
+
 function protocol.is_bridge(self)
        return (not self:is_virtual() and self:type() == "bridge")
 end
@@ -1052,6 +1161,28 @@ function protocol.is_floating(self)
        return false
 end
 
+function protocol.is_dynamic(self)
+       return (self:_ubus("dynamic") == true)
+end
+
+function protocol.is_auto(self)
+       return (self:_get("auto") ~= "0")
+end
+
+function protocol.is_alias(self)
+       local ifn, parent = nil, nil
+
+       for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
+               if #ifn > 1 and ifn:byte(1) == 64 then
+                       parent = ifn:sub(2)
+               elseif parent ~= nil then
+                       parent = nil
+               end
+       end
+
+       return parent
+end
+
 function protocol.is_empty(self)
        if self:is_floating() then
                return false
@@ -1070,6 +1201,10 @@ function protocol.is_empty(self)
        end
 end
 
+function protocol.is_up(self)
+       return (self:_ubus("up") == true)
+end
+
 function protocol.add_interface(self, ifname)
        ifname = _M:ifnameof(ifname)
        if ifname and not self:is_floating() then
@@ -1105,12 +1240,16 @@ function protocol.get_interface(self)
                _bridge["br-" .. self.sid] = true
                return interface("br-" .. self.sid, self)
        else
-               local ifn = nil
-               local num = { }
+               local ifn = self:_ubus("l3_device") or self:_ubus("device")
+               if ifn then
+                       return interface(ifn, self)
+               end
+
                for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
                        ifn = ifn:match("^[^:/]+")
                        return ifn and interface(ifn, self)
                end
+
                ifn = _wifi_netid_by_netname(self.sid)
                return ifn and interface(ifn, self)
        end
@@ -1222,8 +1361,7 @@ function interface.name(self)
 end
 
 function interface.mac(self)
-       local mac = self:_ubus("macaddr")
-       return mac and mac:upper()
+       return ipc.checkmac(self:_ubus("macaddr"))
 end
 
 function interface.ipaddrs(self)
@@ -1235,7 +1373,9 @@ function interface.ip6addrs(self)
 end
 
 function interface.type(self)
-       if self.wif or _wifi_iface(self.ifname) then
+       if self.ifname and self.ifname:byte(1) == 64 then
+               return "alias"
+       elseif self.wif or _wifi_iface(self.ifname) then
                return "wifi"
        elseif _bridge[self.ifname] then
                return "bridge"
@@ -1263,7 +1403,7 @@ function interface.get_i18n(self)
                return "%s: %s %q" %{
                        lng.translate("Wireless Network"),
                        self.wif:active_mode(),
-                       self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
+                       self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id() or "?"
                }
        else
                return "%s: %q" %{ self:get_type_i18n(), self:name() }
@@ -1272,7 +1412,9 @@ end
 
 function interface.get_type_i18n(self)
        local x = self:type()
-       if x == "wifi" then
+       if x == "alias" then
+               return lng.translate("Alias Interface")
+       elseif x == "wifi" then
                return lng.translate("Wireless Adapter")
        elseif x == "bridge" then
                return lng.translate("Bridge")
@@ -1325,7 +1467,11 @@ function interface.bridge_stp(self)
 end
 
 function interface.is_up(self)
-       return self:_ubus("up") or false
+       local up = self:_ubus("up")
+       if up == nil then
+               up = (self:type() == "alias")
+       end
+       return up or false
 end
 
 function interface.is_bridge(self)
@@ -1418,7 +1564,7 @@ function wifidev.hwmodes(self)
 end
 
 function wifidev.get_i18n(self)
-       local t = "Generic"
+       local t = self.iwinfo.hardware_name or "Generic"
        if self.iwinfo.type == "wl" then
                t = "Broadcom"
        end
@@ -1591,7 +1737,7 @@ end
 function wifinet.ifname(self)
        local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
        if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
-               ifname = self.wdev
+               ifname = self.netid
        end
        return ifname
 end