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