cce559aab1740166f9efa82989f01b87f9f5c3db
[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.ifname and self.ifname:byte(1) == 64 then
1352                 return "alias"
1353         elseif self.wif or _wifi_iface(self.ifname) then
1354                 return "wifi"
1355         elseif _bridge[self.ifname] then
1356                 return "bridge"
1357         elseif _tunnel[self.ifname] then
1358                 return "tunnel"
1359         elseif self.ifname:match("%.") then
1360                 return "vlan"
1361         elseif _switch[self.ifname] then
1362                 return "switch"
1363         else
1364                 return "ethernet"
1365         end
1366 end
1367
1368 function interface.shortname(self)
1369         if self.wif then
1370                 return self.wif:shortname()
1371         else
1372                 return self.ifname
1373         end
1374 end
1375
1376 function interface.get_i18n(self)
1377         if self.wif then
1378                 return "%s: %s %q" %{
1379                         lng.translate("Wireless Network"),
1380                         self.wif:active_mode(),
1381                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id() or "?"
1382                 }
1383         else
1384                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1385         end
1386 end
1387
1388 function interface.get_type_i18n(self)
1389         local x = self:type()
1390         if x == "alias" then
1391                 return lng.translate("Alias Interface")
1392         elseif x == "wifi" then
1393                 return lng.translate("Wireless Adapter")
1394         elseif x == "bridge" then
1395                 return lng.translate("Bridge")
1396         elseif x == "switch" then
1397                 return lng.translate("Ethernet Switch")
1398         elseif x == "vlan" then
1399                 if _switch[self.ifname] then
1400                         return lng.translate("Switch VLAN")
1401                 else
1402                         return lng.translate("Software VLAN")
1403                 end
1404         elseif x == "tunnel" then
1405                 return lng.translate("Tunnel Interface")
1406         else
1407                 return lng.translate("Ethernet Adapter")
1408         end
1409 end
1410
1411 function interface.adminlink(self)
1412         if self.wif then
1413                 return self.wif:adminlink()
1414         end
1415 end
1416
1417 function interface.ports(self)
1418         local members = self:_ubus("bridge-members")
1419         if members then
1420                 local _, iface
1421                 local ifaces = { }
1422                 for _, iface in ipairs(members) do
1423                         ifaces[#ifaces+1] = interface(iface)
1424                 end
1425         end
1426 end
1427
1428 function interface.bridge_id(self)
1429         if self.br then
1430                 return self.br.id
1431         else
1432                 return nil
1433         end
1434 end
1435
1436 function interface.bridge_stp(self)
1437         if self.br then
1438                 return self.br.stp
1439         else
1440                 return false
1441         end
1442 end
1443
1444 function interface.is_up(self)
1445         local up = self:_ubus("up")
1446         if up == nil then
1447                 up = (self:type() == "alias")
1448         end
1449         return up or false
1450 end
1451
1452 function interface.is_bridge(self)
1453         return (self:type() == "bridge")
1454 end
1455
1456 function interface.is_bridgeport(self)
1457         return self.dev and self.dev.bridge and true or false
1458 end
1459
1460 function interface.tx_bytes(self)
1461         local stat = self:_ubus("statistics")
1462         return stat and stat.tx_bytes or 0
1463 end
1464
1465 function interface.rx_bytes(self)
1466         local stat = self:_ubus("statistics")
1467         return stat and stat.rx_bytes or 0
1468 end
1469
1470 function interface.tx_packets(self)
1471         local stat = self:_ubus("statistics")
1472         return stat and stat.tx_packets or 0
1473 end
1474
1475 function interface.rx_packets(self)
1476         local stat = self:_ubus("statistics")
1477         return stat and stat.rx_packets or 0
1478 end
1479
1480 function interface.get_network(self)
1481         return self:get_networks()[1]
1482 end
1483
1484 function interface.get_networks(self)
1485         if not self.networks then
1486                 local nets = { }
1487                 local _, net
1488                 for _, net in ipairs(_M:get_networks()) do
1489                         if net:contains_interface(self.ifname) or
1490                            net:ifname() == self.ifname
1491                         then
1492                                 nets[#nets+1] = net
1493                         end
1494                 end
1495                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1496                 self.networks = nets
1497                 return nets
1498         else
1499                 return self.networks
1500         end
1501 end
1502
1503 function interface.get_wifinet(self)
1504         return self.wif
1505 end
1506
1507
1508 wifidev = utl.class()
1509
1510 function wifidev.__init__(self, name)
1511         local t, n = _uci:get("wireless", name)
1512         if t == "wifi-device" and n ~= nil then
1513                 self.sid    = n
1514                 self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true)
1515         end
1516         self.sid    = self.sid    or name
1517         self.iwinfo = self.iwinfo or { ifname = self.sid }
1518 end
1519
1520 function wifidev.get(self, opt)
1521         return _get("wireless", self.sid, opt)
1522 end
1523
1524 function wifidev.set(self, opt, val)
1525         return _set("wireless", self.sid, opt, val)
1526 end
1527
1528 function wifidev.name(self)
1529         return self.sid
1530 end
1531
1532 function wifidev.hwmodes(self)
1533         local l = self.iwinfo.hwmodelist
1534         if l and next(l) then
1535                 return l
1536         else
1537                 return { b = true, g = true }
1538         end
1539 end
1540
1541 function wifidev.get_i18n(self)
1542         local t = self.iwinfo.hardware_name or "Generic"
1543         if self.iwinfo.type == "wl" then
1544                 t = "Broadcom"
1545         end
1546
1547         local m = ""
1548         local l = self:hwmodes()
1549         if l.a then m = m .. "a" end
1550         if l.b then m = m .. "b" end
1551         if l.g then m = m .. "g" end
1552         if l.n then m = m .. "n" end
1553         if l.ac then m = "ac" end
1554
1555         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1556 end
1557
1558 function wifidev.is_up(self)
1559         if _ubuswificache[self.sid] then
1560                 return (_ubuswificache[self.sid].up == true)
1561         end
1562
1563         return false
1564 end
1565
1566 function wifidev.get_wifinet(self, net)
1567         if _uci:get("wireless", net) == "wifi-iface" then
1568                 return wifinet(net)
1569         else
1570                 local wnet = _wifi_sid_by_ifname(net)
1571                 if wnet then
1572                         return wifinet(wnet)
1573                 end
1574         end
1575 end
1576
1577 function wifidev.get_wifinets(self)
1578         local nets = { }
1579
1580         _uci:foreach("wireless", "wifi-iface",
1581                 function(s)
1582                         if s.device == self.sid then
1583                                 nets[#nets+1] = wifinet(s['.name'])
1584                         end
1585                 end)
1586
1587         return nets
1588 end
1589
1590 function wifidev.add_wifinet(self, options)
1591         options = options or { }
1592         options.device = self.sid
1593
1594         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1595         if wnet then
1596                 return wifinet(wnet, options)
1597         end
1598 end
1599
1600 function wifidev.del_wifinet(self, net)
1601         if utl.instanceof(net, wifinet) then
1602                 net = net.sid
1603         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1604                 net = _wifi_sid_by_ifname(net)
1605         end
1606
1607         if net and _uci:get("wireless", net, "device") == self.sid then
1608                 _uci:delete("wireless", net)
1609                 return true
1610         end
1611
1612         return false
1613 end
1614
1615
1616 wifinet = utl.class()
1617
1618 function wifinet.__init__(self, name, data)
1619         local sid, netid, radioname, radiostate, netstate
1620
1621         -- lookup state by radio#.network# notation
1622         sid = _wifi_sid_by_netid(name)
1623         if sid then
1624                 netid = name
1625                 radioname, radiostate, netstate = _wifi_state_by_sid(sid)
1626         else
1627                 -- lookup state by ifname (e.g. wlan0)
1628                 radioname, radiostate, netstate = _wifi_state_by_ifname(name)
1629                 if radioname and radiostate and netstate then
1630                         sid = netstate.section
1631                         netid = _wifi_netid_by_sid(sid)
1632                 else
1633                         -- lookup state by uci section id (e.g. cfg053579)
1634                         radioname, radiostate, netstate = _wifi_state_by_sid(name)
1635                         if radioname and radiostate and netstate then
1636                                 sid = name
1637                                 netid = _wifi_netid_by_sid(sid)
1638                         else
1639                                 -- no state available, try to resolve from uci
1640                                 netid, radioname = _wifi_netid_by_sid(name)
1641                                 if netid and radioname then
1642                                         sid = name
1643                                 end
1644                         end
1645                 end
1646         end
1647
1648         local iwinfo =
1649                 (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or
1650                 (radioname and _wifi_iwinfo_by_ifname(radioname)) or
1651                 { ifname = (netid or sid or name) }
1652
1653         self.sid       = sid or name
1654         self.wdev      = iwinfo.ifname
1655         self.iwinfo    = iwinfo
1656         self.netid     = netid
1657         self._ubusdata = {
1658                 radio = radioname,
1659                 dev   = radiostate,
1660                 net   = netstate
1661         }
1662 end
1663
1664 function wifinet.ubus(self, ...)
1665         local n, v = self._ubusdata
1666         for n = 1, select('#', ...) do
1667                 if type(v) == "table" then
1668                         v = v[select(n, ...)]
1669                 else
1670                         return nil
1671                 end
1672         end
1673         return v
1674 end
1675
1676 function wifinet.get(self, opt)
1677         return _get("wireless", self.sid, opt)
1678 end
1679
1680 function wifinet.set(self, opt, val)
1681         return _set("wireless", self.sid, opt, val)
1682 end
1683
1684 function wifinet.mode(self)
1685         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1686 end
1687
1688 function wifinet.ssid(self)
1689         return self:ubus("net", "config", "ssid") or self:get("ssid")
1690 end
1691
1692 function wifinet.bssid(self)
1693         return self:ubus("net", "config", "bssid") or self:get("bssid")
1694 end
1695
1696 function wifinet.network(self)
1697         local net, networks = nil, { }
1698         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1699                 networks[#networks+1] = net
1700         end
1701         return networks
1702 end
1703
1704 function wifinet.id(self)
1705         return self.netid
1706 end
1707
1708 function wifinet.name(self)
1709         return self.sid
1710 end
1711
1712 function wifinet.ifname(self)
1713         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1714         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1715                 ifname = self.netid
1716         end
1717         return ifname
1718 end
1719
1720 function wifinet.get_device(self)
1721         local dev = self:ubus("radio") or self:get("device")
1722         return dev and wifidev(dev) or nil
1723 end
1724
1725 function wifinet.is_up(self)
1726         local ifc = self:get_interface()
1727         return (ifc and ifc:is_up() or false)
1728 end
1729
1730 function wifinet.active_mode(self)
1731         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1732
1733         if     m == "ap"      then m = "Master"
1734         elseif m == "sta"     then m = "Client"
1735         elseif m == "adhoc"   then m = "Ad-Hoc"
1736         elseif m == "mesh"    then m = "Mesh"
1737         elseif m == "monitor" then m = "Monitor"
1738         end
1739
1740         return m
1741 end
1742
1743 function wifinet.active_mode_i18n(self)
1744         return lng.translate(self:active_mode())
1745 end
1746
1747 function wifinet.active_ssid(self)
1748         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1749 end
1750
1751 function wifinet.active_bssid(self)
1752         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1753 end
1754
1755 function wifinet.active_encryption(self)
1756         local enc = self.iwinfo and self.iwinfo.encryption
1757         return enc and enc.description or "-"
1758 end
1759
1760 function wifinet.assoclist(self)
1761         return self.iwinfo.assoclist or { }
1762 end
1763
1764 function wifinet.frequency(self)
1765         local freq = self.iwinfo.frequency
1766         if freq and freq > 0 then
1767                 return "%.03f" % (freq / 1000)
1768         end
1769 end
1770
1771 function wifinet.bitrate(self)
1772         local rate = self.iwinfo.bitrate
1773         if rate and rate > 0 then
1774                 return (rate / 1000)
1775         end
1776 end
1777
1778 function wifinet.channel(self)
1779         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1780                 tonumber(self:get("channel"))
1781 end
1782
1783 function wifinet.signal(self)
1784         return self.iwinfo.signal or 0
1785 end
1786
1787 function wifinet.noise(self)
1788         return self.iwinfo.noise or 0
1789 end
1790
1791 function wifinet.country(self)
1792         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1793 end
1794
1795 function wifinet.txpower(self)
1796         local pwr = (self.iwinfo.txpower or 0)
1797         return pwr + self:txpower_offset()
1798 end
1799
1800 function wifinet.txpower_offset(self)
1801         return self.iwinfo.txpower_offset or 0
1802 end
1803
1804 function wifinet.signal_level(self, s, n)
1805         if self:active_bssid() ~= "00:00:00:00:00:00" then
1806                 local signal = s or self:signal()
1807                 local noise  = n or self:noise()
1808
1809                 if signal < 0 and noise < 0 then
1810                         local snr = -1 * (noise - signal)
1811                         return math.floor(snr / 5)
1812                 else
1813                         return 0
1814                 end
1815         else
1816                 return -1
1817         end
1818 end
1819
1820 function wifinet.signal_percent(self)
1821         local qc = self.iwinfo.quality or 0
1822         local qm = self.iwinfo.quality_max or 0
1823
1824         if qc > 0 and qm > 0 then
1825                 return math.floor((100 / qm) * qc)
1826         else
1827                 return 0
1828         end
1829 end
1830
1831 function wifinet.shortname(self)
1832         return "%s %q" %{
1833                 lng.translate(self:active_mode()),
1834                 self:active_ssid() or self:active_bssid() or self:id()
1835         }
1836 end
1837
1838 function wifinet.get_i18n(self)
1839         return "%s: %s %q (%s)" %{
1840                 lng.translate("Wireless Network"),
1841                 lng.translate(self:active_mode()),
1842                 self:active_ssid() or self:active_bssid() or self:id(),
1843                 self:ifname()
1844         }
1845 end
1846
1847 function wifinet.adminlink(self)
1848         local stat, dsp = pcall(require, "luci.dispatcher")
1849         return dsp and dsp.build_url("admin", "network", "wireless", self.netid)
1850 end
1851
1852 function wifinet.get_network(self)
1853         return self:get_networks()[1]
1854 end
1855
1856 function wifinet.get_networks(self)
1857         local nets = { }
1858         local net
1859         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1860                 if _uci:get("network", net) == "interface" then
1861                         nets[#nets+1] = network(net)
1862                 end
1863         end
1864         table.sort(nets, function(a, b) return a.sid < b.sid end)
1865         return nets
1866 end
1867
1868 function wifinet.get_interface(self)
1869         return interface(self:ifname())
1870 end
1871
1872
1873 -- setup base protocols
1874 _M:register_protocol("static")
1875 _M:register_protocol("dhcp")
1876 _M:register_protocol("none")
1877
1878 -- load protocol extensions
1879 local exts = nfs.dir(utl.libpath() .. "/model/network")
1880 if exts then
1881         local ext
1882         for ext in exts do
1883                 if ext:match("%.lua$") then
1884                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1885                 end
1886         end
1887 end