X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=modules%2Fluci-base%2Fluasrc%2Fmodel%2Fnetwork.lua;h=cce559aab1740166f9efa82989f01b87f9f5c3db;hb=4a85973a9a22071a0aa3f5add69eca775d488f9f;hp=d9ef4089c8204d99c5a901c167f382cb385f57b4;hpb=1b42d7a02df97c77ff9653de5e53b115610b3121;p=oweals%2Fluci.git diff --git a/modules/luci-base/luasrc/model/network.lua b/modules/luci-base/luasrc/model/network.lua index d9ef4089c..cce559aab 100644 --- a/modules/luci-base/luasrc/model/network.lua +++ b/modules/luci-base/luasrc/model/network.lua @@ -6,14 +6,12 @@ local type, next, pairs, ipairs, loadfile, table, select local tonumber, tostring, math = tonumber, tostring, math -local require = require +local pcall, require, setmetatable = pcall, require, setmetatable local nxo = require "nixio" local nfs = require "nixio.fs" local ipc = require "luci.ip" -local sys = require "luci.sys" local utl = require "luci.util" -local dsp = require "luci.dispatcher" local uci = require "luci.model.uci" local lng = require "luci.i18n" local jsc = require "luci.jsonc" @@ -25,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() @@ -108,6 +122,58 @@ function _set(c, s, o, v) end end +local function _wifi_state() + if not next(_ubuswificache) then + _ubuswificache = utl.ubus("network.wireless", "status", {}) or {} + end + 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 @@ -118,61 +184,113 @@ function _wifi_iface(x) return false end -function _wifi_state(key, val, field) - local radio, radiostate, ifc, ifcstate - - if not next(_ubuswificache) then - _ubuswificache = utl.ubus("network.wireless", "status", {}) or {} +local function _wifi_iwinfo_by_ifname(ifname, force_phy_only) + local stat, iwinfo = pcall(require, "iwinfo") + local iwtype = stat and type(ifname) == "string" and iwinfo.type(ifname) + local is_nonphy_op = { + bitrate = true, + quality = true, + quality_max = true, + mode = true, + ssid = true, + bssid = true, + assoclist = true, + encryption = true + } - -- workaround extended section format - for radio, radiostate in pairs(_ubuswificache) do - for ifc, ifcstate in pairs(radiostate.interfaces) do - if ifcstate.section and ifcstate.section:sub(1, 1) == '@' then - local s = _uci:get_all('wireless.%s' % ifcstate.section) - if s then - ifcstate.section = s['.name'] - end + if iwtype then + -- if we got a type but no real netdev, we're referring to a phy + local phy_only = force_phy_only or (ipc.link(ifname).type ~= 1) + + return setmetatable({}, { + __index = function(t, k) + if k == "ifname" then + return ifname + elseif phy_only and is_nonphy_op[k] then + return nil + elseif iwinfo[iwtype][k] then + return iwinfo[iwtype][k](ifname) end end - end + }) end +end - for radio, radiostate in pairs(_ubuswificache) do - for ifc, ifcstate in pairs(radiostate.interfaces) do - if ifcstate[key] == val then - return ifcstate[field] - end +local function _wifi_sid_by_netid(netid) + if type(netid) == "string" then + local radioname, netidx = netid:match("^(%w+)%.network(%d+)$") + if radioname and netidx then + local i, n = 0, nil + + netidx = tonumber(netidx) + _uci:foreach("wireless", "wifi-iface", + function(s) + if s.device == radioname then + i = i + 1 + if i == netidx then + n = s[".name"] + return false + end + end + end) + + return n end end end -function _wifi_lookup(ifn) - -- got a radio#.network# pseudo iface, locate the corresponding section - local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$") - if radio and ifnidx then - local sid = nil - local num = 0 +function _wifi_sid_by_ifname(ifn) + local sid = _wifi_sid_by_netid(ifn) + if sid then + return sid + end + + local _, _, netstate = _wifi_state_by_ifname(ifn) + if netstate and type(netstate.section) == "string" then + return netstate.section + end +end + +local function _wifi_netid_by_sid(sid) + local t, n = _uci:get("wireless", sid) + if t == "wifi-iface" and n ~= nil then + local radioname = _uci:get("wireless", n, "device") + if type(radioname) == "string" then + local i, netid = 0, nil - ifnidx = tonumber(ifnidx) - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device == radio then - num = num + 1 - if num == ifnidx then - sid = s['.name'] - return false + _uci:foreach("wireless", "wifi-iface", + function(s) + if s.device == radioname then + i = i + 1 + if s[".name"] == n then + netid = "%s.network%d" %{ radioname, i } + return false + end end - end - end) - - return sid + end) - -- looks like wifi, try to locate the section via ubus state - elseif _wifi_iface(ifn) then - return _wifi_state("ifname", ifn, "section") + return netid, radioname + end end end +local function _wifi_netid_by_netname(name) + local netid = nil + + _uci:foreach("wireless", "wifi-iface", + function(s) + local net + for net in utl.imatch(s.network) do + if net == name then + netid = _wifi_netid_by_sid(s[".name"]) + return false + end + end + end) + + return netid +end + function _iface_virtual(x) local _, p for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do @@ -228,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 @@ -393,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") @@ -418,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 @@ -430,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] @@ -441,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) @@ -524,20 +680,8 @@ function get_interface(self, i) if _interfaces[i] or _wifi_iface(i) then return interface(i) else - local ifc - local num = { } - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - if s['.name'] == i then - ifc = interface( - "%s.network%d" %{s.device, num[s.device] }) - return false - end - end - end) - return ifc + local netid = _wifi_netid_by_sid(i) + return netid and interface(netid) end end @@ -644,7 +788,7 @@ function get_wifidevs(self) end function get_wifinet(self, net) - local wnet = _wifi_lookup(net) + local wnet = _wifi_sid_by_ifname(net) if wnet then return wifinet(wnet) end @@ -660,7 +804,7 @@ function add_wifinet(self, net, options) end function del_wifinet(self, net) - local wnet = _wifi_lookup(net) + local wnet = _wifi_sid_by_ifname(net) if wnet then _uci:delete("wireless", wnet) return true @@ -784,22 +928,7 @@ function protocol.ifname(self) ifname = self:_ubus("device") end if not ifname then - local num = { } - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device then - num[s.device] = num[s.device] - and num[s.device] + 1 or 1 - - local net - for net in utl.imatch(s.network) do - if net == self.sid then - ifname = "%s.network%d" %{ s.device, num[s.device] } - return false - end - end - end - end) + ifname = _wifi_netid_by_netname(self.sid) end return ifname end @@ -851,6 +980,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 @@ -923,7 +1062,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 @@ -957,6 +1104,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 @@ -977,36 +1140,51 @@ function protocol.is_floating(self) return false end +function protocol.is_dynamic(self) + return (self:_ubus("dynamic") == true) +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 else - local rv = true + local empty = true if (self:_get("ifname") or ""):match("%S+") then - rv = false + empty = false end - _uci:foreach("wireless", "wifi-iface", - function(s) - local n - for n in utl.imatch(s.network) do - if n == self.sid then - rv = false - return false - end - end - end) + if empty and _wifi_netid_by_netname(self.sid) then + empty = false + end - return rv + return empty 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 -- if its a wifi interface, change its network option - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then _append("wireless", wif, "network", self.sid) @@ -1021,7 +1199,7 @@ function protocol.del_interface(self, ifname) ifname = _M:ifnameof(ifname) if ifname and not self:is_floating() then -- if its a wireless interface, clear its network option - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then _filter("wireless", wif, "network", self.sid) end -- remove the interface @@ -1037,27 +1215,17 @@ 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 = nil - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - local net - for net in utl.imatch(s.network) do - if net == self.sid then - ifn = "%s.network%d" %{ s.device, num[s.device] } - return false - end - end - end - end) + ifn = _wifi_netid_by_netname(self.sid) return ifn and interface(ifn, self) end end @@ -1077,18 +1245,17 @@ function protocol.get_interfaces(self) ifaces[#ifaces+1] = nfs[ifn] end - local num = { } local wfs = { } _uci:foreach("wireless", "wifi-iface", function(s) if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - local net for net in utl.imatch(s.network) do if net == self.sid then - ifn = "%s.network%d" %{ s.device, num[s.device] } - wfs[ifn] = interface(ifn, self) + ifn = _wifi_netid_by_sid(s[".name"]) + if ifn then + wfs[ifn] = interface(ifn, self) + end end end end @@ -1119,7 +1286,7 @@ function protocol.contains_interface(self, ifname) end end - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then local n for n in utl.imatch(_uci:get("wireless", wif, "network")) do @@ -1134,17 +1301,18 @@ function protocol.contains_interface(self, ifname) end function protocol.adminlink(self) - return dsp.build_url("admin", "network", "network", self.sid) + local stat, dsp = pcall(require, "luci.dispatcher") + return stat and dsp.build_url("admin", "network", "network", self.sid) end interface = utl.class() function interface.__init__(self, ifname, network) - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then self.wif = wifinet(wif) - self.ifname = _wifi_state("section", wif, "ifname") + self.ifname = self.wif:ifname() end self.ifname = self.ifname or ifname @@ -1168,8 +1336,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) @@ -1181,7 +1348,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" @@ -1209,7 +1378,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() } @@ -1218,7 +1387,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") @@ -1271,7 +1442,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) @@ -1332,9 +1507,14 @@ end wifidev = utl.class() -function wifidev.__init__(self, dev) - self.sid = dev - self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { } +function wifidev.__init__(self, name) + local t, n = _uci:get("wireless", name) + if t == "wifi-device" and n ~= nil then + self.sid = n + self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true) + end + self.sid = self.sid or name + self.iwinfo = self.iwinfo or { ifname = self.sid } end function wifidev.get(self, opt) @@ -1359,7 +1539,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 @@ -1387,7 +1567,7 @@ function wifidev.get_wifinet(self, net) if _uci:get("wireless", net) == "wifi-iface" then return wifinet(net) else - local wnet = _wifi_lookup(net) + local wnet = _wifi_sid_by_ifname(net) if wnet then return wifinet(wnet) end @@ -1421,7 +1601,7 @@ function wifidev.del_wifinet(self, net) if utl.instanceof(net, wifinet) then net = net.sid elseif _uci:get("wireless", net) ~= "wifi-iface" then - net = _wifi_lookup(net) + net = _wifi_sid_by_ifname(net) end if net and _uci:get("wireless", net, "device") == self.sid then @@ -1435,49 +1615,50 @@ end wifinet = utl.class() -function wifinet.__init__(self, net, data) - self.sid = net - - local n = 0 - local num = { } - local netid, sid - _uci:foreach("wireless", "wifi-iface", - function(s) - n = n + 1 - if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - if s['.name'] == self.sid then - sid = "@wifi-iface[%d]" % n - netid = "%s.network%d" %{ s.device, num[s.device] } - return false - end - end - end) +function wifinet.__init__(self, name, data) + local sid, netid, radioname, radiostate, netstate + -- lookup state by radio#.network# notation + sid = _wifi_sid_by_netid(name) if sid then - local _, k, r, i - for k, r in pairs(_ubuswificache) do - if type(r) == "table" and - type(r.interfaces) == "table" - then - for _, i in ipairs(r.interfaces) do - if type(i) == "table" and i.section == sid then - self._ubusdata = { - radio = k, - dev = r, - net = i - } - end + netid = name + radioname, radiostate, netstate = _wifi_state_by_sid(sid) + else + -- lookup state by ifname (e.g. wlan0) + radioname, radiostate, netstate = _wifi_state_by_ifname(name) + if radioname and radiostate and netstate then + sid = netstate.section + netid = _wifi_netid_by_sid(sid) + else + -- lookup state by uci section id (e.g. cfg053579) + radioname, radiostate, netstate = _wifi_state_by_sid(name) + if radioname and radiostate and netstate then + sid = name + netid = _wifi_netid_by_sid(sid) + else + -- no state available, try to resolve from uci + netid, radioname = _wifi_netid_by_sid(name) + if netid and radioname then + sid = name end end end end - local dev = _wifi_state("section", self.sid, "ifname") or netid + local iwinfo = + (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or + (radioname and _wifi_iwinfo_by_ifname(radioname)) or + { ifname = (netid or sid or name) } - self.netid = netid - self.wdev = dev - self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { } + self.sid = sid or name + self.wdev = iwinfo.ifname + self.iwinfo = iwinfo + self.netid = netid + self._ubusdata = { + radio = radioname, + dev = radiostate, + net = netstate + } end function wifinet.ubus(self, ...) @@ -1531,7 +1712,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 @@ -1664,7 +1845,8 @@ function wifinet.get_i18n(self) end function wifinet.adminlink(self) - return dsp.build_url("admin", "network", "wireless", self.netid) + local stat, dsp = pcall(require, "luci.dispatcher") + return dsp and dsp.build_url("admin", "network", "wireless", self.netid) end function wifinet.get_network(self)