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