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