Fix missing title in df graphs
[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 self.wif:shortname()
1144         else
1145                 return self.ifname
1146         end
1147 end
1148
1149 function interface.get_i18n(self)
1150         if self.wif then
1151                 return "%s: %s %q" %{
1152                         lng.translate("Wireless Network"),
1153                         self.wif:active_mode(),
1154                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
1155                 }
1156         else
1157                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1158         end
1159 end
1160
1161 function interface.get_type_i18n(self)
1162         local x = self:type()
1163         if x == "wifi" then
1164                 return lng.translate("Wireless Adapter")
1165         elseif x == "bridge" then
1166                 return lng.translate("Bridge")
1167         elseif x == "switch" then
1168                 return lng.translate("Ethernet Switch")
1169         elseif x == "vlan" then
1170                 return lng.translate("VLAN Interface")
1171         elseif x == "tunnel" then
1172                 return lng.translate("Tunnel Interface")
1173         else
1174                 return lng.translate("Ethernet Adapter")
1175         end
1176 end
1177
1178 function interface.adminlink(self)
1179         if self.wif then
1180                 return self.wif:adminlink()
1181         end
1182 end
1183
1184 function interface.ports(self)
1185         local members = self:_ubus("bridge-members")
1186         if members then
1187                 local _, iface
1188                 local ifaces = { }
1189                 for _, iface in ipairs(members) do
1190                         ifaces[#ifaces+1] = interface(iface)
1191                 end
1192         end
1193 end
1194
1195 function interface.bridge_id(self)
1196         if self.br then
1197                 return self.br.id
1198         else
1199                 return nil
1200         end
1201 end
1202
1203 function interface.bridge_stp(self)
1204         if self.br then
1205                 return self.br.stp
1206         else
1207                 return false
1208         end
1209 end
1210
1211 function interface.is_up(self)
1212         return self:_ubus("up") or false
1213 end
1214
1215 function interface.is_bridge(self)
1216         return (self:type() == "bridge")
1217 end
1218
1219 function interface.is_bridgeport(self)
1220         return self.dev and self.dev.bridge and true or false
1221 end
1222
1223 function interface.tx_bytes(self)
1224         local stat = self:_ubus("statistics")
1225         return stat and stat.tx_bytes or 0
1226 end
1227
1228 function interface.rx_bytes(self)
1229         local stat = self:_ubus("statistics")
1230         return stat and stat.rx_bytes or 0
1231 end
1232
1233 function interface.tx_packets(self)
1234         local stat = self:_ubus("statistics")
1235         return stat and stat.tx_packets or 0
1236 end
1237
1238 function interface.rx_packets(self)
1239         local stat = self:_ubus("statistics")
1240         return stat and stat.rx_packets or 0
1241 end
1242
1243 function interface.get_network(self)
1244         return self:get_networks()[1]
1245 end
1246
1247 function interface.get_networks(self)
1248         if not self.networks then
1249                 local nets = { }
1250                 local _, net
1251                 for _, net in ipairs(_M:get_networks()) do
1252                         if net:contains_interface(self.ifname) or
1253                            net:ifname() == self.ifname
1254                         then
1255                                 nets[#nets+1] = net
1256                         end
1257                 end
1258                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1259                 self.networks = nets
1260                 return nets
1261         else
1262                 return self.networks
1263         end
1264 end
1265
1266 function interface.get_wifinet(self)
1267         return self.wif
1268 end
1269
1270
1271 wifidev = utl.class()
1272
1273 function wifidev.__init__(self, dev)
1274         self.sid    = dev
1275         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1276 end
1277
1278 function wifidev.get(self, opt)
1279         return _get("wireless", self.sid, opt)
1280 end
1281
1282 function wifidev.set(self, opt, val)
1283         return _set("wireless", self.sid, opt, val)
1284 end
1285
1286 function wifidev.name(self)
1287         return self.sid
1288 end
1289
1290 function wifidev.hwmodes(self)
1291         local l = self.iwinfo.hwmodelist
1292         if l and next(l) then
1293                 return l
1294         else
1295                 return { b = true, g = true }
1296         end
1297 end
1298
1299 function wifidev.get_i18n(self)
1300         local t = "Generic"
1301         if self.iwinfo.type == "wl" then
1302                 t = "Broadcom"
1303         elseif self.iwinfo.type == "madwifi" then
1304                 t = "Atheros"
1305         end
1306
1307         local m = ""
1308         local l = self:hwmodes()
1309         if l.a then m = m .. "a" end
1310         if l.b then m = m .. "b" end
1311         if l.g then m = m .. "g" end
1312         if l.n then m = m .. "n" end
1313         if l.ac then m = "ac" end
1314
1315         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1316 end
1317
1318 function wifidev.is_up(self)
1319         if _ubuswificache[self.sid] then
1320                 return (_ubuswificache[self.sid].up == true)
1321         end
1322
1323         return false
1324 end
1325
1326 function wifidev.get_wifinet(self, net)
1327         if _uci:get("wireless", net) == "wifi-iface" then
1328                 return wifinet(net)
1329         else
1330                 local wnet = _wifi_lookup(net)
1331                 if wnet then
1332                         return wifinet(wnet)
1333                 end
1334         end
1335 end
1336
1337 function wifidev.get_wifinets(self)
1338         local nets = { }
1339
1340         _uci:foreach("wireless", "wifi-iface",
1341                 function(s)
1342                         if s.device == self.sid then
1343                                 nets[#nets+1] = wifinet(s['.name'])
1344                         end
1345                 end)
1346
1347         return nets
1348 end
1349
1350 function wifidev.add_wifinet(self, options)
1351         options = options or { }
1352         options.device = self.sid
1353
1354         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1355         if wnet then
1356                 return wifinet(wnet, options)
1357         end
1358 end
1359
1360 function wifidev.del_wifinet(self, net)
1361         if utl.instanceof(net, wifinet) then
1362                 net = net.sid
1363         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1364                 net = _wifi_lookup(net)
1365         end
1366
1367         if net and _uci:get("wireless", net, "device") == self.sid then
1368                 _uci:delete("wireless", net)
1369                 return true
1370         end
1371
1372         return false
1373 end
1374
1375
1376 wifinet = utl.class()
1377
1378 function wifinet.__init__(self, net, data)
1379         self.sid = net
1380
1381         local n = 0
1382         local num = { }
1383         local netid, sid
1384         _uci:foreach("wireless", "wifi-iface",
1385                 function(s)
1386                         n = n + 1
1387                         if s.device then
1388                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1389                                 if s['.name'] == self.sid then
1390                                         sid = "@wifi-iface[%d]" % n
1391                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1392                                         return false
1393                                 end
1394                         end
1395                 end)
1396
1397         if sid then
1398                 local _, k, r, i
1399                 for k, r in pairs(_ubuswificache) do
1400                         if type(r) == "table" and
1401                            type(r.interfaces) == "table"
1402                         then
1403                                 for _, i in ipairs(r.interfaces) do
1404                                         if type(i) == "table" and i.section == sid then
1405                                                 self._ubusdata = {
1406                                                         radio = k,
1407                                                         dev = r,
1408                                                         net = i
1409                                                 }
1410                                         end
1411                                 end
1412                         end
1413                 end
1414         end
1415
1416         local dev = _wifi_state("section", self.sid, "ifname") or netid
1417
1418         self.netid  = netid
1419         self.wdev   = dev
1420         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1421 end
1422
1423 function wifinet.ubus(self, ...)
1424         local n, v = self._ubusdata
1425         for n = 1, select('#', ...) do
1426                 if type(v) == "table" then
1427                         v = v[select(n, ...)]
1428                 else
1429                         return nil
1430                 end
1431         end
1432         return v
1433 end
1434
1435 function wifinet.get(self, opt)
1436         return _get("wireless", self.sid, opt)
1437 end
1438
1439 function wifinet.set(self, opt, val)
1440         return _set("wireless", self.sid, opt, val)
1441 end
1442
1443 function wifinet.mode(self)
1444         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1445 end
1446
1447 function wifinet.ssid(self)
1448         return self:ubus("net", "config", "ssid") or self:get("ssid")
1449 end
1450
1451 function wifinet.bssid(self)
1452         return self:ubus("net", "config", "bssid") or self:get("bssid")
1453 end
1454
1455 function wifinet.network(self)
1456         local net, networks = nil, { }
1457         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1458                 networks[#networks+1] = net
1459         end
1460         return networks
1461 end
1462
1463 function wifinet.id(self)
1464         return self.netid
1465 end
1466
1467 function wifinet.name(self)
1468         return self.sid
1469 end
1470
1471 function wifinet.ifname(self)
1472         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1473         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1474                 ifname = self.wdev
1475         end
1476         return ifname
1477 end
1478
1479 function wifinet.get_device(self)
1480         local dev = self:ubus("radio") or self:get("device")
1481         return dev and wifidev(dev) or nil
1482 end
1483
1484 function wifinet.is_up(self)
1485         local ifc = self:get_interface()
1486         return (ifc and ifc:is_up() or false)
1487 end
1488
1489 function wifinet.active_mode(self)
1490         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1491
1492         if     m == "ap"      then m = "Master"
1493         elseif m == "sta"     then m = "Client"
1494         elseif m == "adhoc"   then m = "Ad-Hoc"
1495         elseif m == "mesh"    then m = "Mesh"
1496         elseif m == "monitor" then m = "Monitor"
1497         end
1498
1499         return m
1500 end
1501
1502 function wifinet.active_mode_i18n(self)
1503         return lng.translate(self:active_mode())
1504 end
1505
1506 function wifinet.active_ssid(self)
1507         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1508 end
1509
1510 function wifinet.active_bssid(self)
1511         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1512 end
1513
1514 function wifinet.active_encryption(self)
1515         local enc = self.iwinfo and self.iwinfo.encryption
1516         return enc and enc.description or "-"
1517 end
1518
1519 function wifinet.assoclist(self)
1520         return self.iwinfo.assoclist or { }
1521 end
1522
1523 function wifinet.frequency(self)
1524         local freq = self.iwinfo.frequency
1525         if freq and freq > 0 then
1526                 return "%.03f" % (freq / 1000)
1527         end
1528 end
1529
1530 function wifinet.bitrate(self)
1531         local rate = self.iwinfo.bitrate
1532         if rate and rate > 0 then
1533                 return (rate / 1000)
1534         end
1535 end
1536
1537 function wifinet.channel(self)
1538         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1539                 tonumber(self:get("channel"))
1540 end
1541
1542 function wifinet.signal(self)
1543         return self.iwinfo.signal or 0
1544 end
1545
1546 function wifinet.noise(self)
1547         return self.iwinfo.noise or 0
1548 end
1549
1550 function wifinet.country(self)
1551         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1552 end
1553
1554 function wifinet.txpower(self)
1555         local pwr = (self.iwinfo.txpower or 0)
1556         return pwr + self:txpower_offset()
1557 end
1558
1559 function wifinet.txpower_offset(self)
1560         return self.iwinfo.txpower_offset or 0
1561 end
1562
1563 function wifinet.signal_level(self, s, n)
1564         if self:active_bssid() ~= "00:00:00:00:00:00" then
1565                 local signal = s or self:signal()
1566                 local noise  = n or self:noise()
1567
1568                 if signal < 0 and noise < 0 then
1569                         local snr = -1 * (noise - signal)
1570                         return math.floor(snr / 5)
1571                 else
1572                         return 0
1573                 end
1574         else
1575                 return -1
1576         end
1577 end
1578
1579 function wifinet.signal_percent(self)
1580         local qc = self.iwinfo.quality or 0
1581         local qm = self.iwinfo.quality_max or 0
1582
1583         if qc > 0 and qm > 0 then
1584                 return math.floor((100 / qm) * qc)
1585         else
1586                 return 0
1587         end
1588 end
1589
1590 function wifinet.shortname(self)
1591         return "%s %q" %{
1592                 lng.translate(self:active_mode()),
1593                 self:active_ssid() or self:active_bssid() or self:id()
1594         }
1595 end
1596
1597 function wifinet.get_i18n(self)
1598         return "%s: %s %q (%s)" %{
1599                 lng.translate("Wireless Network"),
1600                 lng.translate(self:active_mode()),
1601                 self:active_ssid() or self:active_bssid() or self:id(),
1602                 self:ifname()
1603         }
1604 end
1605
1606 function wifinet.adminlink(self)
1607         return dsp.build_url("admin", "network", "wireless", self.netid)
1608 end
1609
1610 function wifinet.get_network(self)
1611         return self:get_networks()[1]
1612 end
1613
1614 function wifinet.get_networks(self)
1615         local nets = { }
1616         local net
1617         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1618                 if _uci:get("network", net) == "interface" then
1619                         nets[#nets+1] = network(net)
1620                 end
1621         end
1622         table.sort(nets, function(a, b) return a.sid < b.sid end)
1623         return nets
1624 end
1625
1626 function wifinet.get_interface(self)
1627         return interface(self:ifname())
1628 end
1629
1630
1631 -- setup base protocols
1632 _M:register_protocol("static")
1633 _M:register_protocol("dhcp")
1634 _M:register_protocol("none")
1635
1636 -- load protocol extensions
1637 local exts = nfs.dir(utl.libpath() .. "/model/network")
1638 if exts then
1639         local ext
1640         for ext in exts do
1641                 if ext:match("%.lua$") then
1642                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1643                 end
1644         end
1645 end