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