dd2bdfb90633517a20677dcd4d54f1b10e7d4941
[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.ifname or "%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 self.wif = wifinet(wif) end
931
932         self.ifname  = self.ifname or ifname
933         self.dev     = _interfaces[self.ifname]
934         self.network = network
935 end
936
937 function interface.name(self)
938         return self.wif and self.wif:ifname() or self.ifname
939 end
940
941 function interface.mac(self)
942         return (self.dev and self.dev.macaddr or "00:00:00:00:00:00"):upper()
943 end
944
945 function interface.ipaddrs(self)
946         return self.dev and self.dev.ipaddrs or { }
947 end
948
949 function interface.ip6addrs(self)
950         return self.dev and self.dev.ip6addrs or { }
951 end
952
953 function interface.type(self)
954         if self.wif or _wifi_iface(self.ifname) then
955                 return "wifi"
956         elseif _bridge[self.ifname] then
957                 return "bridge"
958         elseif _tunnel[self.ifname] then
959                 return "tunnel"
960         elseif self.ifname:match("%.") then
961                 return "vlan"
962         elseif _switch[self.ifname] then
963                 return "switch"
964         else
965                 return "ethernet"
966         end
967 end
968
969 function interface.shortname(self)
970         if self.wif then
971                 return "%s %q" %{
972                         self.wif:active_mode(),
973                         self.wif:active_ssid() or self.wif:active_bssid()
974                 }
975         else
976                 return self.ifname
977         end
978 end
979
980 function interface.get_i18n(self)
981         if self.wif then
982                 return "%s: %s %q" %{
983                         i18n.translate("Wireless Network"),
984                         self.wif:active_mode(),
985                         self.wif:active_ssid() or self.wif:active_bssid()
986                 }
987         else
988                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
989         end
990 end
991
992 function interface.get_type_i18n(self)
993         local x = self:type()
994         if x == "wifi" then
995                 return i18n.translate("Wireless Adapter")
996         elseif x == "bridge" then
997                 return i18n.translate("Bridge")
998         elseif x == "switch" then
999                 return i18n.translate("Ethernet Switch")
1000         elseif x == "vlan" then
1001                 return i18n.translate("VLAN Interface")
1002         elseif x == "tunnel" then
1003                 return i18n.translate("Tunnel Interface")
1004         else
1005                 return i18n.translate("Ethernet Adapter")
1006         end
1007 end
1008
1009 function interface.adminlink(self)
1010         if self.wif then
1011                 return self.wif:adminlink()
1012         end
1013 end
1014
1015 function interface.ports(self)
1016         if self.br then
1017                 local iface
1018                 local ifaces = { }
1019                 for _, iface in ipairs(self.br.ifnames) do
1020                         ifaces[#ifaces+1] = interface(iface.name)
1021                 end
1022                 return ifaces
1023         end
1024 end
1025
1026 function interface.bridge_id(self)
1027         if self.br then
1028                 return self.br.id
1029         else
1030                 return nil
1031         end
1032 end
1033
1034 function interface.bridge_stp(self)
1035         if self.br then
1036                 return self.br.stp
1037         else
1038                 return false
1039         end
1040 end
1041
1042 function interface.is_up(self)
1043         if self.wif then
1044                 return self.wif:is_up()
1045         else
1046                 return self.dev and self.dev.flags and self.dev.flags.up or false
1047         end
1048 end
1049
1050 function interface.is_bridge(self)
1051         return (self:type() == "bridge")
1052 end
1053
1054 function interface.is_bridgeport(self)
1055         return self.dev and self.dev.bridge and true or false
1056 end
1057
1058 function interface.tx_bytes(self)
1059         return self.dev and self.dev.stats
1060                 and self.dev.stats.tx_bytes or 0
1061 end
1062
1063 function interface.rx_bytes(self)
1064         return self.dev and self.dev.stats
1065                 and self.dev.stats.rx_bytes or 0
1066 end
1067
1068 function interface.tx_packets(self)
1069         return self.dev and self.dev.stats
1070                 and self.dev.stats.tx_packets or 0
1071 end
1072
1073 function interface.rx_packets(self)
1074         return self.dev and self.dev.stats
1075                 and self.dev.stats.rx_packets or 0
1076 end
1077
1078 function interface.get_network(self)
1079         if not self.network then
1080                 if self.dev and self.dev.network then
1081                         self.network = _M:get_network(self.dev.network)
1082                 end
1083         end
1084
1085         if not self.network then
1086                 local net
1087                 for _, net in ipairs(_M:get_networks()) do
1088                         if net:contains_interface(self.ifname) or
1089                            net:ifname() == self.ifname
1090                         then
1091                                 self.network = net
1092                                 return net
1093                         end
1094                 end
1095         else
1096                 return self.network
1097         end
1098 end
1099
1100 function interface.get_wifinet(self)
1101         return self.wif
1102 end
1103
1104
1105 wifidev = utl.class()
1106
1107 function wifidev.__init__(self, dev)
1108         self.sid    = dev
1109         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1110 end
1111
1112 function wifidev.get(self, opt)
1113         return _get("wireless", self.sid, opt)
1114 end
1115
1116 function wifidev.set(self, opt, val)
1117         return _set("wireless", self.sid, opt, val)
1118 end
1119
1120 function wifidev.name(self)
1121         return self.sid
1122 end
1123
1124 function wifidev.hwmodes(self)
1125         local l = self.iwinfo.hwmodelist
1126         if l and next(l) then
1127                 return l
1128         else
1129                 return { b = true, g = true }
1130         end
1131 end
1132
1133 function wifidev.get_i18n(self)
1134         local t = "Generic"
1135         if self.iwinfo.type == "wl" then
1136                 t = "Broadcom"
1137         elseif self.iwinfo.type == "madwifi" then
1138                 t = "Atheros"
1139         end
1140
1141         local m = ""
1142         local l = self:hwmodes()
1143         if l.a then m = m .. "a" end
1144         if l.b then m = m .. "b" end
1145         if l.g then m = m .. "g" end
1146         if l.n then m = m .. "n" end
1147
1148         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1149 end
1150
1151 function wifidev.is_up(self)
1152         local up = false
1153
1154         _uci_state:foreach("wireless", "wifi-iface",
1155                 function(s)
1156                         if s.device == self.sid then
1157                                 if s.up == "1" then
1158                                         up = true
1159                                         return false
1160                                 end
1161                         end
1162                 end)
1163
1164         return up
1165 end
1166
1167 function wifidev.get_wifinet(self, net)
1168         if _uci_real:get("wireless", net) == "wifi-iface" then
1169                 return wifinet(net)
1170         else
1171                 local wnet = _wifi_lookup(net)
1172                 if wnet then
1173                         return wifinet(wnet)
1174                 end
1175         end
1176 end
1177
1178 function wifidev.get_wifinets(self)
1179         local nets = { }
1180
1181         _uci_real:foreach("wireless", "wifi-iface",
1182                 function(s)
1183                         if s.device == self.sid then
1184                                 nets[#nets+1] = wifinet(s['.name'])
1185                         end
1186                 end)
1187
1188         return nets
1189 end
1190
1191 function wifidev.add_wifinet(self, options)
1192         options = options or { }
1193         options.device = self.sid
1194
1195         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1196         if wnet then
1197                 return wifinet(wnet, options)
1198         end
1199 end
1200
1201 function wifidev.del_wifinet(self, net)
1202         if utl.instanceof(net, wifinet) then
1203                 net = net.sid
1204         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1205                 net = _wifi_lookup(net)
1206         end
1207
1208         if net and _uci_real:get("wireless", net, "device") == self.sid then
1209                 _uci_real:delete("wireless", net)
1210                 return true
1211         end
1212
1213         return false
1214 end
1215
1216
1217 wifinet = utl.class()
1218
1219 function wifinet.__init__(self, net, data)
1220         self.sid = net
1221
1222         local num = { }
1223         local netid
1224         _uci_real:foreach("wireless", "wifi-iface",
1225                 function(s)
1226                         if s.device then
1227                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1228                                 if s['.name'] == self.sid then
1229                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1230                                         return false
1231                                 end
1232                         end
1233                 end)
1234
1235         local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1236
1237         self.netid  = netid
1238         self.wdev   = dev
1239         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1240         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1241                 _uci_real:get_all("wireless", self.sid) or { }
1242 end
1243
1244 function wifinet.get(self, opt)
1245         return _get("wireless", self.sid, opt)
1246 end
1247
1248 function wifinet.set(self, opt, val)
1249         return _set("wireless", self.sid, opt, val)
1250 end
1251
1252 function wifinet.mode(self)
1253         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1254 end
1255
1256 function wifinet.ssid(self)
1257         return _uci_state:get("wireless", self.sid, "ssid")
1258 end
1259
1260 function wifinet.bssid(self)
1261         return _uci_state:get("wireless", self.sid, "bssid")
1262 end
1263
1264 function wifinet.network(self)
1265         return _uci_state:get("wifinet", self.sid, "network")
1266 end
1267
1268 function wifinet.id(self)
1269         return self.netid
1270 end
1271
1272 function wifinet.name(self)
1273         return self.sid
1274 end
1275
1276 function wifinet.ifname(self)
1277         local ifname = self.iwinfo.ifname
1278         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1279                 ifname = self.wdev
1280         end
1281         return ifname
1282 end
1283
1284 function wifinet.get_device(self)
1285         if self.iwdata.device then
1286                 return wifidev(self.iwdata.device)
1287         end
1288 end
1289
1290 function wifinet.is_up(self)
1291         return (self.iwdata.up == "1")
1292 end
1293
1294 function wifinet.active_mode(self)
1295         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1296
1297         if     m == "ap"      then m = "Master"
1298         elseif m == "sta"     then m = "Client"
1299         elseif m == "adhoc"   then m = "Ad-Hoc"
1300         elseif m == "mesh"    then m = "Mesh"
1301         elseif m == "monitor" then m = "Monitor"
1302         end
1303
1304         return m
1305 end
1306
1307 function wifinet.active_mode_i18n(self)
1308         return i18n.translate(self:active_mode())
1309 end
1310
1311 function wifinet.active_ssid(self)
1312         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1313 end
1314
1315 function wifinet.active_bssid(self)
1316         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1317 end
1318
1319 function wifinet.active_encryption(self)
1320         local enc = self.iwinfo and self.iwinfo.encryption
1321         return enc and enc.description or "-"
1322 end
1323
1324 function wifinet.assoclist(self)
1325         return self.iwinfo.assoclist or { }
1326 end
1327
1328 function wifinet.frequency(self)
1329         local freq = self.iwinfo.frequency
1330         if freq and freq > 0 then
1331                 return "%.03f" % (freq / 1000)
1332         end
1333 end
1334
1335 function wifinet.bitrate(self)
1336         local rate = self.iwinfo.bitrate
1337         if rate and rate > 0 then
1338                 return (rate / 1000)
1339         end
1340 end
1341
1342 function wifinet.channel(self)
1343         return self.iwinfo.channel or
1344                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1345 end
1346
1347 function wifinet.signal(self)
1348         return self.iwinfo.signal or 0
1349 end
1350
1351 function wifinet.noise(self)
1352         return self.iwinfo.noise or 0
1353 end
1354
1355 function wifinet.country(self)
1356         return self.iwinfo.country or "00"
1357 end
1358
1359 function wifinet.txpower(self)
1360         return self.iwinfo.txpower or 0
1361 end
1362
1363 function wifinet.signal_level(self, s, n)
1364         if self:active_bssid() ~= "00:00:00:00:00:00" then
1365                 local signal = s or self:signal()
1366                 local noise  = n or self:noise()
1367
1368                 if signal < 0 and noise < 0 then
1369                         local snr = -1 * (noise - signal)
1370                         return math.floor(snr / 5)
1371                 else
1372                         return 0
1373                 end
1374         else
1375                 return -1
1376         end
1377 end
1378
1379 function wifinet.signal_percent(self)
1380         local qc = self.iwinfo.quality or 0
1381         local qm = self.iwinfo.quality_max or 0
1382
1383         if qc > 0 and qm > 0 then
1384                 return math.floor((100 / qm) * qc)
1385         else
1386                 return 0
1387         end
1388 end
1389
1390 function wifinet.shortname(self)
1391         return "%s %q" %{
1392                 i18n.translate(self:active_mode()),
1393                 self:active_ssid() or self:active_bssid()
1394         }
1395 end
1396
1397 function wifinet.get_i18n(self)
1398         return "%s: %s %q (%s)" %{
1399                 i18n.translate("Wireless Network"),
1400                 i18n.translate(self:active_mode()),
1401                 self:active_ssid() or self:active_bssid(),
1402                 self:ifname()
1403         }
1404 end
1405
1406 function wifinet.adminlink(self)
1407         return dsp.build_url("admin", "network", "wireless", self.netid)
1408 end
1409
1410 function wifinet.get_network(self)
1411         if _uci_real:get("network", self.iwdata.network) == "interface" then
1412                 return network(self.iwdata.network)
1413         end
1414 end
1415
1416 function wifinet.get_interface(self)
1417         return interface(self:ifname())
1418 end
1419
1420
1421 -- setup base protocols
1422 _M:register_protocol("static")
1423 _M:register_protocol("dhcp")
1424 _M:register_protocol("none")
1425
1426 -- load protocol extensions
1427 local exts = nfs.dir(utl.libpath() .. "/model/network")
1428 if exts then
1429         local ext
1430         for ext in exts do
1431                 if ext:match("%.lua$") then
1432                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1433                 end
1434         end
1435 end