Merge pull request #2657 from msurovcak/msurovcak-statistics-collon
[oweals/luci.git] / modules / luci-base / 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 in 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 false
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                         elseif b then
375                                 b.ifnames[#b.ifnames+1] = _interfaces[r[2]]
376                                 b.ifnames[#b.ifnames].bridge = b
377                         end
378                 end
379         end
380
381         -- read switch topology
382         local boardinfo = jsc.parse(nfs.readfile("/etc/board.json") or "")
383         if type(boardinfo) == "table" and type(boardinfo.switch) == "table" then
384                 local switch, layout
385                 for switch, layout in pairs(boardinfo.switch) do
386                         if type(layout) == "table" and type(layout.ports) == "table" then
387                                 local _, port
388                                 local ports = { }
389                                 local nports = { }
390                                 local netdevs = { }
391
392                                 for _, port in ipairs(layout.ports) do
393                                         if type(port) == "table" and
394                                            type(port.num) == "number" and
395                                            (type(port.role) == "string" or
396                                             type(port.device) == "string")
397                                         then
398                                                 local spec = {
399                                                         num    = port.num,
400                                                         role   = port.role or "cpu",
401                                                         index  = port.index or port.num
402                                                 }
403
404                                                 if port.device then
405                                                         spec.device = port.device
406                                                         spec.tagged = port.need_tag
407                                                         netdevs[tostring(port.num)] = port.device
408                                                 end
409
410                                                 ports[#ports+1] = spec
411
412                                                 if port.role then
413                                                         nports[port.role] = (nports[port.role] or 0) + 1
414                                                 end
415                                         end
416                                 end
417
418                                 table.sort(ports, function(a, b)
419                                         if a.role ~= b.role then
420                                                 return (a.role < b.role)
421                                         end
422
423                                         return (a.index < b.index)
424                                 end)
425
426                                 local pnum, role
427                                 for _, port in ipairs(ports) do
428                                         if port.role ~= role then
429                                                 role = port.role
430                                                 pnum = 1
431                                         end
432
433                                         if role == "cpu" then
434                                                 port.label = "CPU (%s)" % port.device
435                                         elseif nports[role] > 1 then
436                                                 port.label = "%s %d" %{ role:upper(), pnum }
437                                                 pnum = pnum + 1
438                                         else
439                                                 port.label = role:upper()
440                                         end
441
442                                         port.role = nil
443                                         port.index = nil
444                                 end
445
446                                 _swtopo[switch] = {
447                                         ports = ports,
448                                         netdevs = netdevs
449                                 }
450                         end
451                 end
452         end
453
454         return _M
455 end
456
457 function save(self, ...)
458         _uci:save(...)
459         _uci:load(...)
460 end
461
462 function commit(self, ...)
463         _uci:commit(...)
464         _uci:load(...)
465 end
466
467 function ifnameof(self, x)
468         if utl.instanceof(x, interface) then
469                 return x:name()
470         elseif utl.instanceof(x, protocol) then
471                 return x:ifname()
472         elseif type(x) == "string" then
473                 return x:match("^[^:]+")
474         end
475 end
476
477 function get_protocol(self, protoname, netname)
478         local v = _protocols[protoname]
479         if v then
480                 return v(netname or "__dummy__")
481         end
482 end
483
484 function get_protocols(self)
485         local p = { }
486         local _, v
487         for _, v in ipairs(_protocols) do
488                 p[#p+1] = v("__dummy__")
489         end
490         return p
491 end
492
493 function register_protocol(self, protoname)
494         local proto = utl.class(protocol)
495
496         function proto.__init__(self, name)
497                 self.sid = name
498         end
499
500         function proto.proto(self)
501                 return protoname
502         end
503
504         _protocols[#_protocols+1] = proto
505         _protocols[protoname]     = proto
506
507         return proto
508 end
509
510 function register_pattern_virtual(self, pat)
511         IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
512 end
513
514 function register_error_code(self, code, message)
515         if type(code) == "string" and
516            type(message) == "string" and
517            not IFACE_ERRORS[code]
518         then
519                 IFACE_ERRORS[code] = message
520                 return true
521         end
522
523         return false
524 end
525
526 function has_ipv6(self)
527         return nfs.access("/proc/net/ipv6_route")
528 end
529
530 function add_network(self, n, options)
531         local oldnet = self:get_network(n)
532         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
533                 if _uci:section("network", "interface", n, options) then
534                         return network(n)
535                 end
536         elseif oldnet and oldnet:is_empty() then
537                 if options then
538                         local k, v
539                         for k, v in pairs(options) do
540                                 oldnet:set(k, v)
541                         end
542                 end
543                 return oldnet
544         end
545 end
546
547 function get_network(self, n)
548         if n and _uci:get("network", n) == "interface" then
549                 return network(n)
550         elseif n then
551                 local stat = utl.ubus("network.interface", "status", { interface = n })
552                 if type(stat) == "table" and
553                    type(stat.proto) == "string"
554                 then
555                         return network(n, stat.proto)
556                 end
557         end
558 end
559
560 function get_networks(self)
561         local nets = { }
562         local nls = { }
563
564         _uci:foreach("network", "interface",
565                 function(s)
566                         nls[s['.name']] = network(s['.name'])
567                 end)
568
569         local dump = utl.ubus("network.interface", "dump", { })
570         if type(dump) == "table" and
571            type(dump.interface) == "table"
572         then
573                 local _, net
574                 for _, net in ipairs(dump.interface) do
575                         if type(net) == "table" and
576                            type(net.proto) == "string" and
577                            type(net.interface) == "string"
578                         then
579                                 if not nls[net.interface] then
580                                         nls[net.interface] = network(net.interface, net.proto)
581                                 end
582                         end
583                 end
584         end
585
586         local n
587         for n in utl.kspairs(nls) do
588                 nets[#nets+1] = nls[n]
589         end
590
591         return nets
592 end
593
594 function del_network(self, n)
595         local r = _uci:delete("network", n)
596         if r then
597                 _uci:delete_all("luci", "ifstate",
598                         function(s) return (s.interface == n) end)
599
600                 _uci:delete_all("network", "alias",
601                         function(s) return (s.interface == n) end)
602
603                 _uci:delete_all("network", "route",
604                         function(s) return (s.interface == n) end)
605
606                 _uci:delete_all("network", "route6",
607                         function(s) return (s.interface == n) end)
608
609                 _uci:foreach("wireless", "wifi-iface",
610                         function(s)
611                                 local net
612                                 local rest = { }
613                                 for net in utl.imatch(s.network) do
614                                         if net ~= n then
615                                                 rest[#rest+1] = net
616                                         end
617                                 end
618                                 if #rest > 0 then
619                                         _uci:set("wireless", s['.name'], "network",
620                                                       table.concat(rest, " "))
621                                 else
622                                         _uci:delete("wireless", s['.name'], "network")
623                                 end
624                         end)
625         end
626         return r
627 end
628
629 function rename_network(self, old, new)
630         local r
631         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
632                 r = _uci:section("network", "interface", new, _uci:get_all("network", old))
633
634                 if r then
635                         _uci:foreach("network", "alias",
636                                 function(s)
637                                         if s.interface == old then
638                                                 _uci:set("network", s['.name'], "interface", new)
639                                         end
640                                 end)
641
642                         _uci:foreach("network", "route",
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", "route6",
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("wireless", "wifi-iface",
657                                 function(s)
658                                         local net
659                                         local list = { }
660                                         for net in utl.imatch(s.network) do
661                                                 if net == old then
662                                                         list[#list+1] = new
663                                                 else
664                                                         list[#list+1] = net
665                                                 end
666                                         end
667                                         if #list > 0 then
668                                                 _uci:set("wireless", s['.name'], "network",
669                                                               table.concat(list, " "))
670                                         end
671                                 end)
672
673                         _uci:delete("network", old)
674                 end
675         end
676         return r or false
677 end
678
679 function get_interface(self, i)
680         if _interfaces[i] or _wifi_iface(i) then
681                 return interface(i)
682         else
683                 local netid = _wifi_netid_by_sid(i)
684                 return netid and interface(netid)
685         end
686 end
687
688 function get_interfaces(self)
689         local iface
690         local ifaces = { }
691         local nfs = { }
692
693         -- find normal interfaces
694         _uci:foreach("network", "interface",
695                 function(s)
696                         for iface in utl.imatch(s.ifname) do
697                                 if not _iface_ignore(iface) and not _iface_virtual(iface) and not _wifi_iface(iface) then
698                                         nfs[iface] = interface(iface)
699                                 end
700                         end
701                 end)
702
703         for iface in utl.kspairs(_interfaces) do
704                 if not (nfs[iface] or _iface_ignore(iface) or _iface_virtual(iface) or _wifi_iface(iface)) then
705                         nfs[iface] = interface(iface)
706                 end
707         end
708
709         -- find vlan interfaces
710         _uci:foreach("network", "switch_vlan",
711                 function(s)
712                         if type(s.ports) ~= "string" or
713                            type(s.device) ~= "string" or
714                            type(_swtopo[s.device]) ~= "table"
715                         then
716                                 return
717                         end
718
719                         local pnum, ptag
720                         for pnum, ptag in s.ports:gmatch("(%d+)([tu]?)") do
721                                 local netdev = _swtopo[s.device].netdevs[pnum]
722                                 if netdev then
723                                         if not nfs[netdev] then
724                                                 nfs[netdev] = interface(netdev)
725                                         end
726                                         _switch[netdev] = true
727
728                                         if ptag == "t" then
729                                                 local vid = tonumber(s.vid or s.vlan)
730                                                 if vid ~= nil and vid >= 0 and vid <= 4095 then
731                                                         local iface = "%s.%d" %{ netdev, vid }
732                                                         if not nfs[iface] then
733                                                                 nfs[iface] = interface(iface)
734                                                         end
735                                                         _switch[iface] = true
736                                                 end
737                                         end
738                                 end
739                         end
740                 end)
741
742         for iface in utl.kspairs(nfs) do
743                 ifaces[#ifaces+1] = nfs[iface]
744         end
745
746         -- find wifi interfaces
747         local num = { }
748         local wfs = { }
749         _uci:foreach("wireless", "wifi-iface",
750                 function(s)
751                         if s.device then
752                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
753                                 local i = "%s.network%d" %{ s.device, num[s.device] }
754                                 wfs[i] = interface(i)
755                         end
756                 end)
757
758         for iface in utl.kspairs(wfs) do
759                 ifaces[#ifaces+1] = wfs[iface]
760         end
761
762         return ifaces
763 end
764
765 function ignore_interface(self, x)
766         return _iface_ignore(x)
767 end
768
769 function get_wifidev(self, dev)
770         if _uci:get("wireless", dev) == "wifi-device" then
771                 return wifidev(dev)
772         end
773 end
774
775 function get_wifidevs(self)
776         local devs = { }
777         local wfd  = { }
778
779         _uci:foreach("wireless", "wifi-device",
780                 function(s) wfd[#wfd+1] = s['.name'] end)
781
782         local dev
783         for _, dev in utl.vspairs(wfd) do
784                 devs[#devs+1] = wifidev(dev)
785         end
786
787         return devs
788 end
789
790 function get_wifinet(self, net)
791         local wnet = _wifi_sid_by_ifname(net)
792         if wnet then
793                 return wifinet(wnet)
794         end
795 end
796
797 function add_wifinet(self, net, options)
798         if type(options) == "table" and options.device and
799                 _uci:get("wireless", options.device) == "wifi-device"
800         then
801                 local wnet = _uci:section("wireless", "wifi-iface", nil, options)
802                 return wifinet(wnet)
803         end
804 end
805
806 function del_wifinet(self, net)
807         local wnet = _wifi_sid_by_ifname(net)
808         if wnet then
809                 _uci:delete("wireless", wnet)
810                 return true
811         end
812         return false
813 end
814
815 function get_status_by_route(self, addr, mask)
816         local route_statuses = { }
817         local _, object
818         for _, object in ipairs(utl.ubus()) do
819                 local net = object:match("^network%.interface%.(.+)")
820                 if net then
821                         local s = utl.ubus(object, "status", {})
822                         if s and s.route then
823                                 local rt
824                                 for _, rt in ipairs(s.route) do
825                                         if not rt.table and rt.target == addr and rt.mask == mask then
826                                                 route_statuses[net] = s
827                                         end
828                                 end
829                         end
830                 end
831         end
832
833         return route_statuses
834 end
835
836 function get_status_by_address(self, addr)
837         local _, object
838         for _, object in ipairs(utl.ubus()) do
839                 local net = object:match("^network%.interface%.(.+)")
840                 if net then
841                         local s = utl.ubus(object, "status", {})
842                         if s and s['ipv4-address'] then
843                                 local a
844                                 for _, a in ipairs(s['ipv4-address']) do
845                                         if a.address == addr then
846                                                 return net, s
847                                         end
848                                 end
849                         end
850                         if s and s['ipv6-address'] then
851                                 local a
852                                 for _, a in ipairs(s['ipv6-address']) do
853                                         if a.address == addr then
854                                                 return net, s
855                                         end
856                                 end
857                         end
858                         if s and s['ipv6-prefix-assignment'] then
859                                 local a
860                                 for _, a in ipairs(s['ipv6-prefix-assignment']) do
861                                         if a and a['local-address'] and a['local-address'].address == addr then
862                                                 return net, s
863                                         end
864                                 end
865                         end
866                 end
867         end
868 end
869
870 function get_wan_networks(self)
871         local k, v
872         local wan_nets = { }
873         local route_statuses = self:get_status_by_route("0.0.0.0", 0)
874
875         for k, v in pairs(route_statuses) do
876                 wan_nets[#wan_nets+1] = network(k, v.proto)
877         end
878
879         return wan_nets
880 end
881
882 function get_wan6_networks(self)
883         local k, v
884         local wan6_nets = { }
885         local route_statuses = self:get_status_by_route("::", 0)
886
887         for k, v in pairs(route_statuses) do
888                 wan6_nets[#wan6_nets+1] = network(k, v.proto)
889         end
890
891         return wan6_nets
892 end
893
894 function get_switch_topologies(self)
895         return _swtopo
896 end
897
898
899 function network(name, proto)
900         if name then
901                 local p = proto or _uci:get("network", name, "proto")
902                 local c = p and _protocols[p] or protocol
903                 return c(name)
904         end
905 end
906
907 function protocol.__init__(self, name)
908         self.sid = name
909 end
910
911 function protocol._get(self, opt)
912         local v = _uci:get("network", self.sid, opt)
913         if type(v) == "table" then
914                 return table.concat(v, " ")
915         end
916         return v or ""
917 end
918
919 function protocol._ubus(self, field)
920         if not _ubusnetcache[self.sid] then
921                 _ubusnetcache[self.sid] = utl.ubus("network.interface.%s" % self.sid,
922                                                    "status", { })
923         end
924         if _ubusnetcache[self.sid] and field then
925                 return _ubusnetcache[self.sid][field]
926         end
927         return _ubusnetcache[self.sid]
928 end
929
930 function protocol.get(self, opt)
931         return _get("network", self.sid, opt)
932 end
933
934 function protocol.set(self, opt, val)
935         return _set("network", self.sid, opt, val)
936 end
937
938 function protocol.ifname(self)
939         local ifname
940         if self:is_floating() then
941                 ifname = self:_ubus("l3_device")
942         else
943                 ifname = self:_ubus("device")
944         end
945         if not ifname then
946                 ifname = _wifi_netid_by_netname(self.sid)
947         end
948         return ifname
949 end
950
951 function protocol.proto(self)
952         return "none"
953 end
954
955 function protocol.get_i18n(self)
956         local p = self:proto()
957         if p == "none" then
958                 return lng.translate("Unmanaged")
959         elseif p == "static" then
960                 return lng.translate("Static address")
961         elseif p == "dhcp" then
962                 return lng.translate("DHCP client")
963         else
964                 return lng.translate("Unknown")
965         end
966 end
967
968 function protocol.type(self)
969         return self:_get("type")
970 end
971
972 function protocol.name(self)
973         return self.sid
974 end
975
976 function protocol.uptime(self)
977         return self:_ubus("uptime") or 0
978 end
979
980 function protocol.expires(self)
981         local u = self:_ubus("uptime")
982         local d = self:_ubus("data")
983
984         if type(u) == "number" and type(d) == "table" and
985            type(d.leasetime) == "number"
986         then
987                 local r = (d.leasetime - (u % d.leasetime))
988                 return r > 0 and r or 0
989         end
990
991         return -1
992 end
993
994 function protocol.metric(self)
995         return self:_ubus("metric") or 0
996 end
997
998 function protocol.zonename(self)
999         local d = self:_ubus("data")
1000
1001         if type(d) == "table" and type(d.zone) == "string" then
1002                 return d.zone
1003         end
1004
1005         return nil
1006 end
1007
1008 function protocol.ipaddr(self)
1009         local addrs = self:_ubus("ipv4-address")
1010         return addrs and #addrs > 0 and addrs[1].address
1011 end
1012
1013 function protocol.ipaddrs(self)
1014         local addrs = self:_ubus("ipv4-address")
1015         local rv = { }
1016
1017         if type(addrs) == "table" then
1018                 local n, addr
1019                 for n, addr in ipairs(addrs) do
1020                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
1021                 end
1022         end
1023
1024         return rv
1025 end
1026
1027 function protocol.netmask(self)
1028         local addrs = self:_ubus("ipv4-address")
1029         return addrs and #addrs > 0 and
1030                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
1031 end
1032
1033 function protocol.gwaddr(self)
1034         local _, route
1035         for _, route in ipairs(self:_ubus("route") or { }) do
1036                 if route.target == "0.0.0.0" and route.mask == 0 then
1037                         return route.nexthop
1038                 end
1039         end
1040 end
1041
1042 function protocol.dnsaddrs(self)
1043         local dns = { }
1044         local _, addr
1045         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
1046                 if not addr:match(":") then
1047                         dns[#dns+1] = addr
1048                 end
1049         end
1050         return dns
1051 end
1052
1053 function protocol.ip6addr(self)
1054         local addrs = self:_ubus("ipv6-address")
1055         if addrs and #addrs > 0 then
1056                 return "%s/%d" %{ addrs[1].address, addrs[1].mask }
1057         else
1058                 addrs = self:_ubus("ipv6-prefix-assignment")
1059                 if addrs and #addrs > 0 then
1060                         return "%s/%d" %{ addrs[1].address, addrs[1].mask }
1061                 end
1062         end
1063 end
1064
1065 function protocol.ip6addrs(self)
1066         local addrs = self:_ubus("ipv6-address")
1067         local rv = { }
1068         local n, addr
1069
1070         if type(addrs) == "table" then
1071                 for n, addr in ipairs(addrs) do
1072                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
1073                 end
1074         end
1075
1076         addrs = self:_ubus("ipv6-prefix-assignment")
1077
1078         if type(addrs) == "table" then
1079                 for n, addr in ipairs(addrs) do
1080                         if type(addr["local-address"]) == "table" and
1081                            type(addr["local-address"].mask) == "number" and
1082                            type(addr["local-address"].address) == "string"
1083                         then
1084                                 rv[#rv+1] = "%s/%d" %{
1085                                         addr["local-address"].address,
1086                                         addr["local-address"].mask
1087                                 }
1088                         end
1089                 end
1090         end
1091
1092         return rv
1093 end
1094
1095 function protocol.gw6addr(self)
1096         local _, route
1097         for _, route in ipairs(self:_ubus("route") or { }) do
1098                 if route.target == "::" and route.mask == 0 then
1099                         return ipc.IPv6(route.nexthop):string()
1100                 end
1101         end
1102 end
1103
1104 function protocol.dns6addrs(self)
1105         local dns = { }
1106         local _, addr
1107         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
1108                 if addr:match(":") then
1109                         dns[#dns+1] = addr
1110                 end
1111         end
1112         return dns
1113 end
1114
1115 function protocol.ip6prefix(self)
1116         local prefix = self:_ubus("ipv6-prefix")
1117         if prefix and #prefix > 0 then
1118                 return "%s/%d" %{ prefix[1].address, prefix[1].mask }
1119         end
1120 end
1121
1122 function protocol.errors(self)
1123         local _, err, rv
1124         local errors = self:_ubus("errors")
1125         if type(errors) == "table" then
1126                 for _, err in ipairs(errors) do
1127                         if type(err) == "table" and
1128                            type(err.code) == "string"
1129                         then
1130                                 rv = rv or { }
1131                                 rv[#rv+1] = IFACE_ERRORS[err.code] or lng.translatef("Unknown error (%s)", err.code)
1132                         end
1133                 end
1134         end
1135         return rv
1136 end
1137
1138 function protocol.is_bridge(self)
1139         return (not self:is_virtual() and self:type() == "bridge")
1140 end
1141
1142 function protocol.opkg_package(self)
1143         return nil
1144 end
1145
1146 function protocol.is_installed(self)
1147         return true
1148 end
1149
1150 function protocol.is_virtual(self)
1151         return false
1152 end
1153
1154 function protocol.is_floating(self)
1155         return false
1156 end
1157
1158 function protocol.is_dynamic(self)
1159         return (self:_ubus("dynamic") == true)
1160 end
1161
1162 function protocol.is_auto(self)
1163         local auto = self:_get("auto")
1164
1165         if auto == "0" then
1166                 return false
1167         else
1168                 return true
1169         end
1170 end
1171
1172 function protocol.is_alias(self)
1173         local ifn, parent = nil, nil
1174
1175         for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1176                 if #ifn > 1 and ifn:byte(1) == 64 then
1177                         parent = ifn:sub(2)
1178                 elseif parent ~= nil then
1179                         parent = nil
1180                 end
1181         end
1182
1183         return parent
1184 end
1185
1186 function protocol.is_empty(self)
1187         if self:is_floating() then
1188                 return false
1189         else
1190                 local empty = true
1191
1192                 if (self:_get("ifname") or ""):match("%S+") then
1193                         empty = false
1194                 end
1195
1196                 if empty and _wifi_netid_by_netname(self.sid) then
1197                         empty = false
1198                 end
1199
1200                 return empty
1201         end
1202 end
1203
1204 function protocol.is_up(self)
1205         return (self:_ubus("up") == true)
1206 end
1207
1208 function protocol.add_interface(self, ifname)
1209         ifname = _M:ifnameof(ifname)
1210         if ifname and not self:is_floating() then
1211                 -- if its a wifi interface, change its network option
1212                 local wif = _wifi_sid_by_ifname(ifname)
1213                 if wif then
1214                         _append("wireless", wif, "network", self.sid)
1215
1216                 -- add iface to our iface list
1217                 else
1218                         _append("network", self.sid, "ifname", ifname)
1219                 end
1220         end
1221 end
1222
1223 function protocol.del_interface(self, ifname)
1224         ifname = _M:ifnameof(ifname)
1225         if ifname and not self:is_floating() then
1226                 -- if its a wireless interface, clear its network option
1227                 local wif = _wifi_sid_by_ifname(ifname)
1228                 if wif then _filter("wireless", wif, "network", self.sid) end
1229
1230                 -- remove the interface
1231                 _filter("network", self.sid, "ifname", ifname)
1232         end
1233 end
1234
1235 function protocol.get_interface(self)
1236         if self:is_virtual() then
1237                 _tunnel[self:proto() .. "-" .. self.sid] = true
1238                 return interface(self:proto() .. "-" .. self.sid, self)
1239         elseif self:is_bridge() then
1240                 _bridge["br-" .. self.sid] = true
1241                 return interface("br-" .. self.sid, self)
1242         else
1243                 local ifn = self:_ubus("l3_device") or self:_ubus("device")
1244                 if ifn then
1245                         return interface(ifn, self)
1246                 end
1247
1248                 for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1249                         ifn = ifn:match("^[^:/]+")
1250                         return ifn and interface(ifn, self)
1251                 end
1252
1253                 ifn = _wifi_netid_by_netname(self.sid)
1254                 return ifn and interface(ifn, self)
1255         end
1256 end
1257
1258 function protocol.get_interfaces(self)
1259         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
1260                 local ifaces = { }
1261
1262                 local ifn
1263                 local nfs = { }
1264                 for ifn in utl.imatch(self:get("ifname")) do
1265                         ifn = ifn:match("^[^:/]+")
1266                         nfs[ifn] = interface(ifn, self)
1267                 end
1268
1269                 for ifn in utl.kspairs(nfs) do
1270                         ifaces[#ifaces+1] = nfs[ifn]
1271                 end
1272
1273                 local wfs = { }
1274                 _uci:foreach("wireless", "wifi-iface",
1275                         function(s)
1276                                 if s.device then
1277                                         local net
1278                                         for net in utl.imatch(s.network) do
1279                                                 if net == self.sid then
1280                                                         ifn = _wifi_netid_by_sid(s[".name"])
1281                                                         if ifn then
1282                                                                 wfs[ifn] = interface(ifn, self)
1283                                                         end
1284                                                 end
1285                                         end
1286                                 end
1287                         end)
1288
1289                 for ifn in utl.kspairs(wfs) do
1290                         ifaces[#ifaces+1] = wfs[ifn]
1291                 end
1292
1293                 return ifaces
1294         end
1295 end
1296
1297 function protocol.contains_interface(self, ifname)
1298         ifname = _M:ifnameof(ifname)
1299         if not ifname then
1300                 return false
1301         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
1302                 return true
1303         elseif self:is_bridge() and "br-" .. self.sid == ifname then
1304                 return true
1305         else
1306                 local ifn
1307                 for ifn in utl.imatch(self:get("ifname")) do
1308                         ifn = ifn:match("[^:]+")
1309                         if ifn == ifname then
1310                                 return true
1311                         end
1312                 end
1313
1314                 local wif = _wifi_sid_by_ifname(ifname)
1315                 if wif then
1316                         local n
1317                         for n in utl.imatch(_uci:get("wireless", wif, "network")) do
1318                                 if n == self.sid then
1319                                         return true
1320                                 end
1321                         end
1322                 end
1323         end
1324
1325         return false
1326 end
1327
1328 function protocol.adminlink(self)
1329         local stat, dsp = pcall(require, "luci.dispatcher")
1330         return stat and dsp.build_url("admin", "network", "network", self.sid)
1331 end
1332
1333
1334 interface = utl.class()
1335
1336 function interface.__init__(self, ifname, network)
1337         local wif = _wifi_sid_by_ifname(ifname)
1338         if wif then
1339                 self.wif    = wifinet(wif)
1340                 self.ifname = self.wif:ifname()
1341         end
1342
1343         self.ifname  = self.ifname or ifname
1344         self.dev     = _interfaces[self.ifname]
1345         self.network = network
1346 end
1347
1348 function interface._ubus(self, field)
1349         if not _ubusdevcache[self.ifname] then
1350                 _ubusdevcache[self.ifname] = utl.ubus("network.device", "status",
1351                                                       { name = self.ifname })
1352         end
1353         if _ubusdevcache[self.ifname] and field then
1354                 return _ubusdevcache[self.ifname][field]
1355         end
1356         return _ubusdevcache[self.ifname]
1357 end
1358
1359 function interface.name(self)
1360         return self.wif and self.wif:ifname() or self.ifname
1361 end
1362
1363 function interface.mac(self)
1364         return ipc.checkmac(self:_ubus("macaddr"))
1365 end
1366
1367 function interface.ipaddrs(self)
1368         return self.dev and self.dev.ipaddrs or { }
1369 end
1370
1371 function interface.ip6addrs(self)
1372         return self.dev and self.dev.ip6addrs or { }
1373 end
1374
1375 function interface.type(self)
1376         if self.ifname and self.ifname:byte(1) == 64 then
1377                 return "alias"
1378         elseif self.wif or _wifi_iface(self.ifname) then
1379                 return "wifi"
1380         elseif _bridge[self.ifname] then
1381                 return "bridge"
1382         elseif _tunnel[self.ifname] then
1383                 return "tunnel"
1384         elseif self.ifname:match("%.") then
1385                 return "vlan"
1386         elseif _switch[self.ifname] then
1387                 return "switch"
1388         else
1389                 return "ethernet"
1390         end
1391 end
1392
1393 function interface.shortname(self)
1394         if self.wif then
1395                 return self.wif:shortname()
1396         else
1397                 return self.ifname
1398         end
1399 end
1400
1401 function interface.get_i18n(self)
1402         if self.wif then
1403                 return "%s: %s %q" %{
1404                         lng.translate("Wireless Network"),
1405                         self.wif:active_mode(),
1406                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id() or "?"
1407                 }
1408         else
1409                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1410         end
1411 end
1412
1413 function interface.get_type_i18n(self)
1414         local x = self:type()
1415         if x == "alias" then
1416                 return lng.translate("Alias Interface")
1417         elseif x == "wifi" then
1418                 return lng.translate("Wireless Adapter")
1419         elseif x == "bridge" then
1420                 return lng.translate("Bridge")
1421         elseif x == "switch" then
1422                 return lng.translate("Ethernet Switch")
1423         elseif x == "vlan" then
1424                 if _switch[self.ifname] then
1425                         return lng.translate("Switch VLAN")
1426                 else
1427                         return lng.translate("Software VLAN")
1428                 end
1429         elseif x == "tunnel" then
1430                 return lng.translate("Tunnel Interface")
1431         else
1432                 return lng.translate("Ethernet Adapter")
1433         end
1434 end
1435
1436 function interface.adminlink(self)
1437         if self.wif then
1438                 return self.wif:adminlink()
1439         end
1440 end
1441
1442 function interface.ports(self)
1443         local members = self:_ubus("bridge-members")
1444         if members then
1445                 local _, iface
1446                 local ifaces = { }
1447                 for _, iface in ipairs(members) do
1448                         ifaces[#ifaces+1] = interface(iface)
1449                 end
1450         end
1451 end
1452
1453 function interface.bridge_id(self)
1454         if self.br then
1455                 return self.br.id
1456         else
1457                 return nil
1458         end
1459 end
1460
1461 function interface.bridge_stp(self)
1462         if self.br then
1463                 return self.br.stp
1464         else
1465                 return false
1466         end
1467 end
1468
1469 function interface.is_up(self)
1470         local up = self:_ubus("up")
1471         if up == nil then
1472                 up = (self:type() == "alias")
1473         end
1474         return up or false
1475 end
1476
1477 function interface.is_bridge(self)
1478         return (self:type() == "bridge")
1479 end
1480
1481 function interface.is_bridgeport(self)
1482         return self.dev and self.dev.bridge and true or false
1483 end
1484
1485 function interface.tx_bytes(self)
1486         local stat = self:_ubus("statistics")
1487         return stat and stat.tx_bytes or 0
1488 end
1489
1490 function interface.rx_bytes(self)
1491         local stat = self:_ubus("statistics")
1492         return stat and stat.rx_bytes or 0
1493 end
1494
1495 function interface.tx_packets(self)
1496         local stat = self:_ubus("statistics")
1497         return stat and stat.tx_packets or 0
1498 end
1499
1500 function interface.rx_packets(self)
1501         local stat = self:_ubus("statistics")
1502         return stat and stat.rx_packets or 0
1503 end
1504
1505 function interface.get_network(self)
1506         return self:get_networks()[1]
1507 end
1508
1509 function interface.get_networks(self)
1510         if not self.networks then
1511                 local nets = { }
1512                 local _, net
1513                 for _, net in ipairs(_M:get_networks()) do
1514                         if net:contains_interface(self.ifname) or
1515                            net:ifname() == self.ifname
1516                         then
1517                                 nets[#nets+1] = net
1518                         end
1519                 end
1520                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1521                 self.networks = nets
1522                 return nets
1523         else
1524                 return self.networks
1525         end
1526 end
1527
1528 function interface.get_wifinet(self)
1529         return self.wif
1530 end
1531
1532
1533 wifidev = utl.class()
1534
1535 function wifidev.__init__(self, name)
1536         local t, n = _uci:get("wireless", name)
1537         if t == "wifi-device" and n ~= nil then
1538                 self.sid    = n
1539                 self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true)
1540         end
1541         self.sid    = self.sid    or name
1542         self.iwinfo = self.iwinfo or { ifname = self.sid }
1543 end
1544
1545 function wifidev.get(self, opt)
1546         return _get("wireless", self.sid, opt)
1547 end
1548
1549 function wifidev.set(self, opt, val)
1550         return _set("wireless", self.sid, opt, val)
1551 end
1552
1553 function wifidev.name(self)
1554         return self.sid
1555 end
1556
1557 function wifidev.hwmodes(self)
1558         local l = self.iwinfo.hwmodelist
1559         if l and next(l) then
1560                 return l
1561         else
1562                 return { b = true, g = true }
1563         end
1564 end
1565
1566 function wifidev.get_i18n(self)
1567         local t = self.iwinfo.hardware_name or "Generic"
1568         if self.iwinfo.type == "wl" then
1569                 t = "Broadcom"
1570         end
1571
1572         local m = ""
1573         local l = self:hwmodes()
1574         if l.a then m = m .. "a" end
1575         if l.b then m = m .. "b" end
1576         if l.g then m = m .. "g" end
1577         if l.n then m = m .. "n" end
1578         if l.ac then m = "ac" end
1579
1580         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1581 end
1582
1583 function wifidev.is_up(self)
1584         if _ubuswificache[self.sid] then
1585                 return (_ubuswificache[self.sid].up == true)
1586         end
1587
1588         return false
1589 end
1590
1591 function wifidev.get_wifinet(self, net)
1592         if _uci:get("wireless", net) == "wifi-iface" then
1593                 return wifinet(net)
1594         else
1595                 local wnet = _wifi_sid_by_ifname(net)
1596                 if wnet then
1597                         return wifinet(wnet)
1598                 end
1599         end
1600 end
1601
1602 function wifidev.get_wifinets(self)
1603         local nets = { }
1604
1605         _uci:foreach("wireless", "wifi-iface",
1606                 function(s)
1607                         if s.device == self.sid then
1608                                 nets[#nets+1] = wifinet(s['.name'])
1609                         end
1610                 end)
1611
1612         return nets
1613 end
1614
1615 function wifidev.add_wifinet(self, options)
1616         options = options or { }
1617         options.device = self.sid
1618
1619         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1620         if wnet then
1621                 return wifinet(wnet, options)
1622         end
1623 end
1624
1625 function wifidev.del_wifinet(self, net)
1626         if utl.instanceof(net, wifinet) then
1627                 net = net.sid
1628         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1629                 net = _wifi_sid_by_ifname(net)
1630         end
1631
1632         if net and _uci:get("wireless", net, "device") == self.sid then
1633                 _uci:delete("wireless", net)
1634                 return true
1635         end
1636
1637         return false
1638 end
1639
1640
1641 wifinet = utl.class()
1642
1643 function wifinet.__init__(self, name, data)
1644         local sid, netid, radioname, radiostate, netstate
1645
1646         -- lookup state by radio#.network# notation
1647         sid = _wifi_sid_by_netid(name)
1648         if sid then
1649                 netid = name
1650                 radioname, radiostate, netstate = _wifi_state_by_sid(sid)
1651         else
1652                 -- lookup state by ifname (e.g. wlan0)
1653                 radioname, radiostate, netstate = _wifi_state_by_ifname(name)
1654                 if radioname and radiostate and netstate then
1655                         sid = netstate.section
1656                         netid = _wifi_netid_by_sid(sid)
1657                 else
1658                         -- lookup state by uci section id (e.g. cfg053579)
1659                         radioname, radiostate, netstate = _wifi_state_by_sid(name)
1660                         if radioname and radiostate and netstate then
1661                                 sid = name
1662                                 netid = _wifi_netid_by_sid(sid)
1663                         else
1664                                 -- no state available, try to resolve from uci
1665                                 netid, radioname = _wifi_netid_by_sid(name)
1666                                 if netid and radioname then
1667                                         sid = name
1668                                 end
1669                         end
1670                 end
1671         end
1672
1673         local iwinfo =
1674                 (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or
1675                 (radioname and _wifi_iwinfo_by_ifname(radioname)) or
1676                 { ifname = (netid or sid or name) }
1677
1678         self.sid       = sid or name
1679         self.wdev      = iwinfo.ifname
1680         self.iwinfo    = iwinfo
1681         self.netid     = netid
1682         self._ubusdata = {
1683                 radio = radioname,
1684                 dev   = radiostate,
1685                 net   = netstate
1686         }
1687 end
1688
1689 function wifinet.ubus(self, ...)
1690         local n, v = self._ubusdata
1691         for n = 1, select('#', ...) do
1692                 if type(v) == "table" then
1693                         v = v[select(n, ...)]
1694                 else
1695                         return nil
1696                 end
1697         end
1698         return v
1699 end
1700
1701 function wifinet.get(self, opt)
1702         return _get("wireless", self.sid, opt)
1703 end
1704
1705 function wifinet.set(self, opt, val)
1706         return _set("wireless", self.sid, opt, val)
1707 end
1708
1709 function wifinet.mode(self)
1710         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1711 end
1712
1713 function wifinet.ssid(self)
1714         return self:ubus("net", "config", "ssid") or self:get("ssid")
1715 end
1716
1717 function wifinet.bssid(self)
1718         return self:ubus("net", "config", "bssid") or self:get("bssid")
1719 end
1720
1721 function wifinet.network(self)
1722         local net, networks = nil, { }
1723         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1724                 networks[#networks+1] = net
1725         end
1726         return networks
1727 end
1728
1729 function wifinet.id(self)
1730         return self.netid
1731 end
1732
1733 function wifinet.name(self)
1734         return self.sid
1735 end
1736
1737 function wifinet.ifname(self)
1738         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1739         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1740                 ifname = self.netid
1741         end
1742         return ifname
1743 end
1744
1745 function wifinet.get_device(self)
1746         local dev = self:ubus("radio") or self:get("device")
1747         return dev and wifidev(dev) or nil
1748 end
1749
1750 function wifinet.is_up(self)
1751         local ifc = self:get_interface()
1752         return (ifc and ifc:is_up() or false)
1753 end
1754
1755 function wifinet.active_mode(self)
1756         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1757
1758         if     m == "ap"      then m = "Master"
1759         elseif m == "sta"     then m = "Client"
1760         elseif m == "adhoc"   then m = "Ad-Hoc"
1761         elseif m == "mesh"    then m = "Mesh"
1762         elseif m == "monitor" then m = "Monitor"
1763         end
1764
1765         return m
1766 end
1767
1768 function wifinet.active_mode_i18n(self)
1769         return lng.translate(self:active_mode())
1770 end
1771
1772 function wifinet.active_ssid(self)
1773         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1774 end
1775
1776 function wifinet.active_bssid(self)
1777         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1778 end
1779
1780 function wifinet.active_encryption(self)
1781         local enc = self.iwinfo and self.iwinfo.encryption
1782         return enc and enc.description or "-"
1783 end
1784
1785 function wifinet.assoclist(self)
1786         return self.iwinfo.assoclist or { }
1787 end
1788
1789 function wifinet.frequency(self)
1790         local freq = self.iwinfo.frequency
1791         if freq and freq > 0 then
1792                 return "%.03f" % (freq / 1000)
1793         end
1794 end
1795
1796 function wifinet.bitrate(self)
1797         local rate = self.iwinfo.bitrate
1798         if rate and rate > 0 then
1799                 return (rate / 1000)
1800         end
1801 end
1802
1803 function wifinet.channel(self)
1804         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1805                 tonumber(self:get("channel"))
1806 end
1807
1808 function wifinet.signal(self)
1809         return self.iwinfo.signal or 0
1810 end
1811
1812 function wifinet.noise(self)
1813         return self.iwinfo.noise or 0
1814 end
1815
1816 function wifinet.country(self)
1817         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1818 end
1819
1820 function wifinet.txpower(self)
1821         local pwr = (self.iwinfo.txpower or 0)
1822         return pwr + self:txpower_offset()
1823 end
1824
1825 function wifinet.txpower_offset(self)
1826         return self.iwinfo.txpower_offset or 0
1827 end
1828
1829 function wifinet.signal_level(self, s, n)
1830         if self:active_bssid() ~= "00:00:00:00:00:00" then
1831                 local signal = s or self:signal()
1832                 local noise  = n or self:noise()
1833
1834                 if signal < 0 and noise < 0 then
1835                         local snr = -1 * (noise - signal)
1836                         return math.floor(snr / 5)
1837                 else
1838                         return 0
1839                 end
1840         else
1841                 return -1
1842         end
1843 end
1844
1845 function wifinet.signal_percent(self)
1846         local qc = self.iwinfo.quality or 0
1847         local qm = self.iwinfo.quality_max or 0
1848
1849         if qc > 0 and qm > 0 then
1850                 return math.floor((100 / qm) * qc)
1851         else
1852                 return 0
1853         end
1854 end
1855
1856 function wifinet.shortname(self)
1857         return "%s %q" %{
1858                 lng.translate(self:active_mode()),
1859                 self:active_ssid() or self:active_bssid() or self:id()
1860         }
1861 end
1862
1863 function wifinet.get_i18n(self)
1864         return "%s: %s %q (%s)" %{
1865                 lng.translate("Wireless Network"),
1866                 lng.translate(self:active_mode()),
1867                 self:active_ssid() or self:active_bssid() or self:id(),
1868                 self:ifname()
1869         }
1870 end
1871
1872 function wifinet.adminlink(self)
1873         local stat, dsp = pcall(require, "luci.dispatcher")
1874         return dsp and dsp.build_url("admin", "network", "wireless", self.netid)
1875 end
1876
1877 function wifinet.get_network(self)
1878         return self:get_networks()[1]
1879 end
1880
1881 function wifinet.get_networks(self)
1882         local nets = { }
1883         local net
1884         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1885                 if _uci:get("network", net) == "interface" then
1886                         nets[#nets+1] = network(net)
1887                 end
1888         end
1889         table.sort(nets, function(a, b) return a.sid < b.sid end)
1890         return nets
1891 end
1892
1893 function wifinet.get_interface(self)
1894         return interface(self:ifname())
1895 end
1896
1897
1898 -- setup base protocols
1899 _M:register_protocol("static")
1900 _M:register_protocol("dhcp")
1901 _M:register_protocol("none")
1902
1903 -- load protocol extensions
1904 local exts = nfs.dir(utl.libpath() .. "/model/network")
1905 if exts then
1906         local ext
1907         for ext in exts do
1908                 if ext:match("%.lua$") then
1909                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1910                 end
1911         end
1912 end