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