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