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