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