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