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