treewide: cleanup references to madwifi from LuCI
[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.ip6prefix(self)
954         local prefix = self:_ubus("ipv6-prefix")
955         if prefix and #prefix > 0 then
956                 return "%s/%d" %{ prefix[1].address, prefix[1].mask }
957         end
958 end
959
960 function protocol.is_bridge(self)
961         return (not self:is_virtual() and self:type() == "bridge")
962 end
963
964 function protocol.opkg_package(self)
965         return nil
966 end
967
968 function protocol.is_installed(self)
969         return true
970 end
971
972 function protocol.is_virtual(self)
973         return false
974 end
975
976 function protocol.is_floating(self)
977         return false
978 end
979
980 function protocol.is_empty(self)
981         if self:is_floating() then
982                 return false
983         else
984                 local rv = true
985
986                 if (self:_get("ifname") or ""):match("%S+") then
987                         rv = false
988                 end
989
990                 _uci:foreach("wireless", "wifi-iface",
991                         function(s)
992                                 local n
993                                 for n in utl.imatch(s.network) do
994                                         if n == self.sid then
995                                                 rv = false
996                                                 return false
997                                         end
998                                 end
999                         end)
1000
1001                 return rv
1002         end
1003 end
1004
1005 function protocol.add_interface(self, ifname)
1006         ifname = _M:ifnameof(ifname)
1007         if ifname and not self:is_floating() then
1008                 -- if its a wifi interface, change its network option
1009                 local wif = _wifi_lookup(ifname)
1010                 if wif then
1011                         _append("wireless", wif, "network", self.sid)
1012
1013                 -- add iface to our iface list
1014                 else
1015                         _append("network", self.sid, "ifname", ifname)
1016                 end
1017         end
1018 end
1019
1020 function protocol.del_interface(self, ifname)
1021         ifname = _M:ifnameof(ifname)
1022         if ifname and not self:is_floating() then
1023                 -- if its a wireless interface, clear its network option
1024                 local wif = _wifi_lookup(ifname)
1025                 if wif then _filter("wireless", wif, "network", self.sid) end
1026
1027                 -- remove the interface
1028                 _filter("network", self.sid, "ifname", ifname)
1029         end
1030 end
1031
1032 function protocol.get_interface(self)
1033         if self:is_virtual() then
1034                 _tunnel[self:proto() .. "-" .. self.sid] = true
1035                 return interface(self:proto() .. "-" .. self.sid, self)
1036         elseif self:is_bridge() then
1037                 _bridge["br-" .. self.sid] = true
1038                 return interface("br-" .. self.sid, self)
1039         else
1040                 local ifn = nil
1041                 local num = { }
1042                 for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1043                         ifn = ifn:match("^[^:/]+")
1044                         return ifn and interface(ifn, self)
1045                 end
1046                 ifn = nil
1047                 _uci:foreach("wireless", "wifi-iface",
1048                         function(s)
1049                                 if s.device then
1050                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
1051
1052                                         local net
1053                                         for net in utl.imatch(s.network) do
1054                                                 if net == self.sid then
1055                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
1056                                                         return false
1057                                                 end
1058                                         end
1059                                 end
1060                         end)
1061                 return ifn and interface(ifn, self)
1062         end
1063 end
1064
1065 function protocol.get_interfaces(self)
1066         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
1067                 local ifaces = { }
1068
1069                 local ifn
1070                 local nfs = { }
1071                 for ifn in utl.imatch(self:get("ifname")) do
1072                         ifn = ifn:match("^[^:/]+")
1073                         nfs[ifn] = interface(ifn, self)
1074                 end
1075
1076                 for ifn in utl.kspairs(nfs) do
1077                         ifaces[#ifaces+1] = nfs[ifn]
1078                 end
1079
1080                 local num = { }
1081                 local wfs = { }
1082                 _uci:foreach("wireless", "wifi-iface",
1083                         function(s)
1084                                 if s.device then
1085                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
1086
1087                                         local net
1088                                         for net in utl.imatch(s.network) do
1089                                                 if net == self.sid then
1090                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
1091                                                         wfs[ifn] = interface(ifn, self)
1092                                                 end
1093                                         end
1094                                 end
1095                         end)
1096
1097                 for ifn in utl.kspairs(wfs) do
1098                         ifaces[#ifaces+1] = wfs[ifn]
1099                 end
1100
1101                 return ifaces
1102         end
1103 end
1104
1105 function protocol.contains_interface(self, ifname)
1106         ifname = _M:ifnameof(ifname)
1107         if not ifname then
1108                 return false
1109         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
1110                 return true
1111         elseif self:is_bridge() and "br-" .. self.sid == ifname then
1112                 return true
1113         else
1114                 local ifn
1115                 for ifn in utl.imatch(self:get("ifname")) do
1116                         ifn = ifn:match("[^:]+")
1117                         if ifn == ifname then
1118                                 return true
1119                         end
1120                 end
1121
1122                 local wif = _wifi_lookup(ifname)
1123                 if wif then
1124                         local n
1125                         for n in utl.imatch(_uci:get("wireless", wif, "network")) do
1126                                 if n == self.sid then
1127                                         return true
1128                                 end
1129                         end
1130                 end
1131         end
1132
1133         return false
1134 end
1135
1136 function protocol.adminlink(self)
1137         return dsp.build_url("admin", "network", "network", self.sid)
1138 end
1139
1140
1141 interface = utl.class()
1142
1143 function interface.__init__(self, ifname, network)
1144         local wif = _wifi_lookup(ifname)
1145         if wif then
1146                 self.wif    = wifinet(wif)
1147                 self.ifname = _wifi_state("section", wif, "ifname")
1148         end
1149
1150         self.ifname  = self.ifname or ifname
1151         self.dev     = _interfaces[self.ifname]
1152         self.network = network
1153 end
1154
1155 function interface._ubus(self, field)
1156         if not _ubusdevcache[self.ifname] then
1157                 _ubusdevcache[self.ifname] = utl.ubus("network.device", "status",
1158                                                       { name = self.ifname })
1159         end
1160         if _ubusdevcache[self.ifname] and field then
1161                 return _ubusdevcache[self.ifname][field]
1162         end
1163         return _ubusdevcache[self.ifname]
1164 end
1165
1166 function interface.name(self)
1167         return self.wif and self.wif:ifname() or self.ifname
1168 end
1169
1170 function interface.mac(self)
1171         local mac = self:_ubus("macaddr")
1172         return mac and mac:upper()
1173 end
1174
1175 function interface.ipaddrs(self)
1176         return self.dev and self.dev.ipaddrs or { }
1177 end
1178
1179 function interface.ip6addrs(self)
1180         return self.dev and self.dev.ip6addrs or { }
1181 end
1182
1183 function interface.type(self)
1184         if self.wif or _wifi_iface(self.ifname) then
1185                 return "wifi"
1186         elseif _bridge[self.ifname] then
1187                 return "bridge"
1188         elseif _tunnel[self.ifname] then
1189                 return "tunnel"
1190         elseif self.ifname:match("%.") then
1191                 return "vlan"
1192         elseif _switch[self.ifname] then
1193                 return "switch"
1194         else
1195                 return "ethernet"
1196         end
1197 end
1198
1199 function interface.shortname(self)
1200         if self.wif then
1201                 return self.wif:shortname()
1202         else
1203                 return self.ifname
1204         end
1205 end
1206
1207 function interface.get_i18n(self)
1208         if self.wif then
1209                 return "%s: %s %q" %{
1210                         lng.translate("Wireless Network"),
1211                         self.wif:active_mode(),
1212                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
1213                 }
1214         else
1215                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1216         end
1217 end
1218
1219 function interface.get_type_i18n(self)
1220         local x = self:type()
1221         if x == "wifi" then
1222                 return lng.translate("Wireless Adapter")
1223         elseif x == "bridge" then
1224                 return lng.translate("Bridge")
1225         elseif x == "switch" then
1226                 return lng.translate("Ethernet Switch")
1227         elseif x == "vlan" then
1228                 if _switch[self.ifname] then
1229                         return lng.translate("Switch VLAN")
1230                 else
1231                         return lng.translate("Software VLAN")
1232                 end
1233         elseif x == "tunnel" then
1234                 return lng.translate("Tunnel Interface")
1235         else
1236                 return lng.translate("Ethernet Adapter")
1237         end
1238 end
1239
1240 function interface.adminlink(self)
1241         if self.wif then
1242                 return self.wif:adminlink()
1243         end
1244 end
1245
1246 function interface.ports(self)
1247         local members = self:_ubus("bridge-members")
1248         if members then
1249                 local _, iface
1250                 local ifaces = { }
1251                 for _, iface in ipairs(members) do
1252                         ifaces[#ifaces+1] = interface(iface)
1253                 end
1254         end
1255 end
1256
1257 function interface.bridge_id(self)
1258         if self.br then
1259                 return self.br.id
1260         else
1261                 return nil
1262         end
1263 end
1264
1265 function interface.bridge_stp(self)
1266         if self.br then
1267                 return self.br.stp
1268         else
1269                 return false
1270         end
1271 end
1272
1273 function interface.is_up(self)
1274         return self:_ubus("up") or false
1275 end
1276
1277 function interface.is_bridge(self)
1278         return (self:type() == "bridge")
1279 end
1280
1281 function interface.is_bridgeport(self)
1282         return self.dev and self.dev.bridge and true or false
1283 end
1284
1285 function interface.tx_bytes(self)
1286         local stat = self:_ubus("statistics")
1287         return stat and stat.tx_bytes or 0
1288 end
1289
1290 function interface.rx_bytes(self)
1291         local stat = self:_ubus("statistics")
1292         return stat and stat.rx_bytes or 0
1293 end
1294
1295 function interface.tx_packets(self)
1296         local stat = self:_ubus("statistics")
1297         return stat and stat.tx_packets or 0
1298 end
1299
1300 function interface.rx_packets(self)
1301         local stat = self:_ubus("statistics")
1302         return stat and stat.rx_packets or 0
1303 end
1304
1305 function interface.get_network(self)
1306         return self:get_networks()[1]
1307 end
1308
1309 function interface.get_networks(self)
1310         if not self.networks then
1311                 local nets = { }
1312                 local _, net
1313                 for _, net in ipairs(_M:get_networks()) do
1314                         if net:contains_interface(self.ifname) or
1315                            net:ifname() == self.ifname
1316                         then
1317                                 nets[#nets+1] = net
1318                         end
1319                 end
1320                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1321                 self.networks = nets
1322                 return nets
1323         else
1324                 return self.networks
1325         end
1326 end
1327
1328 function interface.get_wifinet(self)
1329         return self.wif
1330 end
1331
1332
1333 wifidev = utl.class()
1334
1335 function wifidev.__init__(self, dev)
1336         self.sid    = dev
1337         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1338 end
1339
1340 function wifidev.get(self, opt)
1341         return _get("wireless", self.sid, opt)
1342 end
1343
1344 function wifidev.set(self, opt, val)
1345         return _set("wireless", self.sid, opt, val)
1346 end
1347
1348 function wifidev.name(self)
1349         return self.sid
1350 end
1351
1352 function wifidev.hwmodes(self)
1353         local l = self.iwinfo.hwmodelist
1354         if l and next(l) then
1355                 return l
1356         else
1357                 return { b = true, g = true }
1358         end
1359 end
1360
1361 function wifidev.get_i18n(self)
1362         local t = "Generic"
1363         if self.iwinfo.type == "wl" then
1364                 t = "Broadcom"
1365         end
1366
1367         local m = ""
1368         local l = self:hwmodes()
1369         if l.a then m = m .. "a" end
1370         if l.b then m = m .. "b" end
1371         if l.g then m = m .. "g" end
1372         if l.n then m = m .. "n" end
1373         if l.ac then m = "ac" end
1374
1375         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1376 end
1377
1378 function wifidev.is_up(self)
1379         if _ubuswificache[self.sid] then
1380                 return (_ubuswificache[self.sid].up == true)
1381         end
1382
1383         return false
1384 end
1385
1386 function wifidev.get_wifinet(self, net)
1387         if _uci:get("wireless", net) == "wifi-iface" then
1388                 return wifinet(net)
1389         else
1390                 local wnet = _wifi_lookup(net)
1391                 if wnet then
1392                         return wifinet(wnet)
1393                 end
1394         end
1395 end
1396
1397 function wifidev.get_wifinets(self)
1398         local nets = { }
1399
1400         _uci:foreach("wireless", "wifi-iface",
1401                 function(s)
1402                         if s.device == self.sid then
1403                                 nets[#nets+1] = wifinet(s['.name'])
1404                         end
1405                 end)
1406
1407         return nets
1408 end
1409
1410 function wifidev.add_wifinet(self, options)
1411         options = options or { }
1412         options.device = self.sid
1413
1414         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1415         if wnet then
1416                 return wifinet(wnet, options)
1417         end
1418 end
1419
1420 function wifidev.del_wifinet(self, net)
1421         if utl.instanceof(net, wifinet) then
1422                 net = net.sid
1423         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1424                 net = _wifi_lookup(net)
1425         end
1426
1427         if net and _uci:get("wireless", net, "device") == self.sid then
1428                 _uci:delete("wireless", net)
1429                 return true
1430         end
1431
1432         return false
1433 end
1434
1435
1436 wifinet = utl.class()
1437
1438 function wifinet.__init__(self, net, data)
1439         self.sid = net
1440
1441         local n = 0
1442         local num = { }
1443         local netid, sid
1444         _uci:foreach("wireless", "wifi-iface",
1445                 function(s)
1446                         n = n + 1
1447                         if s.device then
1448                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1449                                 if s['.name'] == self.sid then
1450                                         sid = "@wifi-iface[%d]" % n
1451                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1452                                         return false
1453                                 end
1454                         end
1455                 end)
1456
1457         if sid then
1458                 local _, k, r, i
1459                 for k, r in pairs(_ubuswificache) do
1460                         if type(r) == "table" and
1461                            type(r.interfaces) == "table"
1462                         then
1463                                 for _, i in ipairs(r.interfaces) do
1464                                         if type(i) == "table" and i.section == sid then
1465                                                 self._ubusdata = {
1466                                                         radio = k,
1467                                                         dev = r,
1468                                                         net = i
1469                                                 }
1470                                         end
1471                                 end
1472                         end
1473                 end
1474         end
1475
1476         local dev = _wifi_state("section", self.sid, "ifname") or netid
1477
1478         self.netid  = netid
1479         self.wdev   = dev
1480         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1481 end
1482
1483 function wifinet.ubus(self, ...)
1484         local n, v = self._ubusdata
1485         for n = 1, select('#', ...) do
1486                 if type(v) == "table" then
1487                         v = v[select(n, ...)]
1488                 else
1489                         return nil
1490                 end
1491         end
1492         return v
1493 end
1494
1495 function wifinet.get(self, opt)
1496         return _get("wireless", self.sid, opt)
1497 end
1498
1499 function wifinet.set(self, opt, val)
1500         return _set("wireless", self.sid, opt, val)
1501 end
1502
1503 function wifinet.mode(self)
1504         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1505 end
1506
1507 function wifinet.ssid(self)
1508         return self:ubus("net", "config", "ssid") or self:get("ssid")
1509 end
1510
1511 function wifinet.bssid(self)
1512         return self:ubus("net", "config", "bssid") or self:get("bssid")
1513 end
1514
1515 function wifinet.network(self)
1516         local net, networks = nil, { }
1517         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1518                 networks[#networks+1] = net
1519         end
1520         return networks
1521 end
1522
1523 function wifinet.id(self)
1524         return self.netid
1525 end
1526
1527 function wifinet.name(self)
1528         return self.sid
1529 end
1530
1531 function wifinet.ifname(self)
1532         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1533         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1534                 ifname = self.wdev
1535         end
1536         return ifname
1537 end
1538
1539 function wifinet.get_device(self)
1540         local dev = self:ubus("radio") or self:get("device")
1541         return dev and wifidev(dev) or nil
1542 end
1543
1544 function wifinet.is_up(self)
1545         local ifc = self:get_interface()
1546         return (ifc and ifc:is_up() or false)
1547 end
1548
1549 function wifinet.active_mode(self)
1550         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1551
1552         if     m == "ap"      then m = "Master"
1553         elseif m == "sta"     then m = "Client"
1554         elseif m == "adhoc"   then m = "Ad-Hoc"
1555         elseif m == "mesh"    then m = "Mesh"
1556         elseif m == "monitor" then m = "Monitor"
1557         end
1558
1559         return m
1560 end
1561
1562 function wifinet.active_mode_i18n(self)
1563         return lng.translate(self:active_mode())
1564 end
1565
1566 function wifinet.active_ssid(self)
1567         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1568 end
1569
1570 function wifinet.active_bssid(self)
1571         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1572 end
1573
1574 function wifinet.active_encryption(self)
1575         local enc = self.iwinfo and self.iwinfo.encryption
1576         return enc and enc.description or "-"
1577 end
1578
1579 function wifinet.assoclist(self)
1580         return self.iwinfo.assoclist or { }
1581 end
1582
1583 function wifinet.frequency(self)
1584         local freq = self.iwinfo.frequency
1585         if freq and freq > 0 then
1586                 return "%.03f" % (freq / 1000)
1587         end
1588 end
1589
1590 function wifinet.bitrate(self)
1591         local rate = self.iwinfo.bitrate
1592         if rate and rate > 0 then
1593                 return (rate / 1000)
1594         end
1595 end
1596
1597 function wifinet.channel(self)
1598         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1599                 tonumber(self:get("channel"))
1600 end
1601
1602 function wifinet.signal(self)
1603         return self.iwinfo.signal or 0
1604 end
1605
1606 function wifinet.noise(self)
1607         return self.iwinfo.noise or 0
1608 end
1609
1610 function wifinet.country(self)
1611         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1612 end
1613
1614 function wifinet.txpower(self)
1615         local pwr = (self.iwinfo.txpower or 0)
1616         return pwr + self:txpower_offset()
1617 end
1618
1619 function wifinet.txpower_offset(self)
1620         return self.iwinfo.txpower_offset or 0
1621 end
1622
1623 function wifinet.signal_level(self, s, n)
1624         if self:active_bssid() ~= "00:00:00:00:00:00" then
1625                 local signal = s or self:signal()
1626                 local noise  = n or self:noise()
1627
1628                 if signal < 0 and noise < 0 then
1629                         local snr = -1 * (noise - signal)
1630                         return math.floor(snr / 5)
1631                 else
1632                         return 0
1633                 end
1634         else
1635                 return -1
1636         end
1637 end
1638
1639 function wifinet.signal_percent(self)
1640         local qc = self.iwinfo.quality or 0
1641         local qm = self.iwinfo.quality_max or 0
1642
1643         if qc > 0 and qm > 0 then
1644                 return math.floor((100 / qm) * qc)
1645         else
1646                 return 0
1647         end
1648 end
1649
1650 function wifinet.shortname(self)
1651         return "%s %q" %{
1652                 lng.translate(self:active_mode()),
1653                 self:active_ssid() or self:active_bssid() or self:id()
1654         }
1655 end
1656
1657 function wifinet.get_i18n(self)
1658         return "%s: %s %q (%s)" %{
1659                 lng.translate("Wireless Network"),
1660                 lng.translate(self:active_mode()),
1661                 self:active_ssid() or self:active_bssid() or self:id(),
1662                 self:ifname()
1663         }
1664 end
1665
1666 function wifinet.adminlink(self)
1667         return dsp.build_url("admin", "network", "wireless", self.netid)
1668 end
1669
1670 function wifinet.get_network(self)
1671         return self:get_networks()[1]
1672 end
1673
1674 function wifinet.get_networks(self)
1675         local nets = { }
1676         local net
1677         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1678                 if _uci:get("network", net) == "interface" then
1679                         nets[#nets+1] = network(net)
1680                 end
1681         end
1682         table.sort(nets, function(a, b) return a.sid < b.sid end)
1683         return nets
1684 end
1685
1686 function wifinet.get_interface(self)
1687         return interface(self:ifname())
1688 end
1689
1690
1691 -- setup base protocols
1692 _M:register_protocol("static")
1693 _M:register_protocol("dhcp")
1694 _M:register_protocol("none")
1695
1696 -- load protocol extensions
1697 local exts = nfs.dir(utl.libpath() .. "/model/network")
1698 if exts then
1699         local ext
1700         for ext in exts do
1701                 if ext:match("%.lua$") then
1702                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1703                 end
1704         end
1705 end