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