libs/core: add new convenience functions to luci.model.network
[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 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                 local llr = ipc.IPv6("fe80::/10")
609                 if ifc and ifc.ip6addrs then
610                         local a
611                         for _, a in ipairs(ifc.ip6addrs) do
612                                 if not llr:contains(a) then
613                                         ip6 = a:string()
614                                         break
615                                 end
616                         end
617                 end
618         end
619         return ip6
620 end
621
622 function network.gw6addr(self)
623         local ip6 = self:_ip("ip6gw", 6)
624         if not ip6 then
625                 local dr6 = sys.net.defaultroute6()
626                 if dr6 and dr6.device == self:ifname() then
627                         return dr6.nexthop:string()
628                 end
629         end
630         return ip6
631 end
632
633 function network.dns6addrs(self)
634         return self:_ip("dns", 6, true)
635 end
636
637 function network.is_bridge(self)
638         return (self:type() == "bridge")
639 end
640
641 function network.is_virtual(self)
642         local p = self:proto()
643         return (
644                 p == "3g" or p == "6in4" or p == "6to4" or p == "ppp" or
645                 p == "pppoe" or p == "pppoa"
646         )
647 end
648
649 function network.is_empty(self)
650         if self:is_virtual() then
651                 return false
652         else
653                 local rv = true
654
655                 if (self:_get("ifname") or ""):match("%S+") then
656                         rv = false
657                 end
658
659                 uci_r:foreach("wireless", "wifi-iface",
660                         function(s)
661                                 if s.network == self.sid then
662                                         rv = false
663                                         return false
664                                 end
665                         end)
666
667                 return rv
668         end
669 end
670
671 function network.add_interface(self, ifname)
672         if not self:is_virtual() then
673                 if type(ifname) ~= "string" then
674                         ifname = ifname:name()
675                 else
676                         ifname = ifname:match("[^%s:]+")
677                 end
678
679                 -- remove the interface from all ifaces
680                 uci_r:foreach("network", "interface",
681                         function(s)
682                                 _list_del("network", s['.name'], "ifname", ifname)
683                         end)
684
685                 -- if its a wifi interface, change its network option
686                 local wif = _wifi_lookup(ifname)
687                 if wif then
688                         uci_r:set("wireless", wif, "network", self.sid)
689
690                 -- add iface to our iface list
691                 else
692                         _list_add("network", self.sid, "ifname", ifname)
693                 end
694         end
695 end
696
697 function network.del_interface(self, ifname)
698         if not self:is_virtual() then
699                 if utl.instanceof(ifname, interface) then
700                         ifname = ifname:name()
701                 else
702                         ifname = ifname:match("[^%s:]+")
703                 end
704
705                 -- if its a wireless interface, clear its network option
706                 local wif = _wifi_lookup(ifname)
707                 if wif then     uci_r:delete("wireless", wif, "network") end
708
709                 -- remove the interface
710                 _list_del("network", self.sid, "ifname", ifname)
711         end
712 end
713
714 function network.get_interfaces(self)
715         local ifaces = { }
716
717         local ifn
718         if self:is_virtual() then
719                 ifn = self:proto() .. "-" .. self.sid
720                 ifaces = { interface(ifn) }
721         else
722                 local nfs = { }
723                 for ifn in utl.imatch(self:get("ifname")) do
724                         ifn = ifn:match("[^:]+")
725                         nfs[ifn] = interface(ifn)
726                 end
727
728                 for ifn in utl.kspairs(nfs) do
729                         ifaces[#ifaces+1] = nfs[ifn]
730                 end
731
732                 local num = { }
733                 local wfs = { }
734                 uci_r:foreach("wireless", "wifi-iface",
735                         function(s)
736                                 if s.device then
737                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
738                                         if s.network == self.sid then
739                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
740                                                 wfs[ifn] = interface(ifn)
741                                         end
742                                 end
743                         end)
744
745                 for ifn in utl.kspairs(wfs) do
746                         ifaces[#ifaces+1] = wfs[ifn]
747                 end
748         end
749
750         return ifaces
751 end
752
753 function network.contains_interface(self, ifname)
754         if type(ifname) ~= "string" then
755                 ifname = ifname:name()
756         else
757                 ifname = ifname:match("[^%s:]+")
758         end
759
760         local ifn
761         if self:is_virtual() then
762                 ifn = self:proto() .. "-" .. self.sid
763                 return ifname == ifn
764         else
765                 for ifn in utl.imatch(self:get("ifname")) do
766                         ifn = ifn:match("[^:]+")
767                         if ifn == ifname then
768                                 return true
769                         end
770                 end
771
772                 local wif = _wifi_lookup(ifname)
773                 if wif then
774                         return (uci_r:get("wireless", wif, "network") == self.sid)
775                 end
776         end
777
778         return false
779 end
780
781 function network.adminlink(self)
782         return dsp.build_url("admin", "network", "network", self.sid)
783 end
784
785
786 interface = utl.class()
787 function interface.__init__(self, ifname)
788         local wif = _wifi_lookup(ifname)
789         if wif then self.wif = wifinet(wif) end
790
791         self.ifname = self.ifname or ifname
792         self.dev    = ifs[self.ifname]
793 end
794
795 function interface.name(self)
796         return self.wif and self.wif:ifname() or self.ifname
797 end
798
799 function interface.mac(self)
800         return self.dev and self.dev.macaddr or "00:00:00:00:00:00"
801 end
802
803 function interface.ipaddrs(self)
804         return self.dev and self.dev.ipaddrs or { }
805 end
806
807 function interface.ip6addrs(self)
808         return self.dev and self.dev.ip6addrs or { }
809 end
810
811 function interface.type(self)
812         if self.wif or _wifi_iface(self.ifname) then
813                 return "wifi"
814         elseif brs[self.ifname] then
815                 return "bridge"
816         elseif sws[self.ifname] or self.ifname:match("%.") then
817                 return "switch"
818         else
819                 return "ethernet"
820         end
821 end
822
823 function interface.shortname(self)
824         if self.wif then
825                 return "%s %q" %{
826                         self.wif:active_mode(),
827                         self.wif:active_ssid() or self.wif:active_bssid()
828                 }
829         else
830                 return self.ifname
831         end
832 end
833
834 function interface.get_i18n(self)
835         if self.wif then
836                 return "%s: %s %q" %{
837                         i18n.translate("Wireless Network"),
838                         self.wif:active_mode(),
839                         self.wif:active_ssid() or self.wif:active_bssid()
840                 }
841         else
842                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
843         end
844 end
845
846 function interface.get_type_i18n(self)
847         local x = self:type()
848         if x == "wifi" then
849                 return i18n.translate("Wireless Adapter")
850         elseif x == "bridge" then
851                 return i18n.translate("Bridge")
852         elseif x == "switch" then
853                 return i18n.translate("Ethernet Switch")
854         else
855                 return i18n.translate("Ethernet Adapter")
856         end
857 end
858
859 function interface.adminlink(self)
860         if self.wif then
861                 return self.wif:adminlink()
862         end
863 end
864
865 function interface.ports(self)
866         if self.br then
867                 local iface
868                 local ifaces = { }
869                 for _, iface in ipairs(self.br.ifnames) do
870                         ifaces[#ifaces+1] = interface(iface.name)
871                 end
872                 return ifaces
873         end
874 end
875
876 function interface.bridge_id(self)
877         if self.br then
878                 return self.br.id
879         else
880                 return nil
881         end
882 end
883
884 function interface.bridge_stp(self)
885         if self.br then
886                 return self.br.stp
887         else
888                 return false
889         end
890 end
891
892 function interface.is_up(self)
893         if self.wif then
894                 return self.wif:is_up()
895         else
896                 return self.dev and self.dev.flags and self.dev.flags.up or false
897         end
898 end
899
900 function interface.is_bridge(self)
901         return (self:type() == "bridge")
902 end
903
904 function interface.is_bridgeport(self)
905         return self.dev and self.dev.bridge and true or false
906 end
907
908 function interface.tx_bytes(self)
909         return self.dev and self.dev.stats
910                 and self.dev.stats.tx_bytes or 0
911 end
912
913 function interface.rx_bytes(self)
914         return self.dev and self.dev.stats
915                 and self.dev.stats.rx_bytes or 0
916 end
917
918 function interface.tx_packets(self)
919         return self.dev and self.dev.stats
920                 and self.dev.stats.tx_packets or 0
921 end
922
923 function interface.rx_packets(self)
924         return self.dev and self.dev.stats
925                 and self.dev.stats.rx_packets or 0
926 end
927
928 function interface.get_network(self)
929         if self.dev and self.dev.network then
930                 self.network = _M:get_network(self.dev.network)
931         end
932
933         if not self.network then
934                 local net
935                 for _, net in ipairs(_M:get_networks()) do
936                         if net:contains_interface(self.ifname) or
937                            net:ifname() == self.ifname
938                         then
939                                 self.network = net
940                                 return net
941                         end
942                 end
943         else
944                 return self.network
945         end
946 end
947
948 function interface.get_wifinet(self)
949         return self.wif
950 end
951
952
953 wifidev = utl.class()
954 function wifidev.__init__(self, dev)
955         self.sid = dev
956 end
957
958 function wifidev.get(self, opt)
959         return _get("wireless", self.sid, opt)
960 end
961
962 function wifidev.set(self, opt, val)
963         return _set("wireless", self.sid, opt, val)
964 end
965
966 function wifidev.name(self)
967         return self.sid
968 end
969
970 function wifidev.is_up(self)
971         local up = false
972
973         uci_s:foreach("wireless", "wifi-iface",
974                 function(s)
975                         if s.device == self.sid then
976                                 if s.up == "1" then
977                                         up = true
978                                         return false
979                                 end
980                         end
981                 end)
982
983         return up
984 end
985
986 function wifidev.get_wifinet(self, net)
987         if uci_r:get("wireless", net) == "wifi-iface" then
988                 return wifinet(net)
989         else
990                 local wnet = _wifi_lookup(net)
991                 if wnet then
992                         return wifinet(wnet)
993                 end
994         end
995 end
996
997 function wifidev.get_wifinets(self)
998         local nets = { }
999
1000         uci_r:foreach("wireless", "wifi-iface",
1001                 function(s)
1002                         if s.device == self.sid then
1003                                 nets[#nets+1] = wifinet(s['.name'])
1004                         end
1005                 end)
1006
1007         return nets
1008 end
1009
1010 function wifidev.add_wifinet(self, options)
1011         options = options or { }
1012         options.device = self.sid
1013
1014         local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
1015         if wnet then
1016                 return wifinet(wnet, options)
1017         end
1018 end
1019
1020 function wifidev.del_wifinet(self, net)
1021         if utl.instanceof(net, wifinet) then
1022                 net = net.sid
1023         elseif uci_r:get("wireless", net) ~= "wifi-iface" then
1024                 net = _wifi_lookup(net)
1025         end
1026
1027         if net and uci_r:get("wireless", net, "device") == self.sid then
1028                 uci_r:delete("wireless", net)
1029                 return true
1030         end
1031
1032         return false
1033 end
1034
1035
1036 wifinet = utl.class()
1037 function wifinet.__init__(self, net, data)
1038         self.sid = net
1039
1040         local num = { }
1041         local netid
1042         uci_r:foreach("wireless", "wifi-iface",
1043                 function(s)
1044                         if s.device then
1045                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1046                                 if s['.name'] == self.sid then
1047                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1048                                         return false
1049                                 end
1050                         end
1051                 end)
1052
1053         local dev = uci_s:get("wireless", self.sid, "ifname") or netid
1054
1055         self.netid  = netid
1056         self.wdev   = dev
1057         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1058         self.iwdata = data or uci_s:get_all("wireless", self.sid) or
1059                 uci_r:get_all("wireless", self.sid) or { }
1060 end
1061
1062 function wifinet.get(self, opt)
1063         return _get("wireless", self.sid, opt)
1064 end
1065
1066 function wifinet.set(self, opt, val)
1067         return _set("wireless", self.sid, opt, val)
1068 end
1069
1070 function wifinet.mode(self)
1071         return uci_s:get("wireless", self.sid, "mode") or "ap"
1072 end
1073
1074 function wifinet.ssid(self)
1075         return uci_s:get("wireless", self.sid, "ssid")
1076 end
1077
1078 function wifinet.bssid(self)
1079         return uci_s:get("wireless", self.sid, "bssid")
1080 end
1081
1082 function wifinet.network(self)
1083         return uci_s:get("wifinet", self.sid, "network")
1084 end
1085
1086 function wifinet.name(self)
1087         return self.sid
1088 end
1089
1090 function wifinet.ifname(self)
1091         local ifname = self.iwinfo.ifname
1092         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1093                 ifname = self.wdev
1094         end
1095         return ifname
1096 end
1097
1098 function wifinet.get_device(self)
1099         if self.iwdata.device then
1100                 return wifidev(self.iwdata.device)
1101         end
1102 end
1103
1104 function wifinet.is_up(self)
1105         return (self.iwdata.up == "1")
1106 end
1107
1108 function wifinet.active_mode(self)
1109         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1110
1111         if     m == "ap"      then m = "Master"
1112         elseif m == "sta"     then m = "Client"
1113         elseif m == "adhoc"   then m = "Ad-Hoc"
1114         elseif m == "mesh"    then m = "Mesh"
1115         elseif m == "monitor" then m = "Monitor"
1116         end
1117
1118         return m
1119 end
1120
1121 function wifinet.active_mode_i18n(self)
1122         return i18n.translate(self:active_mode())
1123 end
1124
1125 function wifinet.active_ssid(self)
1126         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1127 end
1128
1129 function wifinet.active_bssid(self)
1130         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1131 end
1132
1133 function wifinet.active_encryption(self)
1134         local enc = self.iwinfo and self.iwinfo.encryption
1135         return enc and enc.description or "-"
1136 end
1137
1138 function wifinet.assoclist(self)
1139         return self.iwinfo.assoclist or { }
1140 end
1141
1142 function wifinet.frequency(self)
1143         local freq = self.iwinfo.frequency
1144         if freq and freq > 0 then
1145                 return "%.03f" % (freq / 1000)
1146         end
1147 end
1148
1149 function wifinet.bitrate(self)
1150         local rate = self.iwinfo.bitrate
1151         if rate and rate > 0 then
1152                 return (rate / 1000)
1153         end
1154 end
1155
1156 function wifinet.channel(self)
1157         return self.iwinfo.channel or
1158                 tonumber(uci_s:get("wireless", self.iwdata.device, "channel"))
1159 end
1160
1161 function wifinet.signal(self)
1162         return self.iwinfo.signal or 0
1163 end
1164
1165 function wifinet.noise(self)
1166         return self.iwinfo.noise or 0
1167 end
1168
1169 function wifinet.signal_level(self, s, n)
1170         if self:active_bssid() ~= "00:00:00:00:00:00" then
1171                 local signal = s or self:signal()
1172                 local noise  = n or self:noise()
1173
1174                 if signal < 0 and noise < 0 then
1175                         local snr = -1 * (noise - signal)
1176                         return math.floor(snr / 5)
1177                 else
1178                         return 0
1179                 end
1180         else
1181                 return -1
1182         end
1183 end
1184
1185 function wifinet.signal_percent(self)
1186         local qc = self.iwinfo.quality or 0
1187         local qm = self.iwinfo.quality_max or 0
1188
1189         if qc > 0 and qm > 0 then
1190                 return math.floor((100 / qm) * qc)
1191         else
1192                 return 0
1193         end
1194 end
1195
1196 function wifinet.shortname(self)
1197         return "%s %q" %{
1198                 i18n.translate(self:active_mode()),
1199                 self:active_ssid() or self:active_bssid()
1200         }
1201 end
1202
1203 function wifinet.get_i18n(self)
1204         return "%s: %s %q (%s)" %{
1205                 i18n.translate("Wireless Network"),
1206                 i18n.translate(self:active_mode()),
1207                 self:active_ssid() or self:active_bssid(),
1208                 self:ifname()
1209         }
1210 end
1211
1212 function wifinet.adminlink(self)
1213         return dsp.build_url("admin", "network", "wireless", self.netid)
1214 end
1215
1216 function wifinet.get_network(self)
1217         if uci_r:get("network", self.iwdata.network) == "interface" then
1218                 return network(self.iwdata.network)
1219         end
1220 end
1221
1222 function wifinet.get_interface(self)
1223         return interface(self:ifname())
1224 end