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