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