libs/core: network model fixes
[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: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 dev = self:_get("ifname") or
485                         uci_r:get("network", self.sid, "ifname")
486
487                 dev = dev and dev:match("%S+")
488
489                 if not dev then
490                         uci_r:foreach("wireless", "wifi-iface",
491                                 function(s)
492                                         if s.device then
493                                                 num[s.device] = num[s.device]
494                                                         and num[s.device] + 1 or 1
495
496                                                 if s.network == self.sid then
497                                                         dev = "%s.network%d" %{ s.device, num[s.device] }
498                                                         return false
499                                                 end
500                                         end
501                                 end)
502                 end
503
504                 return dev
505         end
506 end
507
508 function network.device(self)
509         local dev = self:_get("device")
510         if not dev or dev:match("[^%w%-%.%s]") then
511                 dev = uci_r:get("network", self.sid, "ifname")
512         end
513         return dev
514 end
515
516 function network.proto(self)
517         return self:_get("proto") or "none"
518 end
519
520 function network.type(self)
521         return self:_get("type")
522 end
523
524 function network.name(self)
525         return self.sid
526 end
527
528 function network.is_bridge(self)
529         return (self:type() == "bridge")
530 end
531
532 function network.is_virtual(self)
533         local p = self:proto()
534         return (
535                 p == "3g" or p == "6in4" or p == "6to4" or p == "ppp" or
536                 p == "pppoe" or p == "pppoa"
537         )
538 end
539
540 function network.is_empty(self)
541         if self:is_virtual() then
542                 return false
543         else
544                 local rv = true
545
546                 if (self:_get("ifname") or ""):match("%S+") then
547                         rv = false
548                 end
549
550                 uci_r:foreach("wireless", "wifi-iface",
551                         function(s)
552                                 if s.network == self.sid then
553                                         rv = false
554                                         return false
555                                 end
556                         end)
557
558                 return rv
559         end
560 end
561
562 function network.add_interface(self, ifname)
563         if not self:is_virtual() then
564                 if type(ifname) ~= "string" then
565                         ifname = ifname:name()
566                 else
567                         ifname = ifname:match("[^%s:]+")
568                 end
569
570                 -- remove the interface from all ifaces
571                 uci_r:foreach("network", "interface",
572                         function(s)
573                                 _list_del("network", s['.name'], "ifname", ifname)
574                         end)
575
576                 -- if its a wifi interface, change its network option
577                 local wif = _wifi_lookup(ifname)
578                 if wif then
579                         uci_r:set("wireless", wif, "network", self.sid)
580
581                 -- add iface to our iface list
582                 else
583                         _list_add("network", self.sid, "ifname", ifname)
584                 end
585         end
586 end
587
588 function network.del_interface(self, ifname)
589         if not self:is_virtual() then
590                 if utl.instanceof(ifname, interface) then
591                         ifname = ifname:name()
592                 else
593                         ifname = ifname:match("[^%s:]+")
594                 end
595
596                 -- if its a wireless interface, clear its network option
597                 local wif = _wifi_lookup(ifname)
598                 if wif then     uci_r:delete("wireless", wif, "network") end
599
600                 -- remove the interface
601                 _list_del("network", self.sid, "ifname", ifname)
602         end
603 end
604
605 function network.get_interfaces(self)
606         local ifaces = { }
607
608         local ifn
609         if self:is_virtual() then
610                 ifn = self:proto() .. "-" .. self.sid
611                 ifaces = { interface(ifn) }
612         else
613                 local nfs = { }
614                 for ifn in utl.imatch(self:get("ifname")) do
615                         ifn = ifn:match("[^:]+")
616                         nfs[ifn] = interface(ifn)
617                 end
618
619                 for ifn in utl.kspairs(nfs) do
620                         ifaces[#ifaces+1] = nfs[ifn]
621                 end
622
623                 local num = { }
624                 local wfs = { }
625                 uci_r:foreach("wireless", "wifi-iface",
626                         function(s)
627                                 if s.device then
628                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
629                                         if s.network == self.sid then
630                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
631                                                 wfs[ifn] = interface(ifn)
632                                         end
633                                 end
634                         end)
635
636                 for ifn in utl.kspairs(wfs) do
637                         ifaces[#ifaces+1] = wfs[ifn]
638                 end
639         end
640
641         return ifaces
642 end
643
644 function network.contains_interface(self, ifname)
645         if type(ifname) ~= "string" then
646                 ifname = ifname:name()
647         else
648                 ifname = ifname:match("[^%s:]+")
649         end
650
651         local ifn
652         if self:is_virtual() then
653                 ifn = self:proto() .. "-" .. self.sid
654                 return ifname == ifn
655         else
656                 for ifn in utl.imatch(self:get("ifname")) do
657                         ifn = ifn:match("[^:]+")
658                         if ifn == ifname then
659                                 return true
660                         end
661                 end
662
663                 local wif = _wifi_lookup(ifname)
664                 if wif then
665                         return (uci_r:get("wireless", wif, "network") == self.sid)
666                 end
667         end
668
669         return false
670 end
671
672 function network.adminlink(self)
673         return dsp.build_url("admin", "network", "network", self.sid)
674 end
675
676
677 interface = utl.class()
678 function interface.__init__(self, ifname)
679         local wif = _wifi_lookup(ifname)
680         if wif then self.wif = wifinet(wif) end
681
682         self.ifname = self.ifname or ifname
683         self.dev    = ifs[self.ifname]
684 end
685
686 function interface.name(self)
687         return self.wif and self.wif:ifname() or self.ifname
688 end
689
690 function interface.mac(self)
691         return self.dev and self.dev.macaddr or "00:00:00:00:00:00"
692 end
693
694 function interface.ipaddrs(self)
695         return self.dev and self.dev.ipaddrs or { }
696 end
697
698 function interface.ip6addrs(self)
699         return self.dev and self.dev.ip6addrs or { }
700 end
701
702 function interface.type(self)
703         if self.wif or _wifi_iface(self.ifname) then
704                 return "wifi"
705         elseif brs[self.ifname] then
706                 return "bridge"
707         elseif sws[self.ifname] or self.ifname:match("%.") then
708                 return "switch"
709         else
710                 return "ethernet"
711         end
712 end
713
714 function interface.shortname(self)
715         if self.wif then
716                 return "%s %q" %{
717                         self.wif:active_mode(),
718                         self.wif:active_ssid() or self.wif:active_bssid()
719                 }
720         else
721                 return self.ifname
722         end
723 end
724
725 function interface.get_i18n(self)
726         if self.wif then
727                 return "%s: %s %q" %{
728                         i18n.translate("Wireless Network"),
729                         self.wif:active_mode(),
730                         self.wif:active_ssid() or self.wif:active_bssid()
731                 }
732         else
733                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
734         end
735 end
736
737 function interface.get_type_i18n(self)
738         local x = self:type()
739         if x == "wifi" then
740                 return i18n.translate("Wireless Adapter")
741         elseif x == "bridge" then
742                 return i18n.translate("Bridge")
743         elseif x == "switch" then
744                 return i18n.translate("Ethernet Switch")
745         else
746                 return i18n.translate("Ethernet Adapter")
747         end
748 end
749
750 function interface.adminlink(self)
751         if self.wif then
752                 return self.wif:adminlink()
753         end
754 end
755
756 function interface.ports(self)
757         if self.br then
758                 local iface
759                 local ifaces = { }
760                 for _, iface in ipairs(self.br.ifnames) do
761                         ifaces[#ifaces+1] = interface(iface.name)
762                 end
763                 return ifaces
764         end
765 end
766
767 function interface.bridge_id(self)
768         if self.br then
769                 return self.br.id
770         else
771                 return nil
772         end
773 end
774
775 function interface.bridge_stp(self)
776         if self.br then
777                 return self.br.stp
778         else
779                 return false
780         end
781 end
782
783 function interface.is_up(self)
784         if self.wif then
785                 return self.wif:is_up()
786         else
787                 return self.dev and self.dev.flags and self.dev.flags.up or false
788         end
789 end
790
791 function interface.is_bridge(self)
792         return (self:type() == "bridge")
793 end
794
795 function interface.is_bridgeport(self)
796         return self.dev and self.dev.bridge and true or false
797 end
798
799 function interface.tx_bytes(self)
800         return self.dev and self.dev.stats
801                 and self.dev.stats.tx_bytes or 0
802 end
803
804 function interface.rx_bytes(self)
805         return self.dev and self.dev.stats
806                 and self.dev.stats.rx_bytes or 0
807 end
808
809 function interface.tx_packets(self)
810         return self.dev and self.dev.stats
811                 and self.dev.stats.tx_packets or 0
812 end
813
814 function interface.rx_packets(self)
815         return self.dev and self.dev.stats
816                 and self.dev.stats.rx_packets or 0
817 end
818
819 function interface.get_network(self)
820         if self.dev and self.dev.network then
821                 self.network = _M:get_network(self.dev.network)
822         end
823
824         if not self.network then
825                 local net
826                 for _, net in ipairs(_M:get_networks()) do
827                         if net:contains_interface(self.ifname) then
828                                 self.network = net
829                                 return net
830                         end
831                 end
832         else
833                 return self.network
834         end
835 end
836
837 function interface.get_wifinet(self)
838         return self.wif
839 end
840
841
842 wifidev = utl.class()
843 function wifidev.__init__(self, dev)
844         self.sid = dev
845 end
846
847 function wifidev.get(self, opt)
848         return _get("wireless", self.sid, opt)
849 end
850
851 function wifidev.set(self, opt, val)
852         return _set("wireless", self.sid, opt, val)
853 end
854
855 function wifidev.name(self)
856         return self.sid
857 end
858
859 function wifidev.is_up(self)
860         local up = false
861
862         uci_s:foreach("wireless", "wifi-iface",
863                 function(s)
864                         if s.device == self.sid then
865                                 if s.up == "1" then
866                                         up = true
867                                         return false
868                                 end
869                         end
870                 end)
871
872         return up
873 end
874
875 function wifidev.get_wifinet(self, net)
876         if uci_r:get("wireless", net) == "wifi-iface" then
877                 return wifinet(net)
878         else
879                 local wnet = _wifi_lookup(net)
880                 if wnet then
881                         return wifinet(wnet)
882                 end
883         end
884 end
885
886 function wifidev.get_wifinets(self)
887         local nets = { }
888
889         uci_r:foreach("wireless", "wifi-iface",
890                 function(s)
891                         if s.device == self.sid then
892                                 nets[#nets+1] = wifinet(s['.name'])
893                         end
894                 end)
895
896         return nets
897 end
898
899 function wifidev.add_wifinet(self, options)
900         options = options or { }
901         options.device = self.sid
902
903         local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
904         if wnet then
905                 return wifinet(wnet, options)
906         end
907 end
908
909 function wifidev.del_wifinet(self, net)
910         if utl.instanceof(net, wifinet) then
911                 net = net.sid
912         elseif uci_r:get("wireless", net) ~= "wifi-iface" then
913                 net = _wifi_lookup(net)
914         end
915
916         if net and uci_r:get("wireless", net, "device") == self.sid then
917                 uci_r:delete("wireless", net)
918                 return true
919         end
920
921         return false
922 end
923
924
925 wifinet = utl.class()
926 function wifinet.__init__(self, net, data)
927         self.sid = net
928
929         local dev = uci_s:get("wireless", self.sid, "ifname")
930         if not dev then
931                 local num = { }
932                 uci_r:foreach("wireless", "wifi-iface",
933                         function(s)
934                                 if s.device then
935                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
936                                         if s['.name'] == self.sid then
937                                                 dev = "%s.network%d" %{ s.device, num[s.device] }
938                                                 return false
939                                         end
940                                 end
941                         end)
942         end
943
944         self.wdev   = dev
945         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
946         self.iwdata = data or uci_s:get_all("wireless", self.sid) or
947                 uci_r:get_all("wireless", self.sid) or { }
948 end
949
950 function wifinet.get(self, opt)
951         return _get("wireless", self.sid, opt)
952 end
953
954 function wifinet.set(self, opt, val)
955         return _set("wireless", self.sid, opt, val)
956 end
957
958 function wifinet.mode(self)
959         return uci_s:get("wireless", self.sid, "mode") or "ap"
960 end
961
962 function wifinet.ssid(self)
963         return uci_s:get("wireless", self.sid, "ssid")
964 end
965
966 function wifinet.bssid(self)
967         return uci_s:get("wireless", self.sid, "bssid")
968 end
969
970 function wifinet.network(self)
971         return uci_s:get("wifinet", self.sid, "network")
972 end
973
974 function wifinet.name(self)
975         return self.sid
976 end
977
978 function wifinet.ifname(self)
979         local ifname = self.iwinfo.ifname
980         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
981                 ifname = self.wdev
982         end
983         return ifname
984 end
985
986 function wifinet.get_device(self)
987         if self.iwdata.device then
988                 return wifidev(self.iwdata.device)
989         end
990 end
991
992 function wifinet.is_up(self)
993         return (self.iwdata.up == "1")
994 end
995
996 function wifinet.active_mode(self)
997         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
998
999         if     m == "ap"      then m = "AP"
1000         elseif m == "sta"     then m = "Client"
1001         elseif m == "adhoc"   then m = "Ad-Hoc"
1002         elseif m == "mesh"    then m = "Mesh"
1003         elseif m == "monitor" then m = "Monitor"
1004         end
1005
1006         return m
1007 end
1008
1009 function wifinet.active_mode_i18n(self)
1010         return i18n.translate(self:active_mode())
1011 end
1012
1013 function wifinet.active_ssid(self)
1014         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1015 end
1016
1017 function wifinet.active_bssid(self)
1018         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1019 end
1020
1021 function wifinet.active_encryption(self)
1022         local enc = self.iwinfo and self.iwinfo.encryption
1023         return enc and enc.description or "-"
1024 end
1025
1026 function wifinet.assoclist(self)
1027         return self.iwinfo.assoclist or { }
1028 end
1029
1030 function wifinet.frequency(self)
1031         local freq = self.iwinfo.frequency
1032         if freq and freq > 0 then
1033                 return "%.03f" % (freq / 1000)
1034         end
1035 end
1036
1037 function wifinet.bitrate(self)
1038         local rate = self.iwinfo.bitrate
1039         if rate and rate > 0 then
1040                 return (rate / 1000)
1041         end
1042 end
1043
1044 function wifinet.channel(self)
1045         return self.iwinfo.channel or
1046                 tonumber(uci_s:get("wireless", self.iwdata.device, "channel"))
1047 end
1048
1049 function wifinet.signal(self)
1050         return self.iwinfo.signal or 0
1051 end
1052
1053 function wifinet.noise(self)
1054         return self.iwinfo.noise or 0
1055 end
1056
1057 function wifinet.signal_level(self, s, n)
1058         if self:active_bssid() ~= "00:00:00:00:00:00" then
1059                 local signal = s or self:signal()
1060                 local noise  = n or self:noise()
1061
1062                 if signal < 0 and noise < 0 then
1063                         local snr = -1 * (noise - signal)
1064                         return math.floor(snr / 5)
1065                 else
1066                         return 0
1067                 end
1068         else
1069                 return -1
1070         end
1071 end
1072
1073 function wifinet.signal_percent(self)
1074         local qc = self.iwinfo.quality or 0
1075         local qm = self.iwinfo.quality_max or 0
1076
1077         if qc > 0 and qm > 0 then
1078                 return math.floor((100 / qm) * qc)
1079         else
1080                 return 0
1081         end
1082 end
1083
1084 function wifinet.shortname(self)
1085         return "%s %q" %{
1086                 i18n.translate(self:active_mode()),
1087                 self:active_ssid() or self:active_bssid()
1088         }
1089 end
1090
1091 function wifinet.get_i18n(self)
1092         return "%s: %s %q (%s)" %{
1093                 i18n.translate("Wireless Network"),
1094                 i18n.translate(self:active_mode()),
1095                 self:active_ssid() or self:active_bssid(),
1096                 self:ifname()
1097         }
1098 end
1099
1100 function wifinet.adminlink(self)
1101         return dsp.build_url("admin", "network", "wireless",
1102                 self.iwdata.device, self.wdev)
1103 end
1104
1105 function wifinet.get_network(self)
1106         if uci_r:get("network", self.iwdata.network) == "interface" then
1107                 return network(self.iwdata.network)
1108         end
1109 end
1110
1111 function wifinet.get_interface(self)
1112         return interface(self:ifname())
1113 end