3c0964841a7545daaf3e8b9aedb69a7324c27764
[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         local oldnet = self:get_network(n)
246         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
247                 if uci_r:section("network", "interface", n, options) then
248                         return network(n)
249                 end
250         elseif oldnet and oldnet:is_empty() then
251                 if options then
252                         local k, v
253                         for k, v in pairs(options) do
254                                 oldnet:set(k, v)
255                         end
256                 end
257                 return oldnet
258         end
259 end
260
261 function get_network(self, n)
262         if n and uci_r:get("network", n) == "interface" then
263                 return network(n)
264         end
265 end
266
267 function get_networks(self)
268         local nets = { }
269         local nls = { }
270
271         uci_r:foreach("network", "interface",
272                 function(s)
273                         nls[s['.name']] = network(s['.name'])
274                 end)
275
276         local n
277         for n in utl.kspairs(nls) do
278                 nets[#nets+1] = nls[n]
279         end
280
281         return nets
282 end
283
284 function del_network(self, n)
285         local r = uci_r:delete("network", n)
286         if r then
287                 uci_r:delete_all("network", "alias",
288                         function(s) return (s.interface == n) end)
289
290                 uci_r:delete_all("network", "route",
291                         function(s) return (s.interface == n) end)
292
293                 uci_r:delete_all("network", "route6",
294                         function(s) return (s.interface == n) end)
295
296                 uci_r:foreach("wireless", "wifi-iface",
297                         function(s)
298                                 if s.network == n then
299                                         uci_r:delete("wireless", s['.name'], "network")
300                                 end
301                         end)
302         end
303         return r
304 end
305
306 function rename_network(self, old, new)
307         local r
308         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
309                 r = uci_r:section("network", "interface", new, uci_r:get_all("network", old))
310
311                 if r then
312                         uci_r:foreach("network", "alias",
313                                 function(s)
314                                         if s.interface == old then
315                                                 uci_r:set("network", s['.name'], "interface", new)
316                                         end
317                                 end)
318
319                         uci_r:foreach("network", "route",
320                                 function(s)
321                                         if s.interface == old then
322                                                 uci_r:set("network", s['.name'], "interface", new)
323                                         end
324                                 end)
325
326                         uci_r:foreach("network", "route6",
327                                 function(s)
328                                         if s.interface == old then
329                                                 uci_r:set("network", s['.name'], "interface", new)
330                                         end
331                                 end)
332
333                         uci_r:foreach("wireless", "wifi-iface",
334                                 function(s)
335                                         if s.network == old then
336                                                 uci_r:set("wireless", s['.name'], "network", new)
337                                         end
338                                 end)
339
340                         uci_r:delete("network", old)
341                 end
342         end
343         return r or false
344 end
345
346 function get_interface(self, i)
347         if ifs[i] or _wifi_iface(i) then
348                 return interface(i)
349         else
350                 local ifc
351                 local num = { }
352                 uci_r:foreach("wireless", "wifi-iface",
353                         function(s)
354                                 if s.device then
355                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
356                                         if s['.name'] == i then
357                                                 ifc = interface(
358                                                         "%s.network%d" %{s.device, num[s.device] })
359                                                 return false
360                                         end
361                                 end
362                         end)
363                 return ifc
364         end
365 end
366
367 function get_interfaces(self)
368         local iface
369         local ifaces = { }
370         local seen = { }
371         local nfs = { }
372
373         -- find normal interfaces
374         uci_r:foreach("network", "interface",
375                 function(s)
376                         for iface in utl.imatch(s.ifname) do
377                                 if not _iface_ignore(iface) and not _wifi_iface(iface) then
378                                         seen[iface] = true
379                                         nfs[iface] = interface(iface)
380                                 end
381                         end
382                 end)
383
384         for iface in utl.kspairs(ifs) do
385                 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
386                         nfs[iface] = interface(iface)
387                 end
388         end
389
390         for iface in utl.kspairs(nfs) do
391                 ifaces[#ifaces+1] = nfs[iface]
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._ip(self, opt, family, list)
479         local ip = uci_s:get("network", self.sid, opt)
480         local fc = (family == 6) and ipc.IPv6 or ipc.IPv4
481         if ip or list then
482                 if list then
483                         local l = { }
484                         for ip in utl.imatch(ip) do
485                                 ip = fc(ip)
486                                 if ip then l[#l+1] = ip:string() end
487                         end
488                         return l
489                 else
490                         ip = fc(ip)
491                         return ip and ip:string()
492                 end
493         end
494 end
495
496 function network.get(self, opt)
497         return _get("network", self.sid, opt)
498 end
499
500 function network.set(self, opt, val)
501         return _set("network", self.sid, opt, val)
502 end
503
504 function network.ifname(self)
505         local p = self:proto()
506         if self:is_bridge() then
507                 return "br-" .. self.sid
508         elseif self:is_virtual() then
509                 return p .. "-" .. self.sid
510         else
511                 local num = { }
512                 local dev = uci_r:get("network", self.sid, "ifname") or
513                         uci_s:get("network", self.sid, "ifname")
514
515                 dev = (type(dev) == "table") and dev[1] or dev
516                 dev = (dev ~= nil) and dev:match("%S+")
517
518                 if not dev then
519                         uci_r:foreach("wireless", "wifi-iface",
520                                 function(s)
521                                         if s.device then
522                                                 num[s.device] = num[s.device]
523                                                         and num[s.device] + 1 or 1
524
525                                                 if s.network == self.sid then
526                                                         dev = "%s.network%d" %{ s.device, num[s.device] }
527                                                         return false
528                                                 end
529                                         end
530                                 end)
531                 end
532
533                 return dev
534         end
535 end
536
537 function network.device(self)
538         local dev = uci_r:get("network", self.sid, "device") or
539                 uci_s:get("network", self.sid, "device")
540
541         dev = (type(dev) == "table") and dev[1] or dev
542
543         if not dev or dev:match("[^%w%-%.%s]") then
544                 dev = uci_r:get("network", self.sid, "ifname") or
545                         uci_s:get("network", self.sid, "ifname")
546
547                 dev = (type(dev) == "table") and dev[1] or dev
548         end
549
550         return dev
551 end
552
553 function network.proto(self)
554         return self:_get("proto") or "none"
555 end
556
557 function network.type(self)
558         return self:_get("type")
559 end
560
561 function network.name(self)
562         return self.sid
563 end
564
565 function network.uptime(self)
566         local cnt = tonumber(uci_s:get("network", self.sid, "connect_time"))
567         if cnt ~= nil then
568                 return nxo.sysinfo().uptime - cnt
569         else
570                 return 0
571         end
572 end
573
574 function network.expires(self)
575         local a = tonumber(uci_s:get("network", self.sid, "lease_acquired"))
576         local l = tonumber(uci_s:get("network", self.sid, "lease_lifetime"))
577         if a and l then
578                 l = l - (nxo.sysinfo().uptime - a)
579                 return l > 0 and l or 0
580         end
581         return -1
582 end
583
584 function network.metric(self)
585         return tonumber(uci_s:get("network", self.sid, "metric")) or 0
586 end
587
588 function network.ipaddr(self)
589         return self:_ip("ipaddr", 4)
590 end
591
592 function network.netmask(self)
593         return self:_ip("netmask", 4)
594 end
595
596 function network.gwaddr(self)
597         return self:_ip("gateway", 4)
598 end
599
600 function network.dnsaddrs(self)
601         return self:_ip("dns", 4, true)
602 end
603
604 function network.ip6addr(self)
605         local ip6 = self:_ip("ip6addr", 6)
606         if not ip6 then
607                 local ifc = ifs[self:ifname()]
608                 if ifc and ifc.ip6addrs then
609                         local a
610                         for _, a in ipairs(ifc.ip6addrs) do
611                                 if not a:is6linklocal() then
612                                         ip6 = a:string()
613                                         break
614                                 end
615                         end
616                 end
617         end
618         return ip6
619 end
620
621 function network.gw6addr(self)
622         local ip6 = self:_ip("ip6gw", 6)
623         if not ip6 then
624                 local dr6 = sys.net.defaultroute6()
625                 if dr6 and dr6.device == self:ifname() then
626                         return dr6.nexthop:string()
627                 end
628         end
629         return ip6
630 end
631
632 function network.dns6addrs(self)
633         return self:_ip("dns", 6, true)
634 end
635
636 function network.is_bridge(self)
637         return (self:type() == "bridge")
638 end
639
640 function network.is_virtual(self)
641         local p = self:proto()
642         return (
643                 p == "3g" or p == "6in4" or p == "6to4" or p == "ppp" or
644                 p == "pppoe" or p == "pppoa"
645         )
646 end
647
648 function network.is_empty(self)
649         if self:is_virtual() then
650                 return false
651         else
652                 local rv = true
653
654                 if (self:_get("ifname") or ""):match("%S+") then
655                         rv = false
656                 end
657
658                 uci_r:foreach("wireless", "wifi-iface",
659                         function(s)
660                                 if s.network == self.sid then
661                                         rv = false
662                                         return false
663                                 end
664                         end)
665
666                 return rv
667         end
668 end
669
670 function network.add_interface(self, ifname)
671         if not self:is_virtual() then
672                 if type(ifname) ~= "string" then
673                         ifname = ifname:name()
674                 else
675                         ifname = ifname:match("[^%s:]+")
676                 end
677
678                 -- remove the interface from all ifaces
679                 uci_r:foreach("network", "interface",
680                         function(s)
681                                 _list_del("network", s['.name'], "ifname", ifname)
682                         end)
683
684                 -- if its a wifi interface, change its network option
685                 local wif = _wifi_lookup(ifname)
686                 if wif then
687                         uci_r:set("wireless", wif, "network", self.sid)
688
689                 -- add iface to our iface list
690                 else
691                         _list_add("network", self.sid, "ifname", ifname)
692                 end
693         end
694 end
695
696 function network.del_interface(self, ifname)
697         if not self:is_virtual() then
698                 if utl.instanceof(ifname, interface) then
699                         ifname = ifname:name()
700                 else
701                         ifname = ifname:match("[^%s:]+")
702                 end
703
704                 -- if its a wireless interface, clear its network option
705                 local wif = _wifi_lookup(ifname)
706                 if wif then     uci_r:delete("wireless", wif, "network") end
707
708                 -- remove the interface
709                 _list_del("network", self.sid, "ifname", ifname)
710         end
711 end
712
713 function network.get_interfaces(self)
714         local ifaces = { }
715
716         local ifn
717         if self:is_virtual() then
718                 ifn = self:proto() .. "-" .. self.sid
719                 ifaces = { interface(ifn) }
720         else
721                 local nfs = { }
722                 for ifn in utl.imatch(self:get("ifname")) do
723                         ifn = ifn:match("[^:]+")
724                         nfs[ifn] = interface(ifn)
725                 end
726
727                 for ifn in utl.kspairs(nfs) do
728                         ifaces[#ifaces+1] = nfs[ifn]
729                 end
730
731                 local num = { }
732                 local wfs = { }
733                 uci_r:foreach("wireless", "wifi-iface",
734                         function(s)
735                                 if s.device then
736                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
737                                         if s.network == self.sid then
738                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
739                                                 wfs[ifn] = interface(ifn)
740                                         end
741                                 end
742                         end)
743
744                 for ifn in utl.kspairs(wfs) do
745                         ifaces[#ifaces+1] = wfs[ifn]
746                 end
747         end
748
749         return ifaces
750 end
751
752 function network.contains_interface(self, ifname)
753         if type(ifname) ~= "string" then
754                 ifname = ifname:name()
755         else
756                 ifname = ifname:match("[^%s:]+")
757         end
758
759         local ifn
760         if self:is_virtual() then
761                 ifn = self:proto() .. "-" .. self.sid
762                 return ifname == ifn
763         else
764                 for ifn in utl.imatch(self:get("ifname")) do
765                         ifn = ifn:match("[^:]+")
766                         if ifn == ifname then
767                                 return true
768                         end
769                 end
770
771                 local wif = _wifi_lookup(ifname)
772                 if wif then
773                         return (uci_r:get("wireless", wif, "network") == self.sid)
774                 end
775         end
776
777         return false
778 end
779
780 function network.adminlink(self)
781         return dsp.build_url("admin", "network", "network", self.sid)
782 end
783
784
785 interface = utl.class()
786 function interface.__init__(self, ifname)
787         local wif = _wifi_lookup(ifname)
788         if wif then self.wif = wifinet(wif) end
789
790         self.ifname = self.ifname or ifname
791         self.dev    = ifs[self.ifname]
792 end
793
794 function interface.name(self)
795         return self.wif and self.wif:ifname() or self.ifname
796 end
797
798 function interface.mac(self)
799         return self.dev and self.dev.macaddr or "00:00:00:00:00:00"
800 end
801
802 function interface.ipaddrs(self)
803         return self.dev and self.dev.ipaddrs or { }
804 end
805
806 function interface.ip6addrs(self)
807         return self.dev and self.dev.ip6addrs or { }
808 end
809
810 function interface.type(self)
811         if self.wif or _wifi_iface(self.ifname) then
812                 return "wifi"
813         elseif brs[self.ifname] then
814                 return "bridge"
815         elseif sws[self.ifname] or self.ifname:match("%.") then
816                 return "switch"
817         else
818                 return "ethernet"
819         end
820 end
821
822 function interface.shortname(self)
823         if self.wif then
824                 return "%s %q" %{
825                         self.wif:active_mode(),
826                         self.wif:active_ssid() or self.wif:active_bssid()
827                 }
828         else
829                 return self.ifname
830         end
831 end
832
833 function interface.get_i18n(self)
834         if self.wif then
835                 return "%s: %s %q" %{
836                         i18n.translate("Wireless Network"),
837                         self.wif:active_mode(),
838                         self.wif:active_ssid() or self.wif:active_bssid()
839                 }
840         else
841                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
842         end
843 end
844
845 function interface.get_type_i18n(self)
846         local x = self:type()
847         if x == "wifi" then
848                 return i18n.translate("Wireless Adapter")
849         elseif x == "bridge" then
850                 return i18n.translate("Bridge")
851         elseif x == "switch" then
852                 return i18n.translate("Ethernet Switch")
853         else
854                 return i18n.translate("Ethernet Adapter")
855         end
856 end
857
858 function interface.adminlink(self)
859         if self.wif then
860                 return self.wif:adminlink()
861         end
862 end
863
864 function interface.ports(self)
865         if self.br then
866                 local iface
867                 local ifaces = { }
868                 for _, iface in ipairs(self.br.ifnames) do
869                         ifaces[#ifaces+1] = interface(iface.name)
870                 end
871                 return ifaces
872         end
873 end
874
875 function interface.bridge_id(self)
876         if self.br then
877                 return self.br.id
878         else
879                 return nil
880         end
881 end
882
883 function interface.bridge_stp(self)
884         if self.br then
885                 return self.br.stp
886         else
887                 return false
888         end
889 end
890
891 function interface.is_up(self)
892         if self.wif then
893                 return self.wif:is_up()
894         else
895                 return self.dev and self.dev.flags and self.dev.flags.up or false
896         end
897 end
898
899 function interface.is_bridge(self)
900         return (self:type() == "bridge")
901 end
902
903 function interface.is_bridgeport(self)
904         return self.dev and self.dev.bridge and true or false
905 end
906
907 function interface.tx_bytes(self)
908         return self.dev and self.dev.stats
909                 and self.dev.stats.tx_bytes or 0
910 end
911
912 function interface.rx_bytes(self)
913         return self.dev and self.dev.stats
914                 and self.dev.stats.rx_bytes or 0
915 end
916
917 function interface.tx_packets(self)
918         return self.dev and self.dev.stats
919                 and self.dev.stats.tx_packets or 0
920 end
921
922 function interface.rx_packets(self)
923         return self.dev and self.dev.stats
924                 and self.dev.stats.rx_packets or 0
925 end
926
927 function interface.get_network(self)
928         if self.dev and self.dev.network then
929                 self.network = _M:get_network(self.dev.network)
930         end
931
932         if not self.network then
933                 local net
934                 for _, net in ipairs(_M:get_networks()) do
935                         if net:contains_interface(self.ifname) or
936                            net:ifname() == self.ifname
937                         then
938                                 self.network = net
939                                 return net
940                         end
941                 end
942         else
943                 return self.network
944         end
945 end
946
947 function interface.get_wifinet(self)
948         return self.wif
949 end
950
951
952 wifidev = utl.class()
953 function wifidev.__init__(self, dev)
954         self.sid = dev
955 end
956
957 function wifidev.get(self, opt)
958         return _get("wireless", self.sid, opt)
959 end
960
961 function wifidev.set(self, opt, val)
962         return _set("wireless", self.sid, opt, val)
963 end
964
965 function wifidev.name(self)
966         return self.sid
967 end
968
969 function wifidev.is_up(self)
970         local up = false
971
972         uci_s:foreach("wireless", "wifi-iface",
973                 function(s)
974                         if s.device == self.sid then
975                                 if s.up == "1" then
976                                         up = true
977                                         return false
978                                 end
979                         end
980                 end)
981
982         return up
983 end
984
985 function wifidev.get_wifinet(self, net)
986         if uci_r:get("wireless", net) == "wifi-iface" then
987                 return wifinet(net)
988         else
989                 local wnet = _wifi_lookup(net)
990                 if wnet then
991                         return wifinet(wnet)
992                 end
993         end
994 end
995
996 function wifidev.get_wifinets(self)
997         local nets = { }
998
999         uci_r:foreach("wireless", "wifi-iface",
1000                 function(s)
1001                         if s.device == self.sid then
1002                                 nets[#nets+1] = wifinet(s['.name'])
1003                         end
1004                 end)
1005
1006         return nets
1007 end
1008
1009 function wifidev.add_wifinet(self, options)
1010         options = options or { }
1011         options.device = self.sid
1012
1013         local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
1014         if wnet then
1015                 return wifinet(wnet, options)
1016         end
1017 end
1018
1019 function wifidev.del_wifinet(self, net)
1020         if utl.instanceof(net, wifinet) then
1021                 net = net.sid
1022         elseif uci_r:get("wireless", net) ~= "wifi-iface" then
1023                 net = _wifi_lookup(net)
1024         end
1025
1026         if net and uci_r:get("wireless", net, "device") == self.sid then
1027                 uci_r:delete("wireless", net)
1028                 return true
1029         end
1030
1031         return false
1032 end
1033
1034
1035 wifinet = utl.class()
1036 function wifinet.__init__(self, net, data)
1037         self.sid = net
1038
1039         local num = { }
1040         local netid
1041         uci_r:foreach("wireless", "wifi-iface",
1042                 function(s)
1043                         if s.device then
1044                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1045                                 if s['.name'] == self.sid then
1046                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1047                                         return false
1048                                 end
1049                         end
1050                 end)
1051
1052         local dev = uci_s:get("wireless", self.sid, "ifname") or netid
1053
1054         self.netid  = netid
1055         self.wdev   = dev
1056         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1057         self.iwdata = data or uci_s:get_all("wireless", self.sid) or
1058                 uci_r:get_all("wireless", self.sid) or { }
1059 end
1060
1061 function wifinet.get(self, opt)
1062         return _get("wireless", self.sid, opt)
1063 end
1064
1065 function wifinet.set(self, opt, val)
1066         return _set("wireless", self.sid, opt, val)
1067 end
1068
1069 function wifinet.mode(self)
1070         return uci_s:get("wireless", self.sid, "mode") or "ap"
1071 end
1072
1073 function wifinet.ssid(self)
1074         return uci_s:get("wireless", self.sid, "ssid")
1075 end
1076
1077 function wifinet.bssid(self)
1078         return uci_s:get("wireless", self.sid, "bssid")
1079 end
1080
1081 function wifinet.network(self)
1082         return uci_s:get("wifinet", self.sid, "network")
1083 end
1084
1085 function wifinet.name(self)
1086         return self.sid
1087 end
1088
1089 function wifinet.ifname(self)
1090         local ifname = self.iwinfo.ifname
1091         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1092                 ifname = self.wdev
1093         end
1094         return ifname
1095 end
1096
1097 function wifinet.get_device(self)
1098         if self.iwdata.device then
1099                 return wifidev(self.iwdata.device)
1100         end
1101 end
1102
1103 function wifinet.is_up(self)
1104         return (self.iwdata.up == "1")
1105 end
1106
1107 function wifinet.active_mode(self)
1108         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1109
1110         if     m == "ap"      then m = "Master"
1111         elseif m == "sta"     then m = "Client"
1112         elseif m == "adhoc"   then m = "Ad-Hoc"
1113         elseif m == "mesh"    then m = "Mesh"
1114         elseif m == "monitor" then m = "Monitor"
1115         end
1116
1117         return m
1118 end
1119
1120 function wifinet.active_mode_i18n(self)
1121         return i18n.translate(self:active_mode())
1122 end
1123
1124 function wifinet.active_ssid(self)
1125         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1126 end
1127
1128 function wifinet.active_bssid(self)
1129         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1130 end
1131
1132 function wifinet.active_encryption(self)
1133         local enc = self.iwinfo and self.iwinfo.encryption
1134         return enc and enc.description or "-"
1135 end
1136
1137 function wifinet.assoclist(self)
1138         return self.iwinfo.assoclist or { }
1139 end
1140
1141 function wifinet.frequency(self)
1142         local freq = self.iwinfo.frequency
1143         if freq and freq > 0 then
1144                 return "%.03f" % (freq / 1000)
1145         end
1146 end
1147
1148 function wifinet.bitrate(self)
1149         local rate = self.iwinfo.bitrate
1150         if rate and rate > 0 then
1151                 return (rate / 1000)
1152         end
1153 end
1154
1155 function wifinet.channel(self)
1156         return self.iwinfo.channel or
1157                 tonumber(uci_s:get("wireless", self.iwdata.device, "channel"))
1158 end
1159
1160 function wifinet.signal(self)
1161         return self.iwinfo.signal or 0
1162 end
1163
1164 function wifinet.noise(self)
1165         return self.iwinfo.noise or 0
1166 end
1167
1168 function wifinet.signal_level(self, s, n)
1169         if self:active_bssid() ~= "00:00:00:00:00:00" then
1170                 local signal = s or self:signal()
1171                 local noise  = n or self:noise()
1172
1173                 if signal < 0 and noise < 0 then
1174                         local snr = -1 * (noise - signal)
1175                         return math.floor(snr / 5)
1176                 else
1177                         return 0
1178                 end
1179         else
1180                 return -1
1181         end
1182 end
1183
1184 function wifinet.signal_percent(self)
1185         local qc = self.iwinfo.quality or 0
1186         local qm = self.iwinfo.quality_max or 0
1187
1188         if qc > 0 and qm > 0 then
1189                 return math.floor((100 / qm) * qc)
1190         else
1191                 return 0
1192         end
1193 end
1194
1195 function wifinet.shortname(self)
1196         return "%s %q" %{
1197                 i18n.translate(self:active_mode()),
1198                 self:active_ssid() or self:active_bssid()
1199         }
1200 end
1201
1202 function wifinet.get_i18n(self)
1203         return "%s: %s %q (%s)" %{
1204                 i18n.translate("Wireless Network"),
1205                 i18n.translate(self:active_mode()),
1206                 self:active_ssid() or self:active_bssid(),
1207                 self:ifname()
1208         }
1209 end
1210
1211 function wifinet.adminlink(self)
1212         return dsp.build_url("admin", "network", "wireless", self.netid)
1213 end
1214
1215 function wifinet.get_network(self)
1216         if uci_r:get("network", self.iwdata.network) == "interface" then
1217                 return network(self.iwdata.network)
1218         end
1219 end
1220
1221 function wifinet.get_interface(self)
1222         return interface(self:ifname())
1223 end