X-Git-Url: http://git.openwrt.org/?p=project%2Fluci.git;a=blobdiff_plain;f=libs%2Fcore%2Fluasrc%2Fmodel%2Fnetwork.lua;h=cc572298c69b5262f02af232ab45040a528c0627;hp=56597a2855d792a3a7c14fd9a6f7ef9ff16f6429;hb=8772db16759352146a069f2bb7394a4a4213ba0f;hpb=8e5590037def3b905cc5b3fcb25c940ea8579b6b diff --git a/libs/core/luasrc/model/network.lua b/libs/core/luasrc/model/network.lua index 56597a2855..cc572298c6 100644 --- a/libs/core/luasrc/model/network.lua +++ b/libs/core/luasrc/model/network.lua @@ -17,23 +17,41 @@ 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" 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" -local ifs, brs, sws, uci_r, uci_s +IFACE_PATTERNS_VIRTUAL = { } +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" } + + +protocol = utl.class() -function _list_del(c, s, o, r) - local val = uci_r:get(c, s, o) +local _protocols = { } + +local _interfaces, _bridge, _switch, _tunnel +local _ubus, _ubusnetcache, _ubusdevcache +local _uci_real, _uci_state + +function _filter(c, s, o, r) + local val = _uci_real:get(c, s, o) if val then local l = { } if type(val) == "string" then @@ -43,9 +61,9 @@ function _list_del(c, s, o, r) end end if #l > 0 then - uci_r:set(c, s, o, table.concat(l, " ")) + _uci_real:set(c, s, o, table.concat(l, " ")) else - uci_r:delete(c, s, o) + _uci_real:delete(c, s, o) end elseif type(val) == "table" then for _, val in ipairs(val) do @@ -54,16 +72,16 @@ function _list_del(c, s, o, r) end end if #l > 0 then - uci_r:set(c, s, o, l) + _uci_real:set(c, s, o, l) else - uci_r:delete(c, s, o) + _uci_real:delete(c, s, o) end end end end -function _list_add(c, s, o, a) - local val = uci_r:get(c, s, o) or "" +function _append(c, s, o, a) + local val = _uci_real:get(c, s, o) or "" if type(val) == "string" then local l = { } for val in val:gmatch("%S+") do @@ -72,7 +90,7 @@ function _list_add(c, s, o, a) end end l[#l+1] = a - uci_r:set(c, s, o, table.concat(l, " ")) + _uci_real:set(c, s, o, table.concat(l, " ")) elseif type(val) == "table" then local l = { } for _, val in ipairs(val) do @@ -81,7 +99,7 @@ function _list_add(c, s, o, a) end end l[#l+1] = a - uci_r:set(c, s, o, l) + _uci_real:set(c, s, o, l) end end @@ -94,23 +112,26 @@ function _stror(s1, s2) end function _get(c, s, o) - return uci_r:get(c, s, o) + return _uci_real:get(c, s, o) end function _set(c, s, o, v) if v ~= nil then if type(v) == "boolean" then v = v and "1" or "0" end - return uci_r:set(c, s, o, v) + return _uci_real:set(c, s, o, v) else - return uci_r:delete(c, s, o) + return _uci_real:delete(c, s, o) end end function _wifi_iface(x) - return ( - x:match("^wlan%d") or x:match("^wl%d") or x:match("^ath%d") or - x:match("^%w+%.network%d") - ) + local _, p + for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do + if x:match(p) then + return true + end + end + return false end function _wifi_lookup(ifn) @@ -121,7 +142,7 @@ function _wifi_lookup(ifn) local num = 0 ifnidx = tonumber(ifnidx) - uci_r:foreach("wireless", "wifi-iface", + _uci_real:foreach("wireless", "wifi-iface", function(s) if s.device == radio then num = num + 1 @@ -138,7 +159,7 @@ function _wifi_lookup(ifn) elseif _wifi_iface(ifn) then local sid = nil - uci_s:foreach("wireless", "wifi-iface", + _uci_state:foreach("wireless", "wifi-iface", function(s) if s.ifname == ifn then sid = s['.name'] @@ -151,29 +172,38 @@ function _wifi_lookup(ifn) end function _iface_virtual(x) - return ( - x:match("^6in4-%w") or x:match("^6to4-%w") or x:match("^3g-%w") or - x:match("^ppp-%w") or x:match("^pppoe-%w") or x:match("^pppoa-%w") or - x:match("^relay-%w") - ) + local _, p + for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do + if x:match(p) then + return true + end + end + return false end function _iface_ignore(x) - return ( - x:match("^wmaster%d") or x:match("^wifi%d") or x:match("^hwsim%d") or - x:match("^imq%d") or x:match("^mon.wlan%d") or - x == "sit0" or x == "lo" or _iface_virtual(x) - ) + local _, p + for _, p in ipairs(IFACE_PATTERNS_IGNORE) do + if x:match(p) then + return true + end + end + return _iface_virtual(x) end function init(cursor) - uci_r = cursor or uci_r or uci.cursor() - uci_s = uci_r:substate() + _uci_real = cursor or _uci_real or uci.cursor() + _uci_state = _uci_real:substate() + + _interfaces = { } + _bridge = { } + _switch = { } + _tunnel = { } - ifs = { } - brs = { } - sws = { } + _ubus = bus.connect() + _ubusnetcache = { } + _ubusdevcache = { } -- read interface information local n, i @@ -181,8 +211,12 @@ function init(cursor) local name = i.name:match("[^:]+") local prnt = name:match("^([^%.]+)%.") - if _iface_virtual(name) or not _iface_ignore(name) then - ifs[name] = ifs[name] or { + if _iface_virtual(name) then + _tunnel[name] = true + end + + if _tunnel[name] or not _iface_ignore(name) then + _interfaces[name] = _interfaces[name] or { idx = i.ifindex or n, name = name, rawname = i.name, @@ -192,18 +226,18 @@ function init(cursor) } if prnt then - sws[name] = true - sws[prnt] = true + _switch[name] = true + _switch[prnt] = true end if i.family == "packet" then - ifs[name].flags = i.flags - ifs[name].stats = i.data - ifs[name].macaddr = i.addr + _interfaces[name].flags = i.flags + _interfaces[name].stats = i.data + _interfaces[name].macaddr = i.addr elseif i.family == "inet" then - ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask) + _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask) elseif i.family == "inet6" then - ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask) + _interfaces[name].ip6addrs[#_interfaces[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask) end end end @@ -218,14 +252,14 @@ function init(cursor) name = r[1], id = r[2], stp = r[3] == "yes", - ifnames = { ifs[r[4]] } + ifnames = { _interfaces[r[4]] } } if b.ifnames[1] then b.ifnames[1].bridge = b end - brs[r[1]] = b + _bridge[r[1]] = b elseif b then - b.ifnames[#b.ifnames+1] = ifs[r[2]] + b.ifnames[#b.ifnames+1] = _interfaces[r[2]] b.ifnames[#b.ifnames].bridge = b end end @@ -235,15 +269,63 @@ function init(cursor) end function save(self, ...) - uci_r:save(...) - uci_r:load(...) + _uci_real:save(...) + _uci_real:load(...) end function commit(self, ...) - uci_r:commit(...) - uci_r:load(...) + _uci_real:commit(...) + _uci_real:load(...) +end + +function ifnameof(self, x) + if utl.instanceof(x, interface) then + return x:name() + 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 @@ -251,7 +333,7 @@ end function add_network(self, n, options) local oldnet = self:get_network(n) if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then - if uci_r:section("network", "interface", n, options) then + if _uci_real:section("network", "interface", n, options) then return network(n) end elseif oldnet and oldnet:is_empty() then @@ -266,7 +348,7 @@ function add_network(self, n, options) end function get_network(self, n) - if n and uci_r:get("network", n) == "interface" then + if n and _uci_real:get("network", n) == "interface" then return network(n) end end @@ -275,7 +357,7 @@ function get_networks(self) local nets = { } local nls = { } - uci_r:foreach("network", "interface", + _uci_real:foreach("network", "interface", function(s) nls[s['.name']] = network(s['.name']) end) @@ -289,21 +371,31 @@ function get_networks(self) end function del_network(self, n) - local r = uci_r:delete("network", n) + local r = _uci_real:delete("network", n) if r then - uci_r:delete_all("network", "alias", + _uci_real:delete_all("network", "alias", function(s) return (s.interface == n) end) - uci_r:delete_all("network", "route", + _uci_real:delete_all("network", "route", function(s) return (s.interface == n) end) - uci_r:delete_all("network", "route6", + _uci_real:delete_all("network", "route6", function(s) return (s.interface == n) end) - uci_r:foreach("wireless", "wifi-iface", + _uci_real:foreach("wireless", "wifi-iface", function(s) - if s.network == n then - uci_r:delete("wireless", s['.name'], "network") + 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) end @@ -313,50 +405,60 @@ end function rename_network(self, old, new) local r if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then - r = uci_r:section("network", "interface", new, uci_r:get_all("network", old)) + r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old)) if r then - uci_r:foreach("network", "alias", + _uci_real:foreach("network", "alias", function(s) if s.interface == old then - uci_r:set("network", s['.name'], "interface", new) + _uci_real:set("network", s['.name'], "interface", new) end end) - uci_r:foreach("network", "route", + _uci_real:foreach("network", "route", function(s) if s.interface == old then - uci_r:set("network", s['.name'], "interface", new) + _uci_real:set("network", s['.name'], "interface", new) end end) - uci_r:foreach("network", "route6", + _uci_real:foreach("network", "route6", function(s) if s.interface == old then - uci_r:set("network", s['.name'], "interface", new) + _uci_real:set("network", s['.name'], "interface", new) end end) - uci_r:foreach("wireless", "wifi-iface", + _uci_real:foreach("wireless", "wifi-iface", function(s) - if s.network == old then - uci_r: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) - uci_r:delete("network", old) + _uci_real:delete("network", old) end end return r or false end function get_interface(self, i) - if ifs[i] or _wifi_iface(i) then + if _interfaces[i] or _wifi_iface(i) then return interface(i) else local ifc local num = { } - uci_r: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 @@ -376,9 +478,10 @@ function get_interfaces(self) local ifaces = { } local seen = { } local nfs = { } + local baseof = { } -- find normal interfaces - uci_r:foreach("network", "interface", + _uci_real:foreach("network", "interface", function(s) for iface in utl.imatch(s.ifname) do if not _iface_ignore(iface) and not _wifi_iface(iface) then @@ -388,12 +491,47 @@ function get_interfaces(self) end end) - for iface in utl.kspairs(ifs) do + for iface in utl.kspairs(_interfaces) do if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then nfs[iface] = interface(iface) end end + -- find vlan interfaces + _uci_real:foreach("network", "switch_vlan", + function(s) + 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) + if vid ~= nil and vid >= 0 and vid <= 4095 then + local iface = "%s.%d" %{ base, vid } + if not seen[iface] then + seen[iface] = true + nfs[iface] = interface(iface) + end + end + end) + for iface in utl.kspairs(nfs) do ifaces[#ifaces+1] = nfs[iface] end @@ -401,7 +539,7 @@ function get_interfaces(self) -- find wifi interfaces local num = { } local wfs = { } - uci_r: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 @@ -422,7 +560,7 @@ function ignore_interface(self, x) end function get_wifidev(self, dev) - if uci_r:get("wireless", dev) == "wifi-device" then + if _uci_real:get("wireless", dev) == "wifi-device" then return wifidev(dev) end end @@ -431,7 +569,7 @@ function get_wifidevs(self) local devs = { } local wfd = { } - uci_r:foreach("wireless", "wifi-device", + _uci_real:foreach("wireless", "wifi-device", function(s) wfd[#wfd+1] = s['.name'] end) local dev @@ -451,9 +589,9 @@ end function add_wifinet(self, net, options) if type(options) == "table" and options.device and - uci_r:get("wireless", options.device) == "wifi-device" + _uci_real:get("wireless", options.device) == "wifi-device" then - local wnet = uci_r:section("wireless", "wifi-iface", nil, options) + local wnet = _uci_real:section("wireless", "wifi-iface", nil, options) return wifinet(wnet) end end @@ -461,130 +599,150 @@ end function del_wifinet(self, net) local wnet = _wifi_lookup(net) if wnet then - uci_r:delete("wireless", wnet) + _uci_real:delete("wireless", wnet) return true end 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_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 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 -network = utl.class() -function network.__init__(self, name) +function network(name, proto) + if name then + local p = proto or _uci_real:get("network", name, "proto") + local c = p and _protocols[p] or protocol + return c(name) + end +end + +function protocol.__init__(self, name) self.sid = name end -function network._get(self, opt) - local v = uci_r:get("network", self.sid, opt) +function protocol._get(self, opt) + local v = _uci_real:get("network", self.sid, opt) if type(v) == "table" then return table.concat(v, " ") end return v or "" end -function network._ip(self, opt, family, list) - local ip = uci_s: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 network.get(self, opt) +function protocol.get(self, opt) return _get("network", self.sid, opt) end -function network.set(self, opt, val) +function protocol.set(self, opt, val) return _set("network", self.sid, opt, val) end -function network.ifname(self) - local p = self:proto() - if self:is_bridge() then - return "br-" .. self.sid - elseif self:proto() == "relay" then - return uci_s:get("network", self.sid, "up") == "1" and "lo" or nil - 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_r:get("network", self.sid, "ifname") or - uci_s: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_r: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 network.device(self) - local dev = uci_r:get("network", self.sid, "device") or - uci_s:get("network", self.sid, "device") - - dev = (type(dev) == "table") and dev[1] or dev - - if not dev or dev:match("[^%w%-%.%s]") then - dev = uci_r:get("network", self.sid, "ifname") or - uci_s:get("network", self.sid, "ifname") - - dev = (type(dev) == "table") and dev[1] or dev - end - - for dev in utl.imatch(dev) do - return dev - end +function protocol.proto(self) + return "none" end -function network.proto(self) - return self:_get("proto") or "none" +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 network.type(self) +function protocol.type(self) return self:_get("type") end -function network.name(self) +function protocol.name(self) return self.sid end -function network.uptime(self) - local cnt = tonumber(uci_s: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 network.expires(self) - local a = tonumber(uci_s:get("network", self.sid, "lease_acquired")) - local l = tonumber(uci_s:get("network", self.sid, "lease_lifetime")) +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 l = l - (nxo.sysinfo().uptime - a) return l > 0 and l or 0 @@ -592,72 +750,89 @@ function network.expires(self) return -1 end -function network.metric(self) - return tonumber(uci_s:get("network", self.sid, "metric")) or 0 +function protocol.metric(self) + return tonumber(_uci_state:get("network", self.sid, "metric")) or 0 end -function network.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 network.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 network.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 network.dnsaddrs(self) - return self:_ip("dns", 4, true) +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 network.ip6addr(self) - local ip6 = self:_ip("ip6addr", 6) - if not ip6 then - local ifc = ifs[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.ip6addr(self) + local addrs = self:_ubus("ipv6-address") + return addrs and #addrs > 0 + and "%s/%d" %{ addrs[1].address, addrs[1].mask } +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 network.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 network.dns6addrs(self) - return self:_ip("dns", 6, true) +function protocol.is_bridge(self) + return (not self:is_virtual() and self:type() == "bridge") end -function network.is_bridge(self) - return (self:type() == "bridge") +function protocol.opkg_package(self) + return nil end -function network.is_virtual(self) - local p = self:proto() - return ( - p == "3g" or p == "6in4" or p == "6to4" or p == "ppp" or - p == "pppoe" or p == "pppoa" or p == "relay" - ) +function protocol.is_installed(self) + return true end -function network.is_empty(self) - if self:is_virtual() then +function protocol.is_virtual(self) + return false +end + +function protocol.is_floating(self) + return false +end + +function protocol.is_empty(self) + if self:is_floating() then return false else local rv = true @@ -666,11 +841,14 @@ function network.is_empty(self) rv = false end - uci_r:foreach("wireless", "wifi-iface", + _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) @@ -678,61 +856,75 @@ function network.is_empty(self) end end -function network.add_interface(self, ifname) - if not self:is_virtual() then - if type(ifname) ~= "string" then - ifname = ifname:name() - else - ifname = ifname:match("[^%s:]+") - end - - -- remove the interface from all ifaces - uci_r:foreach("network", "interface", - function(s) - _list_del("network", s['.name'], "ifname", ifname) - 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) if wif then - uci_r:set("wireless", wif, "network", self.sid) + _append("wireless", wif, "network", self.sid) -- add iface to our iface list else - _list_add("network", self.sid, "ifname", ifname) + _append("network", self.sid, "ifname", ifname) end end end -function network.del_interface(self, ifname) - if not self:is_virtual() then - if utl.instanceof(ifname, interface) then - ifname = ifname:name() - else - ifname = ifname:match("[^%s:]+") - end - +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_r:delete("wireless", wif, "network") end + if wif then _filter("wireless", wif, "network", self.sid) end -- remove the interface - _list_del("network", self.sid, "ifname", ifname) + _filter("network", self.sid, "ifname", ifname) end end -function network.get_interfaces(self) - local ifaces = { } - - local ifn +function protocol.get_interface(self) if self:is_virtual() then - ifn = self:proto() .. "-" .. self.sid - ifaces = { interface(ifn) } + _tunnel[self:proto() .. "-" .. self.sid] = true + return interface(self:proto() .. "-" .. self.sid, self) + elseif self:is_bridge() then + _bridge["br-" .. self.sid] = true + return interface("br-" .. self.sid, self) else + local ifn = nil + local num = { } + 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_real: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) + return ifn and interface(ifn, self) + end +end + +function protocol.get_interfaces(self) + if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then + local ifaces = { } + + local ifn local nfs = { } for ifn in utl.imatch(self:get("ifname")) do - ifn = ifn:match("[^:]+") - nfs[ifn] = interface(ifn) + ifn = ifn:match("^[^:/]+") + nfs[ifn] = interface(ifn, self) end for ifn in utl.kspairs(nfs) do @@ -741,13 +933,17 @@ function network.get_interfaces(self) local num = { } local wfs = { } - uci_r: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.network%d" %{ s.device, num[s.device] } - wfs[ifn] = interface(ifn) + + 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) @@ -755,23 +951,21 @@ function network.get_interfaces(self) for ifn in utl.kspairs(wfs) do ifaces[#ifaces+1] = wfs[ifn] end - end - - return ifaces -end -function network.contains_interface(self, ifname) - if type(ifname) ~= "string" then - ifname = ifname:name() - else - ifname = ifname:match("[^%s:]+") + return ifaces end +end - local ifn - if self:is_virtual() then - ifn = self:proto() .. "-" .. self.sid - return ifname == ifn +function protocol.contains_interface(self, ifname) + ifname = _M:ifnameof(ifname) + if not ifname then + return false + elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then + return true + elseif self:is_bridge() and "br-" .. self.sid == ifname then + return true else + local ifn for ifn in utl.imatch(self:get("ifname")) do ifn = ifn:match("[^:]+") if ifn == ifname then @@ -781,25 +975,46 @@ function network.contains_interface(self, ifname) local wif = _wifi_lookup(ifname) if wif then - return (uci_r: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 network.adminlink(self) +function protocol.adminlink(self) return dsp.build_url("admin", "network", "network", self.sid) end interface = utl.class() -function interface.__init__(self, ifname) + +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 - self.ifname = self.ifname or ifname - self.dev = ifs[self.ifname] +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) @@ -807,7 +1022,7 @@ function interface.name(self) end function interface.mac(self) - return self.dev and self.dev.macaddr or "00:00:00:00:00:00" + return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper() end function interface.ipaddrs(self) @@ -821,9 +1036,13 @@ end function interface.type(self) if self.wif or _wifi_iface(self.ifname) then return "wifi" - elseif brs[self.ifname] then + elseif _bridge[self.ifname] then return "bridge" - elseif sws[self.ifname] or self.ifname:match("%.") then + elseif _tunnel[self.ifname] then + return "tunnel" + elseif self.ifname:match("%.") then + return "vlan" + elseif _switch[self.ifname] then return "switch" else return "ethernet" @@ -844,7 +1063,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() } @@ -856,13 +1075,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 lng.translate("VLAN Interface") + elseif x == "tunnel" then + return lng.translate("Tunnel Interface") else - return i18n.translate("Ethernet Adapter") + return lng.translate("Ethernet Adapter") end end @@ -873,13 +1096,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 @@ -903,7 +1126,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 @@ -915,43 +1138,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 self.dev and self.dev.network then - self.network = _M:get_network(self.dev.network) - 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 @@ -961,6 +1194,7 @@ end wifidev = utl.class() + function wifidev.__init__(self, dev) self.sid = dev self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { } @@ -1008,7 +1242,7 @@ end function wifidev.is_up(self) local up = false - uci_s:foreach("wireless", "wifi-iface", + _uci_state:foreach("wireless", "wifi-iface", function(s) if s.device == self.sid then if s.up == "1" then @@ -1022,7 +1256,7 @@ function wifidev.is_up(self) end function wifidev.get_wifinet(self, net) - if uci_r:get("wireless", net) == "wifi-iface" then + if _uci_real:get("wireless", net) == "wifi-iface" then return wifinet(net) else local wnet = _wifi_lookup(net) @@ -1035,7 +1269,7 @@ end function wifidev.get_wifinets(self) local nets = { } - uci_r:foreach("wireless", "wifi-iface", + _uci_real:foreach("wireless", "wifi-iface", function(s) if s.device == self.sid then nets[#nets+1] = wifinet(s['.name']) @@ -1049,7 +1283,7 @@ function wifidev.add_wifinet(self, options) options = options or { } options.device = self.sid - local wnet = uci_r:section("wireless", "wifi-iface", nil, options) + local wnet = _uci_real:section("wireless", "wifi-iface", nil, options) if wnet then return wifinet(wnet, options) end @@ -1058,12 +1292,12 @@ end function wifidev.del_wifinet(self, net) if utl.instanceof(net, wifinet) then net = net.sid - elseif uci_r:get("wireless", net) ~= "wifi-iface" then + elseif _uci_real:get("wireless", net) ~= "wifi-iface" then net = _wifi_lookup(net) end - if net and uci_r:get("wireless", net, "device") == self.sid then - uci_r:delete("wireless", net) + if net and _uci_real:get("wireless", net, "device") == self.sid then + _uci_real:delete("wireless", net) return true end @@ -1072,12 +1306,13 @@ end wifinet = utl.class() + function wifinet.__init__(self, net, data) self.sid = net local num = { } local netid - uci_r: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 @@ -1088,13 +1323,13 @@ function wifinet.__init__(self, net, data) end end) - local dev = uci_s:get("wireless", self.sid, "ifname") or netid + local dev = _uci_state:get("wireless", self.sid, "ifname") or netid self.netid = netid self.wdev = dev self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { } - self.iwdata = data or uci_s:get_all("wireless", self.sid) or - uci_r:get_all("wireless", self.sid) or { } + self.iwdata = data or _uci_state:get_all("wireless", self.sid) or + _uci_real:get_all("wireless", self.sid) or { } end function wifinet.get(self, opt) @@ -1106,19 +1341,19 @@ function wifinet.set(self, opt, val) end function wifinet.mode(self) - return uci_s:get("wireless", self.sid, "mode") or "ap" + return _uci_state:get("wireless", self.sid, "mode") or "ap" end function wifinet.ssid(self) - return uci_s:get("wireless", self.sid, "ssid") + return _uci_state:get("wireless", self.sid, "ssid") end function wifinet.bssid(self) - return uci_s:get("wireless", self.sid, "bssid") + return _uci_state:get("wireless", self.sid, "bssid") end function wifinet.network(self) - return uci_s:get("wifinet", self.sid, "network") + return _uci_state:get("wifinet", self.sid, "network") end function wifinet.id(self) @@ -1161,7 +1396,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) @@ -1197,7 +1432,7 @@ end function wifinet.channel(self) return self.iwinfo.channel or - tonumber(uci_s:get("wireless", self.iwdata.device, "channel")) + tonumber(_uci_state:get("wireless", self.iwdata.device, "channel")) end function wifinet.signal(self) @@ -1213,7 +1448,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) @@ -1245,15 +1485,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() } @@ -1264,11 +1504,38 @@ function wifinet.adminlink(self) end function wifinet.get_network(self) - if uci_r: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) return interface(self:ifname()) 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 + local ext + for ext in exts do + if ext:match("%.lua$") then + require("luci.model.network." .. ext:gsub("%.lua$", "")) + end + end +end