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