X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fcore%2Fluasrc%2Fmodel%2Fnetwork.lua;h=56f1751f8195732409936a50571e0259f53123bd;hp=07d5fb845980f639318f9f06b1122db1511d9e11;hb=06c8c28bab9975149f94397f3b7027712c0ba424;hpb=35137845dd427dd4da8ed7a4a8f27adc37ae1b22 diff --git a/libs/core/luasrc/model/network.lua b/libs/core/luasrc/model/network.lua index 07d5fb845..56f1751f8 100644 --- a/libs/core/luasrc/model/network.lua +++ b/libs/core/luasrc/model/network.lua @@ -17,11 +17,14 @@ limitations under the License. ]]-- -local type, next, pairs, ipairs, loadfile, table, tonumber, math, i18n - = type, next, pairs, ipairs, loadfile, table, tonumber, math, luci.i18n +local type, next, pairs, ipairs, loadfile, table + = type, next, pairs, ipairs, loadfile, table + +local tonumber, tostring, math = tonumber, tostring, math local require = require +local bus = require "ubus" local nxo = require "nixio" local nfs = require "nixio.fs" local ipc = require "luci.ip" @@ -29,19 +32,22 @@ 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" module "luci.model.network" IFACE_PATTERNS_VIRTUAL = { } -IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^lo$" } +IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^lo$" } IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" } -proto = { } -proto.generic = utl.class() +protocol = utl.class() + +local _protocols = { } local _interfaces, _bridge, _switch, _tunnel +local _ubus, _ubusnetcache, _ubusdevcache local _uci_real, _uci_state function _filter(c, s, o, r) @@ -195,6 +201,10 @@ function init(cursor) _switch = { } _tunnel = { } + _ubus = bus.connect() + _ubusnetcache = { } + _ubusdevcache = { } + -- read interface information local n, i for n, i in ipairs(nxo.getifaddrs()) do @@ -271,13 +281,51 @@ end function ifnameof(self, x) if utl.instanceof(x, interface) then return x:name() - elseif utl.instanceof(x, proto.generic) then + elseif utl.instanceof(x, protocol) then return x:ifname() elseif type(x) == "string" then return x:match("^[^:]+") end end +function get_protocol(self, protoname, netname) + local v = _protocols[protoname] + if v then + return v(netname or "__dummy__") + end +end + +function get_protocols(self) + local p = { } + local _, v + for _, v in ipairs(_protocols) do + p[#p+1] = v("__dummy__") + end + return p +end + +function register_protocol(self, protoname) + local proto = utl.class(protocol) + + function proto.__init__(self, name) + self.sid = name + end + + function proto.proto(self) + return protoname + end + + _protocols[#_protocols+1] = proto + _protocols[protoname] = proto + + return proto +end + +function register_pattern_virtual(self, pat) + IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat +end + + function has_ipv6(self) return nfs.access("/proc/net/ipv6_route") end @@ -336,7 +384,17 @@ function del_network(self, n) _uci_real:foreach("wireless", "wifi-iface", function(s) - if s.network == n then + local net + local rest = { } + for net in utl.imatch(s.network) do + if net ~= n then + rest[#rest+1] = net + end + end + if #rest > 0 then + _uci_real:set("wireless", s['.name'], "network", + table.concat(rest, " ")) + else _uci_real:delete("wireless", s['.name'], "network") end end) @@ -373,8 +431,18 @@ function rename_network(self, old, new) _uci_real:foreach("wireless", "wifi-iface", function(s) - if s.network == old then - _uci_real:set("wireless", s['.name'], "network", new) + local net + local list = { } + for net in utl.imatch(s.network) do + if net == old then + list[#list+1] = new + else + list[#list+1] = net + end + end + if #list > 0 then + _uci_real:set("wireless", s['.name'], "network", + table.concat(list, " ")) end end) @@ -410,6 +478,7 @@ function get_interfaces(self) local ifaces = { } local seen = { } local nfs = { } + local baseof = { } -- find normal interfaces _uci_real:foreach("network", "interface", @@ -431,9 +500,26 @@ function get_interfaces(self) -- find vlan interfaces _uci_real:foreach("network", "switch_vlan", function(s) - local base = s.device or "-" - if not base:match("^eth%d") then - base = "eth0" + if not s.device then + return + end + + local base = baseof[s.device] + if not base then + if not s.device:match("^eth%d") then + local l + for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do + if not base then + base = l:match("^%w+: (%w+)") + end + end + if not base or not base:match("^eth%d") then + base = "eth0" + end + else + base = s.device + end + baseof[s.device] = base end local vid = tonumber(s.vid or s.vlan) @@ -519,20 +605,84 @@ function del_wifinet(self, net) return false end +function get_status_by_route(self, addr, mask) + local _, object + for _, object in ipairs(_ubus:objects()) do + local net = object:match("^network%.interface%.(.+)") + if net then + local s = _ubus:call(object, "status", {}) + if s and s.route then + local rt + for _, rt in ipairs(s.route) do + if rt.target == addr and rt.mask == mask then + return net, s + end + end + end + end + end +end + +function get_status_by_address(self, addr) + local _, object + for _, object in ipairs(_ubus:objects()) do + local net = object:match("^network%.interface%.(.+)") + if net then + local s = _ubus:call(object, "status", {}) + if s and s['ipv4-address'] then + local a + for _, a in ipairs(s['ipv4-address']) do + if a.address == addr then + return net, s + end + end + end + if s and s['ipv6-address'] then + local a + for _, a in ipairs(s['ipv6-address']) do + if a.address == addr then + return net, s + end + end + end + end + end +end + +function get_wannet(self) + local net = self:get_status_by_route("0.0.0.0", 0) + return net and network(net) +end + +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 -function network(name) +function get_wan6net(self) + local net = self:get_status_by_route("::", 0) + return net and network(net) +end + +function get_wan6dev(self) + local _, stat = self:get_status_by_route("::", 0) + return stat and interface(stat.l3_device or stat.device) +end + + +function network(name, proto) if name then - local p = _uci_real:get("network", name, "proto") - local c = p and proto[p] or proto.generic + local p = proto or _uci_real:get("network", name, "proto") + local c = p and _protocols[p] or protocol return c(name) end end -function proto.generic.__init__(self, name) +function protocol.__init__(self, name) self.sid = name end -function proto.generic._get(self, opt) +function protocol._get(self, opt) local v = _uci_real:get("network", self.sid, opt) if type(v) == "table" then return table.concat(v, " ") @@ -540,87 +690,83 @@ function proto.generic._get(self, opt) return v or "" end -function proto.generic._ip(self, opt, family, list) - local ip = _uci_state:get("network", self.sid, opt) - local fc = (family == 6) and ipc.IPv6 or ipc.IPv4 - if ip or list then - if list then - local l = { } - for ip in utl.imatch(ip) do - ip = fc(ip) - if ip then l[#l+1] = ip:string() end - end - return l - else - ip = fc(ip) - return ip and ip:string() - end +function protocol._ubus(self, field) + if not _ubusnetcache[self.sid] then + _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid, + "status", { }) end + if _ubusnetcache[self.sid] and field then + return _ubusnetcache[self.sid][field] + end + return _ubusnetcache[self.sid] end -function proto.generic.get(self, opt) +function protocol.get(self, opt) return _get("network", self.sid, opt) end -function proto.generic.set(self, opt, val) +function protocol.set(self, opt, val) return _set("network", self.sid, opt, val) end -function proto.generic.ifname(self) - local p = self:proto() - if self:is_bridge() then - return "br-" .. self.sid - elseif self:is_virtual() then - return p .. "-" .. self.sid +function protocol.ifname(self) + local ifname + if self:is_floating() then + ifname = self:_ubus("l3_device") else + ifname = self:_ubus("device") + end + if not ifname then local num = { } - local dev = _uci_real:get("network", self.sid, "ifname") or - _uci_state:get("network", self.sid, "ifname") - - dev = (type(dev) == "table") and dev[1] or dev - dev = (dev ~= nil) and dev:match("%S+") - - if not dev then - _uci_real:foreach("wireless", "wifi-iface", - function(s) - if s.device then - num[s.device] = num[s.device] - and num[s.device] + 1 or 1 + _uci_real: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.network == self.sid then - dev = "%s.network%d" %{ s.device, num[s.device] } + 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 - - return dev + end + end) end + return ifname end -function proto.generic.proto(self) - return self:_get("proto") or "none" +function protocol.proto(self) + return "none" end -function proto.generic.type(self) +function protocol.get_i18n(self) + local p = self:proto() + if p == "none" then + return lng.translate("Unmanaged") + elseif p == "static" then + return lng.translate("Static address") + elseif p == "dhcp" then + return lng.translate("DHCP client") + else + return lng.translate("Unknown") + end +end + +function protocol.type(self) return self:_get("type") end -function proto.generic.name(self) +function protocol.name(self) return self.sid end -function proto.generic.uptime(self) - local cnt = tonumber(_uci_state:get("network", self.sid, "connect_time")) - if cnt ~= nil then - return nxo.sysinfo().uptime - cnt - else - return 0 - end +function protocol.uptime(self) + return self:_ubus("uptime") or 0 end -function proto.generic.expires(self) +function protocol.expires(self) local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired")) local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime")) if a and l then @@ -630,72 +776,89 @@ function proto.generic.expires(self) return -1 end -function proto.generic.metric(self) +function protocol.metric(self) return tonumber(_uci_state:get("network", self.sid, "metric")) or 0 end -function proto.generic.ipaddr(self) - return self:_ip("ipaddr", 4) +function protocol.ipaddr(self) + local addrs = self:_ubus("ipv4-address") + return addrs and #addrs > 0 and addrs[1].address end -function proto.generic.netmask(self) - return self:_ip("netmask", 4) +function protocol.netmask(self) + local addrs = self:_ubus("ipv4-address") + return addrs and #addrs > 0 and + ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string() end -function proto.generic.gwaddr(self) - return self:_ip("gateway", 4) +function protocol.gwaddr(self) + local _, route + for _, route in ipairs(self:_ubus("route") or { }) do + if route.target == "0.0.0.0" and route.mask == 0 then + return route.nexthop + end + end +end + +function protocol.dnsaddrs(self) + local dns = { } + local _, addr + for _, addr in ipairs(self:_ubus("dns-server") or { }) do + if not addr:match(":") then + dns[#dns+1] = addr + end + end + return dns end -function proto.generic.dnsaddrs(self) - return self:_ip("dns", 4, true) +function protocol.ip6addr(self) + local addrs = self:_ubus("ipv6-address") + return addrs and #addrs > 0 + and "%s/%d" %{ addrs[1].address, addrs[1].mask } end -function proto.generic.ip6addr(self) - local ip6 = self:_ip("ip6addr", 6) - if not ip6 then - local ifc = _interfaces[self:ifname()] - if ifc and ifc.ip6addrs then - local a - for _, a in ipairs(ifc.ip6addrs) do - if not a:is6linklocal() then - ip6 = a:string() - break - end - end +function protocol.gw6addr(self) + local _, route + for _, route in ipairs(self:_ubus("route") or { }) do + if route.target == "::" and route.mask == 0 then + return ipc.IPv6(route.nexthop):string() end end - return ip6 end -function proto.generic.gw6addr(self) - local ip6 = self:_ip("ip6gw", 6) - if not ip6 then - local dr6 = sys.net.defaultroute6() - if dr6 and dr6.device == self:ifname() then - return dr6.nexthop:string() +function protocol.dns6addrs(self) + local dns = { } + local _, addr + for _, addr in ipairs(self:_ubus("dns-server") or { }) do + if addr:match(":") then + dns[#dns+1] = addr end end - return ip6 + return dns end -function proto.generic.dns6addrs(self) - return self:_ip("dns", 6, true) +function protocol.is_bridge(self) + return (not self:is_virtual() and self:type() == "bridge") end -function proto.generic.is_bridge(self) - return (self:type() == "bridge") +function protocol.opkg_package(self) + return nil +end + +function protocol.is_installed(self) + return true end -function proto.generic.is_virtual(self) +function protocol.is_virtual(self) return false end -function proto.generic.is_floating(self) +function protocol.is_floating(self) return false end -function proto.generic.is_empty(self) - if self:is_virtual() then +function protocol.is_empty(self) + if self:is_floating() then return false else local rv = true @@ -706,9 +869,12 @@ function proto.generic.is_empty(self) _uci_real:foreach("wireless", "wifi-iface", function(s) - if s.network == self.sid then - rv = false - return false + local n + for n in utl.imatch(s.network) do + if n == self.sid then + rv = false + return false + end end end) @@ -716,19 +882,13 @@ function proto.generic.is_empty(self) end end -function proto.generic.add_interface(self, ifname) +function protocol.add_interface(self, ifname) ifname = _M:ifnameof(ifname) if ifname and not self:is_floating() then - -- remove the interface from all ifaces - _uci_real:foreach("network", "interface", - function(s) - _filter("network", s['.name'], "ifname", ifname) - end) - -- if its a wifi interface, change its network option local wif = _wifi_lookup(ifname) if wif then - _uci_real:set("wireless", wif, "network", self.sid) + _append("wireless", wif, "network", self.sid) -- add iface to our iface list else @@ -737,19 +897,19 @@ function proto.generic.add_interface(self, ifname) end end -function proto.generic.del_interface(self, ifname) +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) - if wif then _uci_real:delete("wireless", wif, "network") end + if wif then _filter("wireless", wif, "network", self.sid) end -- remove the interface _filter("network", self.sid, "ifname", ifname) end end -function proto.generic.get_interface(self) +function protocol.get_interface(self) if self:is_virtual() then _tunnel[self:proto() .. "-" .. self.sid] = true return interface(self:proto() .. "-" .. self.sid, self) @@ -759,18 +919,22 @@ function proto.generic.get_interface(self) else local ifn = nil local num = { } - for ifn in utl.imatch(_uci_state:get("network", self.sid, "ifname")) do + for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do ifn = ifn:match("^[^:/]+") return ifn and interface(ifn, self) end ifn = nil - _uci_state:foreach("wireless", "wifi-iface", + _uci_real: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.network == self.sid then - ifn = s.ifname or "%s.network%d" %{ s.device, num[s.device] } - return false + + 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) @@ -778,7 +942,7 @@ function proto.generic.get_interface(self) end end -function proto.generic.get_interfaces(self) +function protocol.get_interfaces(self) if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then local ifaces = { } @@ -799,9 +963,13 @@ function proto.generic.get_interfaces(self) function(s) if s.device then num[s.device] = num[s.device] and num[s.device] + 1 or 1 - if s.network == self.sid then - ifn = "%s.network%d" %{ s.device, num[s.device] } - wfs[ifn] = interface(ifn, self) + + 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) + end end end end) @@ -814,7 +982,7 @@ function proto.generic.get_interfaces(self) end end -function proto.generic.contains_interface(self, ifname) +function protocol.contains_interface(self, ifname) ifname = _M:ifnameof(ifname) if not ifname then return false @@ -833,14 +1001,19 @@ function proto.generic.contains_interface(self, ifname) local wif = _wifi_lookup(ifname) if wif then - return (_uci_real:get("wireless", wif, "network") == self.sid) + local n + for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do + if n == self.sid then + return true + end + end end end return false end -function proto.generic.adminlink(self) +function protocol.adminlink(self) return dsp.build_url("admin", "network", "network", self.sid) end @@ -849,19 +1022,33 @@ interface = utl.class() function interface.__init__(self, ifname, network) local wif = _wifi_lookup(ifname) - if wif then self.wif = wifinet(wif) end + if wif then + self.wif = wifinet(wif) + self.ifname = _uci_state:get("wireless", wif, "ifname") + end self.ifname = self.ifname or ifname self.dev = _interfaces[self.ifname] self.network = network end +function interface._ubus(self, field) + if not _ubusdevcache[self.ifname] then + _ubusdevcache[self.ifname] = _ubus:call("network.device", "status", + { name = self.ifname }) + end + if _ubusdevcache[self.ifname] and field then + return _ubusdevcache[self.ifname][field] + end + return _ubusdevcache[self.ifname] +end + function interface.name(self) return self.wif and self.wif:ifname() or self.ifname end function interface.mac(self) - return (self.dev and self.dev.macaddr or "00:00:00:00:00:00"):upper() + return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper() end function interface.ipaddrs(self) @@ -902,7 +1089,7 @@ end function interface.get_i18n(self) if self.wif then return "%s: %s %q" %{ - i18n.translate("Wireless Network"), + lng.translate("Wireless Network"), self.wif:active_mode(), self.wif:active_ssid() or self.wif:active_bssid() } @@ -914,17 +1101,17 @@ end function interface.get_type_i18n(self) local x = self:type() if x == "wifi" then - return i18n.translate("Wireless Adapter") + return lng.translate("Wireless Adapter") elseif x == "bridge" then - return i18n.translate("Bridge") + return lng.translate("Bridge") elseif x == "switch" then - return i18n.translate("Ethernet Switch") + return lng.translate("Ethernet Switch") elseif x == "vlan" then - return i18n.translate("VLAN Interface") + return lng.translate("VLAN Interface") elseif x == "tunnel" then - return i18n.translate("Tunnel Interface") + return lng.translate("Tunnel Interface") else - return i18n.translate("Ethernet Adapter") + return lng.translate("Ethernet Adapter") end end @@ -935,13 +1122,13 @@ function interface.adminlink(self) end function interface.ports(self) - if self.br then - local iface + local members = self:_ubus("bridge-members") + if members then + local _, iface local ifaces = { } - for _, iface in ipairs(self.br.ifnames) do - ifaces[#ifaces+1] = interface(iface.name) + for _, iface in ipairs(members) do + ifaces[#ifaces+1] = interface(iface) end - return ifaces end end @@ -965,7 +1152,7 @@ function interface.is_up(self) if self.wif then return self.wif:is_up() else - return self.dev and self.dev.flags and self.dev.flags.up or false + return self:_ubus("up") or false end end @@ -977,45 +1164,53 @@ function interface.is_bridgeport(self) return self.dev and self.dev.bridge and true or false end +local function uint(x) + if x then + return (x < 0) and ((2^32) + x) or x + end + return 0 +end + function interface.tx_bytes(self) - return self.dev and self.dev.stats - and self.dev.stats.tx_bytes or 0 + local stat = self:_ubus("statistics") + return stat and uint(stat.tx_bytes) or 0 end function interface.rx_bytes(self) - return self.dev and self.dev.stats - and self.dev.stats.rx_bytes or 0 + local stat = self:_ubus("statistics") + return stat and uint(stat.rx_bytes) or 0 end function interface.tx_packets(self) - return self.dev and self.dev.stats - and self.dev.stats.tx_packets or 0 + local stat = self:_ubus("statistics") + return stat and uint(stat.tx_packets) or 0 end function interface.rx_packets(self) - return self.dev and self.dev.stats - and self.dev.stats.rx_packets or 0 + local stat = self:_ubus("statistics") + return stat and uint(stat.rx_packets) or 0 end function interface.get_network(self) - if not self.network then - if self.dev and self.dev.network then - self.network = _M:get_network(self.dev.network) - end - end + return self:get_networks()[1] +end - if not self.network then - local net +function interface.get_networks(self) + if not self.networks then + local nets = { } + local _, net for _, net in ipairs(_M:get_networks()) do if net:contains_interface(self.ifname) or net:ifname() == self.ifname then - self.network = net - return net + nets[#nets+1] = net end end + table.sort(nets, function(a, b) return a.sid < b.sid end) + self.networks = nets + return nets else - return self.network + return self.networks end end @@ -1227,7 +1422,7 @@ function wifinet.active_mode(self) end function wifinet.active_mode_i18n(self) - return i18n.translate(self:active_mode()) + return lng.translate(self:active_mode()) end function wifinet.active_ssid(self) @@ -1279,7 +1474,12 @@ function wifinet.country(self) end function wifinet.txpower(self) - return self.iwinfo.txpower or 0 + local pwr = (self.iwinfo.txpower or 0) + return pwr + self:txpower_offset() +end + +function wifinet.txpower_offset(self) + return self.iwinfo.txpower_offset or 0 end function wifinet.signal_level(self, s, n) @@ -1311,15 +1511,15 @@ end function wifinet.shortname(self) return "%s %q" %{ - i18n.translate(self:active_mode()), + lng.translate(self:active_mode()), self:active_ssid() or self:active_bssid() } end function wifinet.get_i18n(self) return "%s: %s %q (%s)" %{ - i18n.translate("Wireless Network"), - i18n.translate(self:active_mode()), + lng.translate("Wireless Network"), + lng.translate(self:active_mode()), self:active_ssid() or self:active_bssid(), self:ifname() } @@ -1330,9 +1530,19 @@ function wifinet.adminlink(self) end function wifinet.get_network(self) - if _uci_real:get("network", self.iwdata.network) == "interface" then - return network(self.iwdata.network) + return self:get_networks()[1] +end + +function wifinet.get_networks(self) + local nets = { } + local net + for net in utl.imatch(tostring(self.iwdata.network)) do + if _uci_real:get("network", net) == "interface" then + nets[#nets+1] = network(net) + end end + table.sort(nets, function(a, b) return a.sid < b.sid end) + return nets end function wifinet.get_interface(self) @@ -1340,6 +1550,11 @@ function wifinet.get_interface(self) end +-- setup base protocols +_M:register_protocol("static") +_M:register_protocol("dhcp") +_M:register_protocol("none") + -- load protocol extensions local exts = nfs.dir(utl.libpath() .. "/model/network") if exts then