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