Merge pull request #1662 from dibdot/travelmate
[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" 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         local mac = self:_ubus("macaddr")
1237         return mac and mac:upper()
1238 end
1239
1240 function interface.ipaddrs(self)
1241         return self.dev and self.dev.ipaddrs or { }
1242 end
1243
1244 function interface.ip6addrs(self)
1245         return self.dev and self.dev.ip6addrs or { }
1246 end
1247
1248 function interface.type(self)
1249         if self.wif or _wifi_iface(self.ifname) then
1250                 return "wifi"
1251         elseif _bridge[self.ifname] then
1252                 return "bridge"
1253         elseif _tunnel[self.ifname] then
1254                 return "tunnel"
1255         elseif self.ifname:match("%.") then
1256                 return "vlan"
1257         elseif _switch[self.ifname] then
1258                 return "switch"
1259         else
1260                 return "ethernet"
1261         end
1262 end
1263
1264 function interface.shortname(self)
1265         if self.wif then
1266                 return self.wif:shortname()
1267         else
1268                 return self.ifname
1269         end
1270 end
1271
1272 function interface.get_i18n(self)
1273         if self.wif then
1274                 return "%s: %s %q" %{
1275                         lng.translate("Wireless Network"),
1276                         self.wif:active_mode(),
1277                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
1278                 }
1279         else
1280                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1281         end
1282 end
1283
1284 function interface.get_type_i18n(self)
1285         local x = self:type()
1286         if x == "wifi" then
1287                 return lng.translate("Wireless Adapter")
1288         elseif x == "bridge" then
1289                 return lng.translate("Bridge")
1290         elseif x == "switch" then
1291                 return lng.translate("Ethernet Switch")
1292         elseif x == "vlan" then
1293                 if _switch[self.ifname] then
1294                         return lng.translate("Switch VLAN")
1295                 else
1296                         return lng.translate("Software VLAN")
1297                 end
1298         elseif x == "tunnel" then
1299                 return lng.translate("Tunnel Interface")
1300         else
1301                 return lng.translate("Ethernet Adapter")
1302         end
1303 end
1304
1305 function interface.adminlink(self)
1306         if self.wif then
1307                 return self.wif:adminlink()
1308         end
1309 end
1310
1311 function interface.ports(self)
1312         local members = self:_ubus("bridge-members")
1313         if members then
1314                 local _, iface
1315                 local ifaces = { }
1316                 for _, iface in ipairs(members) do
1317                         ifaces[#ifaces+1] = interface(iface)
1318                 end
1319         end
1320 end
1321
1322 function interface.bridge_id(self)
1323         if self.br then
1324                 return self.br.id
1325         else
1326                 return nil
1327         end
1328 end
1329
1330 function interface.bridge_stp(self)
1331         if self.br then
1332                 return self.br.stp
1333         else
1334                 return false
1335         end
1336 end
1337
1338 function interface.is_up(self)
1339         return self:_ubus("up") or false
1340 end
1341
1342 function interface.is_bridge(self)
1343         return (self:type() == "bridge")
1344 end
1345
1346 function interface.is_bridgeport(self)
1347         return self.dev and self.dev.bridge and true or false
1348 end
1349
1350 function interface.tx_bytes(self)
1351         local stat = self:_ubus("statistics")
1352         return stat and stat.tx_bytes or 0
1353 end
1354
1355 function interface.rx_bytes(self)
1356         local stat = self:_ubus("statistics")
1357         return stat and stat.rx_bytes or 0
1358 end
1359
1360 function interface.tx_packets(self)
1361         local stat = self:_ubus("statistics")
1362         return stat and stat.tx_packets or 0
1363 end
1364
1365 function interface.rx_packets(self)
1366         local stat = self:_ubus("statistics")
1367         return stat and stat.rx_packets or 0
1368 end
1369
1370 function interface.get_network(self)
1371         return self:get_networks()[1]
1372 end
1373
1374 function interface.get_networks(self)
1375         if not self.networks then
1376                 local nets = { }
1377                 local _, net
1378                 for _, net in ipairs(_M:get_networks()) do
1379                         if net:contains_interface(self.ifname) or
1380                            net:ifname() == self.ifname
1381                         then
1382                                 nets[#nets+1] = net
1383                         end
1384                 end
1385                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1386                 self.networks = nets
1387                 return nets
1388         else
1389                 return self.networks
1390         end
1391 end
1392
1393 function interface.get_wifinet(self)
1394         return self.wif
1395 end
1396
1397
1398 wifidev = utl.class()
1399
1400 function wifidev.__init__(self, name)
1401         local t, n = _uci:get("wireless", name)
1402         if t == "wifi-device" and n ~= nil then
1403                 self.sid    = n
1404                 self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true)
1405         end
1406         self.sid    = self.sid    or name
1407         self.iwinfo = self.iwinfo or { ifname = self.sid }
1408 end
1409
1410 function wifidev.get(self, opt)
1411         return _get("wireless", self.sid, opt)
1412 end
1413
1414 function wifidev.set(self, opt, val)
1415         return _set("wireless", self.sid, opt, val)
1416 end
1417
1418 function wifidev.name(self)
1419         return self.sid
1420 end
1421
1422 function wifidev.hwmodes(self)
1423         local l = self.iwinfo.hwmodelist
1424         if l and next(l) then
1425                 return l
1426         else
1427                 return { b = true, g = true }
1428         end
1429 end
1430
1431 function wifidev.get_i18n(self)
1432         local t = "Generic"
1433         if self.iwinfo.type == "wl" then
1434                 t = "Broadcom"
1435         end
1436
1437         local m = ""
1438         local l = self:hwmodes()
1439         if l.a then m = m .. "a" end
1440         if l.b then m = m .. "b" end
1441         if l.g then m = m .. "g" end
1442         if l.n then m = m .. "n" end
1443         if l.ac then m = "ac" end
1444
1445         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1446 end
1447
1448 function wifidev.is_up(self)
1449         if _ubuswificache[self.sid] then
1450                 return (_ubuswificache[self.sid].up == true)
1451         end
1452
1453         return false
1454 end
1455
1456 function wifidev.get_wifinet(self, net)
1457         if _uci:get("wireless", net) == "wifi-iface" then
1458                 return wifinet(net)
1459         else
1460                 local wnet = _wifi_sid_by_ifname(net)
1461                 if wnet then
1462                         return wifinet(wnet)
1463                 end
1464         end
1465 end
1466
1467 function wifidev.get_wifinets(self)
1468         local nets = { }
1469
1470         _uci:foreach("wireless", "wifi-iface",
1471                 function(s)
1472                         if s.device == self.sid then
1473                                 nets[#nets+1] = wifinet(s['.name'])
1474                         end
1475                 end)
1476
1477         return nets
1478 end
1479
1480 function wifidev.add_wifinet(self, options)
1481         options = options or { }
1482         options.device = self.sid
1483
1484         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1485         if wnet then
1486                 return wifinet(wnet, options)
1487         end
1488 end
1489
1490 function wifidev.del_wifinet(self, net)
1491         if utl.instanceof(net, wifinet) then
1492                 net = net.sid
1493         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1494                 net = _wifi_sid_by_ifname(net)
1495         end
1496
1497         if net and _uci:get("wireless", net, "device") == self.sid then
1498                 _uci:delete("wireless", net)
1499                 return true
1500         end
1501
1502         return false
1503 end
1504
1505
1506 wifinet = utl.class()
1507
1508 function wifinet.__init__(self, name, data)
1509         local sid, netid, radioname, radiostate, netstate
1510
1511         -- lookup state by radio#.network# notation
1512         sid = _wifi_sid_by_netid(name)
1513         if sid then
1514                 netid = name
1515                 radioname, radiostate, netstate = _wifi_state_by_sid(sid)
1516         else
1517                 -- lookup state by ifname (e.g. wlan0)
1518                 radioname, radiostate, netstate = _wifi_state_by_ifname(name)
1519                 if radioname and radiostate and netstate then
1520                         sid = netstate.section
1521                         netid = _wifi_netid_by_sid(sid)
1522                 else
1523                         -- lookup state by uci section id (e.g. cfg053579)
1524                         radioname, radiostate, netstate = _wifi_state_by_sid(name)
1525                         if radioname and radiostate and netstate then
1526                                 sid = name
1527                                 netid = _wifi_netid_by_sid(sid)
1528                         else
1529                                 -- no state available, try to resolve from uci
1530                                 netid, radioname = _wifi_netid_by_sid(name)
1531                                 if netid and radioname then
1532                                         sid = name
1533                                 end
1534                         end
1535                 end
1536         end
1537
1538         local iwinfo =
1539                 (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or
1540                 (radioname and _wifi_iwinfo_by_ifname(radioname)) or
1541                 { ifname = (netid or sid or name) }
1542
1543         self.sid       = sid or name
1544         self.wdev      = iwinfo.ifname
1545         self.iwinfo    = iwinfo
1546         self.netid     = netid
1547         self._ubusdata = {
1548                 radio = radioname,
1549                 dev   = radiostate,
1550                 net   = netstate
1551         }
1552 end
1553
1554 function wifinet.ubus(self, ...)
1555         local n, v = self._ubusdata
1556         for n = 1, select('#', ...) do
1557                 if type(v) == "table" then
1558                         v = v[select(n, ...)]
1559                 else
1560                         return nil
1561                 end
1562         end
1563         return v
1564 end
1565
1566 function wifinet.get(self, opt)
1567         return _get("wireless", self.sid, opt)
1568 end
1569
1570 function wifinet.set(self, opt, val)
1571         return _set("wireless", self.sid, opt, val)
1572 end
1573
1574 function wifinet.mode(self)
1575         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1576 end
1577
1578 function wifinet.ssid(self)
1579         return self:ubus("net", "config", "ssid") or self:get("ssid")
1580 end
1581
1582 function wifinet.bssid(self)
1583         return self:ubus("net", "config", "bssid") or self:get("bssid")
1584 end
1585
1586 function wifinet.network(self)
1587         local net, networks = nil, { }
1588         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1589                 networks[#networks+1] = net
1590         end
1591         return networks
1592 end
1593
1594 function wifinet.id(self)
1595         return self.netid
1596 end
1597
1598 function wifinet.name(self)
1599         return self.sid
1600 end
1601
1602 function wifinet.ifname(self)
1603         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1604         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1605                 ifname = self.wdev
1606         end
1607         return ifname
1608 end
1609
1610 function wifinet.get_device(self)
1611         local dev = self:ubus("radio") or self:get("device")
1612         return dev and wifidev(dev) or nil
1613 end
1614
1615 function wifinet.is_up(self)
1616         local ifc = self:get_interface()
1617         return (ifc and ifc:is_up() or false)
1618 end
1619
1620 function wifinet.active_mode(self)
1621         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1622
1623         if     m == "ap"      then m = "Master"
1624         elseif m == "sta"     then m = "Client"
1625         elseif m == "adhoc"   then m = "Ad-Hoc"
1626         elseif m == "mesh"    then m = "Mesh"
1627         elseif m == "monitor" then m = "Monitor"
1628         end
1629
1630         return m
1631 end
1632
1633 function wifinet.active_mode_i18n(self)
1634         return lng.translate(self:active_mode())
1635 end
1636
1637 function wifinet.active_ssid(self)
1638         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1639 end
1640
1641 function wifinet.active_bssid(self)
1642         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1643 end
1644
1645 function wifinet.active_encryption(self)
1646         local enc = self.iwinfo and self.iwinfo.encryption
1647         return enc and enc.description or "-"
1648 end
1649
1650 function wifinet.assoclist(self)
1651         return self.iwinfo.assoclist or { }
1652 end
1653
1654 function wifinet.frequency(self)
1655         local freq = self.iwinfo.frequency
1656         if freq and freq > 0 then
1657                 return "%.03f" % (freq / 1000)
1658         end
1659 end
1660
1661 function wifinet.bitrate(self)
1662         local rate = self.iwinfo.bitrate
1663         if rate and rate > 0 then
1664                 return (rate / 1000)
1665         end
1666 end
1667
1668 function wifinet.channel(self)
1669         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1670                 tonumber(self:get("channel"))
1671 end
1672
1673 function wifinet.signal(self)
1674         return self.iwinfo.signal or 0
1675 end
1676
1677 function wifinet.noise(self)
1678         return self.iwinfo.noise or 0
1679 end
1680
1681 function wifinet.country(self)
1682         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1683 end
1684
1685 function wifinet.txpower(self)
1686         local pwr = (self.iwinfo.txpower or 0)
1687         return pwr + self:txpower_offset()
1688 end
1689
1690 function wifinet.txpower_offset(self)
1691         return self.iwinfo.txpower_offset or 0
1692 end
1693
1694 function wifinet.signal_level(self, s, n)
1695         if self:active_bssid() ~= "00:00:00:00:00:00" then
1696                 local signal = s or self:signal()
1697                 local noise  = n or self:noise()
1698
1699                 if signal < 0 and noise < 0 then
1700                         local snr = -1 * (noise - signal)
1701                         return math.floor(snr / 5)
1702                 else
1703                         return 0
1704                 end
1705         else
1706                 return -1
1707         end
1708 end
1709
1710 function wifinet.signal_percent(self)
1711         local qc = self.iwinfo.quality or 0
1712         local qm = self.iwinfo.quality_max or 0
1713
1714         if qc > 0 and qm > 0 then
1715                 return math.floor((100 / qm) * qc)
1716         else
1717                 return 0
1718         end
1719 end
1720
1721 function wifinet.shortname(self)
1722         return "%s %q" %{
1723                 lng.translate(self:active_mode()),
1724                 self:active_ssid() or self:active_bssid() or self:id()
1725         }
1726 end
1727
1728 function wifinet.get_i18n(self)
1729         return "%s: %s %q (%s)" %{
1730                 lng.translate("Wireless Network"),
1731                 lng.translate(self:active_mode()),
1732                 self:active_ssid() or self:active_bssid() or self:id(),
1733                 self:ifname()
1734         }
1735 end
1736
1737 function wifinet.adminlink(self)
1738         local stat, dsp = pcall(require, "luci.dispatcher")
1739         return dsp and dsp.build_url("admin", "network", "wireless", self.netid)
1740 end
1741
1742 function wifinet.get_network(self)
1743         return self:get_networks()[1]
1744 end
1745
1746 function wifinet.get_networks(self)
1747         local nets = { }
1748         local net
1749         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1750                 if _uci:get("network", net) == "interface" then
1751                         nets[#nets+1] = network(net)
1752                 end
1753         end
1754         table.sort(nets, function(a, b) return a.sid < b.sid end)
1755         return nets
1756 end
1757
1758 function wifinet.get_interface(self)
1759         return interface(self:ifname())
1760 end
1761
1762
1763 -- setup base protocols
1764 _M:register_protocol("static")
1765 _M:register_protocol("dhcp")
1766 _M:register_protocol("none")
1767
1768 -- load protocol extensions
1769 local exts = nfs.dir(utl.libpath() .. "/model/network")
1770 if exts then
1771         local ext
1772         for ext in exts do
1773                 if ext:match("%.lua$") then
1774                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1775                 end
1776         end
1777 end