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