libs/core: fix lookups bridge interface -> network in luci.model.network
[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, pairs, ipairs, loadfile, table, tonumber, math, i18n
21         = type, 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_ignore(x)
154         return (
155                 x:match("^wmaster%d") or x:match("^wifi%d") or x:match("^hwsim%d") or
156                 x:match("^imq%d") or x:match("^mon.wlan%d") or x:match("^6in4-%w") or
157                 x:match("^6to4-%w") or x:match("^3g-%w") or x:match("^ppp-%w") or
158                 x:match("^pppoe-%w") or x:match("^pppoa-%w") or x == "sit0" or x == "lo"
159         )
160 end
161
162
163 function init(cursor)
164         uci_r = cursor or uci_r or uci.cursor()
165         uci_s = uci_r:substate()
166
167         ifs = { }
168         brs = { }
169         sws = { }
170
171         -- read interface information
172         local n, i
173         for n, i in ipairs(nxo.getifaddrs()) do
174                 local name = i.name:match("[^:]+")
175                 local prnt = name:match("^([^%.]+)%.")
176
177                 if not _iface_ignore(name) then
178                         ifs[name] = ifs[name] or {
179                                 idx      = i.ifindex or n,
180                                 name     = name,
181                                 rawname  = i.name,
182                                 flags    = { },
183                                 ipaddrs  = { },
184                                 ip6addrs = { }
185                         }
186
187                         if prnt then
188                                 sws[name] = true
189                                 sws[prnt] = true
190                         end
191
192                         if i.family == "packet" then
193                                 ifs[name].flags   = i.flags
194                                 ifs[name].stats   = i.data
195                                 ifs[name].macaddr = i.addr
196                         elseif i.family == "inet" then
197                                 ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
198                         elseif i.family == "inet6" then
199                                 ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
200                         end
201                 end
202         end
203
204         -- read bridge informaton
205         local b, l
206         for l in utl.execi("brctl show") do
207                 if not l:match("STP") then
208                         local r = utl.split(l, "%s+", nil, true)
209                         if #r == 4 then
210                                 b = {
211                                         name    = r[1],
212                                         id      = r[2],
213                                         stp     = r[3] == "yes",
214                                         ifnames = { ifs[r[4]] }
215                                 }
216                                 if b.ifnames[1] then
217                                         b.ifnames[1].bridge = b
218                                 end
219                                 brs[r[1]] = b
220                         elseif b then
221                                 b.ifnames[#b.ifnames+1] = ifs[r[2]]
222                                 b.ifnames[#b.ifnames].bridge = b
223                         end
224                 end
225         end
226
227         return _M
228 end
229
230 function save(self, ...)
231         uci_r:save(...)
232         uci_r:load(...)
233 end
234
235 function commit(self, ...)
236         uci_r:commit(...)
237         uci_r:load(...)
238 end
239
240 function has_ipv6(self)
241         return nfs.access("/proc/net/ipv6_route")
242 end
243
244 function add_network(self, n, options)
245         local oldnet = self:get_network(n)
246         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
247                 if uci_r:section("network", "interface", n, options) then
248                         return network(n)
249                 end
250         elseif oldnet and oldnet:is_empty() then
251                 if options then
252                         local k, v
253                         for k, v in pairs(options) do
254                                 oldnet:set(k, v)
255                         end
256                 end
257                 return oldnet
258         end
259 end
260
261 function get_network(self, n)
262         if n and uci_r:get("network", n) == "interface" then
263                 return network(n)
264         end
265 end
266
267 function get_networks(self)
268         local nets = { }
269         local nls = { }
270
271         uci_r:foreach("network", "interface",
272                 function(s)
273                         nls[s['.name']] = network(s['.name'])
274                 end)
275
276         local n
277         for n in utl.kspairs(nls) do
278                 nets[#nets+1] = nls[n]
279         end
280
281         return nets
282 end
283
284 function del_network(self, n)
285         local r = uci_r:delete("network", n)
286         if r then
287                 uci_r:delete_all("network", "alias",
288                         function(s) return (s.interface == n) end)
289
290                 uci_r:delete_all("network", "route",
291                         function(s) return (s.interface == n) end)
292
293                 uci_r:delete_all("network", "route6",
294                         function(s) return (s.interface == n) end)
295
296                 uci_r:foreach("wireless", "wifi-iface",
297                         function(s)
298                                 if s.network == n then
299                                         uci_r:delete("wireless", s['.name'], "network")
300                                 end
301                         end)
302         end
303         return r
304 end
305
306 function rename_network(self, old, new)
307         local r
308         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
309                 r = uci_r:section("network", "interface", new, uci_r:get_all("network", old))
310
311                 if r then
312                         uci_r:foreach("network", "alias",
313                                 function(s)
314                                         if s.interface == old then
315                                                 uci_r:set("network", s['.name'], "interface", new)
316                                         end
317                                 end)
318
319                         uci_r:foreach("network", "route",
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", "route6",
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("wireless", "wifi-iface",
334                                 function(s)
335                                         if s.network == old then
336                                                 uci_r:set("wireless", s['.name'], "network", new)
337                                         end
338                                 end)
339
340                         uci_r:delete("network", old)
341                 end
342         end
343         return r or false
344 end
345
346 function get_interface(self, i)
347         if ifs[i] or _wifi_iface(i) then
348                 return interface(i)
349         else
350                 local ifc
351                 local num = { }
352                 uci_r:foreach("wireless", "wifi-iface",
353                         function(s)
354                                 if s.device then
355                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
356                                         if s['.name'] == i then
357                                                 ifc = interface(
358                                                         "%s.network%d" %{s.device, num[s.device] })
359                                                 return false
360                                         end
361                                 end
362                         end)
363                 return ifc
364         end
365 end
366
367 function get_interfaces(self)
368         local iface
369         local ifaces = { }
370         local seen = { }
371         local nfs = { }
372
373         -- find normal interfaces
374         uci_r:foreach("network", "interface",
375                 function(s)
376                         for iface in utl.imatch(s.ifname) do
377                                 if not _iface_ignore(iface) and not _wifi_iface(iface) then
378                                         seen[iface] = true
379                                         nfs[iface] = interface(iface)
380                                 end
381                         end
382                 end)
383
384         for iface in utl.kspairs(ifs) do
385                 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
386                         nfs[iface] = interface(iface)
387                 end
388         end
389
390         for iface in utl.kspairs(nfs) do
391                 ifaces[#ifaces+1] = nfs[iface]
392         end
393
394         -- find wifi interfaces
395         local num = { }
396         local wfs = { }
397         uci_r:foreach("wireless", "wifi-iface",
398                 function(s)
399                         if s.device then
400                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
401                                 local i = "%s.network%d" %{ s.device, num[s.device] }
402                                 wfs[i] = interface(i)
403                         end
404                 end)
405
406         for iface in utl.kspairs(wfs) do
407                 ifaces[#ifaces+1] = wfs[iface]
408         end
409
410         return ifaces
411 end
412
413 function ignore_interface(self, x)
414         return _iface_ignore(x)
415 end
416
417 function get_wifidev(self, dev)
418         if uci_r:get("wireless", dev) == "wifi-device" then
419                 return wifidev(dev)
420         end
421 end
422
423 function get_wifidevs(self)
424         local devs = { }
425         local wfd  = { }
426
427         uci_r:foreach("wireless", "wifi-device",
428                 function(s) wfd[#wfd+1] = s['.name'] end)
429
430         local dev
431         for _, dev in utl.vspairs(wfd) do
432                 devs[#devs+1] = wifidev(dev)
433         end
434
435         return devs
436 end
437
438 function get_wifinet(self, net)
439         local wnet = _wifi_lookup(net)
440         if wnet then
441                 return wifinet(wnet)
442         end
443 end
444
445 function add_wifinet(self, net, options)
446         if type(options) == "table" and options.device and
447                 uci_r:get("wireless", options.device) == "wifi-device"
448         then
449                 local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
450                 return wifinet(wnet)
451         end
452 end
453
454 function del_wifinet(self, net)
455         local wnet = _wifi_lookup(net)
456         if wnet then
457                 uci_r:delete("wireless", wnet)
458                 return true
459         end
460         return false
461 end
462
463
464 network = utl.class()
465
466 function network.__init__(self, name)
467         self.sid = name
468 end
469
470 function network._get(self, opt)
471         local v = uci_r:get("network", self.sid, opt)
472         if type(v) == "table" then
473                 return table.concat(v, " ")
474         end
475         return v or ""
476 end
477
478 function network.get(self, opt)
479         return _get("network", self.sid, opt)
480 end
481
482 function network.set(self, opt, val)
483         return _set("network", self.sid, opt, val)
484 end
485
486 function network.ifname(self)
487         local p = self:proto()
488         if self:is_bridge() then
489                 return "br-" .. self.sid
490         elseif self:is_virtual() then
491                 return p .. "-" .. self.sid
492         else
493                 local num = { }
494                 local dev = uci_r:get("network", self.sid, "ifname") or
495                         uci_s:get("network", self.sid, "ifname")
496
497                 dev = (type(dev) == "table") and dev[1] or dev
498                 dev = (dev ~= nil) and dev:match("%S+")
499
500                 if not dev then
501                         uci_r:foreach("wireless", "wifi-iface",
502                                 function(s)
503                                         if s.device then
504                                                 num[s.device] = num[s.device]
505                                                         and num[s.device] + 1 or 1
506
507                                                 if s.network == self.sid then
508                                                         dev = "%s.network%d" %{ s.device, num[s.device] }
509                                                         return false
510                                                 end
511                                         end
512                                 end)
513                 end
514
515                 return dev
516         end
517 end
518
519 function network.device(self)
520         local dev = uci_r:get("network", self.sid, "device") or
521                 uci_s:get("network", self.sid, "device")
522
523         dev = (type(dev) == "table") and dev[1] or dev
524
525         if not dev or dev:match("[^%w%-%.%s]") then
526                 dev = uci_r:get("network", self.sid, "ifname") or
527                         uci_s:get("network", self.sid, "ifname")
528
529                 dev = (type(dev) == "table") and dev[1] or dev
530         end
531
532         return dev
533 end
534
535 function network.proto(self)
536         return self:_get("proto") or "none"
537 end
538
539 function network.type(self)
540         return self:_get("type")
541 end
542
543 function network.name(self)
544         return self.sid
545 end
546
547 function network.uptime(self)
548         local cnt = tonumber(uci_s:get("network", self.sid, "connect_time"))
549         if cnt ~= nil then
550                 return nxo.sysinfo().uptime - cnt
551         else
552                 return 0
553         end
554 end
555
556 function network.is_bridge(self)
557         return (self:type() == "bridge")
558 end
559
560 function network.is_virtual(self)
561         local p = self:proto()
562         return (
563                 p == "3g" or p == "6in4" or p == "6to4" or p == "ppp" or
564                 p == "pppoe" or p == "pppoa"
565         )
566 end
567
568 function network.is_empty(self)
569         if self:is_virtual() then
570                 return false
571         else
572                 local rv = true
573
574                 if (self:_get("ifname") or ""):match("%S+") then
575                         rv = false
576                 end
577
578                 uci_r:foreach("wireless", "wifi-iface",
579                         function(s)
580                                 if s.network == self.sid then
581                                         rv = false
582                                         return false
583                                 end
584                         end)
585
586                 return rv
587         end
588 end
589
590 function network.add_interface(self, ifname)
591         if not self:is_virtual() then
592                 if type(ifname) ~= "string" then
593                         ifname = ifname:name()
594                 else
595                         ifname = ifname:match("[^%s:]+")
596                 end
597
598                 -- remove the interface from all ifaces
599                 uci_r:foreach("network", "interface",
600                         function(s)
601                                 _list_del("network", s['.name'], "ifname", ifname)
602                         end)
603
604                 -- if its a wifi interface, change its network option
605                 local wif = _wifi_lookup(ifname)
606                 if wif then
607                         uci_r:set("wireless", wif, "network", self.sid)
608
609                 -- add iface to our iface list
610                 else
611                         _list_add("network", self.sid, "ifname", ifname)
612                 end
613         end
614 end
615
616 function network.del_interface(self, ifname)
617         if not self:is_virtual() then
618                 if utl.instanceof(ifname, interface) then
619                         ifname = ifname:name()
620                 else
621                         ifname = ifname:match("[^%s:]+")
622                 end
623
624                 -- if its a wireless interface, clear its network option
625                 local wif = _wifi_lookup(ifname)
626                 if wif then     uci_r:delete("wireless", wif, "network") end
627
628                 -- remove the interface
629                 _list_del("network", self.sid, "ifname", ifname)
630         end
631 end
632
633 function network.get_interfaces(self)
634         local ifaces = { }
635
636         local ifn
637         if self:is_virtual() then
638                 ifn = self:proto() .. "-" .. self.sid
639                 ifaces = { interface(ifn) }
640         else
641                 local nfs = { }
642                 for ifn in utl.imatch(self:get("ifname")) do
643                         ifn = ifn:match("[^:]+")
644                         nfs[ifn] = interface(ifn)
645                 end
646
647                 for ifn in utl.kspairs(nfs) do
648                         ifaces[#ifaces+1] = nfs[ifn]
649                 end
650
651                 local num = { }
652                 local wfs = { }
653                 uci_r:foreach("wireless", "wifi-iface",
654                         function(s)
655                                 if s.device then
656                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
657                                         if s.network == self.sid then
658                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
659                                                 wfs[ifn] = interface(ifn)
660                                         end
661                                 end
662                         end)
663
664                 for ifn in utl.kspairs(wfs) do
665                         ifaces[#ifaces+1] = wfs[ifn]
666                 end
667         end
668
669         return ifaces
670 end
671
672 function network.contains_interface(self, ifname)
673         if type(ifname) ~= "string" then
674                 ifname = ifname:name()
675         else
676                 ifname = ifname:match("[^%s:]+")
677         end
678
679         local ifn
680         if self:is_virtual() then
681                 ifn = self:proto() .. "-" .. self.sid
682                 return ifname == ifn
683         else
684                 for ifn in utl.imatch(self:get("ifname")) do
685                         ifn = ifn:match("[^:]+")
686                         if ifn == ifname then
687                                 return true
688                         end
689                 end
690
691                 local wif = _wifi_lookup(ifname)
692                 if wif then
693                         return (uci_r:get("wireless", wif, "network") == self.sid)
694                 end
695         end
696
697         return false
698 end
699
700 function network.adminlink(self)
701         return dsp.build_url("admin", "network", "network", self.sid)
702 end
703
704
705 interface = utl.class()
706 function interface.__init__(self, ifname)
707         local wif = _wifi_lookup(ifname)
708         if wif then self.wif = wifinet(wif) end
709
710         self.ifname = self.ifname or ifname
711         self.dev    = ifs[self.ifname]
712 end
713
714 function interface.name(self)
715         return self.wif and self.wif:ifname() or self.ifname
716 end
717
718 function interface.mac(self)
719         return self.dev and self.dev.macaddr or "00:00:00:00:00:00"
720 end
721
722 function interface.ipaddrs(self)
723         return self.dev and self.dev.ipaddrs or { }
724 end
725
726 function interface.ip6addrs(self)
727         return self.dev and self.dev.ip6addrs or { }
728 end
729
730 function interface.type(self)
731         if self.wif or _wifi_iface(self.ifname) then
732                 return "wifi"
733         elseif brs[self.ifname] then
734                 return "bridge"
735         elseif sws[self.ifname] or self.ifname:match("%.") then
736                 return "switch"
737         else
738                 return "ethernet"
739         end
740 end
741
742 function interface.shortname(self)
743         if self.wif then
744                 return "%s %q" %{
745                         self.wif:active_mode(),
746                         self.wif:active_ssid() or self.wif:active_bssid()
747                 }
748         else
749                 return self.ifname
750         end
751 end
752
753 function interface.get_i18n(self)
754         if self.wif then
755                 return "%s: %s %q" %{
756                         i18n.translate("Wireless Network"),
757                         self.wif:active_mode(),
758                         self.wif:active_ssid() or self.wif:active_bssid()
759                 }
760         else
761                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
762         end
763 end
764
765 function interface.get_type_i18n(self)
766         local x = self:type()
767         if x == "wifi" then
768                 return i18n.translate("Wireless Adapter")
769         elseif x == "bridge" then
770                 return i18n.translate("Bridge")
771         elseif x == "switch" then
772                 return i18n.translate("Ethernet Switch")
773         else
774                 return i18n.translate("Ethernet Adapter")
775         end
776 end
777
778 function interface.adminlink(self)
779         if self.wif then
780                 return self.wif:adminlink()
781         end
782 end
783
784 function interface.ports(self)
785         if self.br then
786                 local iface
787                 local ifaces = { }
788                 for _, iface in ipairs(self.br.ifnames) do
789                         ifaces[#ifaces+1] = interface(iface.name)
790                 end
791                 return ifaces
792         end
793 end
794
795 function interface.bridge_id(self)
796         if self.br then
797                 return self.br.id
798         else
799                 return nil
800         end
801 end
802
803 function interface.bridge_stp(self)
804         if self.br then
805                 return self.br.stp
806         else
807                 return false
808         end
809 end
810
811 function interface.is_up(self)
812         if self.wif then
813                 return self.wif:is_up()
814         else
815                 return self.dev and self.dev.flags and self.dev.flags.up or false
816         end
817 end
818
819 function interface.is_bridge(self)
820         return (self:type() == "bridge")
821 end
822
823 function interface.is_bridgeport(self)
824         return self.dev and self.dev.bridge and true or false
825 end
826
827 function interface.tx_bytes(self)
828         return self.dev and self.dev.stats
829                 and self.dev.stats.tx_bytes or 0
830 end
831
832 function interface.rx_bytes(self)
833         return self.dev and self.dev.stats
834                 and self.dev.stats.rx_bytes or 0
835 end
836
837 function interface.tx_packets(self)
838         return self.dev and self.dev.stats
839                 and self.dev.stats.tx_packets or 0
840 end
841
842 function interface.rx_packets(self)
843         return self.dev and self.dev.stats
844                 and self.dev.stats.rx_packets or 0
845 end
846
847 function interface.get_network(self)
848         if self.dev and self.dev.network then
849                 self.network = _M:get_network(self.dev.network)
850         end
851
852         if not self.network then
853                 local net
854                 for _, net in ipairs(_M:get_networks()) do
855                         if net:contains_interface(self.ifname) or
856                            net:ifname() == self.ifname
857                         then
858                                 self.network = net
859                                 return net
860                         end
861                 end
862         else
863                 return self.network
864         end
865 end
866
867 function interface.get_wifinet(self)
868         return self.wif
869 end
870
871
872 wifidev = utl.class()
873 function wifidev.__init__(self, dev)
874         self.sid = dev
875 end
876
877 function wifidev.get(self, opt)
878         return _get("wireless", self.sid, opt)
879 end
880
881 function wifidev.set(self, opt, val)
882         return _set("wireless", self.sid, opt, val)
883 end
884
885 function wifidev.name(self)
886         return self.sid
887 end
888
889 function wifidev.is_up(self)
890         local up = false
891
892         uci_s:foreach("wireless", "wifi-iface",
893                 function(s)
894                         if s.device == self.sid then
895                                 if s.up == "1" then
896                                         up = true
897                                         return false
898                                 end
899                         end
900                 end)
901
902         return up
903 end
904
905 function wifidev.get_wifinet(self, net)
906         if uci_r:get("wireless", net) == "wifi-iface" then
907                 return wifinet(net)
908         else
909                 local wnet = _wifi_lookup(net)
910                 if wnet then
911                         return wifinet(wnet)
912                 end
913         end
914 end
915
916 function wifidev.get_wifinets(self)
917         local nets = { }
918
919         uci_r:foreach("wireless", "wifi-iface",
920                 function(s)
921                         if s.device == self.sid then
922                                 nets[#nets+1] = wifinet(s['.name'])
923                         end
924                 end)
925
926         return nets
927 end
928
929 function wifidev.add_wifinet(self, options)
930         options = options or { }
931         options.device = self.sid
932
933         local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
934         if wnet then
935                 return wifinet(wnet, options)
936         end
937 end
938
939 function wifidev.del_wifinet(self, net)
940         if utl.instanceof(net, wifinet) then
941                 net = net.sid
942         elseif uci_r:get("wireless", net) ~= "wifi-iface" then
943                 net = _wifi_lookup(net)
944         end
945
946         if net and uci_r:get("wireless", net, "device") == self.sid then
947                 uci_r:delete("wireless", net)
948                 return true
949         end
950
951         return false
952 end
953
954
955 wifinet = utl.class()
956 function wifinet.__init__(self, net, data)
957         self.sid = net
958
959         local num = { }
960         local netid
961         uci_r:foreach("wireless", "wifi-iface",
962                 function(s)
963                         if s.device then
964                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
965                                 if s['.name'] == self.sid then
966                                         netid = "%s.network%d" %{ s.device, num[s.device] }
967                                         return false
968                                 end
969                         end
970                 end)
971
972         local dev = uci_s:get("wireless", self.sid, "ifname") or netid
973
974         self.netid  = netid
975         self.wdev   = dev
976         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
977         self.iwdata = data or uci_s:get_all("wireless", self.sid) or
978                 uci_r:get_all("wireless", self.sid) or { }
979 end
980
981 function wifinet.get(self, opt)
982         return _get("wireless", self.sid, opt)
983 end
984
985 function wifinet.set(self, opt, val)
986         return _set("wireless", self.sid, opt, val)
987 end
988
989 function wifinet.mode(self)
990         return uci_s:get("wireless", self.sid, "mode") or "ap"
991 end
992
993 function wifinet.ssid(self)
994         return uci_s:get("wireless", self.sid, "ssid")
995 end
996
997 function wifinet.bssid(self)
998         return uci_s:get("wireless", self.sid, "bssid")
999 end
1000
1001 function wifinet.network(self)
1002         return uci_s:get("wifinet", self.sid, "network")
1003 end
1004
1005 function wifinet.name(self)
1006         return self.sid
1007 end
1008
1009 function wifinet.ifname(self)
1010         local ifname = self.iwinfo.ifname
1011         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1012                 ifname = self.wdev
1013         end
1014         return ifname
1015 end
1016
1017 function wifinet.get_device(self)
1018         if self.iwdata.device then
1019                 return wifidev(self.iwdata.device)
1020         end
1021 end
1022
1023 function wifinet.is_up(self)
1024         return (self.iwdata.up == "1")
1025 end
1026
1027 function wifinet.active_mode(self)
1028         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1029
1030         if     m == "ap"      then m = "Master"
1031         elseif m == "sta"     then m = "Client"
1032         elseif m == "adhoc"   then m = "Ad-Hoc"
1033         elseif m == "mesh"    then m = "Mesh"
1034         elseif m == "monitor" then m = "Monitor"
1035         end
1036
1037         return m
1038 end
1039
1040 function wifinet.active_mode_i18n(self)
1041         return i18n.translate(self:active_mode())
1042 end
1043
1044 function wifinet.active_ssid(self)
1045         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1046 end
1047
1048 function wifinet.active_bssid(self)
1049         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1050 end
1051
1052 function wifinet.active_encryption(self)
1053         local enc = self.iwinfo and self.iwinfo.encryption
1054         return enc and enc.description or "-"
1055 end
1056
1057 function wifinet.assoclist(self)
1058         return self.iwinfo.assoclist or { }
1059 end
1060
1061 function wifinet.frequency(self)
1062         local freq = self.iwinfo.frequency
1063         if freq and freq > 0 then
1064                 return "%.03f" % (freq / 1000)
1065         end
1066 end
1067
1068 function wifinet.bitrate(self)
1069         local rate = self.iwinfo.bitrate
1070         if rate and rate > 0 then
1071                 return (rate / 1000)
1072         end
1073 end
1074
1075 function wifinet.channel(self)
1076         return self.iwinfo.channel or
1077                 tonumber(uci_s:get("wireless", self.iwdata.device, "channel"))
1078 end
1079
1080 function wifinet.signal(self)
1081         return self.iwinfo.signal or 0
1082 end
1083
1084 function wifinet.noise(self)
1085         return self.iwinfo.noise or 0
1086 end
1087
1088 function wifinet.signal_level(self, s, n)
1089         if self:active_bssid() ~= "00:00:00:00:00:00" then
1090                 local signal = s or self:signal()
1091                 local noise  = n or self:noise()
1092
1093                 if signal < 0 and noise < 0 then
1094                         local snr = -1 * (noise - signal)
1095                         return math.floor(snr / 5)
1096                 else
1097                         return 0
1098                 end
1099         else
1100                 return -1
1101         end
1102 end
1103
1104 function wifinet.signal_percent(self)
1105         local qc = self.iwinfo.quality or 0
1106         local qm = self.iwinfo.quality_max or 0
1107
1108         if qc > 0 and qm > 0 then
1109                 return math.floor((100 / qm) * qc)
1110         else
1111                 return 0
1112         end
1113 end
1114
1115 function wifinet.shortname(self)
1116         return "%s %q" %{
1117                 i18n.translate(self:active_mode()),
1118                 self:active_ssid() or self:active_bssid()
1119         }
1120 end
1121
1122 function wifinet.get_i18n(self)
1123         return "%s: %s %q (%s)" %{
1124                 i18n.translate("Wireless Network"),
1125                 i18n.translate(self:active_mode()),
1126                 self:active_ssid() or self:active_bssid(),
1127                 self:ifname()
1128         }
1129 end
1130
1131 function wifinet.adminlink(self)
1132         return dsp.build_url("admin", "network", "wireless", self.netid)
1133 end
1134
1135 function wifinet.get_network(self)
1136         if uci_r:get("network", self.iwdata.network) == "interface" then
1137                 return network(self.iwdata.network)
1138         end
1139 end
1140
1141 function wifinet.get_interface(self)
1142         return interface(self:ifname())
1143 end