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