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