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