libs/core: fix status reporting of standalone wifi iface in network model
[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         local baseof = { }
453
454         -- find normal interfaces
455         _uci_real:foreach("network", "interface",
456                 function(s)
457                         for iface in utl.imatch(s.ifname) do
458                                 if not _iface_ignore(iface) and not _wifi_iface(iface) then
459                                         seen[iface] = true
460                                         nfs[iface] = interface(iface)
461                                 end
462                         end
463                 end)
464
465         for iface in utl.kspairs(_interfaces) do
466                 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
467                         nfs[iface] = interface(iface)
468                 end
469         end
470
471         -- find vlan interfaces
472         _uci_real:foreach("network", "switch_vlan",
473                 function(s)
474                         if not s.device then
475                                 return
476                         end
477
478                         local base = baseof[s.device]
479                         if not base then
480                                 if not s.device:match("^eth%d") then
481                                         local l
482                                         for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
483                                                 if not base then
484                                                         base = l:match("^%w+: (%w+)")
485                                                 end
486                                         end
487                                         if not base or not base:match("^eth%d") then
488                                                 base = "eth0"
489                                         end
490                                 else
491                                         base = s.device
492                                 end
493                                 baseof[s.device] = base
494                         end
495
496                         local vid = tonumber(s.vid or s.vlan)
497                         if vid ~= nil and vid >= 0 and vid <= 4095 then
498                                 local iface = "%s.%d" %{ base, vid }
499                                 if not seen[iface] then
500                                         seen[iface] = true
501                                         nfs[iface] = interface(iface)
502                                 end
503                         end
504                 end)
505
506         for iface in utl.kspairs(nfs) do
507                 ifaces[#ifaces+1] = nfs[iface]
508         end
509
510         -- find wifi interfaces
511         local num = { }
512         local wfs = { }
513         _uci_real:foreach("wireless", "wifi-iface",
514                 function(s)
515                         if s.device then
516                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
517                                 local i = "%s.network%d" %{ s.device, num[s.device] }
518                                 wfs[i] = interface(i)
519                         end
520                 end)
521
522         for iface in utl.kspairs(wfs) do
523                 ifaces[#ifaces+1] = wfs[iface]
524         end
525
526         return ifaces
527 end
528
529 function ignore_interface(self, x)
530         return _iface_ignore(x)
531 end
532
533 function get_wifidev(self, dev)
534         if _uci_real:get("wireless", dev) == "wifi-device" then
535                 return wifidev(dev)
536         end
537 end
538
539 function get_wifidevs(self)
540         local devs = { }
541         local wfd  = { }
542
543         _uci_real:foreach("wireless", "wifi-device",
544                 function(s) wfd[#wfd+1] = s['.name'] end)
545
546         local dev
547         for _, dev in utl.vspairs(wfd) do
548                 devs[#devs+1] = wifidev(dev)
549         end
550
551         return devs
552 end
553
554 function get_wifinet(self, net)
555         local wnet = _wifi_lookup(net)
556         if wnet then
557                 return wifinet(wnet)
558         end
559 end
560
561 function add_wifinet(self, net, options)
562         if type(options) == "table" and options.device and
563                 _uci_real:get("wireless", options.device) == "wifi-device"
564         then
565                 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
566                 return wifinet(wnet)
567         end
568 end
569
570 function del_wifinet(self, net)
571         local wnet = _wifi_lookup(net)
572         if wnet then
573                 _uci_real:delete("wireless", wnet)
574                 return true
575         end
576         return false
577 end
578
579
580 function network(name, proto)
581         if name then
582                 local p = proto or _uci_real:get("network", name, "proto")
583                 local c = p and _protocols[p] or protocol
584                 return c(name)
585         end
586 end
587
588 function protocol.__init__(self, name)
589         self.sid = name
590 end
591
592 function protocol._get(self, opt)
593         local v = _uci_real:get("network", self.sid, opt)
594         if type(v) == "table" then
595                 return table.concat(v, " ")
596         end
597         return v or ""
598 end
599
600 function protocol._ip(self, opt, family, list)
601         local ip = _uci_state:get("network", self.sid, opt)
602         local fc = (family == 6) and ipc.IPv6 or ipc.IPv4
603         if ip or list then
604                 if list then
605                         local l = { }
606                         for ip in utl.imatch(ip) do
607                                 ip = fc(ip)
608                                 if ip then l[#l+1] = ip:string() end
609                         end
610                         return l
611                 else
612                         ip = fc(ip)
613                         return ip and ip:string()
614                 end
615         end
616 end
617
618 function protocol.get(self, opt)
619         return _get("network", self.sid, opt)
620 end
621
622 function protocol.set(self, opt, val)
623         return _set("network", self.sid, opt, val)
624 end
625
626 function protocol.ifname(self)
627         local p = self:proto()
628         if self:is_bridge() then
629                 return "br-" .. self.sid
630         elseif self:is_virtual() then
631                 return p .. "-" .. self.sid
632         else
633                 local num = { }
634                 local dev = _uci_real:get("network", self.sid, "ifname") or
635                         _uci_state:get("network", self.sid, "ifname")
636
637                 dev = (type(dev) == "table") and dev[1] or dev
638                 dev = (dev ~= nil) and dev:match("%S+")
639
640                 if not dev then
641                         _uci_real:foreach("wireless", "wifi-iface",
642                                 function(s)
643                                         if s.device then
644                                                 num[s.device] = num[s.device]
645                                                         and num[s.device] + 1 or 1
646
647                                                 if s.network == self.sid then
648                                                         dev = "%s.network%d" %{ s.device, num[s.device] }
649                                                         return false
650                                                 end
651                                         end
652                                 end)
653                 end
654
655                 return dev
656         end
657 end
658
659 function protocol.proto(self)
660         return "none"
661 end
662
663 function protocol.get_i18n(self)
664         local p = self:proto()
665         if p == "none" then
666                 return i18n.translate("Unmanaged")
667         elseif p == "static" then
668                 return i18n.translate("Static address")
669         elseif p == "dhcp" then
670                 return i18n.translate("DHCP client")
671         else
672                 return i18n.translate("Unknown")
673         end
674 end
675
676 function protocol.type(self)
677         return self:_get("type")
678 end
679
680 function protocol.name(self)
681         return self.sid
682 end
683
684 function protocol.uptime(self)
685         local cnt = tonumber(_uci_state:get("network", self.sid, "connect_time"))
686         if cnt ~= nil then
687                 return nxo.sysinfo().uptime - cnt
688         else
689                 return 0
690         end
691 end
692
693 function protocol.expires(self)
694         local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
695         local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
696         if a and l then
697                 l = l - (nxo.sysinfo().uptime - a)
698                 return l > 0 and l or 0
699         end
700         return -1
701 end
702
703 function protocol.metric(self)
704         return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
705 end
706
707 function protocol.ipaddr(self)
708         return self:_ip("ipaddr", 4)
709 end
710
711 function protocol.netmask(self)
712         return self:_ip("netmask", 4)
713 end
714
715 function protocol.gwaddr(self)
716         return self:_ip("gateway", 4)
717 end
718
719 function protocol.dnsaddrs(self)
720         return self:_ip("dns", 4, true)
721 end
722
723 function protocol.ip6addr(self)
724         local ip6 = self:_ip("ip6addr", 6)
725         if not ip6 then
726                 local ifc = _interfaces[self:ifname()]
727                 if ifc and ifc.ip6addrs then
728                         local a
729                         for _, a in ipairs(ifc.ip6addrs) do
730                                 if not a:is6linklocal() then
731                                         ip6 = a:string()
732                                         break
733                                 end
734                         end
735                 end
736         end
737         return ip6
738 end
739
740 function protocol.gw6addr(self)
741         local ip6 = self:_ip("ip6gw", 6)
742         if not ip6 then
743                 local dr6 = sys.net.defaultroute6()
744                 if dr6 and dr6.device == self:ifname() then
745                         return dr6.nexthop:string()
746                 end
747         end
748         return ip6
749 end
750
751 function protocol.dns6addrs(self)
752         return self:_ip("dns", 6, true)
753 end
754
755 function protocol.is_bridge(self)
756         return (not self:is_virtual() and self:type() == "bridge")
757 end
758
759 function protocol.opkg_package(self)
760         return nil
761 end
762
763 function protocol.is_installed(self)
764         return true
765 end
766
767 function protocol.is_virtual(self)
768         return false
769 end
770
771 function protocol.is_floating(self)
772         return false
773 end
774
775 function protocol.is_empty(self)
776         if self:is_floating() then
777                 return false
778         else
779                 local rv = true
780
781                 if (self:_get("ifname") or ""):match("%S+") then
782                         rv = false
783                 end
784
785                 _uci_real:foreach("wireless", "wifi-iface",
786                         function(s)
787                                 if s.network == self.sid then
788                                         rv = false
789                                         return false
790                                 end
791                         end)
792
793                 return rv
794         end
795 end
796
797 function protocol.add_interface(self, ifname)
798         ifname = _M:ifnameof(ifname)
799         if ifname and not self:is_floating() then
800                 -- remove the interface from all ifaces
801                 _uci_real:foreach("network", "interface",
802                         function(s)
803                                 _filter("network", s['.name'], "ifname", ifname)
804                         end)
805
806                 -- if its a wifi interface, change its network option
807                 local wif = _wifi_lookup(ifname)
808                 if wif then
809                         _uci_real:set("wireless", wif, "network", self.sid)
810
811                 -- add iface to our iface list
812                 else
813                         _append("network", self.sid, "ifname", ifname)
814                 end
815         end
816 end
817
818 function protocol.del_interface(self, ifname)
819         ifname = _M:ifnameof(ifname)
820         if ifname and not self:is_floating() then
821                 -- if its a wireless interface, clear its network option
822                 local wif = _wifi_lookup(ifname)
823                 if wif then     _uci_real:delete("wireless", wif, "network") end
824
825                 -- remove the interface
826                 _filter("network", self.sid, "ifname", ifname)
827         end
828 end
829
830 function protocol.get_interface(self)
831         if self:is_virtual() then
832                 _tunnel[self:proto() .. "-" .. self.sid] = true
833                 return interface(self:proto() .. "-" .. self.sid, self)
834         elseif self:is_bridge() then
835                 _bridge["br-" .. self.sid] = true
836                 return interface("br-" .. self.sid, self)
837         else
838                 local ifn = nil
839                 local num = { }
840                 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
841                         ifn = ifn:match("^[^:/]+")
842                         return ifn and interface(ifn, self)
843                 end
844                 ifn = nil
845                 _uci_real:foreach("wireless", "wifi-iface",
846                         function(s)
847                                 if s.device then
848                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
849                                         if s.network == self.sid then
850                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
851                                                 return false
852                                         end
853                                 end
854                         end)
855                 return ifn and interface(ifn, self)
856         end
857 end
858
859 function protocol.get_interfaces(self)
860         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
861                 local ifaces = { }
862
863                 local ifn
864                 local nfs = { }
865                 for ifn in utl.imatch(self:get("ifname")) do
866                         ifn = ifn:match("^[^:/]+")
867                         nfs[ifn] = interface(ifn, self)
868                 end
869
870                 for ifn in utl.kspairs(nfs) do
871                         ifaces[#ifaces+1] = nfs[ifn]
872                 end
873
874                 local num = { }
875                 local wfs = { }
876                 _uci_real:foreach("wireless", "wifi-iface",
877                         function(s)
878                                 if s.device then
879                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
880                                         if s.network == self.sid then
881                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
882                                                 wfs[ifn] = interface(ifn, self)
883                                         end
884                                 end
885                         end)
886
887                 for ifn in utl.kspairs(wfs) do
888                         ifaces[#ifaces+1] = wfs[ifn]
889                 end
890
891                 return ifaces
892         end
893 end
894
895 function protocol.contains_interface(self, ifname)
896         ifname = _M:ifnameof(ifname)
897         if not ifname then
898                 return false
899         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
900                 return true
901         elseif self:is_bridge() and "br-" .. self.sid == ifname then
902                 return true
903         else
904                 local ifn
905                 for ifn in utl.imatch(self:get("ifname")) do
906                         ifn = ifn:match("[^:]+")
907                         if ifn == ifname then
908                                 return true
909                         end
910                 end
911
912                 local wif = _wifi_lookup(ifname)
913                 if wif then
914                         return (_uci_real:get("wireless", wif, "network") == self.sid)
915                 end
916         end
917
918         return false
919 end
920
921 function protocol.adminlink(self)
922         return dsp.build_url("admin", "network", "network", self.sid)
923 end
924
925
926 interface = utl.class()
927
928 function interface.__init__(self, ifname, network)
929         local wif = _wifi_lookup(ifname)
930         if wif then 
931                 self.wif    = wifinet(wif) 
932                 self.ifname = _uci_state:get("wireless", wif, "ifname")
933         end
934
935         self.ifname  = self.ifname or ifname
936         self.dev     = _interfaces[self.ifname]
937         self.network = network
938 end
939
940 function interface.name(self)
941         return self.wif and self.wif:ifname() or self.ifname
942 end
943
944 function interface.mac(self)
945         return (self.dev and self.dev.macaddr or "00:00:00:00:00:00"):upper()
946 end
947
948 function interface.ipaddrs(self)
949         return self.dev and self.dev.ipaddrs or { }
950 end
951
952 function interface.ip6addrs(self)
953         return self.dev and self.dev.ip6addrs or { }
954 end
955
956 function interface.type(self)
957         if self.wif or _wifi_iface(self.ifname) then
958                 return "wifi"
959         elseif _bridge[self.ifname] then
960                 return "bridge"
961         elseif _tunnel[self.ifname] then
962                 return "tunnel"
963         elseif self.ifname:match("%.") then
964                 return "vlan"
965         elseif _switch[self.ifname] then
966                 return "switch"
967         else
968                 return "ethernet"
969         end
970 end
971
972 function interface.shortname(self)
973         if self.wif then
974                 return "%s %q" %{
975                         self.wif:active_mode(),
976                         self.wif:active_ssid() or self.wif:active_bssid()
977                 }
978         else
979                 return self.ifname
980         end
981 end
982
983 function interface.get_i18n(self)
984         if self.wif then
985                 return "%s: %s %q" %{
986                         i18n.translate("Wireless Network"),
987                         self.wif:active_mode(),
988                         self.wif:active_ssid() or self.wif:active_bssid()
989                 }
990         else
991                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
992         end
993 end
994
995 function interface.get_type_i18n(self)
996         local x = self:type()
997         if x == "wifi" then
998                 return i18n.translate("Wireless Adapter")
999         elseif x == "bridge" then
1000                 return i18n.translate("Bridge")
1001         elseif x == "switch" then
1002                 return i18n.translate("Ethernet Switch")
1003         elseif x == "vlan" then
1004                 return i18n.translate("VLAN Interface")
1005         elseif x == "tunnel" then
1006                 return i18n.translate("Tunnel Interface")
1007         else
1008                 return i18n.translate("Ethernet Adapter")
1009         end
1010 end
1011
1012 function interface.adminlink(self)
1013         if self.wif then
1014                 return self.wif:adminlink()
1015         end
1016 end
1017
1018 function interface.ports(self)
1019         if self.br then
1020                 local iface
1021                 local ifaces = { }
1022                 for _, iface in ipairs(self.br.ifnames) do
1023                         ifaces[#ifaces+1] = interface(iface.name)
1024                 end
1025                 return ifaces
1026         end
1027 end
1028
1029 function interface.bridge_id(self)
1030         if self.br then
1031                 return self.br.id
1032         else
1033                 return nil
1034         end
1035 end
1036
1037 function interface.bridge_stp(self)
1038         if self.br then
1039                 return self.br.stp
1040         else
1041                 return false
1042         end
1043 end
1044
1045 function interface.is_up(self)
1046         if self.wif then
1047                 return self.wif:is_up()
1048         else
1049                 return self.dev and self.dev.flags and self.dev.flags.up or false
1050         end
1051 end
1052
1053 function interface.is_bridge(self)
1054         return (self:type() == "bridge")
1055 end
1056
1057 function interface.is_bridgeport(self)
1058         return self.dev and self.dev.bridge and true or false
1059 end
1060
1061 function interface.tx_bytes(self)
1062         return self.dev and self.dev.stats
1063                 and self.dev.stats.tx_bytes or 0
1064 end
1065
1066 function interface.rx_bytes(self)
1067         return self.dev and self.dev.stats
1068                 and self.dev.stats.rx_bytes or 0
1069 end
1070
1071 function interface.tx_packets(self)
1072         return self.dev and self.dev.stats
1073                 and self.dev.stats.tx_packets or 0
1074 end
1075
1076 function interface.rx_packets(self)
1077         return self.dev and self.dev.stats
1078                 and self.dev.stats.rx_packets or 0
1079 end
1080
1081 function interface.get_network(self)
1082         if not self.network then
1083                 if self.dev and self.dev.network then
1084                         self.network = _M:get_network(self.dev.network)
1085                 end
1086         end
1087
1088         if not self.network then
1089                 local net
1090                 for _, net in ipairs(_M:get_networks()) do
1091                         if net:contains_interface(self.ifname) or
1092                            net:ifname() == self.ifname
1093                         then
1094                                 self.network = net
1095                                 return net
1096                         end
1097                 end
1098         else
1099                 return self.network
1100         end
1101 end
1102
1103 function interface.get_wifinet(self)
1104         return self.wif
1105 end
1106
1107
1108 wifidev = utl.class()
1109
1110 function wifidev.__init__(self, dev)
1111         self.sid    = dev
1112         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1113 end
1114
1115 function wifidev.get(self, opt)
1116         return _get("wireless", self.sid, opt)
1117 end
1118
1119 function wifidev.set(self, opt, val)
1120         return _set("wireless", self.sid, opt, val)
1121 end
1122
1123 function wifidev.name(self)
1124         return self.sid
1125 end
1126
1127 function wifidev.hwmodes(self)
1128         local l = self.iwinfo.hwmodelist
1129         if l and next(l) then
1130                 return l
1131         else
1132                 return { b = true, g = true }
1133         end
1134 end
1135
1136 function wifidev.get_i18n(self)
1137         local t = "Generic"
1138         if self.iwinfo.type == "wl" then
1139                 t = "Broadcom"
1140         elseif self.iwinfo.type == "madwifi" then
1141                 t = "Atheros"
1142         end
1143
1144         local m = ""
1145         local l = self:hwmodes()
1146         if l.a then m = m .. "a" end
1147         if l.b then m = m .. "b" end
1148         if l.g then m = m .. "g" end
1149         if l.n then m = m .. "n" end
1150
1151         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1152 end
1153
1154 function wifidev.is_up(self)
1155         local up = false
1156
1157         _uci_state:foreach("wireless", "wifi-iface",
1158                 function(s)
1159                         if s.device == self.sid then
1160                                 if s.up == "1" then
1161                                         up = true
1162                                         return false
1163                                 end
1164                         end
1165                 end)
1166
1167         return up
1168 end
1169
1170 function wifidev.get_wifinet(self, net)
1171         if _uci_real:get("wireless", net) == "wifi-iface" then
1172                 return wifinet(net)
1173         else
1174                 local wnet = _wifi_lookup(net)
1175                 if wnet then
1176                         return wifinet(wnet)
1177                 end
1178         end
1179 end
1180
1181 function wifidev.get_wifinets(self)
1182         local nets = { }
1183
1184         _uci_real:foreach("wireless", "wifi-iface",
1185                 function(s)
1186                         if s.device == self.sid then
1187                                 nets[#nets+1] = wifinet(s['.name'])
1188                         end
1189                 end)
1190
1191         return nets
1192 end
1193
1194 function wifidev.add_wifinet(self, options)
1195         options = options or { }
1196         options.device = self.sid
1197
1198         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1199         if wnet then
1200                 return wifinet(wnet, options)
1201         end
1202 end
1203
1204 function wifidev.del_wifinet(self, net)
1205         if utl.instanceof(net, wifinet) then
1206                 net = net.sid
1207         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1208                 net = _wifi_lookup(net)
1209         end
1210
1211         if net and _uci_real:get("wireless", net, "device") == self.sid then
1212                 _uci_real:delete("wireless", net)
1213                 return true
1214         end
1215
1216         return false
1217 end
1218
1219
1220 wifinet = utl.class()
1221
1222 function wifinet.__init__(self, net, data)
1223         self.sid = net
1224
1225         local num = { }
1226         local netid
1227         _uci_real:foreach("wireless", "wifi-iface",
1228                 function(s)
1229                         if s.device then
1230                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1231                                 if s['.name'] == self.sid then
1232                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1233                                         return false
1234                                 end
1235                         end
1236                 end)
1237
1238         local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1239
1240         self.netid  = netid
1241         self.wdev   = dev
1242         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1243         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1244                 _uci_real:get_all("wireless", self.sid) or { }
1245 end
1246
1247 function wifinet.get(self, opt)
1248         return _get("wireless", self.sid, opt)
1249 end
1250
1251 function wifinet.set(self, opt, val)
1252         return _set("wireless", self.sid, opt, val)
1253 end
1254
1255 function wifinet.mode(self)
1256         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1257 end
1258
1259 function wifinet.ssid(self)
1260         return _uci_state:get("wireless", self.sid, "ssid")
1261 end
1262
1263 function wifinet.bssid(self)
1264         return _uci_state:get("wireless", self.sid, "bssid")
1265 end
1266
1267 function wifinet.network(self)
1268         return _uci_state:get("wifinet", self.sid, "network")
1269 end
1270
1271 function wifinet.id(self)
1272         return self.netid
1273 end
1274
1275 function wifinet.name(self)
1276         return self.sid
1277 end
1278
1279 function wifinet.ifname(self)
1280         local ifname = self.iwinfo.ifname
1281         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1282                 ifname = self.wdev
1283         end
1284         return ifname
1285 end
1286
1287 function wifinet.get_device(self)
1288         if self.iwdata.device then
1289                 return wifidev(self.iwdata.device)
1290         end
1291 end
1292
1293 function wifinet.is_up(self)
1294         return (self.iwdata.up == "1")
1295 end
1296
1297 function wifinet.active_mode(self)
1298         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1299
1300         if     m == "ap"      then m = "Master"
1301         elseif m == "sta"     then m = "Client"
1302         elseif m == "adhoc"   then m = "Ad-Hoc"
1303         elseif m == "mesh"    then m = "Mesh"
1304         elseif m == "monitor" then m = "Monitor"
1305         end
1306
1307         return m
1308 end
1309
1310 function wifinet.active_mode_i18n(self)
1311         return i18n.translate(self:active_mode())
1312 end
1313
1314 function wifinet.active_ssid(self)
1315         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1316 end
1317
1318 function wifinet.active_bssid(self)
1319         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1320 end
1321
1322 function wifinet.active_encryption(self)
1323         local enc = self.iwinfo and self.iwinfo.encryption
1324         return enc and enc.description or "-"
1325 end
1326
1327 function wifinet.assoclist(self)
1328         return self.iwinfo.assoclist or { }
1329 end
1330
1331 function wifinet.frequency(self)
1332         local freq = self.iwinfo.frequency
1333         if freq and freq > 0 then
1334                 return "%.03f" % (freq / 1000)
1335         end
1336 end
1337
1338 function wifinet.bitrate(self)
1339         local rate = self.iwinfo.bitrate
1340         if rate and rate > 0 then
1341                 return (rate / 1000)
1342         end
1343 end
1344
1345 function wifinet.channel(self)
1346         return self.iwinfo.channel or
1347                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1348 end
1349
1350 function wifinet.signal(self)
1351         return self.iwinfo.signal or 0
1352 end
1353
1354 function wifinet.noise(self)
1355         return self.iwinfo.noise or 0
1356 end
1357
1358 function wifinet.country(self)
1359         return self.iwinfo.country or "00"
1360 end
1361
1362 function wifinet.txpower(self)
1363         return self.iwinfo.txpower or 0
1364 end
1365
1366 function wifinet.signal_level(self, s, n)
1367         if self:active_bssid() ~= "00:00:00:00:00:00" then
1368                 local signal = s or self:signal()
1369                 local noise  = n or self:noise()
1370
1371                 if signal < 0 and noise < 0 then
1372                         local snr = -1 * (noise - signal)
1373                         return math.floor(snr / 5)
1374                 else
1375                         return 0
1376                 end
1377         else
1378                 return -1
1379         end
1380 end
1381
1382 function wifinet.signal_percent(self)
1383         local qc = self.iwinfo.quality or 0
1384         local qm = self.iwinfo.quality_max or 0
1385
1386         if qc > 0 and qm > 0 then
1387                 return math.floor((100 / qm) * qc)
1388         else
1389                 return 0
1390         end
1391 end
1392
1393 function wifinet.shortname(self)
1394         return "%s %q" %{
1395                 i18n.translate(self:active_mode()),
1396                 self:active_ssid() or self:active_bssid()
1397         }
1398 end
1399
1400 function wifinet.get_i18n(self)
1401         return "%s: %s %q (%s)" %{
1402                 i18n.translate("Wireless Network"),
1403                 i18n.translate(self:active_mode()),
1404                 self:active_ssid() or self:active_bssid(),
1405                 self:ifname()
1406         }
1407 end
1408
1409 function wifinet.adminlink(self)
1410         return dsp.build_url("admin", "network", "wireless", self.netid)
1411 end
1412
1413 function wifinet.get_network(self)
1414         if _uci_real:get("network", self.iwdata.network) == "interface" then
1415                 return network(self.iwdata.network)
1416         end
1417 end
1418
1419 function wifinet.get_interface(self)
1420         return interface(self:ifname())
1421 end
1422
1423
1424 -- setup base protocols
1425 _M:register_protocol("static")
1426 _M:register_protocol("dhcp")
1427 _M:register_protocol("none")
1428
1429 -- load protocol extensions
1430 local exts = nfs.dir(utl.libpath() .. "/model/network")
1431 if exts then
1432         local ext
1433         for ext in exts do
1434                 if ext:match("%.lua$") then
1435                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1436                 end
1437         end
1438 end