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