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"
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()
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
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
- end
- end
- end)
+ local _, _, netstate = _wifi_state_by_ifname(ifn)
+ if netstate and type(netstate.section) == "string" then
+ return netstate.section
+ end
+end
- return sid
+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)
- -- 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
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
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")
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
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]
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)
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
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
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
end
function get_status_by_route(self, addr, mask)
+ local route_statuses = { }
local _, object
for _, object in ipairs(utl.ubus()) do
local net = object:match("^network%.interface%.(.+)")
local rt
for _, rt in ipairs(s.route) do
if not rt.table and rt.target == addr and rt.mask == mask then
- return net, s
+ route_statuses[net] = s
end
end
end
end
end
+
+ return route_statuses
end
function get_status_by_address(self, addr)
end
end
end
+ if s and s['ipv6-prefix-assignment'] then
+ local a
+ for _, a in ipairs(s['ipv6-prefix-assignment']) do
+ if a and a['local-address'] and a['local-address'].address == addr then
+ return net, s
+ end
+ end
+ end
end
end
end
-function get_wannet(self)
- local net, stat = self:get_status_by_route("0.0.0.0", 0)
- return net and network(net, stat.proto)
-end
+function get_wan_networks(self)
+ local k, v
+ local wan_nets = { }
+ local route_statuses = self:get_status_by_route("0.0.0.0", 0)
-function get_wandev(self)
- local _, stat = self:get_status_by_route("0.0.0.0", 0)
- return stat and interface(stat.l3_device or stat.device)
-end
+ for k, v in pairs(route_statuses) do
+ wan_nets[#wan_nets+1] = network(k, v.proto)
+ end
-function get_wan6net(self)
- local net, stat = self:get_status_by_route("::", 0)
- return net and network(net, stat.proto)
+ return wan_nets
end
-function get_wan6dev(self)
- local _, stat = self:get_status_by_route("::", 0)
- return stat and interface(stat.l3_device or stat.device)
+function get_wan6_networks(self)
+ local k, v
+ local wan6_nets = { }
+ local route_statuses = self:get_status_by_route("::", 0)
+
+ for k, v in pairs(route_statuses) do
+ wan6_nets[#wan6_nets+1] = network(k, v.proto)
+ end
+
+ return wan6_nets
end
function get_switch_topologies(self)
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
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
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
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
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)
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
_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
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
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
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
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)
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"
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() }
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")
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)
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)
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
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
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
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, ...)
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
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)