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