luci-base, luci-mod-admin-full: store backup vars in luci configuration
[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 = 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_netname(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" then
1005                                 rv[#rv+1] = "%s/%d" %{
1006                                         addr["local-address"].address,
1007                                         addr["local-address"].mask
1008                                 }
1009                         end
1010                 end
1011         end
1012
1013         return rv
1014 end
1015
1016 function protocol.gw6addr(self)
1017         local _, route
1018         for _, route in ipairs(self:_ubus("route") or { }) do
1019                 if route.target == "::" and route.mask == 0 then
1020                         return ipc.IPv6(route.nexthop):string()
1021                 end
1022         end
1023 end
1024
1025 function protocol.dns6addrs(self)
1026         local dns = { }
1027         local _, addr
1028         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
1029                 if addr:match(":") then
1030                         dns[#dns+1] = addr
1031                 end
1032         end
1033         return dns
1034 end
1035
1036 function protocol.ip6prefix(self)
1037         local prefix = self:_ubus("ipv6-prefix")
1038         if prefix and #prefix > 0 then
1039                 return "%s/%d" %{ prefix[1].address, prefix[1].mask }
1040         end
1041 end
1042
1043 function protocol.is_bridge(self)
1044         return (not self:is_virtual() and self:type() == "bridge")
1045 end
1046
1047 function protocol.opkg_package(self)
1048         return nil
1049 end
1050
1051 function protocol.is_installed(self)
1052         return true
1053 end
1054
1055 function protocol.is_virtual(self)
1056         return false
1057 end
1058
1059 function protocol.is_floating(self)
1060         return false
1061 end
1062
1063 function protocol.is_empty(self)
1064         if self:is_floating() then
1065                 return false
1066         else
1067                 local empty = true
1068
1069                 if (self:_get("ifname") or ""):match("%S+") then
1070                         empty = false
1071                 end
1072
1073                 if empty and _wifi_netid_by_netname(self.sid) then
1074                         empty = false
1075                 end
1076
1077                 return empty
1078         end
1079 end
1080
1081 function protocol.add_interface(self, ifname)
1082         ifname = _M:ifnameof(ifname)
1083         if ifname and not self:is_floating() then
1084                 -- if its a wifi interface, change its network option
1085                 local wif = _wifi_sid_by_ifname(ifname)
1086                 if wif then
1087                         _append("wireless", wif, "network", self.sid)
1088
1089                 -- add iface to our iface list
1090                 else
1091                         _append("network", self.sid, "ifname", ifname)
1092                 end
1093         end
1094 end
1095
1096 function protocol.del_interface(self, ifname)
1097         ifname = _M:ifnameof(ifname)
1098         if ifname and not self:is_floating() then
1099                 -- if its a wireless interface, clear its network option
1100                 local wif = _wifi_sid_by_ifname(ifname)
1101                 if wif then _filter("wireless", wif, "network", self.sid) end
1102
1103                 -- remove the interface
1104                 _filter("network", self.sid, "ifname", ifname)
1105         end
1106 end
1107
1108 function protocol.get_interface(self)
1109         if self:is_virtual() then
1110                 _tunnel[self:proto() .. "-" .. self.sid] = true
1111                 return interface(self:proto() .. "-" .. self.sid, self)
1112         elseif self:is_bridge() then
1113                 _bridge["br-" .. self.sid] = true
1114                 return interface("br-" .. self.sid, self)
1115         else
1116                 local ifn = nil
1117                 local num = { }
1118                 for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1119                         ifn = ifn:match("^[^:/]+")
1120                         return ifn and interface(ifn, self)
1121                 end
1122                 ifn = _wifi_netid_by_netname(self.sid)
1123                 return ifn and interface(ifn, self)
1124         end
1125 end
1126
1127 function protocol.get_interfaces(self)
1128         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
1129                 local ifaces = { }
1130
1131                 local ifn
1132                 local nfs = { }
1133                 for ifn in utl.imatch(self:get("ifname")) do
1134                         ifn = ifn:match("^[^:/]+")
1135                         nfs[ifn] = interface(ifn, self)
1136                 end
1137
1138                 for ifn in utl.kspairs(nfs) do
1139                         ifaces[#ifaces+1] = nfs[ifn]
1140                 end
1141
1142                 local wfs = { }
1143                 _uci:foreach("wireless", "wifi-iface",
1144                         function(s)
1145                                 if s.device then
1146                                         local net
1147                                         for net in utl.imatch(s.network) do
1148                                                 if net == self.sid then
1149                                                         ifn = _wifi_netid_by_sid(s[".name"])
1150                                                         if ifn then
1151                                                                 wfs[ifn] = interface(ifn, self)
1152                                                         end
1153                                                 end
1154                                         end
1155                                 end
1156                         end)
1157
1158                 for ifn in utl.kspairs(wfs) do
1159                         ifaces[#ifaces+1] = wfs[ifn]
1160                 end
1161
1162                 return ifaces
1163         end
1164 end
1165
1166 function protocol.contains_interface(self, ifname)
1167         ifname = _M:ifnameof(ifname)
1168         if not ifname then
1169                 return false
1170         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
1171                 return true
1172         elseif self:is_bridge() and "br-" .. self.sid == ifname then
1173                 return true
1174         else
1175                 local ifn
1176                 for ifn in utl.imatch(self:get("ifname")) do
1177                         ifn = ifn:match("[^:]+")
1178                         if ifn == ifname then
1179                                 return true
1180                         end
1181                 end
1182
1183                 local wif = _wifi_sid_by_ifname(ifname)
1184                 if wif then
1185                         local n
1186                         for n in utl.imatch(_uci:get("wireless", wif, "network")) do
1187                                 if n == self.sid then
1188                                         return true
1189                                 end
1190                         end
1191                 end
1192         end
1193
1194         return false
1195 end
1196
1197 function protocol.adminlink(self)
1198         local stat, dsp = pcall(require, "luci.dispatcher")
1199         return stat and dsp.build_url("admin", "network", "network", self.sid)
1200 end
1201
1202
1203 interface = utl.class()
1204
1205 function interface.__init__(self, ifname, network)
1206         local wif = _wifi_sid_by_ifname(ifname)
1207         if wif then
1208                 self.wif    = wifinet(wif)
1209                 self.ifname = self.wif:ifname()
1210         end
1211
1212         self.ifname  = self.ifname or ifname
1213         self.dev     = _interfaces[self.ifname]
1214         self.network = network
1215 end
1216
1217 function interface._ubus(self, field)
1218         if not _ubusdevcache[self.ifname] then
1219                 _ubusdevcache[self.ifname] = utl.ubus("network.device", "status",
1220                                                       { name = self.ifname })
1221         end
1222         if _ubusdevcache[self.ifname] and field then
1223                 return _ubusdevcache[self.ifname][field]
1224         end
1225         return _ubusdevcache[self.ifname]
1226 end
1227
1228 function interface.name(self)
1229         return self.wif and self.wif:ifname() or self.ifname
1230 end
1231
1232 function interface.mac(self)
1233         local mac = self:_ubus("macaddr")
1234         return mac and mac:upper()
1235 end
1236
1237 function interface.ipaddrs(self)
1238         return self.dev and self.dev.ipaddrs or { }
1239 end
1240
1241 function interface.ip6addrs(self)
1242         return self.dev and self.dev.ip6addrs or { }
1243 end
1244
1245 function interface.type(self)
1246         if self.wif or _wifi_iface(self.ifname) then
1247                 return "wifi"
1248         elseif _bridge[self.ifname] then
1249                 return "bridge"
1250         elseif _tunnel[self.ifname] then
1251                 return "tunnel"
1252         elseif self.ifname:match("%.") then
1253                 return "vlan"
1254         elseif _switch[self.ifname] then
1255                 return "switch"
1256         else
1257                 return "ethernet"
1258         end
1259 end
1260
1261 function interface.shortname(self)
1262         if self.wif then
1263                 return self.wif:shortname()
1264         else
1265                 return self.ifname
1266         end
1267 end
1268
1269 function interface.get_i18n(self)
1270         if self.wif then
1271                 return "%s: %s %q" %{
1272                         lng.translate("Wireless Network"),
1273                         self.wif:active_mode(),
1274                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
1275                 }
1276         else
1277                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1278         end
1279 end
1280
1281 function interface.get_type_i18n(self)
1282         local x = self:type()
1283         if x == "wifi" then
1284                 return lng.translate("Wireless Adapter")
1285         elseif x == "bridge" then
1286                 return lng.translate("Bridge")
1287         elseif x == "switch" then
1288                 return lng.translate("Ethernet Switch")
1289         elseif x == "vlan" then
1290                 if _switch[self.ifname] then
1291                         return lng.translate("Switch VLAN")
1292                 else
1293                         return lng.translate("Software VLAN")
1294                 end
1295         elseif x == "tunnel" then
1296                 return lng.translate("Tunnel Interface")
1297         else
1298                 return lng.translate("Ethernet Adapter")
1299         end
1300 end
1301
1302 function interface.adminlink(self)
1303         if self.wif then
1304                 return self.wif:adminlink()
1305         end
1306 end
1307
1308 function interface.ports(self)
1309         local members = self:_ubus("bridge-members")
1310         if members then
1311                 local _, iface
1312                 local ifaces = { }
1313                 for _, iface in ipairs(members) do
1314                         ifaces[#ifaces+1] = interface(iface)
1315                 end
1316         end
1317 end
1318
1319 function interface.bridge_id(self)
1320         if self.br then
1321                 return self.br.id
1322         else
1323                 return nil
1324         end
1325 end
1326
1327 function interface.bridge_stp(self)
1328         if self.br then
1329                 return self.br.stp
1330         else
1331                 return false
1332         end
1333 end
1334
1335 function interface.is_up(self)
1336         return self:_ubus("up") or false
1337 end
1338
1339 function interface.is_bridge(self)
1340         return (self:type() == "bridge")
1341 end
1342
1343 function interface.is_bridgeport(self)
1344         return self.dev and self.dev.bridge and true or false
1345 end
1346
1347 function interface.tx_bytes(self)
1348         local stat = self:_ubus("statistics")
1349         return stat and stat.tx_bytes or 0
1350 end
1351
1352 function interface.rx_bytes(self)
1353         local stat = self:_ubus("statistics")
1354         return stat and stat.rx_bytes or 0
1355 end
1356
1357 function interface.tx_packets(self)
1358         local stat = self:_ubus("statistics")
1359         return stat and stat.tx_packets or 0
1360 end
1361
1362 function interface.rx_packets(self)
1363         local stat = self:_ubus("statistics")
1364         return stat and stat.rx_packets or 0
1365 end
1366
1367 function interface.get_network(self)
1368         return self:get_networks()[1]
1369 end
1370
1371 function interface.get_networks(self)
1372         if not self.networks then
1373                 local nets = { }
1374                 local _, net
1375                 for _, net in ipairs(_M:get_networks()) do
1376                         if net:contains_interface(self.ifname) or
1377                            net:ifname() == self.ifname
1378                         then
1379                                 nets[#nets+1] = net
1380                         end
1381                 end
1382                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1383                 self.networks = nets
1384                 return nets
1385         else
1386                 return self.networks
1387         end
1388 end
1389
1390 function interface.get_wifinet(self)
1391         return self.wif
1392 end
1393
1394
1395 wifidev = utl.class()
1396
1397 function wifidev.__init__(self, name)
1398         local t, n = _uci:get("wireless", name)
1399         if t == "wifi-device" and n ~= nil then
1400                 self.sid    = n
1401                 self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true)
1402         end
1403         self.sid    = self.sid    or name
1404         self.iwinfo = self.iwinfo or { ifname = self.sid }
1405 end
1406
1407 function wifidev.get(self, opt)
1408         return _get("wireless", self.sid, opt)
1409 end
1410
1411 function wifidev.set(self, opt, val)
1412         return _set("wireless", self.sid, opt, val)
1413 end
1414
1415 function wifidev.name(self)
1416         return self.sid
1417 end
1418
1419 function wifidev.hwmodes(self)
1420         local l = self.iwinfo.hwmodelist
1421         if l and next(l) then
1422                 return l
1423         else
1424                 return { b = true, g = true }
1425         end
1426 end
1427
1428 function wifidev.get_i18n(self)
1429         local t = "Generic"
1430         if self.iwinfo.type == "wl" then
1431                 t = "Broadcom"
1432         end
1433
1434         local m = ""
1435         local l = self:hwmodes()
1436         if l.a then m = m .. "a" end
1437         if l.b then m = m .. "b" end
1438         if l.g then m = m .. "g" end
1439         if l.n then m = m .. "n" end
1440         if l.ac then m = "ac" end
1441
1442         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1443 end
1444
1445 function wifidev.is_up(self)
1446         if _ubuswificache[self.sid] then
1447                 return (_ubuswificache[self.sid].up == true)
1448         end
1449
1450         return false
1451 end
1452
1453 function wifidev.get_wifinet(self, net)
1454         if _uci:get("wireless", net) == "wifi-iface" then
1455                 return wifinet(net)
1456         else
1457                 local wnet = _wifi_sid_by_ifname(net)
1458                 if wnet then
1459                         return wifinet(wnet)
1460                 end
1461         end
1462 end
1463
1464 function wifidev.get_wifinets(self)
1465         local nets = { }
1466
1467         _uci:foreach("wireless", "wifi-iface",
1468                 function(s)
1469                         if s.device == self.sid then
1470                                 nets[#nets+1] = wifinet(s['.name'])
1471                         end
1472                 end)
1473
1474         return nets
1475 end
1476
1477 function wifidev.add_wifinet(self, options)
1478         options = options or { }
1479         options.device = self.sid
1480
1481         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1482         if wnet then
1483                 return wifinet(wnet, options)
1484         end
1485 end
1486
1487 function wifidev.del_wifinet(self, net)
1488         if utl.instanceof(net, wifinet) then
1489                 net = net.sid
1490         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1491                 net = _wifi_sid_by_ifname(net)
1492         end
1493
1494         if net and _uci:get("wireless", net, "device") == self.sid then
1495                 _uci:delete("wireless", net)
1496                 return true
1497         end
1498
1499         return false
1500 end
1501
1502
1503 wifinet = utl.class()
1504
1505 function wifinet.__init__(self, name, data)
1506         local sid, netid, radioname, radiostate, netstate
1507
1508         -- lookup state by radio#.network# notation
1509         sid = _wifi_sid_by_netid(name)
1510         if sid then
1511                 netid = name
1512                 radioname, radiostate, netstate = _wifi_state_by_sid(sid)
1513         else
1514                 -- lookup state by ifname (e.g. wlan0)
1515                 radioname, radiostate, netstate = _wifi_state_by_ifname(name)
1516                 if radioname and radiostate and netstate then
1517                         sid = netstate.section
1518                         netid = _wifi_netid_by_sid(sid)
1519                 else
1520                         -- lookup state by uci section id (e.g. cfg053579)
1521                         radioname, radiostate, netstate = _wifi_state_by_sid(name)
1522                         if radioname and radiostate and netstate then
1523                                 sid = name
1524                                 netid = _wifi_netid_by_sid(sid)
1525                         else
1526                                 -- no state available, try to resolve from uci
1527                                 netid, radioname = _wifi_netid_by_sid(name)
1528                                 if netid and radioname then
1529                                         sid = name
1530                                 end
1531                         end
1532                 end
1533         end
1534
1535         local iwinfo =
1536                 (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or
1537                 (radioname and _wifi_iwinfo_by_ifname(radioname)) or
1538                 { ifname = (netid or sid or name) }
1539
1540         self.sid       = sid or name
1541         self.wdev      = iwinfo.ifname
1542         self.iwinfo    = iwinfo
1543         self.netid     = netid
1544         self._ubusdata = {
1545                 radio = radioname,
1546                 dev   = radiostate,
1547                 net   = netstate
1548         }
1549 end
1550
1551 function wifinet.ubus(self, ...)
1552         local n, v = self._ubusdata
1553         for n = 1, select('#', ...) do
1554                 if type(v) == "table" then
1555                         v = v[select(n, ...)]
1556                 else
1557                         return nil
1558                 end
1559         end
1560         return v
1561 end
1562
1563 function wifinet.get(self, opt)
1564         return _get("wireless", self.sid, opt)
1565 end
1566
1567 function wifinet.set(self, opt, val)
1568         return _set("wireless", self.sid, opt, val)
1569 end
1570
1571 function wifinet.mode(self)
1572         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1573 end
1574
1575 function wifinet.ssid(self)
1576         return self:ubus("net", "config", "ssid") or self:get("ssid")
1577 end
1578
1579 function wifinet.bssid(self)
1580         return self:ubus("net", "config", "bssid") or self:get("bssid")
1581 end
1582
1583 function wifinet.network(self)
1584         local net, networks = nil, { }
1585         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1586                 networks[#networks+1] = net
1587         end
1588         return networks
1589 end
1590
1591 function wifinet.id(self)
1592         return self.netid
1593 end
1594
1595 function wifinet.name(self)
1596         return self.sid
1597 end
1598
1599 function wifinet.ifname(self)
1600         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1601         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1602                 ifname = self.wdev
1603         end
1604         return ifname
1605 end
1606
1607 function wifinet.get_device(self)
1608         local dev = self:ubus("radio") or self:get("device")
1609         return dev and wifidev(dev) or nil
1610 end
1611
1612 function wifinet.is_up(self)
1613         local ifc = self:get_interface()
1614         return (ifc and ifc:is_up() or false)
1615 end
1616
1617 function wifinet.active_mode(self)
1618         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1619
1620         if     m == "ap"      then m = "Master"
1621         elseif m == "sta"     then m = "Client"
1622         elseif m == "adhoc"   then m = "Ad-Hoc"
1623         elseif m == "mesh"    then m = "Mesh"
1624         elseif m == "monitor" then m = "Monitor"
1625         end
1626
1627         return m
1628 end
1629
1630 function wifinet.active_mode_i18n(self)
1631         return lng.translate(self:active_mode())
1632 end
1633
1634 function wifinet.active_ssid(self)
1635         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1636 end
1637
1638 function wifinet.active_bssid(self)
1639         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1640 end
1641
1642 function wifinet.active_encryption(self)
1643         local enc = self.iwinfo and self.iwinfo.encryption
1644         return enc and enc.description or "-"
1645 end
1646
1647 function wifinet.assoclist(self)
1648         return self.iwinfo.assoclist or { }
1649 end
1650
1651 function wifinet.frequency(self)
1652         local freq = self.iwinfo.frequency
1653         if freq and freq > 0 then
1654                 return "%.03f" % (freq / 1000)
1655         end
1656 end
1657
1658 function wifinet.bitrate(self)
1659         local rate = self.iwinfo.bitrate
1660         if rate and rate > 0 then
1661                 return (rate / 1000)
1662         end
1663 end
1664
1665 function wifinet.channel(self)
1666         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1667                 tonumber(self:get("channel"))
1668 end
1669
1670 function wifinet.signal(self)
1671         return self.iwinfo.signal or 0
1672 end
1673
1674 function wifinet.noise(self)
1675         return self.iwinfo.noise or 0
1676 end
1677
1678 function wifinet.country(self)
1679         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1680 end
1681
1682 function wifinet.txpower(self)
1683         local pwr = (self.iwinfo.txpower or 0)
1684         return pwr + self:txpower_offset()
1685 end
1686
1687 function wifinet.txpower_offset(self)
1688         return self.iwinfo.txpower_offset or 0
1689 end
1690
1691 function wifinet.signal_level(self, s, n)
1692         if self:active_bssid() ~= "00:00:00:00:00:00" then
1693                 local signal = s or self:signal()
1694                 local noise  = n or self:noise()
1695
1696                 if signal < 0 and noise < 0 then
1697                         local snr = -1 * (noise - signal)
1698                         return math.floor(snr / 5)
1699                 else
1700                         return 0
1701                 end
1702         else
1703                 return -1
1704         end
1705 end
1706
1707 function wifinet.signal_percent(self)
1708         local qc = self.iwinfo.quality or 0
1709         local qm = self.iwinfo.quality_max or 0
1710
1711         if qc > 0 and qm > 0 then
1712                 return math.floor((100 / qm) * qc)
1713         else
1714                 return 0
1715         end
1716 end
1717
1718 function wifinet.shortname(self)
1719         return "%s %q" %{
1720                 lng.translate(self:active_mode()),
1721                 self:active_ssid() or self:active_bssid() or self:id()
1722         }
1723 end
1724
1725 function wifinet.get_i18n(self)
1726         return "%s: %s %q (%s)" %{
1727                 lng.translate("Wireless Network"),
1728                 lng.translate(self:active_mode()),
1729                 self:active_ssid() or self:active_bssid() or self:id(),
1730                 self:ifname()
1731         }
1732 end
1733
1734 function wifinet.adminlink(self)
1735         local stat, dsp = pcall(require, "luci.dispatcher")
1736         return dsp and dsp.build_url("admin", "network", "wireless", self.netid)
1737 end
1738
1739 function wifinet.get_network(self)
1740         return self:get_networks()[1]
1741 end
1742
1743 function wifinet.get_networks(self)
1744         local nets = { }
1745         local net
1746         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1747                 if _uci:get("network", net) == "interface" then
1748                         nets[#nets+1] = network(net)
1749                 end
1750         end
1751         table.sort(nets, function(a, b) return a.sid < b.sid end)
1752         return nets
1753 end
1754
1755 function wifinet.get_interface(self)
1756         return interface(self:ifname())
1757 end
1758
1759
1760 -- setup base protocols
1761 _M:register_protocol("static")
1762 _M:register_protocol("dhcp")
1763 _M:register_protocol("none")
1764
1765 -- load protocol extensions
1766 local exts = nfs.dir(utl.libpath() .. "/model/network")
1767 if exts then
1768         local ext
1769         for ext in exts do
1770                 if ext:match("%.lua$") then
1771                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1772                 end
1773         end
1774 end