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