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