Merge pull request #1524 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("network", "alias",
547                         function(s) return (s.interface == n) end)
548
549                 _uci:delete_all("network", "route",
550                         function(s) return (s.interface == n) end)
551
552                 _uci:delete_all("network", "route6",
553                         function(s) return (s.interface == n) end)
554
555                 _uci:foreach("wireless", "wifi-iface",
556                         function(s)
557                                 local net
558                                 local rest = { }
559                                 for net in utl.imatch(s.network) do
560                                         if net ~= n then
561                                                 rest[#rest+1] = net
562                                         end
563                                 end
564                                 if #rest > 0 then
565                                         _uci:set("wireless", s['.name'], "network",
566                                                       table.concat(rest, " "))
567                                 else
568                                         _uci:delete("wireless", s['.name'], "network")
569                                 end
570                         end)
571         end
572         return r
573 end
574
575 function rename_network(self, old, new)
576         local r
577         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
578                 r = _uci:section("network", "interface", new, _uci:get_all("network", old))
579
580                 if r then
581                         _uci:foreach("network", "alias",
582                                 function(s)
583                                         if s.interface == old then
584                                                 _uci:set("network", s['.name'], "interface", new)
585                                         end
586                                 end)
587
588                         _uci:foreach("network", "route",
589                                 function(s)
590                                         if s.interface == old then
591                                                 _uci:set("network", s['.name'], "interface", new)
592                                         end
593                                 end)
594
595                         _uci:foreach("network", "route6",
596                                 function(s)
597                                         if s.interface == old then
598                                                 _uci:set("network", s['.name'], "interface", new)
599                                         end
600                                 end)
601
602                         _uci:foreach("wireless", "wifi-iface",
603                                 function(s)
604                                         local net
605                                         local list = { }
606                                         for net in utl.imatch(s.network) do
607                                                 if net == old then
608                                                         list[#list+1] = new
609                                                 else
610                                                         list[#list+1] = net
611                                                 end
612                                         end
613                                         if #list > 0 then
614                                                 _uci:set("wireless", s['.name'], "network",
615                                                               table.concat(list, " "))
616                                         end
617                                 end)
618
619                         _uci:delete("network", old)
620                 end
621         end
622         return r or false
623 end
624
625 function get_interface(self, i)
626         if _interfaces[i] or _wifi_iface(i) then
627                 return interface(i)
628         else
629                 local netid = _wifi_netid_by_netname(i)
630                 return netid and interface(netid)
631         end
632 end
633
634 function get_interfaces(self)
635         local iface
636         local ifaces = { }
637         local nfs = { }
638
639         -- find normal interfaces
640         _uci:foreach("network", "interface",
641                 function(s)
642                         for iface in utl.imatch(s.ifname) do
643                                 if not _iface_ignore(iface) and not _iface_virtual(iface) and not _wifi_iface(iface) then
644                                         nfs[iface] = interface(iface)
645                                 end
646                         end
647                 end)
648
649         for iface in utl.kspairs(_interfaces) do
650                 if not (nfs[iface] or _iface_ignore(iface) or _iface_virtual(iface) or _wifi_iface(iface)) then
651                         nfs[iface] = interface(iface)
652                 end
653         end
654
655         -- find vlan interfaces
656         _uci:foreach("network", "switch_vlan",
657                 function(s)
658                         if type(s.ports) ~= "string" or
659                            type(s.device) ~= "string" or
660                            type(_swtopo[s.device]) ~= "table"
661                         then
662                                 return
663                         end
664
665                         local pnum, ptag
666                         for pnum, ptag in s.ports:gmatch("(%d+)([tu]?)") do
667                                 local netdev = _swtopo[s.device].netdevs[pnum]
668                                 if netdev then
669                                         if not nfs[netdev] then
670                                                 nfs[netdev] = interface(netdev)
671                                         end
672                                         _switch[netdev] = true
673
674                                         if ptag == "t" then
675                                                 local vid = tonumber(s.vid or s.vlan)
676                                                 if vid ~= nil and vid >= 0 and vid <= 4095 then
677                                                         local iface = "%s.%d" %{ netdev, vid }
678                                                         if not nfs[iface] then
679                                                                 nfs[iface] = interface(iface)
680                                                         end
681                                                         _switch[iface] = true
682                                                 end
683                                         end
684                                 end
685                         end
686                 end)
687
688         for iface in utl.kspairs(nfs) do
689                 ifaces[#ifaces+1] = nfs[iface]
690         end
691
692         -- find wifi interfaces
693         local num = { }
694         local wfs = { }
695         _uci:foreach("wireless", "wifi-iface",
696                 function(s)
697                         if s.device then
698                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
699                                 local i = "%s.network%d" %{ s.device, num[s.device] }
700                                 wfs[i] = interface(i)
701                         end
702                 end)
703
704         for iface in utl.kspairs(wfs) do
705                 ifaces[#ifaces+1] = wfs[iface]
706         end
707
708         return ifaces
709 end
710
711 function ignore_interface(self, x)
712         return _iface_ignore(x)
713 end
714
715 function get_wifidev(self, dev)
716         if _uci:get("wireless", dev) == "wifi-device" then
717                 return wifidev(dev)
718         end
719 end
720
721 function get_wifidevs(self)
722         local devs = { }
723         local wfd  = { }
724
725         _uci:foreach("wireless", "wifi-device",
726                 function(s) wfd[#wfd+1] = s['.name'] end)
727
728         local dev
729         for _, dev in utl.vspairs(wfd) do
730                 devs[#devs+1] = wifidev(dev)
731         end
732
733         return devs
734 end
735
736 function get_wifinet(self, net)
737         local wnet = _wifi_sid_by_ifname(net)
738         if wnet then
739                 return wifinet(wnet)
740         end
741 end
742
743 function add_wifinet(self, net, options)
744         if type(options) == "table" and options.device and
745                 _uci:get("wireless", options.device) == "wifi-device"
746         then
747                 local wnet = _uci:section("wireless", "wifi-iface", nil, options)
748                 return wifinet(wnet)
749         end
750 end
751
752 function del_wifinet(self, net)
753         local wnet = _wifi_sid_by_ifname(net)
754         if wnet then
755                 _uci:delete("wireless", wnet)
756                 return true
757         end
758         return false
759 end
760
761 function get_status_by_route(self, addr, mask)
762         local _, object
763         for _, object in ipairs(utl.ubus()) do
764                 local net = object:match("^network%.interface%.(.+)")
765                 if net then
766                         local s = utl.ubus(object, "status", {})
767                         if s and s.route then
768                                 local rt
769                                 for _, rt in ipairs(s.route) do
770                                         if not rt.table and rt.target == addr and rt.mask == mask then
771                                                 return net, s
772                                         end
773                                 end
774                         end
775                 end
776         end
777 end
778
779 function get_status_by_address(self, addr)
780         local _, object
781         for _, object in ipairs(utl.ubus()) do
782                 local net = object:match("^network%.interface%.(.+)")
783                 if net then
784                         local s = utl.ubus(object, "status", {})
785                         if s and s['ipv4-address'] then
786                                 local a
787                                 for _, a in ipairs(s['ipv4-address']) do
788                                         if a.address == addr then
789                                                 return net, s
790                                         end
791                                 end
792                         end
793                         if s and s['ipv6-address'] then
794                                 local a
795                                 for _, a in ipairs(s['ipv6-address']) do
796                                         if a.address == addr then
797                                                 return net, s
798                                         end
799                                 end
800                         end
801                 end
802         end
803 end
804
805 function get_wannet(self)
806         local net, stat = self:get_status_by_route("0.0.0.0", 0)
807         return net and network(net, stat.proto)
808 end
809
810 function get_wandev(self)
811         local _, stat = self:get_status_by_route("0.0.0.0", 0)
812         return stat and interface(stat.l3_device or stat.device)
813 end
814
815 function get_wan6net(self)
816         local net, stat = self:get_status_by_route("::", 0)
817         return net and network(net, stat.proto)
818 end
819
820 function get_wan6dev(self)
821         local _, stat = self:get_status_by_route("::", 0)
822         return stat and interface(stat.l3_device or stat.device)
823 end
824
825 function get_switch_topologies(self)
826         return _swtopo
827 end
828
829
830 function network(name, proto)
831         if name then
832                 local p = proto or _uci:get("network", name, "proto")
833                 local c = p and _protocols[p] or protocol
834                 return c(name)
835         end
836 end
837
838 function protocol.__init__(self, name)
839         self.sid = name
840 end
841
842 function protocol._get(self, opt)
843         local v = _uci:get("network", self.sid, opt)
844         if type(v) == "table" then
845                 return table.concat(v, " ")
846         end
847         return v or ""
848 end
849
850 function protocol._ubus(self, field)
851         if not _ubusnetcache[self.sid] then
852                 _ubusnetcache[self.sid] = utl.ubus("network.interface.%s" % self.sid,
853                                                    "status", { })
854         end
855         if _ubusnetcache[self.sid] and field then
856                 return _ubusnetcache[self.sid][field]
857         end
858         return _ubusnetcache[self.sid]
859 end
860
861 function protocol.get(self, opt)
862         return _get("network", self.sid, opt)
863 end
864
865 function protocol.set(self, opt, val)
866         return _set("network", self.sid, opt, val)
867 end
868
869 function protocol.ifname(self)
870         local ifname
871         if self:is_floating() then
872                 ifname = self:_ubus("l3_device")
873         else
874                 ifname = self:_ubus("device")
875         end
876         if not ifname then
877                 ifname = _wifi_netid_by_netname(self.sid)
878         end
879         return ifname
880 end
881
882 function protocol.proto(self)
883         return "none"
884 end
885
886 function protocol.get_i18n(self)
887         local p = self:proto()
888         if p == "none" then
889                 return lng.translate("Unmanaged")
890         elseif p == "static" then
891                 return lng.translate("Static address")
892         elseif p == "dhcp" then
893                 return lng.translate("DHCP client")
894         else
895                 return lng.translate("Unknown")
896         end
897 end
898
899 function protocol.type(self)
900         return self:_get("type")
901 end
902
903 function protocol.name(self)
904         return self.sid
905 end
906
907 function protocol.uptime(self)
908         return self:_ubus("uptime") or 0
909 end
910
911 function protocol.expires(self)
912         local u = self:_ubus("uptime")
913         local d = self:_ubus("data")
914
915         if type(u) == "number" and type(d) == "table" and
916            type(d.leasetime) == "number"
917         then
918                 local r = (d.leasetime - (u % d.leasetime))
919                 return r > 0 and r or 0
920         end
921
922         return -1
923 end
924
925 function protocol.metric(self)
926         return self:_ubus("metric") or 0
927 end
928
929 function protocol.ipaddr(self)
930         local addrs = self:_ubus("ipv4-address")
931         return addrs and #addrs > 0 and addrs[1].address
932 end
933
934 function protocol.ipaddrs(self)
935         local addrs = self:_ubus("ipv4-address")
936         local rv = { }
937
938         if type(addrs) == "table" then
939                 local n, addr
940                 for n, addr in ipairs(addrs) do
941                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
942                 end
943         end
944
945         return rv
946 end
947
948 function protocol.netmask(self)
949         local addrs = self:_ubus("ipv4-address")
950         return addrs and #addrs > 0 and
951                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
952 end
953
954 function protocol.gwaddr(self)
955         local _, route
956         for _, route in ipairs(self:_ubus("route") or { }) do
957                 if route.target == "0.0.0.0" and route.mask == 0 then
958                         return route.nexthop
959                 end
960         end
961 end
962
963 function protocol.dnsaddrs(self)
964         local dns = { }
965         local _, addr
966         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
967                 if not addr:match(":") then
968                         dns[#dns+1] = addr
969                 end
970         end
971         return dns
972 end
973
974 function protocol.ip6addr(self)
975         local addrs = self:_ubus("ipv6-address")
976         if addrs and #addrs > 0 then
977                 return "%s/%d" %{ addrs[1].address, addrs[1].mask }
978         else
979                 addrs = self:_ubus("ipv6-prefix-assignment")
980                 if addrs and #addrs > 0 then
981                         return "%s/%d" %{ addrs[1].address, addrs[1].mask }
982                 end
983         end
984 end
985
986 function protocol.ip6addrs(self)
987         local addrs = self:_ubus("ipv6-address")
988         local rv = { }
989         local n, addr
990
991         if type(addrs) == "table" then
992                 for n, addr in ipairs(addrs) do
993                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
994                 end
995         end
996
997         addrs = self:_ubus("ipv6-prefix-assignment")
998
999         if type(addrs) == "table" then
1000                 for n, addr in ipairs(addrs) do
1001                         rv[#rv+1] = "%s1/%d" %{ addr.address, addr.mask }
1002                 end
1003         end
1004
1005         return rv
1006 end
1007
1008 function protocol.gw6addr(self)
1009         local _, route
1010         for _, route in ipairs(self:_ubus("route") or { }) do
1011                 if route.target == "::" and route.mask == 0 then
1012                         return ipc.IPv6(route.nexthop):string()
1013                 end
1014         end
1015 end
1016
1017 function protocol.dns6addrs(self)
1018         local dns = { }
1019         local _, addr
1020         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
1021                 if addr:match(":") then
1022                         dns[#dns+1] = addr
1023                 end
1024         end
1025         return dns
1026 end
1027
1028 function protocol.ip6prefix(self)
1029         local prefix = self:_ubus("ipv6-prefix")
1030         if prefix and #prefix > 0 then
1031                 return "%s/%d" %{ prefix[1].address, prefix[1].mask }
1032         end
1033 end
1034
1035 function protocol.is_bridge(self)
1036         return (not self:is_virtual() and self:type() == "bridge")
1037 end
1038
1039 function protocol.opkg_package(self)
1040         return nil
1041 end
1042
1043 function protocol.is_installed(self)
1044         return true
1045 end
1046
1047 function protocol.is_virtual(self)
1048         return false
1049 end
1050
1051 function protocol.is_floating(self)
1052         return false
1053 end
1054
1055 function protocol.is_empty(self)
1056         if self:is_floating() then
1057                 return false
1058         else
1059                 local empty = true
1060
1061                 if (self:_get("ifname") or ""):match("%S+") then
1062                         empty = false
1063                 end
1064
1065                 if empty and _wifi_netid_by_netname(self.sid) then
1066                         empty = false
1067                 end
1068
1069                 return empty
1070         end
1071 end
1072
1073 function protocol.add_interface(self, ifname)
1074         ifname = _M:ifnameof(ifname)
1075         if ifname and not self:is_floating() then
1076                 -- if its a wifi interface, change its network option
1077                 local wif = _wifi_sid_by_ifname(ifname)
1078                 if wif then
1079                         _append("wireless", wif, "network", self.sid)
1080
1081                 -- add iface to our iface list
1082                 else
1083                         _append("network", self.sid, "ifname", ifname)
1084                 end
1085         end
1086 end
1087
1088 function protocol.del_interface(self, ifname)
1089         ifname = _M:ifnameof(ifname)
1090         if ifname and not self:is_floating() then
1091                 -- if its a wireless interface, clear its network option
1092                 local wif = _wifi_sid_by_ifname(ifname)
1093                 if wif then _filter("wireless", wif, "network", self.sid) end
1094
1095                 -- remove the interface
1096                 _filter("network", self.sid, "ifname", ifname)
1097         end
1098 end
1099
1100 function protocol.get_interface(self)
1101         if self:is_virtual() then
1102                 _tunnel[self:proto() .. "-" .. self.sid] = true
1103                 return interface(self:proto() .. "-" .. self.sid, self)
1104         elseif self:is_bridge() then
1105                 _bridge["br-" .. self.sid] = true
1106                 return interface("br-" .. self.sid, self)
1107         else
1108                 local ifn = nil
1109                 local num = { }
1110                 for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1111                         ifn = ifn:match("^[^:/]+")
1112                         return ifn and interface(ifn, self)
1113                 end
1114                 ifn = _wifi_netid_by_netname(self.sid)
1115                 return ifn and interface(ifn, self)
1116         end
1117 end
1118
1119 function protocol.get_interfaces(self)
1120         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
1121                 local ifaces = { }
1122
1123                 local ifn
1124                 local nfs = { }
1125                 for ifn in utl.imatch(self:get("ifname")) do
1126                         ifn = ifn:match("^[^:/]+")
1127                         nfs[ifn] = interface(ifn, self)
1128                 end
1129
1130                 for ifn in utl.kspairs(nfs) do
1131                         ifaces[#ifaces+1] = nfs[ifn]
1132                 end
1133
1134                 local wfs = { }
1135                 _uci:foreach("wireless", "wifi-iface",
1136                         function(s)
1137                                 if s.device then
1138                                         local net
1139                                         for net in utl.imatch(s.network) do
1140                                                 if net == self.sid then
1141                                                         ifn = _wifi_netid_by_sid(s[".name"])
1142                                                         if ifn then
1143                                                                 wfs[ifn] = interface(ifn, self)
1144                                                         end
1145                                                 end
1146                                         end
1147                                 end
1148                         end)
1149
1150                 for ifn in utl.kspairs(wfs) do
1151                         ifaces[#ifaces+1] = wfs[ifn]
1152                 end
1153
1154                 return ifaces
1155         end
1156 end
1157
1158 function protocol.contains_interface(self, ifname)
1159         ifname = _M:ifnameof(ifname)
1160         if not ifname then
1161                 return false
1162         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
1163                 return true
1164         elseif self:is_bridge() and "br-" .. self.sid == ifname then
1165                 return true
1166         else
1167                 local ifn
1168                 for ifn in utl.imatch(self:get("ifname")) do
1169                         ifn = ifn:match("[^:]+")
1170                         if ifn == ifname then
1171                                 return true
1172                         end
1173                 end
1174
1175                 local wif = _wifi_sid_by_ifname(ifname)
1176                 if wif then
1177                         local n
1178                         for n in utl.imatch(_uci:get("wireless", wif, "network")) do
1179                                 if n == self.sid then
1180                                         return true
1181                                 end
1182                         end
1183                 end
1184         end
1185
1186         return false
1187 end
1188
1189 function protocol.adminlink(self)
1190         local stat, dsp = pcall(require, "luci.dispatcher")
1191         return stat and dsp.build_url("admin", "network", "network", self.sid)
1192 end
1193
1194
1195 interface = utl.class()
1196
1197 function interface.__init__(self, ifname, network)
1198         local wif = _wifi_sid_by_ifname(ifname)
1199         if wif then
1200                 self.wif    = wifinet(wif)
1201                 self.ifname = self.wif:ifname()
1202         end
1203
1204         self.ifname  = self.ifname or ifname
1205         self.dev     = _interfaces[self.ifname]
1206         self.network = network
1207 end
1208
1209 function interface._ubus(self, field)
1210         if not _ubusdevcache[self.ifname] then
1211                 _ubusdevcache[self.ifname] = utl.ubus("network.device", "status",
1212                                                       { name = self.ifname })
1213         end
1214         if _ubusdevcache[self.ifname] and field then
1215                 return _ubusdevcache[self.ifname][field]
1216         end
1217         return _ubusdevcache[self.ifname]
1218 end
1219
1220 function interface.name(self)
1221         return self.wif and self.wif:ifname() or self.ifname
1222 end
1223
1224 function interface.mac(self)
1225         local mac = self:_ubus("macaddr")
1226         return mac and mac:upper()
1227 end
1228
1229 function interface.ipaddrs(self)
1230         return self.dev and self.dev.ipaddrs or { }
1231 end
1232
1233 function interface.ip6addrs(self)
1234         return self.dev and self.dev.ip6addrs or { }
1235 end
1236
1237 function interface.type(self)
1238         if self.wif or _wifi_iface(self.ifname) then
1239                 return "wifi"
1240         elseif _bridge[self.ifname] then
1241                 return "bridge"
1242         elseif _tunnel[self.ifname] then
1243                 return "tunnel"
1244         elseif self.ifname:match("%.") then
1245                 return "vlan"
1246         elseif _switch[self.ifname] then
1247                 return "switch"
1248         else
1249                 return "ethernet"
1250         end
1251 end
1252
1253 function interface.shortname(self)
1254         if self.wif then
1255                 return self.wif:shortname()
1256         else
1257                 return self.ifname
1258         end
1259 end
1260
1261 function interface.get_i18n(self)
1262         if self.wif then
1263                 return "%s: %s %q" %{
1264                         lng.translate("Wireless Network"),
1265                         self.wif:active_mode(),
1266                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
1267                 }
1268         else
1269                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1270         end
1271 end
1272
1273 function interface.get_type_i18n(self)
1274         local x = self:type()
1275         if x == "wifi" then
1276                 return lng.translate("Wireless Adapter")
1277         elseif x == "bridge" then
1278                 return lng.translate("Bridge")
1279         elseif x == "switch" then
1280                 return lng.translate("Ethernet Switch")
1281         elseif x == "vlan" then
1282                 if _switch[self.ifname] then
1283                         return lng.translate("Switch VLAN")
1284                 else
1285                         return lng.translate("Software VLAN")
1286                 end
1287         elseif x == "tunnel" then
1288                 return lng.translate("Tunnel Interface")
1289         else
1290                 return lng.translate("Ethernet Adapter")
1291         end
1292 end
1293
1294 function interface.adminlink(self)
1295         if self.wif then
1296                 return self.wif:adminlink()
1297         end
1298 end
1299
1300 function interface.ports(self)
1301         local members = self:_ubus("bridge-members")
1302         if members then
1303                 local _, iface
1304                 local ifaces = { }
1305                 for _, iface in ipairs(members) do
1306                         ifaces[#ifaces+1] = interface(iface)
1307                 end
1308         end
1309 end
1310
1311 function interface.bridge_id(self)
1312         if self.br then
1313                 return self.br.id
1314         else
1315                 return nil
1316         end
1317 end
1318
1319 function interface.bridge_stp(self)
1320         if self.br then
1321                 return self.br.stp
1322         else
1323                 return false
1324         end
1325 end
1326
1327 function interface.is_up(self)
1328         return self:_ubus("up") or false
1329 end
1330
1331 function interface.is_bridge(self)
1332         return (self:type() == "bridge")
1333 end
1334
1335 function interface.is_bridgeport(self)
1336         return self.dev and self.dev.bridge and true or false
1337 end
1338
1339 function interface.tx_bytes(self)
1340         local stat = self:_ubus("statistics")
1341         return stat and stat.tx_bytes or 0
1342 end
1343
1344 function interface.rx_bytes(self)
1345         local stat = self:_ubus("statistics")
1346         return stat and stat.rx_bytes or 0
1347 end
1348
1349 function interface.tx_packets(self)
1350         local stat = self:_ubus("statistics")
1351         return stat and stat.tx_packets or 0
1352 end
1353
1354 function interface.rx_packets(self)
1355         local stat = self:_ubus("statistics")
1356         return stat and stat.rx_packets or 0
1357 end
1358
1359 function interface.get_network(self)
1360         return self:get_networks()[1]
1361 end
1362
1363 function interface.get_networks(self)
1364         if not self.networks then
1365                 local nets = { }
1366                 local _, net
1367                 for _, net in ipairs(_M:get_networks()) do
1368                         if net:contains_interface(self.ifname) or
1369                            net:ifname() == self.ifname
1370                         then
1371                                 nets[#nets+1] = net
1372                         end
1373                 end
1374                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1375                 self.networks = nets
1376                 return nets
1377         else
1378                 return self.networks
1379         end
1380 end
1381
1382 function interface.get_wifinet(self)
1383         return self.wif
1384 end
1385
1386
1387 wifidev = utl.class()
1388
1389 function wifidev.__init__(self, name)
1390         local t, n = _uci:get("wireless", name)
1391         if t == "wifi-device" and n ~= nil then
1392                 self.sid    = n
1393                 self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true)
1394         end
1395         self.sid    = self.sid    or name
1396         self.iwinfo = self.iwinfo or { ifname = self.sid }
1397 end
1398
1399 function wifidev.get(self, opt)
1400         return _get("wireless", self.sid, opt)
1401 end
1402
1403 function wifidev.set(self, opt, val)
1404         return _set("wireless", self.sid, opt, val)
1405 end
1406
1407 function wifidev.name(self)
1408         return self.sid
1409 end
1410
1411 function wifidev.hwmodes(self)
1412         local l = self.iwinfo.hwmodelist
1413         if l and next(l) then
1414                 return l
1415         else
1416                 return { b = true, g = true }
1417         end
1418 end
1419
1420 function wifidev.get_i18n(self)
1421         local t = "Generic"
1422         if self.iwinfo.type == "wl" then
1423                 t = "Broadcom"
1424         end
1425
1426         local m = ""
1427         local l = self:hwmodes()
1428         if l.a then m = m .. "a" end
1429         if l.b then m = m .. "b" end
1430         if l.g then m = m .. "g" end
1431         if l.n then m = m .. "n" end
1432         if l.ac then m = "ac" end
1433
1434         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1435 end
1436
1437 function wifidev.is_up(self)
1438         if _ubuswificache[self.sid] then
1439                 return (_ubuswificache[self.sid].up == true)
1440         end
1441
1442         return false
1443 end
1444
1445 function wifidev.get_wifinet(self, net)
1446         if _uci:get("wireless", net) == "wifi-iface" then
1447                 return wifinet(net)
1448         else
1449                 local wnet = _wifi_sid_by_ifname(net)
1450                 if wnet then
1451                         return wifinet(wnet)
1452                 end
1453         end
1454 end
1455
1456 function wifidev.get_wifinets(self)
1457         local nets = { }
1458
1459         _uci:foreach("wireless", "wifi-iface",
1460                 function(s)
1461                         if s.device == self.sid then
1462                                 nets[#nets+1] = wifinet(s['.name'])
1463                         end
1464                 end)
1465
1466         return nets
1467 end
1468
1469 function wifidev.add_wifinet(self, options)
1470         options = options or { }
1471         options.device = self.sid
1472
1473         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1474         if wnet then
1475                 return wifinet(wnet, options)
1476         end
1477 end
1478
1479 function wifidev.del_wifinet(self, net)
1480         if utl.instanceof(net, wifinet) then
1481                 net = net.sid
1482         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1483                 net = _wifi_sid_by_ifname(net)
1484         end
1485
1486         if net and _uci:get("wireless", net, "device") == self.sid then
1487                 _uci:delete("wireless", net)
1488                 return true
1489         end
1490
1491         return false
1492 end
1493
1494
1495 wifinet = utl.class()
1496
1497 function wifinet.__init__(self, name, data)
1498         local sid, netid, radioname, radiostate, netstate
1499
1500         -- lookup state by radio#.network# notation
1501         sid = _wifi_sid_by_netid(name)
1502         if sid then
1503                 netid = name
1504                 radioname, radiostate, netstate = _wifi_state_by_sid(sid)
1505         else
1506                 -- lookup state by ifname (e.g. wlan0)
1507                 radioname, radiostate, netstate = _wifi_state_by_ifname(name)
1508                 if radioname and radiostate and netstate then
1509                         sid = netstate.section
1510                         netid = _wifi_netid_by_sid(sid)
1511                 else
1512                         -- lookup state by uci section id (e.g. cfg053579)
1513                         radioname, radiostate, netstate = _wifi_state_by_sid(name)
1514                         if radioname and radiostate and netstate then
1515                                 sid = name
1516                                 netid = _wifi_netid_by_sid(sid)
1517                         else
1518                                 -- no state available, try to resolve from uci
1519                                 netid, radioname = _wifi_netid_by_sid(name)
1520                                 if netid and radioname then
1521                                         sid = name
1522                                 end
1523                         end
1524                 end
1525         end
1526
1527         local iwinfo =
1528                 (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or
1529                 (radioname and _wifi_iwinfo_by_ifname(radioname)) or
1530                 { ifname = (netid or sid or name) }
1531
1532         self.sid       = sid or name
1533         self.wdev      = iwinfo.ifname
1534         self.iwinfo    = iwinfo
1535         self.netid     = netid
1536         self._ubusdata = {
1537                 radio = radioname,
1538                 dev   = radiostate,
1539                 net   = netstate
1540         }
1541 end
1542
1543 function wifinet.ubus(self, ...)
1544         local n, v = self._ubusdata
1545         for n = 1, select('#', ...) do
1546                 if type(v) == "table" then
1547                         v = v[select(n, ...)]
1548                 else
1549                         return nil
1550                 end
1551         end
1552         return v
1553 end
1554
1555 function wifinet.get(self, opt)
1556         return _get("wireless", self.sid, opt)
1557 end
1558
1559 function wifinet.set(self, opt, val)
1560         return _set("wireless", self.sid, opt, val)
1561 end
1562
1563 function wifinet.mode(self)
1564         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1565 end
1566
1567 function wifinet.ssid(self)
1568         return self:ubus("net", "config", "ssid") or self:get("ssid")
1569 end
1570
1571 function wifinet.bssid(self)
1572         return self:ubus("net", "config", "bssid") or self:get("bssid")
1573 end
1574
1575 function wifinet.network(self)
1576         local net, networks = nil, { }
1577         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1578                 networks[#networks+1] = net
1579         end
1580         return networks
1581 end
1582
1583 function wifinet.id(self)
1584         return self.netid
1585 end
1586
1587 function wifinet.name(self)
1588         return self.sid
1589 end
1590
1591 function wifinet.ifname(self)
1592         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1593         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1594                 ifname = self.wdev
1595         end
1596         return ifname
1597 end
1598
1599 function wifinet.get_device(self)
1600         local dev = self:ubus("radio") or self:get("device")
1601         return dev and wifidev(dev) or nil
1602 end
1603
1604 function wifinet.is_up(self)
1605         local ifc = self:get_interface()
1606         return (ifc and ifc:is_up() or false)
1607 end
1608
1609 function wifinet.active_mode(self)
1610         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1611
1612         if     m == "ap"      then m = "Master"
1613         elseif m == "sta"     then m = "Client"
1614         elseif m == "adhoc"   then m = "Ad-Hoc"
1615         elseif m == "mesh"    then m = "Mesh"
1616         elseif m == "monitor" then m = "Monitor"
1617         end
1618
1619         return m
1620 end
1621
1622 function wifinet.active_mode_i18n(self)
1623         return lng.translate(self:active_mode())
1624 end
1625
1626 function wifinet.active_ssid(self)
1627         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1628 end
1629
1630 function wifinet.active_bssid(self)
1631         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1632 end
1633
1634 function wifinet.active_encryption(self)
1635         local enc = self.iwinfo and self.iwinfo.encryption
1636         return enc and enc.description or "-"
1637 end
1638
1639 function wifinet.assoclist(self)
1640         return self.iwinfo.assoclist or { }
1641 end
1642
1643 function wifinet.frequency(self)
1644         local freq = self.iwinfo.frequency
1645         if freq and freq > 0 then
1646                 return "%.03f" % (freq / 1000)
1647         end
1648 end
1649
1650 function wifinet.bitrate(self)
1651         local rate = self.iwinfo.bitrate
1652         if rate and rate > 0 then
1653                 return (rate / 1000)
1654         end
1655 end
1656
1657 function wifinet.channel(self)
1658         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1659                 tonumber(self:get("channel"))
1660 end
1661
1662 function wifinet.signal(self)
1663         return self.iwinfo.signal or 0
1664 end
1665
1666 function wifinet.noise(self)
1667         return self.iwinfo.noise or 0
1668 end
1669
1670 function wifinet.country(self)
1671         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1672 end
1673
1674 function wifinet.txpower(self)
1675         local pwr = (self.iwinfo.txpower or 0)
1676         return pwr + self:txpower_offset()
1677 end
1678
1679 function wifinet.txpower_offset(self)
1680         return self.iwinfo.txpower_offset or 0
1681 end
1682
1683 function wifinet.signal_level(self, s, n)
1684         if self:active_bssid() ~= "00:00:00:00:00:00" then
1685                 local signal = s or self:signal()
1686                 local noise  = n or self:noise()
1687
1688                 if signal < 0 and noise < 0 then
1689                         local snr = -1 * (noise - signal)
1690                         return math.floor(snr / 5)
1691                 else
1692                         return 0
1693                 end
1694         else
1695                 return -1
1696         end
1697 end
1698
1699 function wifinet.signal_percent(self)
1700         local qc = self.iwinfo.quality or 0
1701         local qm = self.iwinfo.quality_max or 0
1702
1703         if qc > 0 and qm > 0 then
1704                 return math.floor((100 / qm) * qc)
1705         else
1706                 return 0
1707         end
1708 end
1709
1710 function wifinet.shortname(self)
1711         return "%s %q" %{
1712                 lng.translate(self:active_mode()),
1713                 self:active_ssid() or self:active_bssid() or self:id()
1714         }
1715 end
1716
1717 function wifinet.get_i18n(self)
1718         return "%s: %s %q (%s)" %{
1719                 lng.translate("Wireless Network"),
1720                 lng.translate(self:active_mode()),
1721                 self:active_ssid() or self:active_bssid() or self:id(),
1722                 self:ifname()
1723         }
1724 end
1725
1726 function wifinet.adminlink(self)
1727         local stat, dsp = pcall(require, "luci.dispatcher")
1728         return dsp and dsp.build_url("admin", "network", "wireless", self.netid)
1729 end
1730
1731 function wifinet.get_network(self)
1732         return self:get_networks()[1]
1733 end
1734
1735 function wifinet.get_networks(self)
1736         local nets = { }
1737         local net
1738         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1739                 if _uci:get("network", net) == "interface" then
1740                         nets[#nets+1] = network(net)
1741                 end
1742         end
1743         table.sort(nets, function(a, b) return a.sid < b.sid end)
1744         return nets
1745 end
1746
1747 function wifinet.get_interface(self)
1748         return interface(self:ifname())
1749 end
1750
1751
1752 -- setup base protocols
1753 _M:register_protocol("static")
1754 _M:register_protocol("dhcp")
1755 _M:register_protocol("none")
1756
1757 -- load protocol extensions
1758 local exts = nfs.dir(utl.libpath() .. "/model/network")
1759 if exts then
1760         local ext
1761         for ext in exts do
1762                 if ext:match("%.lua$") then
1763                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1764                 end
1765         end
1766 end