libs/core: various interface handling fixes in network model
[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_floating(self)
678         return (self:is_virtual() and self:proto() ~= "pppoe")
679 end
680
681 function network.is_empty(self)
682         if self:is_virtual() then
683                 return false
684         else
685                 local rv = true
686
687                 if (self:_get("ifname") or ""):match("%S+") then
688                         rv = false
689                 end
690
691                 uci_r:foreach("wireless", "wifi-iface",
692                         function(s)
693                                 if s.network == self.sid then
694                                         rv = false
695                                         return false
696                                 end
697                         end)
698
699                 return rv
700         end
701 end
702
703 function network.add_interface(self, ifname)
704         if not self:is_floating() then
705                 if type(ifname) ~= "string" then
706                         ifname = ifname:name()
707                 else
708                         ifname = ifname:match("[^%s:]+")
709                 end
710
711                 -- remove the interface from all ifaces
712                 uci_r:foreach("network", "interface",
713                         function(s)
714                                 _list_del("network", s['.name'], "ifname", ifname)
715                         end)
716
717                 -- if its a wifi interface, change its network option
718                 local wif = _wifi_lookup(ifname)
719                 if wif then
720                         uci_r:set("wireless", wif, "network", self.sid)
721
722                 -- add iface to our iface list
723                 else
724                         _list_add("network", self.sid, "ifname", ifname)
725                 end
726         end
727 end
728
729 function network.del_interface(self, ifname)
730         if not self:is_floating() then
731                 if utl.instanceof(ifname, interface) then
732                         ifname = ifname:name()
733                 else
734                         ifname = ifname:match("[^%s:]+")
735                 end
736
737                 -- if its a wireless interface, clear its network option
738                 local wif = _wifi_lookup(ifname)
739                 if wif then     uci_r:delete("wireless", wif, "network") end
740
741                 -- remove the interface
742                 _list_del("network", self.sid, "ifname", ifname)
743         end
744 end
745
746 function network.get_interface(self)
747         if self:is_virtual() then
748                 return interface(self:proto() .. "-" .. self.sid)
749         elseif self:is_bridge() then
750                 return interface("br-" .. self.sid)
751         else
752                 local ifn = nil
753                 local num = { }
754                 for ifn in utl.imatch(uci_s:get("network", self.sid, "ifname")) do
755                         ifn = ifn:match("^[^:/]+")
756                         return ifn and interface(ifn)
757                 end
758                 ifn = nil
759                 uci_s:foreach("wireless", "wifi-iface",
760                         function(s)
761                                 if s.device then
762                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
763                                         if s.network == self.sid then
764                                                 ifn = s.ifname or "%s.network%d" %{ s.device, num[s.device] }
765                                                 return false
766                                         end
767                                 end
768                         end)
769                 return ifn and interface(ifn)
770         end
771 end
772
773 function network.get_interfaces(self)
774         local ifaces = { }
775
776         local ifn
777         local nfs = { }
778         for ifn in utl.imatch(self:get("ifname")) do
779                 ifn = ifn:match("^[^:/]+")
780                 nfs[ifn] = interface(ifn)
781         end
782
783         for ifn in utl.kspairs(nfs) do
784                 ifaces[#ifaces+1] = nfs[ifn]
785         end
786
787         local num = { }
788         local wfs = { }
789         uci_r:foreach("wireless", "wifi-iface",
790                 function(s)
791                         if s.device then
792                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
793                                 if s.network == self.sid then
794                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
795                                         wfs[ifn] = interface(ifn)
796                                 end
797                         end
798                 end)
799
800         for ifn in utl.kspairs(wfs) do
801                 ifaces[#ifaces+1] = wfs[ifn]
802
803                 -- only bridges may cover more than one interface
804                 --if not self:is_bridge() then
805                 --      break
806                 --end
807         end
808
809         return ifaces
810 end
811
812 function network.contains_interface(self, ifname)
813         if type(ifname) ~= "string" then
814                 ifname = ifname:name()
815         else
816                 ifname = ifname:match("[^%s:]+")
817         end
818
819         if self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
820                 return true
821         elseif self:is_bridge() and "br-" .. self.sid == ifname then
822                 return true
823         else
824                 local ifn
825                 for ifn in utl.imatch(self:get("ifname")) do
826                         ifn = ifn:match("[^:]+")
827                         if ifn == ifname then
828                                 return true
829                         end
830                 end
831
832                 local wif = _wifi_lookup(ifname)
833                 if wif then
834                         return (uci_r:get("wireless", wif, "network") == self.sid)
835                 end
836         end
837
838         return false
839 end
840
841 function network.adminlink(self)
842         return dsp.build_url("admin", "network", "network", self.sid)
843 end
844
845
846 interface = utl.class()
847 function interface.__init__(self, ifname)
848         local wif = _wifi_lookup(ifname)
849         if wif then self.wif = wifinet(wif) end
850
851         self.ifname = self.ifname or ifname
852         self.dev    = ifs[self.ifname]
853 end
854
855 function interface.name(self)
856         return self.wif and self.wif:ifname() or self.ifname
857 end
858
859 function interface.mac(self)
860         return self.dev and self.dev.macaddr or "00:00:00:00:00:00"
861 end
862
863 function interface.ipaddrs(self)
864         return self.dev and self.dev.ipaddrs or { }
865 end
866
867 function interface.ip6addrs(self)
868         return self.dev and self.dev.ip6addrs or { }
869 end
870
871 function interface.type(self)
872         if self.wif or _wifi_iface(self.ifname) then
873                 return "wifi"
874         elseif brs[self.ifname] then
875                 return "bridge"
876         elseif self.ifname:match("%.") then
877                 return "vlan"
878         elseif sws[self.ifname] then
879                 return "switch"
880         else
881                 return "ethernet"
882         end
883 end
884
885 function interface.shortname(self)
886         if self.wif then
887                 return "%s %q" %{
888                         self.wif:active_mode(),
889                         self.wif:active_ssid() or self.wif:active_bssid()
890                 }
891         else
892                 return self.ifname
893         end
894 end
895
896 function interface.get_i18n(self)
897         if self.wif then
898                 return "%s: %s %q" %{
899                         i18n.translate("Wireless Network"),
900                         self.wif:active_mode(),
901                         self.wif:active_ssid() or self.wif:active_bssid()
902                 }
903         else
904                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
905         end
906 end
907
908 function interface.get_type_i18n(self)
909         local x = self:type()
910         if x == "wifi" then
911                 return i18n.translate("Wireless Adapter")
912         elseif x == "bridge" then
913                 return i18n.translate("Bridge")
914         elseif x == "switch" then
915                 return i18n.translate("Ethernet Switch")
916         elseif x == "vlan" then
917                 return i18n.translate("VLAN Interface")
918         else
919                 return i18n.translate("Ethernet Adapter")
920         end
921 end
922
923 function interface.adminlink(self)
924         if self.wif then
925                 return self.wif:adminlink()
926         end
927 end
928
929 function interface.ports(self)
930         if self.br then
931                 local iface
932                 local ifaces = { }
933                 for _, iface in ipairs(self.br.ifnames) do
934                         ifaces[#ifaces+1] = interface(iface.name)
935                 end
936                 return ifaces
937         end
938 end
939
940 function interface.bridge_id(self)
941         if self.br then
942                 return self.br.id
943         else
944                 return nil
945         end
946 end
947
948 function interface.bridge_stp(self)
949         if self.br then
950                 return self.br.stp
951         else
952                 return false
953         end
954 end
955
956 function interface.is_up(self)
957         if self.wif then
958                 return self.wif:is_up()
959         else
960                 return self.dev and self.dev.flags and self.dev.flags.up or false
961         end
962 end
963
964 function interface.is_bridge(self)
965         return (self:type() == "bridge")
966 end
967
968 function interface.is_bridgeport(self)
969         return self.dev and self.dev.bridge and true or false
970 end
971
972 function interface.tx_bytes(self)
973         return self.dev and self.dev.stats
974                 and self.dev.stats.tx_bytes or 0
975 end
976
977 function interface.rx_bytes(self)
978         return self.dev and self.dev.stats
979                 and self.dev.stats.rx_bytes or 0
980 end
981
982 function interface.tx_packets(self)
983         return self.dev and self.dev.stats
984                 and self.dev.stats.tx_packets or 0
985 end
986
987 function interface.rx_packets(self)
988         return self.dev and self.dev.stats
989                 and self.dev.stats.rx_packets or 0
990 end
991
992 function interface.get_network(self)
993         if self.dev and self.dev.network then
994                 self.network = _M:get_network(self.dev.network)
995         end
996
997         if not self.network then
998                 local net
999                 for _, net in ipairs(_M:get_networks()) do
1000                         if net:contains_interface(self.ifname) or
1001                            net:ifname() == self.ifname
1002                         then
1003                                 self.network = net
1004                                 return net
1005                         end
1006                 end
1007         else
1008                 return self.network
1009         end
1010 end
1011
1012 function interface.get_wifinet(self)
1013         return self.wif
1014 end
1015
1016
1017 wifidev = utl.class()
1018 function wifidev.__init__(self, dev)
1019         self.sid    = dev
1020         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1021 end
1022
1023 function wifidev.get(self, opt)
1024         return _get("wireless", self.sid, opt)
1025 end
1026
1027 function wifidev.set(self, opt, val)
1028         return _set("wireless", self.sid, opt, val)
1029 end
1030
1031 function wifidev.name(self)
1032         return self.sid
1033 end
1034
1035 function wifidev.hwmodes(self)
1036         local l = self.iwinfo.hwmodelist
1037         if l and next(l) then
1038                 return l
1039         else
1040                 return { b = true, g = true }
1041         end
1042 end
1043
1044 function wifidev.get_i18n(self)
1045         local t = "Generic"
1046         if self.iwinfo.type == "wl" then
1047                 t = "Broadcom"
1048         elseif self.iwinfo.type == "madwifi" then
1049                 t = "Atheros"
1050         end
1051
1052         local m = ""
1053         local l = self:hwmodes()
1054         if l.a then m = m .. "a" end
1055         if l.b then m = m .. "b" end
1056         if l.g then m = m .. "g" end
1057         if l.n then m = m .. "n" end
1058
1059         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1060 end
1061
1062 function wifidev.is_up(self)
1063         local up = false
1064
1065         uci_s:foreach("wireless", "wifi-iface",
1066                 function(s)
1067                         if s.device == self.sid then
1068                                 if s.up == "1" then
1069                                         up = true
1070                                         return false
1071                                 end
1072                         end
1073                 end)
1074
1075         return up
1076 end
1077
1078 function wifidev.get_wifinet(self, net)
1079         if uci_r:get("wireless", net) == "wifi-iface" then
1080                 return wifinet(net)
1081         else
1082                 local wnet = _wifi_lookup(net)
1083                 if wnet then
1084                         return wifinet(wnet)
1085                 end
1086         end
1087 end
1088
1089 function wifidev.get_wifinets(self)
1090         local nets = { }
1091
1092         uci_r:foreach("wireless", "wifi-iface",
1093                 function(s)
1094                         if s.device == self.sid then
1095                                 nets[#nets+1] = wifinet(s['.name'])
1096                         end
1097                 end)
1098
1099         return nets
1100 end
1101
1102 function wifidev.add_wifinet(self, options)
1103         options = options or { }
1104         options.device = self.sid
1105
1106         local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
1107         if wnet then
1108                 return wifinet(wnet, options)
1109         end
1110 end
1111
1112 function wifidev.del_wifinet(self, net)
1113         if utl.instanceof(net, wifinet) then
1114                 net = net.sid
1115         elseif uci_r:get("wireless", net) ~= "wifi-iface" then
1116                 net = _wifi_lookup(net)
1117         end
1118
1119         if net and uci_r:get("wireless", net, "device") == self.sid then
1120                 uci_r:delete("wireless", net)
1121                 return true
1122         end
1123
1124         return false
1125 end
1126
1127
1128 wifinet = utl.class()
1129 function wifinet.__init__(self, net, data)
1130         self.sid = net
1131
1132         local num = { }
1133         local netid
1134         uci_r:foreach("wireless", "wifi-iface",
1135                 function(s)
1136                         if s.device then
1137                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1138                                 if s['.name'] == self.sid then
1139                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1140                                         return false
1141                                 end
1142                         end
1143                 end)
1144
1145         local dev = uci_s:get("wireless", self.sid, "ifname") or netid
1146
1147         self.netid  = netid
1148         self.wdev   = dev
1149         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1150         self.iwdata = data or uci_s:get_all("wireless", self.sid) or
1151                 uci_r:get_all("wireless", self.sid) or { }
1152 end
1153
1154 function wifinet.get(self, opt)
1155         return _get("wireless", self.sid, opt)
1156 end
1157
1158 function wifinet.set(self, opt, val)
1159         return _set("wireless", self.sid, opt, val)
1160 end
1161
1162 function wifinet.mode(self)
1163         return uci_s:get("wireless", self.sid, "mode") or "ap"
1164 end
1165
1166 function wifinet.ssid(self)
1167         return uci_s:get("wireless", self.sid, "ssid")
1168 end
1169
1170 function wifinet.bssid(self)
1171         return uci_s:get("wireless", self.sid, "bssid")
1172 end
1173
1174 function wifinet.network(self)
1175         return uci_s:get("wifinet", self.sid, "network")
1176 end
1177
1178 function wifinet.id(self)
1179         return self.netid
1180 end
1181
1182 function wifinet.name(self)
1183         return self.sid
1184 end
1185
1186 function wifinet.ifname(self)
1187         local ifname = self.iwinfo.ifname
1188         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1189                 ifname = self.wdev
1190         end
1191         return ifname
1192 end
1193
1194 function wifinet.get_device(self)
1195         if self.iwdata.device then
1196                 return wifidev(self.iwdata.device)
1197         end
1198 end
1199
1200 function wifinet.is_up(self)
1201         return (self.iwdata.up == "1")
1202 end
1203
1204 function wifinet.active_mode(self)
1205         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1206
1207         if     m == "ap"      then m = "Master"
1208         elseif m == "sta"     then m = "Client"
1209         elseif m == "adhoc"   then m = "Ad-Hoc"
1210         elseif m == "mesh"    then m = "Mesh"
1211         elseif m == "monitor" then m = "Monitor"
1212         end
1213
1214         return m
1215 end
1216
1217 function wifinet.active_mode_i18n(self)
1218         return i18n.translate(self:active_mode())
1219 end
1220
1221 function wifinet.active_ssid(self)
1222         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1223 end
1224
1225 function wifinet.active_bssid(self)
1226         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1227 end
1228
1229 function wifinet.active_encryption(self)
1230         local enc = self.iwinfo and self.iwinfo.encryption
1231         return enc and enc.description or "-"
1232 end
1233
1234 function wifinet.assoclist(self)
1235         return self.iwinfo.assoclist or { }
1236 end
1237
1238 function wifinet.frequency(self)
1239         local freq = self.iwinfo.frequency
1240         if freq and freq > 0 then
1241                 return "%.03f" % (freq / 1000)
1242         end
1243 end
1244
1245 function wifinet.bitrate(self)
1246         local rate = self.iwinfo.bitrate
1247         if rate and rate > 0 then
1248                 return (rate / 1000)
1249         end
1250 end
1251
1252 function wifinet.channel(self)
1253         return self.iwinfo.channel or
1254                 tonumber(uci_s:get("wireless", self.iwdata.device, "channel"))
1255 end
1256
1257 function wifinet.signal(self)
1258         return self.iwinfo.signal or 0
1259 end
1260
1261 function wifinet.noise(self)
1262         return self.iwinfo.noise or 0
1263 end
1264
1265 function wifinet.country(self)
1266         return self.iwinfo.country or "00"
1267 end
1268
1269 function wifinet.txpower(self)
1270         return self.iwinfo.txpower or 0
1271 end
1272
1273 function wifinet.signal_level(self, s, n)
1274         if self:active_bssid() ~= "00:00:00:00:00:00" then
1275                 local signal = s or self:signal()
1276                 local noise  = n or self:noise()
1277
1278                 if signal < 0 and noise < 0 then
1279                         local snr = -1 * (noise - signal)
1280                         return math.floor(snr / 5)
1281                 else
1282                         return 0
1283                 end
1284         else
1285                 return -1
1286         end
1287 end
1288
1289 function wifinet.signal_percent(self)
1290         local qc = self.iwinfo.quality or 0
1291         local qm = self.iwinfo.quality_max or 0
1292
1293         if qc > 0 and qm > 0 then
1294                 return math.floor((100 / qm) * qc)
1295         else
1296                 return 0
1297         end
1298 end
1299
1300 function wifinet.shortname(self)
1301         return "%s %q" %{
1302                 i18n.translate(self:active_mode()),
1303                 self:active_ssid() or self:active_bssid()
1304         }
1305 end
1306
1307 function wifinet.get_i18n(self)
1308         return "%s: %s %q (%s)" %{
1309                 i18n.translate("Wireless Network"),
1310                 i18n.translate(self:active_mode()),
1311                 self:active_ssid() or self:active_bssid(),
1312                 self:ifname()
1313         }
1314 end
1315
1316 function wifinet.adminlink(self)
1317         return dsp.build_url("admin", "network", "wireless", self.netid)
1318 end
1319
1320 function wifinet.get_network(self)
1321         if uci_r:get("network", self.iwdata.network) == "interface" then
1322                 return network(self.iwdata.network)
1323         end
1324 end
1325
1326 function wifinet.get_interface(self)
1327         return interface(self:ifname())
1328 end