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