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