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