summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-base/luasrc/model
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2018-01-02 00:24:10 +0100
committerJo-Philipp Wich <jo@mein.io>2018-01-02 00:24:10 +0100
commitf9b8d7ff7bbd3c82f9889b65af07a28d94d9fed7 (patch)
treec1da9bd9e0dbc7ddae9556eefd340fcc2716be47 /modules/luci-base/luasrc/model
parentf2162a9a668db1c45bd0bc7ad59ebfd9b7abe762 (diff)
luci-base: rework wireless state handling (#1179)
- fix mapping of ubus wireless state to uci declared vifs - fix leaking foreign vif info into per-phy iwinfo stats Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules/luci-base/luasrc/model')
-rw-r--r--modules/luci-base/luasrc/model/network.lua367
1 files changed, 214 insertions, 153 deletions
diff --git a/modules/luci-base/luasrc/model/network.lua b/modules/luci-base/luasrc/model/network.lua
index d9ef4089c8..48a03393eb 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"
@@ -108,6 +106,13 @@ 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
+
function _wifi_iface(x)
local _, p
for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
@@ -118,58 +123,155 @@ 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
- 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
+ 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
+
+ _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)
+
+ 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
+ end)
- return sid
+ return netid
+end
- -- looks like wifi, try to locate the section via ubus state
- elseif _wifi_iface(ifn) then
- return _wifi_state("ifname", ifn, "section")
+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
@@ -524,20 +626,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_netname(i)
+ return netid and interface(netid)
end
end
@@ -644,7 +734,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 +750,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 +874,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
@@ -981,24 +1056,17 @@ 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
@@ -1006,7 +1074,7 @@ 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 +1089,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
@@ -1043,21 +1111,7 @@ function protocol.get_interface(self)
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 +1131,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 +1172,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 +1187,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
@@ -1332,9 +1386,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)
@@ -1387,7 +1446,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 +1480,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 +1494,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, ...)
@@ -1664,7 +1724,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)