Merge pull request #3458 from janh/vnstat2
[oweals/luci.git] / modules / luci-compat / luasrc / model / network.lua
1 -- Copyright 2009-2015 Jo-Philipp Wich <jow@openwrt.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 local type, next, pairs, ipairs, loadfile, table, select
5         = type, next, pairs, ipairs, loadfile, table, select
6
7 local tonumber, tostring, math = tonumber, tostring, math
8
9 local pcall, require, setmetatable = pcall, require, setmetatable
10
11 local nxo = require "nixio"
12 local nfs = require "nixio.fs"
13 local ipc = require "luci.ip"
14 local utl = require "luci.util"
15 local uci = require "luci.model.uci"
16 local lng = require "luci.i18n"
17 local jsc = require "luci.jsonc"
18
19 module "luci.model.network"
20
21
22 IFACE_PATTERNS_VIRTUAL  = { }
23 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$" }
24 IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
25
26 IFACE_ERRORS = {
27         CONNECT_FAILED                  = lng.translate("Connection attempt failed"),
28         INVALID_ADDRESS                 = lng.translate("IP address is invalid"),
29         INVALID_GATEWAY                 = lng.translate("Gateway address is invalid"),
30         INVALID_LOCAL_ADDRESS   = lng.translate("Local IP address is invalid"),
31         MISSING_ADDRESS                 = lng.translate("IP address is missing"),
32         MISSING_PEER_ADDRESS    = lng.translate("Peer address is missing"),
33         NO_DEVICE                               = lng.translate("Network device is not present"),
34         NO_IFACE                                = lng.translate("Unable to determine device name"),
35         NO_IFNAME                               = lng.translate("Unable to determine device name"),
36         NO_WAN_ADDRESS                  = lng.translate("Unable to determine external IP address"),
37         NO_WAN_LINK                             = lng.translate("Unable to determine upstream interface"),
38         PEER_RESOLVE_FAIL               = lng.translate("Unable to resolve peer host name"),
39         PIN_FAILED                              = lng.translate("PIN code rejected")
40 }
41
42
43 protocol = utl.class()
44
45 local _protocols = { }
46
47 local _interfaces, _bridge, _switch, _tunnel, _swtopo
48 local _ubusnetcache, _ubusdevcache, _ubuswificache
49 local _uci
50
51 function _filter(c, s, o, r)
52         local val = _uci:get(c, s, o)
53         if val then
54                 local l = { }
55                 if type(val) == "string" then
56                         for val in val:gmatch("%S+") do
57                                 if val ~= r then
58                                         l[#l+1] = val
59                                 end
60                         end
61                         if #l > 0 then
62                                 _uci:set(c, s, o, table.concat(l, " "))
63                         else
64                                 _uci:delete(c, s, o)
65                         end
66                 elseif type(val) == "table" then
67                         for _, val in ipairs(val) do
68                                 if val ~= r then
69                                         l[#l+1] = val
70                                 end
71                         end
72                         if #l > 0 then
73                                 _uci:set(c, s, o, l)
74                         else
75                                 _uci:delete(c, s, o)
76                         end
77                 end
78         end
79 end
80
81 function _append(c, s, o, a)
82         local val = _uci:get(c, s, o) or ""
83         if type(val) == "string" then
84                 local l = { }
85                 for val in val:gmatch("%S+") do
86                         if val ~= a then
87                                 l[#l+1] = val
88                         end
89                 end
90                 l[#l+1] = a
91                 _uci:set(c, s, o, table.concat(l, " "))
92         elseif type(val) == "table" then
93                 local l = { }
94                 for _, val in ipairs(val) do
95                         if val ~= a then
96                                 l[#l+1] = val
97                         end
98                 end
99                 l[#l+1] = a
100                 _uci:set(c, s, o, l)
101         end
102 end
103
104 function _stror(s1, s2)
105         if not s1 or #s1 == 0 then
106                 return s2 and #s2 > 0 and s2
107         else
108                 return s1
109         end
110 end
111
112 function _get(c, s, o)
113         return _uci:get(c, s, o)
114 end
115
116 function _set(c, s, o, v)
117         if v ~= nil then
118                 if type(v) == "boolean" then v = v and "1" or "0" end
119                 return _uci:set(c, s, o, v)
120         else
121                 return _uci:delete(c, s, o)
122         end
123 end
124
125 local function _wifi_state()
126         if not next(_ubuswificache) then
127                 _ubuswificache = utl.ubus("network.wireless", "status", {}) or {}
128         end
129         return _ubuswificache
130 end
131
132 local function _wifi_state_by_sid(sid)
133         local t1, n1 = _uci:get("wireless", sid)
134         if t1 == "wifi-iface" and n1 ~= nil then
135                 local radioname, radiostate
136                 for radioname, radiostate in pairs(_wifi_state()) do
137                         if type(radiostate) == "table" and
138                            type(radiostate.interfaces) == "table"
139                         then
140                                 local netidx, netstate
141                                 for netidx, netstate in ipairs(radiostate.interfaces) do
142                                         if type(netstate) == "table" and
143                                            type(netstate.section) == "string"
144                                         then
145                                                 local t2, n2 = _uci:get("wireless", netstate.section)
146                                                 if t1 == t2 and n1 == n2 then
147                                                         return radioname, radiostate, netstate
148                                                 end
149                                         end
150                                 end
151                         end
152                 end
153         end
154 end
155
156 local function _wifi_state_by_ifname(ifname)
157         if type(ifname) == "string" then
158                 local radioname, radiostate
159                 for radioname, radiostate in pairs(_wifi_state()) do
160                         if type(radiostate) == "table" and
161                            type(radiostate.interfaces) == "table"
162                         then
163                                 local netidx, netstate
164                                 for netidx, netstate in ipairs(radiostate.interfaces) do
165                                         if type(netstate) == "table" and
166                                            type(netstate.ifname) == "string" and
167                                            netstate.ifname == ifname
168                                         then
169                                                 return radioname, radiostate, netstate
170                                         end
171                                 end
172                         end
173                 end
174         end
175 end
176
177 function _wifi_iface(x)
178         local _, p
179         for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
180                 if x:match(p) then
181                         return true
182                 end
183         end
184         return (nfs.access("/sys/class/net/%s/phy80211" % x) == true)
185 end
186
187 local function _wifi_iwinfo_by_ifname(ifname, force_phy_only)
188         local stat, iwinfo = pcall(require, "iwinfo")
189         local iwtype = stat and type(ifname) == "string" and iwinfo.type(ifname)
190         local is_nonphy_op = {
191                 bitrate     = true,
192                 quality     = true,
193                 quality_max = true,
194                 mode        = true,
195                 ssid        = true,
196                 bssid       = true,
197                 assoclist   = true,
198                 encryption  = true
199         }
200
201         if iwtype then
202                 -- if we got a type but no real netdev, we're referring to a phy
203                 local phy_only = force_phy_only or (ipc.link(ifname).type ~= 1)
204
205                 return setmetatable({}, {
206                         __index = function(t, k)
207                                 if k == "ifname" then
208                                         return ifname
209                                 elseif phy_only and is_nonphy_op[k] then
210                                         return nil
211                                 elseif iwinfo[iwtype][k] then
212                                         return iwinfo[iwtype][k](ifname)
213                                 end
214                         end
215                 })
216         end
217 end
218
219 local function _wifi_sid_by_netid(netid)
220         if type(netid) == "string" then
221                 local radioname, netidx = netid:match("^(%w+)%.network(%d+)$")
222                 if radioname and netidx then
223                         local i, n = 0, nil
224
225                         netidx = tonumber(netidx)
226                         _uci:foreach("wireless", "wifi-iface",
227                                 function(s)
228                                         if s.device == radioname then
229                                                 i = i + 1
230                                                 if i == netidx then
231                                                         n = s[".name"]
232                                                         return false
233                                                 end
234                                         end
235                                 end)
236
237                         return n
238                 end
239         end
240 end
241
242 function _wifi_sid_by_ifname(ifn)
243         local sid = _wifi_sid_by_netid(ifn)
244         if sid then
245                 return sid
246         end
247
248         local _, _, netstate = _wifi_state_by_ifname(ifn)
249         if netstate and type(netstate.section) == "string" then
250                 return netstate.section
251         end
252 end
253
254 local function _wifi_netid_by_sid(sid)
255         local t, n = _uci:get("wireless", sid)
256         if t == "wifi-iface" and n ~= nil then
257                 local radioname = _uci:get("wireless", n, "device")
258                 if type(radioname) == "string" then
259                         local i, netid = 0, nil
260
261                         _uci:foreach("wireless", "wifi-iface",
262                                 function(s)
263                                         if s.device == radioname then
264                                                 i = i + 1
265                                                 if s[".name"] == n then
266                                                         netid = "%s.network%d" %{ radioname, i }
267                                                         return false
268                                                 end
269                                         end
270                                 end)
271
272                         return netid, radioname
273                 end
274         end
275 end
276
277 local function _wifi_netid_by_netname(name)
278         local netid = nil
279
280         _uci:foreach("wireless", "wifi-iface",
281                 function(s)
282                         local net
283                         for net in utl.imatch(s.network) do
284                                 if net == name then
285                                         netid = _wifi_netid_by_sid(s[".name"])
286                                         return false
287                                 end
288                         end
289                 end)
290
291         return netid
292 end
293
294 function _iface_virtual(x)
295         local _, p
296         for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do
297                 if x:match(p) then
298                         return true
299                 end
300         end
301         return false
302 end
303
304 function _iface_ignore(x)
305         local _, p
306         for _, p in ipairs(IFACE_PATTERNS_IGNORE) do
307                 if x:match(p) then
308                         return true
309                 end
310         end
311         return false
312 end
313
314 function init(cursor)
315         _uci = cursor or _uci or uci.cursor()
316
317         _interfaces = { }
318         _bridge     = { }
319         _switch     = { }
320         _tunnel     = { }
321         _swtopo     = { }
322
323         _ubusnetcache  = { }
324         _ubusdevcache  = { }
325         _ubuswificache = { }
326
327         -- read interface information
328         local n, i
329         for n, i in ipairs(nxo.getifaddrs()) do
330                 local name = i.name:match("[^:]+")
331
332                 if _iface_virtual(name) then
333                         _tunnel[name] = true
334                 end
335
336                 if _tunnel[name] or not (_iface_ignore(name) or _iface_virtual(name)) then
337                         _interfaces[name] = _interfaces[name] or {
338                                 idx      = i.ifindex or n,
339                                 name     = name,
340                                 rawname  = i.name,
341                                 flags    = { },
342                                 ipaddrs  = { },
343                                 ip6addrs = { }
344                         }
345
346                         if i.family == "packet" then
347                                 _interfaces[name].flags   = i.flags
348                                 _interfaces[name].stats   = i.data
349                                 _interfaces[name].macaddr = ipc.checkmac(i.addr)
350                         elseif i.family == "inet" then
351                                 _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
352                         elseif i.family == "inet6" then
353                                 _interfaces[name].ip6addrs[#_interfaces[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
354                         end
355                 end
356         end
357
358         -- read bridge informaton
359         local b, l
360         for l in utl.execi("brctl show") do
361                 if not l:match("STP") then
362                         local r = utl.split(l, "%s+", nil, true)
363                         if #r == 4 then
364                                 b = {
365                                         name    = r[1],
366                                         id      = r[2],
367                                         stp     = r[3] == "yes",
368                                         ifnames = { _interfaces[r[4]] }
369                                 }
370                                 if b.ifnames[1] then
371                                         b.ifnames[1].bridge = b
372                                 end
373                                 _bridge[r[1]] = b
374                                 _interfaces[r[1]].bridge = b
375                         elseif b then
376                                 b.ifnames[#b.ifnames+1] = _interfaces[r[2]]
377                                 b.ifnames[#b.ifnames].bridge = b
378                         end
379                 end
380         end
381
382         -- read switch topology
383         local boardinfo = jsc.parse(nfs.readfile("/etc/board.json") or "")
384         if type(boardinfo) == "table" and type(boardinfo.switch) == "table" then
385                 local switch, layout
386                 for switch, layout in pairs(boardinfo.switch) do
387                         if type(layout) == "table" and type(layout.ports) == "table" then
388                                 local _, port
389                                 local ports = { }
390                                 local nports = { }
391                                 local netdevs = { }
392
393                                 for _, port in ipairs(layout.ports) do
394                                         if type(port) == "table" and
395                                            type(port.num) == "number" and
396                                            (type(port.role) == "string" or
397                                             type(port.device) == "string")
398                                         then
399                                                 local spec = {
400                                                         num    = port.num,
401                                                         role   = port.role or "cpu",
402                                                         index  = port.index or port.num
403                                                 }
404
405                                                 if port.device then
406                                                         spec.device = port.device
407                                                         spec.tagged = port.need_tag
408                                                         netdevs[tostring(port.num)] = port.device
409                                                 end
410
411                                                 ports[#ports+1] = spec
412
413                                                 if port.role then
414                                                         nports[port.role] = (nports[port.role] or 0) + 1
415                                                 end
416                                         end
417                                 end
418
419                                 table.sort(ports, function(a, b)
420                                         if a.role ~= b.role then
421                                                 return (a.role < b.role)
422                                         end
423
424                                         return (a.index < b.index)
425                                 end)
426
427                                 local pnum, role
428                                 for _, port in ipairs(ports) do
429                                         if port.role ~= role then
430                                                 role = port.role
431                                                 pnum = 1
432                                         end
433
434                                         if role == "cpu" then
435                                                 port.label = "CPU (%s)" % port.device
436                                         elseif nports[role] > 1 then
437                                                 port.label = "%s %d" %{ role:upper(), pnum }
438                                                 pnum = pnum + 1
439                                         else
440                                                 port.label = role:upper()
441                                         end
442
443                                         port.role = nil
444                                         port.index = nil
445                                 end
446
447                                 _swtopo[switch] = {
448                                         ports = ports,
449                                         netdevs = netdevs
450                                 }
451                         end
452                 end
453         end
454
455         return _M
456 end
457
458 function save(self, ...)
459         _uci:save(...)
460         _uci:load(...)
461 end
462
463 function commit(self, ...)
464         _uci:commit(...)
465         _uci:load(...)
466 end
467
468 function ifnameof(self, x)
469         if utl.instanceof(x, interface) then
470                 return x:name()
471         elseif utl.instanceof(x, protocol) then
472                 return x:ifname()
473         elseif type(x) == "string" then
474                 return x:match("^[^:]+")
475         end
476 end
477
478 function get_protocol(self, protoname, netname)
479         local v = _protocols[protoname]
480         if v then
481                 return v(netname or "__dummy__")
482         end
483 end
484
485 function get_protocols(self)
486         local p = { }
487         local _, v
488         for _, v in ipairs(_protocols) do
489                 p[#p+1] = v("__dummy__")
490         end
491         return p
492 end
493
494 function register_protocol(self, protoname)
495         local proto = utl.class(protocol)
496
497         function proto.__init__(self, name)
498                 self.sid = name
499         end
500
501         function proto.proto(self)
502                 return protoname
503         end
504
505         _protocols[#_protocols+1] = proto
506         _protocols[protoname]     = proto
507
508         return proto
509 end
510
511 function register_pattern_virtual(self, pat)
512         IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
513 end
514
515 function register_error_code(self, code, message)
516         if type(code) == "string" and
517            type(message) == "string" and
518            not IFACE_ERRORS[code]
519         then
520                 IFACE_ERRORS[code] = message
521                 return true
522         end
523
524         return false
525 end
526
527 function has_ipv6(self)
528         return nfs.access("/proc/net/ipv6_route")
529 end
530
531 function add_network(self, n, options)
532         local oldnet = self:get_network(n)
533         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
534                 if _uci:section("network", "interface", n, options) then
535                         return network(n)
536                 end
537         elseif oldnet and oldnet:is_empty() then
538                 if options then
539                         local k, v
540                         for k, v in pairs(options) do
541                                 oldnet:set(k, v)
542                         end
543                 end
544                 return oldnet
545         end
546 end
547
548 function get_network(self, n)
549         if n and _uci:get("network", n) == "interface" then
550                 return network(n)
551         elseif n then
552                 local stat = utl.ubus("network.interface", "status", { interface = n })
553                 if type(stat) == "table" and
554                    type(stat.proto) == "string"
555                 then
556                         return network(n, stat.proto)
557                 end
558         end
559 end
560
561 function get_networks(self)
562         local nets = { }
563         local nls = { }
564
565         _uci:foreach("network", "interface",
566                 function(s)
567                         nls[s['.name']] = network(s['.name'])
568                 end)
569
570         local dump = utl.ubus("network.interface", "dump", { })
571         if type(dump) == "table" and
572            type(dump.interface) == "table"
573         then
574                 local _, net
575                 for _, net in ipairs(dump.interface) do
576                         if type(net) == "table" and
577                            type(net.proto) == "string" and
578                            type(net.interface) == "string"
579                         then
580                                 if not nls[net.interface] then
581                                         nls[net.interface] = network(net.interface, net.proto)
582                                 end
583                         end
584                 end
585         end
586
587         local n
588         for n in utl.kspairs(nls) do
589                 nets[#nets+1] = nls[n]
590         end
591
592         return nets
593 end
594
595 function del_network(self, n)
596         local r = _uci:delete("network", n)
597         if r then
598                 _uci:delete_all("luci", "ifstate",
599                         function(s) return (s.interface == n) end)
600
601                 _uci:delete_all("network", "alias",
602                         function(s) return (s.interface == n) end)
603
604                 _uci:delete_all("network", "route",
605                         function(s) return (s.interface == n) end)
606
607                 _uci:delete_all("network", "route6",
608                         function(s) return (s.interface == n) end)
609
610                 _uci:foreach("wireless", "wifi-iface",
611                         function(s)
612                                 local net
613                                 local rest = { }
614                                 for net in utl.imatch(s.network) do
615                                         if net ~= n then
616                                                 rest[#rest+1] = net
617                                         end
618                                 end
619                                 if #rest > 0 then
620                                         _uci:set("wireless", s['.name'], "network",
621                                                       table.concat(rest, " "))
622                                 else
623                                         _uci:delete("wireless", s['.name'], "network")
624                                 end
625                         end)
626
627                 local ok, fw = pcall(require, "luci.model.firewall")
628                 if ok then
629                         fw.init()
630                         fw:del_network(n)
631                 end
632         end
633         return r
634 end
635
636 function rename_network(self, old, new)
637         local r
638         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
639                 r = _uci:section("network", "interface", new, _uci:get_all("network", old))
640
641                 if r then
642                         _uci:foreach("network", "alias",
643                                 function(s)
644                                         if s.interface == old then
645                                                 _uci:set("network", s['.name'], "interface", new)
646                                         end
647                                 end)
648
649                         _uci:foreach("network", "route",
650                                 function(s)
651                                         if s.interface == old then
652                                                 _uci:set("network", s['.name'], "interface", new)
653                                         end
654                                 end)
655
656                         _uci:foreach("network", "route6",
657                                 function(s)
658                                         if s.interface == old then
659                                                 _uci:set("network", s['.name'], "interface", new)
660                                         end
661                                 end)
662
663                         _uci:foreach("wireless", "wifi-iface",
664                                 function(s)
665                                         local net
666                                         local list = { }
667                                         for net in utl.imatch(s.network) do
668                                                 if net == old then
669                                                         list[#list+1] = new
670                                                 else
671                                                         list[#list+1] = net
672                                                 end
673                                         end
674                                         if #list > 0 then
675                                                 _uci:set("wireless", s['.name'], "network",
676                                                               table.concat(list, " "))
677                                         end
678                                 end)
679
680                         _uci:delete("network", old)
681                 end
682         end
683         return r or false
684 end
685
686 function get_interface(self, i)
687         if _interfaces[i] or _wifi_iface(i) then
688                 return interface(i)
689         else
690                 local netid = _wifi_netid_by_sid(i)
691                 return netid and interface(netid)
692         end
693 end
694
695 function get_interfaces(self)
696         local iface
697         local ifaces = { }
698         local nfs = { }
699
700         -- find normal interfaces
701         _uci:foreach("network", "interface",
702                 function(s)
703                         for iface in utl.imatch(s.ifname) do
704                                 if not _iface_ignore(iface) and not _iface_virtual(iface) and not _wifi_iface(iface) then
705                                         nfs[iface] = interface(iface)
706                                 end
707                         end
708                 end)
709
710         for iface in utl.kspairs(_interfaces) do
711                 if not (nfs[iface] or _iface_ignore(iface) or _iface_virtual(iface) or _wifi_iface(iface)) then
712                         nfs[iface] = interface(iface)
713                 end
714         end
715
716         -- find vlan interfaces
717         _uci:foreach("network", "switch_vlan",
718                 function(s)
719                         if type(s.ports) ~= "string" or
720                            type(s.device) ~= "string" or
721                            type(_swtopo[s.device]) ~= "table"
722                         then
723                                 return
724                         end
725
726                         local pnum, ptag
727                         for pnum, ptag in s.ports:gmatch("(%d+)([tu]?)") do
728                                 local netdev = _swtopo[s.device].netdevs[pnum]
729                                 if netdev then
730                                         if not nfs[netdev] then
731                                                 nfs[netdev] = interface(netdev)
732                                         end
733                                         _switch[netdev] = true
734
735                                         if ptag == "t" then
736                                                 local vid = tonumber(s.vid or s.vlan)
737                                                 if vid ~= nil and vid >= 0 and vid <= 4095 then
738                                                         local iface = "%s.%d" %{ netdev, vid }
739                                                         if not nfs[iface] then
740                                                                 nfs[iface] = interface(iface)
741                                                         end
742                                                         _switch[iface] = true
743                                                 end
744                                         end
745                                 end
746                         end
747                 end)
748
749         for iface in utl.kspairs(nfs) do
750                 ifaces[#ifaces+1] = nfs[iface]
751         end
752
753         -- find wifi interfaces
754         local num = { }
755         local wfs = { }
756         _uci:foreach("wireless", "wifi-iface",
757                 function(s)
758                         if s.device then
759                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
760                                 local i = "%s.network%d" %{ s.device, num[s.device] }
761                                 wfs[i] = interface(i)
762                         end
763                 end)
764
765         for iface in utl.kspairs(wfs) do
766                 ifaces[#ifaces+1] = wfs[iface]
767         end
768
769         return ifaces
770 end
771
772 function ignore_interface(self, x)
773         return _iface_ignore(x)
774 end
775
776 function get_wifidev(self, dev)
777         if _uci:get("wireless", dev) == "wifi-device" then
778                 return wifidev(dev)
779         end
780 end
781
782 function get_wifidevs(self)
783         local devs = { }
784         local wfd  = { }
785
786         _uci:foreach("wireless", "wifi-device",
787                 function(s) wfd[#wfd+1] = s['.name'] end)
788
789         local dev
790         for _, dev in utl.vspairs(wfd) do
791                 devs[#devs+1] = wifidev(dev)
792         end
793
794         return devs
795 end
796
797 function get_wifinet(self, net)
798         local wnet = _wifi_sid_by_ifname(net)
799         if wnet then
800                 return wifinet(wnet)
801         end
802 end
803
804 function add_wifinet(self, net, options)
805         if type(options) == "table" and options.device and
806                 _uci:get("wireless", options.device) == "wifi-device"
807         then
808                 local wnet = _uci:section("wireless", "wifi-iface", nil, options)
809                 return wifinet(wnet)
810         end
811 end
812
813 function del_wifinet(self, net)
814         local wnet = _wifi_sid_by_ifname(net)
815         if wnet then
816                 _uci:delete("wireless", wnet)
817                 return true
818         end
819         return false
820 end
821
822 function get_status_by_route(self, addr, mask)
823         local route_statuses = { }
824         local _, object
825         for _, object in ipairs(utl.ubus()) do
826                 local net = object:match("^network%.interface%.(.+)")
827                 if net then
828                         local s = utl.ubus(object, "status", {})
829                         if s and s.route then
830                                 local rt
831                                 for _, rt in ipairs(s.route) do
832                                         if not rt.table and rt.target == addr and rt.mask == mask then
833                                                 route_statuses[net] = s
834                                         end
835                                 end
836                         end
837                 end
838         end
839
840         return route_statuses
841 end
842
843 function get_status_by_address(self, addr)
844         local _, object
845         for _, object in ipairs(utl.ubus()) do
846                 local net = object:match("^network%.interface%.(.+)")
847                 if net then
848                         local s = utl.ubus(object, "status", {})
849                         if s and s['ipv4-address'] then
850                                 local a
851                                 for _, a in ipairs(s['ipv4-address']) do
852                                         if a.address == addr then
853                                                 return net, s
854                                         end
855                                 end
856                         end
857                         if s and s['ipv6-address'] then
858                                 local a
859                                 for _, a in ipairs(s['ipv6-address']) do
860                                         if a.address == addr then
861                                                 return net, s
862                                         end
863                                 end
864                         end
865                         if s and s['ipv6-prefix-assignment'] then
866                                 local a
867                                 for _, a in ipairs(s['ipv6-prefix-assignment']) do
868                                         if a and a['local-address'] and a['local-address'].address == addr then
869                                                 return net, s
870                                         end
871                                 end
872                         end
873                 end
874         end
875 end
876
877 function get_wan_networks(self)
878         local k, v
879         local wan_nets = { }
880         local route_statuses = self:get_status_by_route("0.0.0.0", 0)
881
882         for k, v in pairs(route_statuses) do
883                 wan_nets[#wan_nets+1] = network(k, v.proto)
884         end
885
886         return wan_nets
887 end
888
889 function get_wan6_networks(self)
890         local k, v
891         local wan6_nets = { }
892         local route_statuses = self:get_status_by_route("::", 0)
893
894         for k, v in pairs(route_statuses) do
895                 wan6_nets[#wan6_nets+1] = network(k, v.proto)
896         end
897
898         return wan6_nets
899 end
900
901 function get_switch_topologies(self)
902         return _swtopo
903 end
904
905
906 function network(name, proto)
907         if name then
908                 local p = proto or _uci:get("network", name, "proto")
909                 local c = p and _protocols[p] or protocol
910                 return c(name)
911         end
912 end
913
914 function protocol.__init__(self, name)
915         self.sid = name
916 end
917
918 function protocol._get(self, opt)
919         local v = _uci:get("network", self.sid, opt)
920         if type(v) == "table" then
921                 return table.concat(v, " ")
922         end
923         return v or ""
924 end
925
926 function protocol._ubus(self, field)
927         if not _ubusnetcache[self.sid] then
928                 _ubusnetcache[self.sid] = utl.ubus("network.interface.%s" % self.sid,
929                                                    "status", { })
930         end
931         if _ubusnetcache[self.sid] and field then
932                 return _ubusnetcache[self.sid][field]
933         end
934         return _ubusnetcache[self.sid]
935 end
936
937 function protocol.get(self, opt)
938         return _get("network", self.sid, opt)
939 end
940
941 function protocol.set(self, opt, val)
942         return _set("network", self.sid, opt, val)
943 end
944
945 function protocol.ifname(self)
946         local ifname
947         if self:is_floating() then
948                 ifname = self:_ubus("l3_device")
949         else
950                 ifname = self:_ubus("device")
951         end
952         if not ifname then
953                 ifname = _wifi_netid_by_netname(self.sid)
954         end
955         return ifname
956 end
957
958 function protocol.proto(self)
959         return "none"
960 end
961
962 function protocol.get_i18n(self)
963         local p = self:proto()
964         if p == "none" then
965                 return lng.translate("Unmanaged")
966         elseif p == "static" then
967                 return lng.translate("Static address")
968         elseif p == "dhcp" then
969                 return lng.translate("DHCP client")
970         else
971                 return lng.translate("Unknown")
972         end
973 end
974
975 function protocol.type(self)
976         return self:_get("type")
977 end
978
979 function protocol.name(self)
980         return self.sid
981 end
982
983 function protocol.uptime(self)
984         return self:_ubus("uptime") or 0
985 end
986
987 function protocol.expires(self)
988         local u = self:_ubus("uptime")
989         local d = self:_ubus("data")
990
991         if type(u) == "number" and type(d) == "table" and
992            type(d.leasetime) == "number"
993         then
994                 local r = (d.leasetime - (u % d.leasetime))
995                 return r > 0 and r or 0
996         end
997
998         return -1
999 end
1000
1001 function protocol.metric(self)
1002         return self:_ubus("metric") or 0
1003 end
1004
1005 function protocol.zonename(self)
1006         local d = self:_ubus("data")
1007
1008         if type(d) == "table" and type(d.zone) == "string" then
1009                 return d.zone
1010         end
1011
1012         return nil
1013 end
1014
1015 function protocol.ipaddr(self)
1016         local addrs = self:_ubus("ipv4-address")
1017         return addrs and #addrs > 0 and addrs[1].address
1018 end
1019
1020 function protocol.ipaddrs(self)
1021         local addrs = self:_ubus("ipv4-address")
1022         local rv = { }
1023
1024         if type(addrs) == "table" then
1025                 local n, addr
1026                 for n, addr in ipairs(addrs) do
1027                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
1028                 end
1029         end
1030
1031         return rv
1032 end
1033
1034 function protocol.netmask(self)
1035         local addrs = self:_ubus("ipv4-address")
1036         return addrs and #addrs > 0 and
1037                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
1038 end
1039
1040 function protocol.gwaddr(self)
1041         local _, route
1042         for _, route in ipairs(self:_ubus("route") or { }) do
1043                 if route.target == "0.0.0.0" and route.mask == 0 then
1044                         return route.nexthop
1045                 end
1046         end
1047 end
1048
1049 function protocol.dnsaddrs(self)
1050         local dns = { }
1051         local _, addr
1052         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
1053                 if not addr:match(":") then
1054                         dns[#dns+1] = addr
1055                 end
1056         end
1057         return dns
1058 end
1059
1060 function protocol.ip6addr(self)
1061         local addrs = self:_ubus("ipv6-address")
1062         if addrs and #addrs > 0 then
1063                 return "%s/%d" %{ addrs[1].address, addrs[1].mask }
1064         else
1065                 addrs = self:_ubus("ipv6-prefix-assignment")
1066                 if addrs and #addrs > 0 then
1067                         return "%s/%d" %{ addrs[1].address, addrs[1].mask }
1068                 end
1069         end
1070 end
1071
1072 function protocol.ip6addrs(self)
1073         local addrs = self:_ubus("ipv6-address")
1074         local rv = { }
1075         local n, addr
1076
1077         if type(addrs) == "table" then
1078                 for n, addr in ipairs(addrs) do
1079                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
1080                 end
1081         end
1082
1083         addrs = self:_ubus("ipv6-prefix-assignment")
1084
1085         if type(addrs) == "table" then
1086                 for n, addr in ipairs(addrs) do
1087                         if type(addr["local-address"]) == "table" and
1088                            type(addr["local-address"].mask) == "number" and
1089                            type(addr["local-address"].address) == "string"
1090                         then
1091                                 rv[#rv+1] = "%s/%d" %{
1092                                         addr["local-address"].address,
1093                                         addr["local-address"].mask
1094                                 }
1095                         end
1096                 end
1097         end
1098
1099         return rv
1100 end
1101
1102 function protocol.gw6addr(self)
1103         local _, route
1104         for _, route in ipairs(self:_ubus("route") or { }) do
1105                 if route.target == "::" and route.mask == 0 then
1106                         return ipc.IPv6(route.nexthop):string()
1107                 end
1108         end
1109 end
1110
1111 function protocol.dns6addrs(self)
1112         local dns = { }
1113         local _, addr
1114         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
1115                 if addr:match(":") then
1116                         dns[#dns+1] = addr
1117                 end
1118         end
1119         return dns
1120 end
1121
1122 function protocol.ip6prefix(self)
1123         local prefix = self:_ubus("ipv6-prefix")
1124         if prefix and #prefix > 0 then
1125                 return "%s/%d" %{ prefix[1].address, prefix[1].mask }
1126         end
1127 end
1128
1129 function protocol.errors(self)
1130         local _, err, rv
1131         local errors = self:_ubus("errors")
1132         if type(errors) == "table" then
1133                 for _, err in ipairs(errors) do
1134                         if type(err) == "table" and
1135                            type(err.code) == "string"
1136                         then
1137                                 rv = rv or { }
1138                                 rv[#rv+1] = IFACE_ERRORS[err.code] or lng.translatef("Unknown error (%s)", err.code)
1139                         end
1140                 end
1141         end
1142         return rv
1143 end
1144
1145 function protocol.is_bridge(self)
1146         return (not self:is_virtual() and self:type() == "bridge")
1147 end
1148
1149 function protocol.opkg_package(self)
1150         return nil
1151 end
1152
1153 function protocol.is_installed(self)
1154         return true
1155 end
1156
1157 function protocol.is_virtual(self)
1158         return false
1159 end
1160
1161 function protocol.is_floating(self)
1162         return false
1163 end
1164
1165 function protocol.is_dynamic(self)
1166         return (self:_ubus("dynamic") == true)
1167 end
1168
1169 function protocol.is_auto(self)
1170         return (self:_get("auto") ~= "0")
1171 end
1172
1173 function protocol.is_alias(self)
1174         local ifn, parent = nil, nil
1175
1176         for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1177                 if #ifn > 1 and ifn:byte(1) == 64 then
1178                         parent = ifn:sub(2)
1179                 elseif parent ~= nil then
1180                         parent = nil
1181                 end
1182         end
1183
1184         return parent
1185 end
1186
1187 function protocol.is_empty(self)
1188         if self:is_floating() then
1189                 return false
1190         else
1191                 local empty = true
1192
1193                 if (self:_get("ifname") or ""):match("%S+") then
1194                         empty = false
1195                 end
1196
1197                 if empty and _wifi_netid_by_netname(self.sid) then
1198                         empty = false
1199                 end
1200
1201                 return empty
1202         end
1203 end
1204
1205 function protocol.is_up(self)
1206         return (self:_ubus("up") == true)
1207 end
1208
1209 function protocol.add_interface(self, ifname)
1210         ifname = _M:ifnameof(ifname)
1211         if ifname and not self:is_floating() then
1212                 -- if its a wifi interface, change its network option
1213                 local wif = _wifi_sid_by_ifname(ifname)
1214                 if wif then
1215                         _append("wireless", wif, "network", self.sid)
1216
1217                 -- add iface to our iface list
1218                 else
1219                         _append("network", self.sid, "ifname", ifname)
1220                 end
1221         end
1222 end
1223
1224 function protocol.del_interface(self, ifname)
1225         ifname = _M:ifnameof(ifname)
1226         if ifname and not self:is_floating() then
1227                 -- if its a wireless interface, clear its network option
1228                 local wif = _wifi_sid_by_ifname(ifname)
1229                 if wif then _filter("wireless", wif, "network", self.sid) end
1230
1231                 -- remove the interface
1232                 _filter("network", self.sid, "ifname", ifname)
1233         end
1234 end
1235
1236 function protocol.get_interface(self)
1237         if self:is_virtual() then
1238                 _tunnel[self:proto() .. "-" .. self.sid] = true
1239                 return interface(self:proto() .. "-" .. self.sid, self)
1240         elseif self:is_bridge() then
1241                 _bridge["br-" .. self.sid] = true
1242                 return interface("br-" .. self.sid, self)
1243         else
1244                 local ifn = self:_ubus("l3_device") or self:_ubus("device")
1245                 if ifn then
1246                         return interface(ifn, self)
1247                 end
1248
1249                 for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1250                         ifn = ifn:match("^[^:/]+")
1251                         return ifn and interface(ifn, self)
1252                 end
1253
1254                 ifn = _wifi_netid_by_netname(self.sid)
1255                 return ifn and interface(ifn, self)
1256         end
1257 end
1258
1259 function protocol.get_interfaces(self)
1260         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
1261                 local ifaces = { }
1262
1263                 local ifn
1264                 local nfs = { }
1265                 for ifn in utl.imatch(self:get("ifname")) do
1266                         ifn = ifn:match("^[^:/]+")
1267                         nfs[ifn] = interface(ifn, self)
1268                 end
1269
1270                 for ifn in utl.kspairs(nfs) do
1271                         ifaces[#ifaces+1] = nfs[ifn]
1272                 end
1273
1274                 local wfs = { }
1275                 _uci:foreach("wireless", "wifi-iface",
1276                         function(s)
1277                                 if s.device then
1278                                         local net
1279                                         for net in utl.imatch(s.network) do
1280                                                 if net == self.sid then
1281                                                         ifn = _wifi_netid_by_sid(s[".name"])
1282                                                         if ifn then
1283                                                                 wfs[ifn] = interface(ifn, self)
1284                                                         end
1285                                                 end
1286                                         end
1287                                 end
1288                         end)
1289
1290                 for ifn in utl.kspairs(wfs) do
1291                         ifaces[#ifaces+1] = wfs[ifn]
1292                 end
1293
1294                 return ifaces
1295         end
1296 end
1297
1298 function protocol.contains_interface(self, ifname)
1299         ifname = _M:ifnameof(ifname)
1300         if not ifname then
1301                 return false
1302         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
1303                 return true
1304         elseif self:is_bridge() and "br-" .. self.sid == ifname then
1305                 return true
1306         else
1307                 local ifn
1308                 for ifn in utl.imatch(self:get("ifname")) do
1309                         ifn = ifn:match("[^:]+")
1310                         if ifn == ifname then
1311                                 return true
1312                         end
1313                 end
1314
1315                 local wif = _wifi_sid_by_ifname(ifname)
1316                 if wif then
1317                         local n
1318                         for n in utl.imatch(_uci:get("wireless", wif, "network")) do
1319                                 if n == self.sid then
1320                                         return true
1321                                 end
1322                         end
1323                 end
1324         end
1325
1326         return false
1327 end
1328
1329 function protocol.adminlink(self)
1330         local stat, dsp = pcall(require, "luci.dispatcher")
1331         return stat and dsp.build_url("admin", "network", "network", self.sid)
1332 end
1333
1334
1335 interface = utl.class()
1336
1337 function interface.__init__(self, ifname, network)
1338         local wif = _wifi_sid_by_ifname(ifname)
1339         if wif then
1340                 self.wif    = wifinet(wif)
1341                 self.ifname = self.wif:ifname()
1342         end
1343
1344         self.ifname  = self.ifname or ifname
1345         self.dev     = _interfaces[self.ifname]
1346         self.network = network
1347 end
1348
1349 function interface._ubus(self, field)
1350         if not _ubusdevcache[self.ifname] then
1351                 _ubusdevcache[self.ifname] = utl.ubus("network.device", "status",
1352                                                       { name = self.ifname })
1353         end
1354         if _ubusdevcache[self.ifname] and field then
1355                 return _ubusdevcache[self.ifname][field]
1356         end
1357         return _ubusdevcache[self.ifname]
1358 end
1359
1360 function interface.name(self)
1361         return self.wif and self.wif:ifname() or self.ifname
1362 end
1363
1364 function interface.mac(self)
1365         return ipc.checkmac(self:_ubus("macaddr"))
1366 end
1367
1368 function interface.ipaddrs(self)
1369         return self.dev and self.dev.ipaddrs or { }
1370 end
1371
1372 function interface.ip6addrs(self)
1373         return self.dev and self.dev.ip6addrs or { }
1374 end
1375
1376 function interface.type(self)
1377         if self.ifname and self.ifname:byte(1) == 64 then
1378                 return "alias"
1379         elseif self.wif or _wifi_iface(self.ifname) then
1380                 return "wifi"
1381         elseif _bridge[self.ifname] then
1382                 return "bridge"
1383         elseif _tunnel[self.ifname] then
1384                 return "tunnel"
1385         elseif self.ifname:match("%.") then
1386                 return "vlan"
1387         elseif _switch[self.ifname] then
1388                 return "switch"
1389         else
1390                 return "ethernet"
1391         end
1392 end
1393
1394 function interface.shortname(self)
1395         if self.wif then
1396                 return self.wif:shortname()
1397         else
1398                 return self.ifname
1399         end
1400 end
1401
1402 function interface.get_i18n(self)
1403         if self.wif then
1404                 return "%s: %s %q" %{
1405                         lng.translate("Wireless Network"),
1406                         self.wif:active_mode(),
1407                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id() or "?"
1408                 }
1409         else
1410                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1411         end
1412 end
1413
1414 function interface.get_type_i18n(self)
1415         local x = self:type()
1416         if x == "alias" then
1417                 return lng.translate("Alias Interface")
1418         elseif x == "wifi" then
1419                 return lng.translate("Wireless Adapter")
1420         elseif x == "bridge" then
1421                 return lng.translate("Bridge")
1422         elseif x == "switch" then
1423                 return lng.translate("Ethernet Switch")
1424         elseif x == "vlan" then
1425                 if _switch[self.ifname] then
1426                         return lng.translate("Switch VLAN")
1427                 else
1428                         return lng.translate("Software VLAN")
1429                 end
1430         elseif x == "tunnel" then
1431                 return lng.translate("Tunnel Interface")
1432         else
1433                 return lng.translate("Ethernet Adapter")
1434         end
1435 end
1436
1437 function interface.adminlink(self)
1438         if self.wif then
1439                 return self.wif:adminlink()
1440         end
1441 end
1442
1443 function interface.ports(self)
1444         local members = self:_ubus("bridge-members")
1445         if members then
1446                 local _, iface
1447                 local ifaces = { }
1448                 for _, iface in ipairs(members) do
1449                         ifaces[#ifaces+1] = interface(iface)
1450                 end
1451                 return ifaces
1452         end
1453 end
1454
1455 function interface.bridge_id(self)
1456         if self.dev and self.dev.bridge then
1457                 return self.dev.bridge.id
1458         else
1459                 return nil
1460         end
1461 end
1462
1463 function interface.bridge_stp(self)
1464         if self.dev and self.dev.bridge then
1465                 return self.dev.bridge.stp
1466         else
1467                 return false
1468         end
1469 end
1470
1471 function interface.is_up(self)
1472         local up = self:_ubus("up")
1473         if up == nil then
1474                 up = (self:type() == "alias")
1475         end
1476         return up or false
1477 end
1478
1479 function interface.is_bridge(self)
1480         return (self:type() == "bridge")
1481 end
1482
1483 function interface.is_bridgeport(self)
1484         return self.dev and self.dev.bridge and
1485                (self.dev.bridge.name ~= self:name()) and true or false
1486 end
1487
1488 function interface.tx_bytes(self)
1489         local stat = self:_ubus("statistics")
1490         return stat and stat.tx_bytes or 0
1491 end
1492
1493 function interface.rx_bytes(self)
1494         local stat = self:_ubus("statistics")
1495         return stat and stat.rx_bytes or 0
1496 end
1497
1498 function interface.tx_packets(self)
1499         local stat = self:_ubus("statistics")
1500         return stat and stat.tx_packets or 0
1501 end
1502
1503 function interface.rx_packets(self)
1504         local stat = self:_ubus("statistics")
1505         return stat and stat.rx_packets or 0
1506 end
1507
1508 function interface.get_network(self)
1509         return self:get_networks()[1]
1510 end
1511
1512 function interface.get_networks(self)
1513         if not self.networks then
1514                 local nets = { }
1515                 local _, net
1516                 for _, net in ipairs(_M:get_networks()) do
1517                         if net:contains_interface(self.ifname) or
1518                            net:ifname() == self.ifname
1519                         then
1520                                 nets[#nets+1] = net
1521                         end
1522                 end
1523                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1524                 self.networks = nets
1525                 return nets
1526         else
1527                 return self.networks
1528         end
1529 end
1530
1531 function interface.get_wifinet(self)
1532         return self.wif
1533 end
1534
1535
1536 wifidev = utl.class()
1537
1538 function wifidev.__init__(self, name)
1539         local t, n = _uci:get("wireless", name)
1540         if t == "wifi-device" and n ~= nil then
1541                 self.sid    = n
1542                 self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true)
1543         end
1544         self.sid    = self.sid    or name
1545         self.iwinfo = self.iwinfo or { ifname = self.sid }
1546 end
1547
1548 function wifidev.get(self, opt)
1549         return _get("wireless", self.sid, opt)
1550 end
1551
1552 function wifidev.set(self, opt, val)
1553         return _set("wireless", self.sid, opt, val)
1554 end
1555
1556 function wifidev.name(self)
1557         return self.sid
1558 end
1559
1560 function wifidev.hwmodes(self)
1561         local l = self.iwinfo.hwmodelist
1562         if l and next(l) then
1563                 return l
1564         else
1565                 return { b = true, g = true }
1566         end
1567 end
1568
1569 function wifidev.get_i18n(self)
1570         local t = self.iwinfo.hardware_name or "Generic"
1571         if self.iwinfo.type == "wl" then
1572                 t = "Broadcom"
1573         end
1574
1575         local m = ""
1576         local l = self:hwmodes()
1577         if l.a then m = m .. "a" end
1578         if l.b then m = m .. "b" end
1579         if l.g then m = m .. "g" end
1580         if l.n then m = m .. "n" end
1581         if l.ac then m = "ac" end
1582
1583         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1584 end
1585
1586 function wifidev.is_up(self)
1587         if _ubuswificache[self.sid] then
1588                 return (_ubuswificache[self.sid].up == true)
1589         end
1590
1591         return false
1592 end
1593
1594 function wifidev.get_wifinet(self, net)
1595         if _uci:get("wireless", net) == "wifi-iface" then
1596                 return wifinet(net)
1597         else
1598                 local wnet = _wifi_sid_by_ifname(net)
1599                 if wnet then
1600                         return wifinet(wnet)
1601                 end
1602         end
1603 end
1604
1605 function wifidev.get_wifinets(self)
1606         local nets = { }
1607
1608         _uci:foreach("wireless", "wifi-iface",
1609                 function(s)
1610                         if s.device == self.sid then
1611                                 nets[#nets+1] = wifinet(s['.name'])
1612                         end
1613                 end)
1614
1615         return nets
1616 end
1617
1618 function wifidev.add_wifinet(self, options)
1619         options = options or { }
1620         options.device = self.sid
1621
1622         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1623         if wnet then
1624                 return wifinet(wnet, options)
1625         end
1626 end
1627
1628 function wifidev.del_wifinet(self, net)
1629         if utl.instanceof(net, wifinet) then
1630                 net = net.sid
1631         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1632                 net = _wifi_sid_by_ifname(net)
1633         end
1634
1635         if net and _uci:get("wireless", net, "device") == self.sid then
1636                 _uci:delete("wireless", net)
1637                 return true
1638         end
1639
1640         return false
1641 end
1642
1643
1644 wifinet = utl.class()
1645
1646 function wifinet.__init__(self, name, data)
1647         local sid, netid, radioname, radiostate, netstate
1648
1649         -- lookup state by radio#.network# notation
1650         sid = _wifi_sid_by_netid(name)
1651         if sid then
1652                 netid = name
1653                 radioname, radiostate, netstate = _wifi_state_by_sid(sid)
1654         else
1655                 -- lookup state by ifname (e.g. wlan0)
1656                 radioname, radiostate, netstate = _wifi_state_by_ifname(name)
1657                 if radioname and radiostate and netstate then
1658                         sid = netstate.section
1659                         netid = _wifi_netid_by_sid(sid)
1660                 else
1661                         -- lookup state by uci section id (e.g. cfg053579)
1662                         radioname, radiostate, netstate = _wifi_state_by_sid(name)
1663                         if radioname and radiostate and netstate then
1664                                 sid = name
1665                                 netid = _wifi_netid_by_sid(sid)
1666                         else
1667                                 -- no state available, try to resolve from uci
1668                                 netid, radioname = _wifi_netid_by_sid(name)
1669                                 if netid and radioname then
1670                                         sid = name
1671                                 end
1672                         end
1673                 end
1674         end
1675
1676         local iwinfo =
1677                 (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or
1678                 (radioname and _wifi_iwinfo_by_ifname(radioname)) or
1679                 { ifname = (netid or sid or name) }
1680
1681         self.sid       = sid or name
1682         self.wdev      = iwinfo.ifname
1683         self.iwinfo    = iwinfo
1684         self.netid     = netid
1685         self._ubusdata = {
1686                 radio = radioname,
1687                 dev   = radiostate,
1688                 net   = netstate
1689         }
1690 end
1691
1692 function wifinet.ubus(self, ...)
1693         local n, v = self._ubusdata
1694         for n = 1, select('#', ...) do
1695                 if type(v) == "table" then
1696                         v = v[select(n, ...)]
1697                 else
1698                         return nil
1699                 end
1700         end
1701         return v
1702 end
1703
1704 function wifinet.get(self, opt)
1705         return _get("wireless", self.sid, opt)
1706 end
1707
1708 function wifinet.set(self, opt, val)
1709         return _set("wireless", self.sid, opt, val)
1710 end
1711
1712 function wifinet.mode(self)
1713         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1714 end
1715
1716 function wifinet.ssid(self)
1717         return self:ubus("net", "config", "ssid") or self:get("ssid")
1718 end
1719
1720 function wifinet.bssid(self)
1721         return self:ubus("net", "config", "bssid") or self:get("bssid")
1722 end
1723
1724 function wifinet.network(self)
1725         local net, networks = nil, { }
1726         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1727                 networks[#networks+1] = net
1728         end
1729         return networks
1730 end
1731
1732 function wifinet.id(self)
1733         return self.netid
1734 end
1735
1736 function wifinet.name(self)
1737         return self.sid
1738 end
1739
1740 function wifinet.ifname(self)
1741         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1742         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1743                 ifname = self.netid
1744         end
1745         return ifname
1746 end
1747
1748 function wifinet.get_device(self)
1749         local dev = self:ubus("radio") or self:get("device")
1750         return dev and wifidev(dev) or nil
1751 end
1752
1753 function wifinet.is_up(self)
1754         local ifc = self:get_interface()
1755         return (ifc and ifc:is_up() or false)
1756 end
1757
1758 function wifinet.active_mode(self)
1759         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1760
1761         if     m == "ap"      then m = "Master"
1762         elseif m == "sta"     then m = "Client"
1763         elseif m == "adhoc"   then m = "Ad-Hoc"
1764         elseif m == "mesh"    then m = "Mesh"
1765         elseif m == "monitor" then m = "Monitor"
1766         end
1767
1768         return m
1769 end
1770
1771 function wifinet.active_mode_i18n(self)
1772         return lng.translate(self:active_mode())
1773 end
1774
1775 function wifinet.active_ssid(self)
1776         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1777 end
1778
1779 function wifinet.active_bssid(self)
1780         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1781 end
1782
1783 function wifinet.active_encryption(self)
1784         local enc = self.iwinfo and self.iwinfo.encryption
1785         return enc and enc.description or "-"
1786 end
1787
1788 function wifinet.assoclist(self)
1789         return self.iwinfo.assoclist or { }
1790 end
1791
1792 function wifinet.frequency(self)
1793         local freq = self.iwinfo.frequency
1794         if freq and freq > 0 then
1795                 return "%.03f" % (freq / 1000)
1796         end
1797 end
1798
1799 function wifinet.bitrate(self)
1800         local rate = self.iwinfo.bitrate
1801         if rate and rate > 0 then
1802                 return (rate / 1000)
1803         end
1804 end
1805
1806 function wifinet.channel(self)
1807         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1808                 tonumber(self:get("channel"))
1809 end
1810
1811 function wifinet.signal(self)
1812         return self.iwinfo.signal or 0
1813 end
1814
1815 function wifinet.noise(self)
1816         return self.iwinfo.noise or 0
1817 end
1818
1819 function wifinet.country(self)
1820         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1821 end
1822
1823 function wifinet.txpower(self)
1824         local pwr = (self.iwinfo.txpower or 0)
1825         return pwr + self:txpower_offset()
1826 end
1827
1828 function wifinet.txpower_offset(self)
1829         return self.iwinfo.txpower_offset or 0
1830 end
1831
1832 function wifinet.signal_level(self, s, n)
1833         if self:active_bssid() ~= "00:00:00:00:00:00" then
1834                 local signal = s or self:signal()
1835                 local noise  = n or self:noise()
1836
1837                 if signal < 0 and noise < 0 then
1838                         local snr = -1 * (noise - signal)
1839                         return math.floor(snr / 5)
1840                 else
1841                         return 0
1842                 end
1843         else
1844                 return -1
1845         end
1846 end
1847
1848 function wifinet.signal_percent(self)
1849         local qc = self.iwinfo.quality or 0
1850         local qm = self.iwinfo.quality_max or 0
1851
1852         if qc > 0 and qm > 0 then
1853                 return math.floor((100 / qm) * qc)
1854         else
1855                 return 0
1856         end
1857 end
1858
1859 function wifinet.shortname(self)
1860         return "%s %q" %{
1861                 lng.translate(self:active_mode()),
1862                 self:active_ssid() or self:active_bssid() or self:id()
1863         }
1864 end
1865
1866 function wifinet.get_i18n(self)
1867         return "%s: %s %q (%s)" %{
1868                 lng.translate("Wireless Network"),
1869                 lng.translate(self:active_mode()),
1870                 self:active_ssid() or self:active_bssid() or self:id(),
1871                 self:ifname()
1872         }
1873 end
1874
1875 function wifinet.adminlink(self)
1876         local stat, dsp = pcall(require, "luci.dispatcher")
1877         return dsp and dsp.build_url("admin", "network", "wireless", self.netid)
1878 end
1879
1880 function wifinet.get_network(self)
1881         return self:get_networks()[1]
1882 end
1883
1884 function wifinet.get_networks(self)
1885         local nets = { }
1886         local net
1887         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1888                 if _uci:get("network", net) == "interface" then
1889                         nets[#nets+1] = network(net)
1890                 end
1891         end
1892         table.sort(nets, function(a, b) return a.sid < b.sid end)
1893         return nets
1894 end
1895
1896 function wifinet.get_interface(self)
1897         return interface(self:ifname())
1898 end
1899
1900
1901 -- setup base protocols
1902 _M:register_protocol("static")
1903 _M:register_protocol("dhcp")
1904 _M:register_protocol("none")
1905
1906 -- load protocol extensions
1907 local exts = nfs.dir(utl.libpath() .. "/model/network")
1908 if exts then
1909         local ext
1910         for ext in exts do
1911                 if ext:match("%.lua$") then
1912                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1913                 end
1914         end
1915 end