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