libs/core: use ubus to fetch interface and device status information, depend on libub...
[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
21         = type, next, pairs, ipairs, loadfile, table
22
23 local tonumber, tostring, math, i18n
24         = tonumber, tostring, math, luci.i18n
25
26 local require = require
27
28 local bus = require "ubus"
29 local nxo = require "nixio"
30 local nfs = require "nixio.fs"
31 local ipc = require "luci.ip"
32 local sys = require "luci.sys"
33 local utl = require "luci.util"
34 local dsp = require "luci.dispatcher"
35 local uci = require "luci.model.uci"
36
37 module "luci.model.network"
38
39
40 IFACE_PATTERNS_VIRTUAL  = { }
41 IFACE_PATTERNS_IGNORE   = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^lo$" }
42 IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
43
44
45 protocol = utl.class()
46
47 local _protocols = { }
48
49 local _interfaces, _bridge, _switch, _tunnel
50 local _ubus, _ubusnetcache, _ubusdevcache
51 local _uci_real, _uci_state
52
53 function _filter(c, s, o, r)
54         local val = _uci_real:get(c, s, o)
55         if val then
56                 local l = { }
57                 if type(val) == "string" then
58                         for val in val:gmatch("%S+") do
59                                 if val ~= r then
60                                         l[#l+1] = val
61                                 end
62                         end
63                         if #l > 0 then
64                                 _uci_real:set(c, s, o, table.concat(l, " "))
65                         else
66                                 _uci_real:delete(c, s, o)
67                         end
68                 elseif type(val) == "table" then
69                         for _, val in ipairs(val) do
70                                 if val ~= r then
71                                         l[#l+1] = val
72                                 end
73                         end
74                         if #l > 0 then
75                                 _uci_real:set(c, s, o, l)
76                         else
77                                 _uci_real:delete(c, s, o)
78                         end
79                 end
80         end
81 end
82
83 function _append(c, s, o, a)
84         local val = _uci_real:get(c, s, o) or ""
85         if type(val) == "string" then
86                 local l = { }
87                 for val in val:gmatch("%S+") do
88                         if val ~= a then
89                                 l[#l+1] = val
90                         end
91                 end
92                 l[#l+1] = a
93                 _uci_real:set(c, s, o, table.concat(l, " "))
94         elseif type(val) == "table" then
95                 local l = { }
96                 for _, val in ipairs(val) do
97                         if val ~= a then
98                                 l[#l+1] = val
99                         end
100                 end
101                 l[#l+1] = a
102                 _uci_real:set(c, s, o, l)
103         end
104 end
105
106 function _stror(s1, s2)
107         if not s1 or #s1 == 0 then
108                 return s2 and #s2 > 0 and s2
109         else
110                 return s1
111         end
112 end
113
114 function _get(c, s, o)
115         return _uci_real:get(c, s, o)
116 end
117
118 function _set(c, s, o, v)
119         if v ~= nil then
120                 if type(v) == "boolean" then v = v and "1" or "0" end
121                 return _uci_real:set(c, s, o, v)
122         else
123                 return _uci_real:delete(c, s, o)
124         end
125 end
126
127 function _wifi_iface(x)
128         local _, p
129         for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
130                 if x:match(p) then
131                         return true
132                 end
133         end
134         return false
135 end
136
137 function _wifi_lookup(ifn)
138         -- got a radio#.network# pseudo iface, locate the corresponding section
139         local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
140         if radio and ifnidx then
141                 local sid = nil
142                 local num = 0
143
144                 ifnidx = tonumber(ifnidx)
145                 _uci_real:foreach("wireless", "wifi-iface",
146                         function(s)
147                                 if s.device == radio then
148                                         num = num + 1
149                                         if num == ifnidx then
150                                                 sid = s['.name']
151                                                 return false
152                                         end
153                                 end
154                         end)
155
156                 return sid
157
158         -- looks like wifi, try to locate the section via state vars
159         elseif _wifi_iface(ifn) then
160                 local sid = nil
161
162                 _uci_state:foreach("wireless", "wifi-iface",
163                         function(s)
164                                 if s.ifname == ifn then
165                                         sid = s['.name']
166                                         return false
167                                 end
168                         end)
169
170                 return sid
171         end
172 end
173
174 function _iface_virtual(x)
175         local _, p
176         for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do
177                 if x:match(p) then
178                         return true
179                 end
180         end
181         return false
182 end
183
184 function _iface_ignore(x)
185         local _, p
186         for _, p in ipairs(IFACE_PATTERNS_IGNORE) do
187                 if x:match(p) then
188                         return true
189                 end
190         end
191         return _iface_virtual(x)
192 end
193
194
195 function init(cursor)
196         _uci_real  = cursor or _uci_real or uci.cursor()
197         _uci_state = _uci_real:substate()
198
199         _interfaces = { }
200         _bridge     = { }
201         _switch     = { }
202         _tunnel     = { }
203
204         _ubus         = bus.connect()
205         _ubusnetcache = { }
206         _ubusdevcache = { }
207
208         -- read interface information
209         local n, i
210         for n, i in ipairs(nxo.getifaddrs()) do
211                 local name = i.name:match("[^:]+")
212                 local prnt = name:match("^([^%.]+)%.")
213
214                 if _iface_virtual(name) then
215                         _tunnel[name] = true
216                 end
217
218                 if _tunnel[name] or not _iface_ignore(name) then
219                         _interfaces[name] = _interfaces[name] or {
220                                 idx      = i.ifindex or n,
221                                 name     = name,
222                                 rawname  = i.name,
223                                 flags    = { },
224                                 ipaddrs  = { },
225                                 ip6addrs = { }
226                         }
227
228                         if prnt then
229                                 _switch[name] = true
230                                 _switch[prnt] = true
231                         end
232
233                         if i.family == "packet" then
234                                 _interfaces[name].flags   = i.flags
235                                 _interfaces[name].stats   = i.data
236                                 _interfaces[name].macaddr = i.addr
237                         elseif i.family == "inet" then
238                                 _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
239                         elseif i.family == "inet6" then
240                                 _interfaces[name].ip6addrs[#_interfaces[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
241                         end
242                 end
243         end
244
245         -- read bridge informaton
246         local b, l
247         for l in utl.execi("brctl show") do
248                 if not l:match("STP") then
249                         local r = utl.split(l, "%s+", nil, true)
250                         if #r == 4 then
251                                 b = {
252                                         name    = r[1],
253                                         id      = r[2],
254                                         stp     = r[3] == "yes",
255                                         ifnames = { _interfaces[r[4]] }
256                                 }
257                                 if b.ifnames[1] then
258                                         b.ifnames[1].bridge = b
259                                 end
260                                 _bridge[r[1]] = b
261                         elseif b then
262                                 b.ifnames[#b.ifnames+1] = _interfaces[r[2]]
263                                 b.ifnames[#b.ifnames].bridge = b
264                         end
265                 end
266         end
267
268         return _M
269 end
270
271 function save(self, ...)
272         _uci_real:save(...)
273         _uci_real:load(...)
274 end
275
276 function commit(self, ...)
277         _uci_real:commit(...)
278         _uci_real:load(...)
279 end
280
281 function ifnameof(self, x)
282         if utl.instanceof(x, interface) then
283                 return x:name()
284         elseif utl.instanceof(x, protocol) then
285                 return x:ifname()
286         elseif type(x) == "string" then
287                 return x:match("^[^:]+")
288         end
289 end
290
291 function get_protocol(self, protoname, netname)
292         local v = _protocols[protoname]
293         if v then
294                 return v(netname or "__dummy__")
295         end
296 end
297
298 function get_protocols(self)
299         local p = { }
300         local _, v
301         for _, v in ipairs(_protocols) do
302                 p[#p+1] = v("__dummy__")
303         end
304         return p
305 end
306
307 function register_protocol(self, protoname)
308         local proto = utl.class(protocol)
309
310         function proto.__init__(self, name)
311                 self.sid = name
312         end
313
314         function proto.proto(self)
315                 return protoname
316         end
317
318         _protocols[#_protocols+1] = proto
319         _protocols[protoname]     = proto
320
321         return proto
322 end
323
324 function register_pattern_virtual(self, pat)
325         IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
326 end
327
328
329 function has_ipv6(self)
330         return nfs.access("/proc/net/ipv6_route")
331 end
332
333 function add_network(self, n, options)
334         local oldnet = self:get_network(n)
335         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
336                 if _uci_real:section("network", "interface", n, options) then
337                         return network(n)
338                 end
339         elseif oldnet and oldnet:is_empty() then
340                 if options then
341                         local k, v
342                         for k, v in pairs(options) do
343                                 oldnet:set(k, v)
344                         end
345                 end
346                 return oldnet
347         end
348 end
349
350 function get_network(self, n)
351         if n and _uci_real:get("network", n) == "interface" then
352                 return network(n)
353         end
354 end
355
356 function get_networks(self)
357         local nets = { }
358         local nls = { }
359
360         _uci_real:foreach("network", "interface",
361                 function(s)
362                         nls[s['.name']] = network(s['.name'])
363                 end)
364
365         local n
366         for n in utl.kspairs(nls) do
367                 nets[#nets+1] = nls[n]
368         end
369
370         return nets
371 end
372
373 function del_network(self, n)
374         local r = _uci_real:delete("network", n)
375         if r then
376                 _uci_real:delete_all("network", "alias",
377                         function(s) return (s.interface == n) end)
378
379                 _uci_real:delete_all("network", "route",
380                         function(s) return (s.interface == n) end)
381
382                 _uci_real:delete_all("network", "route6",
383                         function(s) return (s.interface == n) end)
384
385                 _uci_real:foreach("wireless", "wifi-iface",
386                         function(s)
387                                 if s.network == n then
388                                         _uci_real:delete("wireless", s['.name'], "network")
389                                 end
390                         end)
391         end
392         return r
393 end
394
395 function rename_network(self, old, new)
396         local r
397         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
398                 r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
399
400                 if r then
401                         _uci_real:foreach("network", "alias",
402                                 function(s)
403                                         if s.interface == old then
404                                                 _uci_real:set("network", s['.name'], "interface", new)
405                                         end
406                                 end)
407
408                         _uci_real:foreach("network", "route",
409                                 function(s)
410                                         if s.interface == old then
411                                                 _uci_real:set("network", s['.name'], "interface", new)
412                                         end
413                                 end)
414
415                         _uci_real:foreach("network", "route6",
416                                 function(s)
417                                         if s.interface == old then
418                                                 _uci_real:set("network", s['.name'], "interface", new)
419                                         end
420                                 end)
421
422                         _uci_real:foreach("wireless", "wifi-iface",
423                                 function(s)
424                                         if s.network == old then
425                                                 _uci_real:set("wireless", s['.name'], "network", new)
426                                         end
427                                 end)
428
429                         _uci_real:delete("network", old)
430                 end
431         end
432         return r or false
433 end
434
435 function get_interface(self, i)
436         if _interfaces[i] or _wifi_iface(i) then
437                 return interface(i)
438         else
439                 local ifc
440                 local num = { }
441                 _uci_real:foreach("wireless", "wifi-iface",
442                         function(s)
443                                 if s.device then
444                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
445                                         if s['.name'] == i then
446                                                 ifc = interface(
447                                                         "%s.network%d" %{s.device, num[s.device] })
448                                                 return false
449                                         end
450                                 end
451                         end)
452                 return ifc
453         end
454 end
455
456 function get_interfaces(self)
457         local iface
458         local ifaces = { }
459         local seen = { }
460         local nfs = { }
461         local baseof = { }
462
463         -- find normal interfaces
464         _uci_real:foreach("network", "interface",
465                 function(s)
466                         for iface in utl.imatch(s.ifname) do
467                                 if not _iface_ignore(iface) and not _wifi_iface(iface) then
468                                         seen[iface] = true
469                                         nfs[iface] = interface(iface)
470                                 end
471                         end
472                 end)
473
474         for iface in utl.kspairs(_interfaces) do
475                 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
476                         nfs[iface] = interface(iface)
477                 end
478         end
479
480         -- find vlan interfaces
481         _uci_real:foreach("network", "switch_vlan",
482                 function(s)
483                         if not s.device then
484                                 return
485                         end
486
487                         local base = baseof[s.device]
488                         if not base then
489                                 if not s.device:match("^eth%d") then
490                                         local l
491                                         for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
492                                                 if not base then
493                                                         base = l:match("^%w+: (%w+)")
494                                                 end
495                                         end
496                                         if not base or not base:match("^eth%d") then
497                                                 base = "eth0"
498                                         end
499                                 else
500                                         base = s.device
501                                 end
502                                 baseof[s.device] = base
503                         end
504
505                         local vid = tonumber(s.vid or s.vlan)
506                         if vid ~= nil and vid >= 0 and vid <= 4095 then
507                                 local iface = "%s.%d" %{ base, vid }
508                                 if not seen[iface] then
509                                         seen[iface] = true
510                                         nfs[iface] = interface(iface)
511                                 end
512                         end
513                 end)
514
515         for iface in utl.kspairs(nfs) do
516                 ifaces[#ifaces+1] = nfs[iface]
517         end
518
519         -- find wifi interfaces
520         local num = { }
521         local wfs = { }
522         _uci_real:foreach("wireless", "wifi-iface",
523                 function(s)
524                         if s.device then
525                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
526                                 local i = "%s.network%d" %{ s.device, num[s.device] }
527                                 wfs[i] = interface(i)
528                         end
529                 end)
530
531         for iface in utl.kspairs(wfs) do
532                 ifaces[#ifaces+1] = wfs[iface]
533         end
534
535         return ifaces
536 end
537
538 function ignore_interface(self, x)
539         return _iface_ignore(x)
540 end
541
542 function get_wifidev(self, dev)
543         if _uci_real:get("wireless", dev) == "wifi-device" then
544                 return wifidev(dev)
545         end
546 end
547
548 function get_wifidevs(self)
549         local devs = { }
550         local wfd  = { }
551
552         _uci_real:foreach("wireless", "wifi-device",
553                 function(s) wfd[#wfd+1] = s['.name'] end)
554
555         local dev
556         for _, dev in utl.vspairs(wfd) do
557                 devs[#devs+1] = wifidev(dev)
558         end
559
560         return devs
561 end
562
563 function get_wifinet(self, net)
564         local wnet = _wifi_lookup(net)
565         if wnet then
566                 return wifinet(wnet)
567         end
568 end
569
570 function add_wifinet(self, net, options)
571         if type(options) == "table" and options.device and
572                 _uci_real:get("wireless", options.device) == "wifi-device"
573         then
574                 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
575                 return wifinet(wnet)
576         end
577 end
578
579 function del_wifinet(self, net)
580         local wnet = _wifi_lookup(net)
581         if wnet then
582                 _uci_real:delete("wireless", wnet)
583                 return true
584         end
585         return false
586 end
587
588
589 function network(name, proto)
590         if name then
591                 local p = proto or _uci_real:get("network", name, "proto")
592                 local c = p and _protocols[p] or protocol
593                 return c(name)
594         end
595 end
596
597 function protocol.__init__(self, name)
598         self.sid = name
599 end
600
601 function protocol._get(self, opt)
602         local v = _uci_real:get("network", self.sid, opt)
603         if type(v) == "table" then
604                 return table.concat(v, " ")
605         end
606         return v or ""
607 end
608
609 function protocol._ubus(self, field)
610         if not _ubusnetcache[self.sid] then
611                 _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid,
612                                                   "status", { })
613         end
614
615         return _ubusnetcache[self.sid] and (field and _ubusnetcache[self.sid][field]
616                                                 or _ubusnetcache[self.sid])
617 end
618
619 function protocol.get(self, opt)
620         return _get("network", self.sid, opt)
621 end
622
623 function protocol.set(self, opt, val)
624         return _set("network", self.sid, opt, val)
625 end
626
627 function protocol.ifname(self)
628         local p = self:proto()
629         if self:is_bridge() then
630                 return "br-" .. self.sid
631         elseif self:is_virtual() then
632                 return p .. "-" .. self.sid
633         else
634                 local num = { }
635                 local dev = _uci_real:get("network", self.sid, "ifname") or
636                         _uci_state:get("network", self.sid, "ifname")
637
638                 dev = (type(dev) == "table") and dev[1] or dev
639                 dev = (dev ~= nil) and dev:match("%S+")
640
641                 if not dev then
642                         _uci_real:foreach("wireless", "wifi-iface",
643                                 function(s)
644                                         if s.device then
645                                                 num[s.device] = num[s.device]
646                                                         and num[s.device] + 1 or 1
647
648                                                 if s.network == self.sid then
649                                                         dev = "%s.network%d" %{ s.device, num[s.device] }
650                                                         return false
651                                                 end
652                                         end
653                                 end)
654                 end
655
656                 return dev
657         end
658 end
659
660 function protocol.proto(self)
661         return "none"
662 end
663
664 function protocol.get_i18n(self)
665         local p = self:proto()
666         if p == "none" then
667                 return i18n.translate("Unmanaged")
668         elseif p == "static" then
669                 return i18n.translate("Static address")
670         elseif p == "dhcp" then
671                 return i18n.translate("DHCP client")
672         else
673                 return i18n.translate("Unknown")
674         end
675 end
676
677 function protocol.type(self)
678         return self:_get("type")
679 end
680
681 function protocol.name(self)
682         return self.sid
683 end
684
685 function protocol.uptime(self)
686         return self:_ubus("uptime") or 0
687 end
688
689 function protocol.expires(self)
690         local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
691         local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
692         if a and l then
693                 l = l - (nxo.sysinfo().uptime - a)
694                 return l > 0 and l or 0
695         end
696         return -1
697 end
698
699 function protocol.metric(self)
700         return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
701 end
702
703 function protocol.ipaddr(self)
704         local addrs = self:_ubus("ipv4-address")
705         return addrs and #addrs > 0 and addrs[1].address
706 end
707
708 function protocol.netmask(self)
709         local addrs = self:_ubus("ipv4-address")
710         return addrs and #addrs > 0 and
711                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
712 end
713
714 function protocol.gwaddr(self)
715         local _, route
716         for _, route in ipairs(self:_ubus("route")) do
717                 if route.target == "0.0.0.0" and route.mask == 0 then
718                         return route.nexthop
719                 end
720         end
721 end
722
723 function protocol.dnsaddrs(self)
724         local dns = { }
725         local _, addr
726         for _, addr in ipairs(self:_ubus("dns-server")) do
727                 if not addr:match(":") then
728                         dns[#dns+1] = addr
729                 end
730         end
731         return dns
732 end
733
734 function protocol.ip6addr(self)
735         local addrs = self:_ubus("ipv6-address")
736         return addrs and #addrs > 0
737                 and "%s/%d" %{ addrs[1].address, addrs[1].mask }
738 end
739
740 function protocol.gw6addr(self)
741         local _, route
742         for _, route in ipairs(self:_ubus("route")) do
743                 if route.target == "::" and route.mask == 0 then
744                         return ipc.IPv6(route.nexthop):string()
745                 end
746         end
747 end
748
749 function protocol.dns6addrs(self)
750         local dns = { }
751         local _, addr
752         for _, addr in ipairs(self:_ubus("dns-server")) do
753                 if addr:match(":") then
754                         dns[#dns+1] = addr
755                 end
756         end
757         return dns
758 end
759
760 function protocol.is_bridge(self)
761         return (not self:is_virtual() and self:type() == "bridge")
762 end
763
764 function protocol.opkg_package(self)
765         return nil
766 end
767
768 function protocol.is_installed(self)
769         return true
770 end
771
772 function protocol.is_virtual(self)
773         return false
774 end
775
776 function protocol.is_floating(self)
777         return false
778 end
779
780 function protocol.is_empty(self)
781         if self:is_floating() then
782                 return false
783         else
784                 local rv = true
785
786                 if (self:_get("ifname") or ""):match("%S+") then
787                         rv = false
788                 end
789
790                 _uci_real:foreach("wireless", "wifi-iface",
791                         function(s)
792                                 if s.network == self.sid then
793                                         rv = false
794                                         return false
795                                 end
796                         end)
797
798                 return rv
799         end
800 end
801
802 function protocol.add_interface(self, ifname)
803         ifname = _M:ifnameof(ifname)
804         if ifname and not self:is_floating() then
805                 -- remove the interface from all ifaces
806                 _uci_real:foreach("network", "interface",
807                         function(s)
808                                 _filter("network", s['.name'], "ifname", ifname)
809                         end)
810
811                 -- if its a wifi interface, change its network option
812                 local wif = _wifi_lookup(ifname)
813                 if wif then
814                         _uci_real:set("wireless", wif, "network", self.sid)
815
816                 -- add iface to our iface list
817                 else
818                         _append("network", self.sid, "ifname", ifname)
819                 end
820         end
821 end
822
823 function protocol.del_interface(self, ifname)
824         ifname = _M:ifnameof(ifname)
825         if ifname and not self:is_floating() then
826                 -- if its a wireless interface, clear its network option
827                 local wif = _wifi_lookup(ifname)
828                 if wif then     _uci_real:delete("wireless", wif, "network") end
829
830                 -- remove the interface
831                 _filter("network", self.sid, "ifname", ifname)
832         end
833 end
834
835 function protocol.get_interface(self)
836         if self:is_virtual() then
837                 _tunnel[self:proto() .. "-" .. self.sid] = true
838                 return interface(self:proto() .. "-" .. self.sid, self)
839         elseif self:is_bridge() then
840                 _bridge["br-" .. self.sid] = true
841                 return interface("br-" .. self.sid, self)
842         else
843                 local ifn = nil
844                 local num = { }
845                 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
846                         ifn = ifn:match("^[^:/]+")
847                         return ifn and interface(ifn, self)
848                 end
849                 ifn = nil
850                 _uci_real:foreach("wireless", "wifi-iface",
851                         function(s)
852                                 if s.device then
853                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
854                                         if s.network == self.sid then
855                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
856                                                 return false
857                                         end
858                                 end
859                         end)
860                 return ifn and interface(ifn, self)
861         end
862 end
863
864 function protocol.get_interfaces(self)
865         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
866                 local ifaces = { }
867
868                 local ifn
869                 local nfs = { }
870                 for ifn in utl.imatch(self:get("ifname")) do
871                         ifn = ifn:match("^[^:/]+")
872                         nfs[ifn] = interface(ifn, self)
873                 end
874
875                 for ifn in utl.kspairs(nfs) do
876                         ifaces[#ifaces+1] = nfs[ifn]
877                 end
878
879                 local num = { }
880                 local wfs = { }
881                 _uci_real:foreach("wireless", "wifi-iface",
882                         function(s)
883                                 if s.device then
884                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
885                                         if s.network == self.sid then
886                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
887                                                 wfs[ifn] = interface(ifn, self)
888                                         end
889                                 end
890                         end)
891
892                 for ifn in utl.kspairs(wfs) do
893                         ifaces[#ifaces+1] = wfs[ifn]
894                 end
895
896                 return ifaces
897         end
898 end
899
900 function protocol.contains_interface(self, ifname)
901         ifname = _M:ifnameof(ifname)
902         if not ifname then
903                 return false
904         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
905                 return true
906         elseif self:is_bridge() and "br-" .. self.sid == ifname then
907                 return true
908         else
909                 local ifn
910                 for ifn in utl.imatch(self:get("ifname")) do
911                         ifn = ifn:match("[^:]+")
912                         if ifn == ifname then
913                                 return true
914                         end
915                 end
916
917                 local wif = _wifi_lookup(ifname)
918                 if wif then
919                         return (_uci_real:get("wireless", wif, "network") == self.sid)
920                 end
921         end
922
923         return false
924 end
925
926 function protocol.adminlink(self)
927         return dsp.build_url("admin", "network", "network", self.sid)
928 end
929
930
931 interface = utl.class()
932
933 function interface.__init__(self, ifname, network)
934         local wif = _wifi_lookup(ifname)
935         if wif then
936                 self.wif    = wifinet(wif)
937                 self.ifname = _uci_state:get("wireless", wif, "ifname")
938         end
939
940         self.ifname  = self.ifname or ifname
941         self.dev     = _interfaces[self.ifname]
942         self.network = network
943 end
944
945 function interface._ubus(self, field)
946         if not _ubusdevcache[self.ifname] then
947                 _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
948                                                         { name = self.ifname })
949         end
950         return _ubusdevcache[self.ifname] and
951                 (field and _ubusdevcache[self.ifname][field] or
952                            _ubusdevcache[self.ifname])
953 end
954
955 function interface.name(self)
956         return self.wif and self.wif:ifname() or self.ifname
957 end
958
959 function interface.mac(self)
960         return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
961 end
962
963 function interface.ipaddrs(self)
964         return self.dev and self.dev.ipaddrs or { }
965 end
966
967 function interface.ip6addrs(self)
968         return self.dev and self.dev.ip6addrs or { }
969 end
970
971 function interface.type(self)
972         if self.wif or _wifi_iface(self.ifname) then
973                 return "wifi"
974         elseif _bridge[self.ifname] then
975                 return "bridge"
976         elseif _tunnel[self.ifname] then
977                 return "tunnel"
978         elseif self.ifname:match("%.") then
979                 return "vlan"
980         elseif _switch[self.ifname] then
981                 return "switch"
982         else
983                 return "ethernet"
984         end
985 end
986
987 function interface.shortname(self)
988         if self.wif then
989                 return "%s %q" %{
990                         self.wif:active_mode(),
991                         self.wif:active_ssid() or self.wif:active_bssid()
992                 }
993         else
994                 return self.ifname
995         end
996 end
997
998 function interface.get_i18n(self)
999         if self.wif then
1000                 return "%s: %s %q" %{
1001                         i18n.translate("Wireless Network"),
1002                         self.wif:active_mode(),
1003                         self.wif:active_ssid() or self.wif:active_bssid()
1004                 }
1005         else
1006                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1007         end
1008 end
1009
1010 function interface.get_type_i18n(self)
1011         local x = self:type()
1012         if x == "wifi" then
1013                 return i18n.translate("Wireless Adapter")
1014         elseif x == "bridge" then
1015                 return i18n.translate("Bridge")
1016         elseif x == "switch" then
1017                 return i18n.translate("Ethernet Switch")
1018         elseif x == "vlan" then
1019                 return i18n.translate("VLAN Interface")
1020         elseif x == "tunnel" then
1021                 return i18n.translate("Tunnel Interface")
1022         else
1023                 return i18n.translate("Ethernet Adapter")
1024         end
1025 end
1026
1027 function interface.adminlink(self)
1028         if self.wif then
1029                 return self.wif:adminlink()
1030         end
1031 end
1032
1033 function interface.ports(self)
1034         local members = self:_ubus("bridge-members")
1035         if members then
1036                 local _, iface
1037                 local ifaces = { }
1038                 for _, iface in ipairs(members) do
1039                         ifaces[#ifaces+1] = interface(iface)
1040                 end
1041         end
1042 end
1043
1044 function interface.bridge_id(self)
1045         if self.br then
1046                 return self.br.id
1047         else
1048                 return nil
1049         end
1050 end
1051
1052 function interface.bridge_stp(self)
1053         if self.br then
1054                 return self.br.stp
1055         else
1056                 return false
1057         end
1058 end
1059
1060 function interface.is_up(self)
1061         if self.wif then
1062                 return self.wif:is_up()
1063         else
1064                 return self:_ubus("up") or false
1065         end
1066 end
1067
1068 function interface.is_bridge(self)
1069         return (self:type() == "bridge")
1070 end
1071
1072 function interface.is_bridgeport(self)
1073         return self.dev and self.dev.bridge and true or false
1074 end
1075
1076 local function uint(x)
1077         return (x < 0) and ((2^32) + x) or x
1078 end
1079
1080 function interface.tx_bytes(self)
1081         local stat = self:_ubus("statistics")
1082         return stat and uint(stat.tx_bytes) or 0
1083 end
1084
1085 function interface.rx_bytes(self)
1086         local stat = self:_ubus("statistics")
1087         return stat and uint(stat.rx_bytes) or 0
1088 end
1089
1090 function interface.tx_packets(self)
1091         local stat = self:_ubus("statistics")
1092         return stat and uint(stat.tx_packets) or 0
1093 end
1094
1095 function interface.rx_packets(self)
1096         local stat = self:_ubus("statistics")
1097         return stat and uint(stat.rx_packets) or 0
1098 end
1099
1100 function interface.get_network(self)
1101         if not self.network then
1102                 if self.dev and self.dev.network then
1103                         self.network = _M:get_network(self.dev.network)
1104                 end
1105         end
1106
1107         if not self.network then
1108                 local net
1109                 for _, net in ipairs(_M:get_networks()) do
1110                         if net:contains_interface(self.ifname) or
1111                            net:ifname() == self.ifname
1112                         then
1113                                 self.network = net
1114                                 return net
1115                         end
1116                 end
1117         else
1118                 return self.network
1119         end
1120 end
1121
1122 function interface.get_wifinet(self)
1123         return self.wif
1124 end
1125
1126
1127 wifidev = utl.class()
1128
1129 function wifidev.__init__(self, dev)
1130         self.sid    = dev
1131         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1132 end
1133
1134 function wifidev.get(self, opt)
1135         return _get("wireless", self.sid, opt)
1136 end
1137
1138 function wifidev.set(self, opt, val)
1139         return _set("wireless", self.sid, opt, val)
1140 end
1141
1142 function wifidev.name(self)
1143         return self.sid
1144 end
1145
1146 function wifidev.hwmodes(self)
1147         local l = self.iwinfo.hwmodelist
1148         if l and next(l) then
1149                 return l
1150         else
1151                 return { b = true, g = true }
1152         end
1153 end
1154
1155 function wifidev.get_i18n(self)
1156         local t = "Generic"
1157         if self.iwinfo.type == "wl" then
1158                 t = "Broadcom"
1159         elseif self.iwinfo.type == "madwifi" then
1160                 t = "Atheros"
1161         end
1162
1163         local m = ""
1164         local l = self:hwmodes()
1165         if l.a then m = m .. "a" end
1166         if l.b then m = m .. "b" end
1167         if l.g then m = m .. "g" end
1168         if l.n then m = m .. "n" end
1169
1170         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1171 end
1172
1173 function wifidev.is_up(self)
1174         local up = false
1175
1176         _uci_state:foreach("wireless", "wifi-iface",
1177                 function(s)
1178                         if s.device == self.sid then
1179                                 if s.up == "1" then
1180                                         up = true
1181                                         return false
1182                                 end
1183                         end
1184                 end)
1185
1186         return up
1187 end
1188
1189 function wifidev.get_wifinet(self, net)
1190         if _uci_real:get("wireless", net) == "wifi-iface" then
1191                 return wifinet(net)
1192         else
1193                 local wnet = _wifi_lookup(net)
1194                 if wnet then
1195                         return wifinet(wnet)
1196                 end
1197         end
1198 end
1199
1200 function wifidev.get_wifinets(self)
1201         local nets = { }
1202
1203         _uci_real:foreach("wireless", "wifi-iface",
1204                 function(s)
1205                         if s.device == self.sid then
1206                                 nets[#nets+1] = wifinet(s['.name'])
1207                         end
1208                 end)
1209
1210         return nets
1211 end
1212
1213 function wifidev.add_wifinet(self, options)
1214         options = options or { }
1215         options.device = self.sid
1216
1217         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1218         if wnet then
1219                 return wifinet(wnet, options)
1220         end
1221 end
1222
1223 function wifidev.del_wifinet(self, net)
1224         if utl.instanceof(net, wifinet) then
1225                 net = net.sid
1226         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1227                 net = _wifi_lookup(net)
1228         end
1229
1230         if net and _uci_real:get("wireless", net, "device") == self.sid then
1231                 _uci_real:delete("wireless", net)
1232                 return true
1233         end
1234
1235         return false
1236 end
1237
1238
1239 wifinet = utl.class()
1240
1241 function wifinet.__init__(self, net, data)
1242         self.sid = net
1243
1244         local num = { }
1245         local netid
1246         _uci_real:foreach("wireless", "wifi-iface",
1247                 function(s)
1248                         if s.device then
1249                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1250                                 if s['.name'] == self.sid then
1251                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1252                                         return false
1253                                 end
1254                         end
1255                 end)
1256
1257         local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1258
1259         self.netid  = netid
1260         self.wdev   = dev
1261         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1262         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1263                 _uci_real:get_all("wireless", self.sid) or { }
1264 end
1265
1266 function wifinet.get(self, opt)
1267         return _get("wireless", self.sid, opt)
1268 end
1269
1270 function wifinet.set(self, opt, val)
1271         return _set("wireless", self.sid, opt, val)
1272 end
1273
1274 function wifinet.mode(self)
1275         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1276 end
1277
1278 function wifinet.ssid(self)
1279         return _uci_state:get("wireless", self.sid, "ssid")
1280 end
1281
1282 function wifinet.bssid(self)
1283         return _uci_state:get("wireless", self.sid, "bssid")
1284 end
1285
1286 function wifinet.network(self)
1287         return _uci_state:get("wifinet", self.sid, "network")
1288 end
1289
1290 function wifinet.id(self)
1291         return self.netid
1292 end
1293
1294 function wifinet.name(self)
1295         return self.sid
1296 end
1297
1298 function wifinet.ifname(self)
1299         local ifname = self.iwinfo.ifname
1300         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1301                 ifname = self.wdev
1302         end
1303         return ifname
1304 end
1305
1306 function wifinet.get_device(self)
1307         if self.iwdata.device then
1308                 return wifidev(self.iwdata.device)
1309         end
1310 end
1311
1312 function wifinet.is_up(self)
1313         return (self.iwdata.up == "1")
1314 end
1315
1316 function wifinet.active_mode(self)
1317         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1318
1319         if     m == "ap"      then m = "Master"
1320         elseif m == "sta"     then m = "Client"
1321         elseif m == "adhoc"   then m = "Ad-Hoc"
1322         elseif m == "mesh"    then m = "Mesh"
1323         elseif m == "monitor" then m = "Monitor"
1324         end
1325
1326         return m
1327 end
1328
1329 function wifinet.active_mode_i18n(self)
1330         return i18n.translate(self:active_mode())
1331 end
1332
1333 function wifinet.active_ssid(self)
1334         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1335 end
1336
1337 function wifinet.active_bssid(self)
1338         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1339 end
1340
1341 function wifinet.active_encryption(self)
1342         local enc = self.iwinfo and self.iwinfo.encryption
1343         return enc and enc.description or "-"
1344 end
1345
1346 function wifinet.assoclist(self)
1347         return self.iwinfo.assoclist or { }
1348 end
1349
1350 function wifinet.frequency(self)
1351         local freq = self.iwinfo.frequency
1352         if freq and freq > 0 then
1353                 return "%.03f" % (freq / 1000)
1354         end
1355 end
1356
1357 function wifinet.bitrate(self)
1358         local rate = self.iwinfo.bitrate
1359         if rate and rate > 0 then
1360                 return (rate / 1000)
1361         end
1362 end
1363
1364 function wifinet.channel(self)
1365         return self.iwinfo.channel or
1366                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1367 end
1368
1369 function wifinet.signal(self)
1370         return self.iwinfo.signal or 0
1371 end
1372
1373 function wifinet.noise(self)
1374         return self.iwinfo.noise or 0
1375 end
1376
1377 function wifinet.country(self)
1378         return self.iwinfo.country or "00"
1379 end
1380
1381 function wifinet.txpower(self)
1382         local pwr = (self.iwinfo.txpower or 0)
1383         return pwr + self:txpower_offset()
1384 end
1385
1386 function wifinet.txpower_offset(self)
1387         return self.iwinfo.txpower_offset or 0
1388 end
1389
1390 function wifinet.signal_level(self, s, n)
1391         if self:active_bssid() ~= "00:00:00:00:00:00" then
1392                 local signal = s or self:signal()
1393                 local noise  = n or self:noise()
1394
1395                 if signal < 0 and noise < 0 then
1396                         local snr = -1 * (noise - signal)
1397                         return math.floor(snr / 5)
1398                 else
1399                         return 0
1400                 end
1401         else
1402                 return -1
1403         end
1404 end
1405
1406 function wifinet.signal_percent(self)
1407         local qc = self.iwinfo.quality or 0
1408         local qm = self.iwinfo.quality_max or 0
1409
1410         if qc > 0 and qm > 0 then
1411                 return math.floor((100 / qm) * qc)
1412         else
1413                 return 0
1414         end
1415 end
1416
1417 function wifinet.shortname(self)
1418         return "%s %q" %{
1419                 i18n.translate(self:active_mode()),
1420                 self:active_ssid() or self:active_bssid()
1421         }
1422 end
1423
1424 function wifinet.get_i18n(self)
1425         return "%s: %s %q (%s)" %{
1426                 i18n.translate("Wireless Network"),
1427                 i18n.translate(self:active_mode()),
1428                 self:active_ssid() or self:active_bssid(),
1429                 self:ifname()
1430         }
1431 end
1432
1433 function wifinet.adminlink(self)
1434         return dsp.build_url("admin", "network", "wireless", self.netid)
1435 end
1436
1437 function wifinet.get_network(self)
1438         local net = tostring(self.iwdata.network)
1439         if net and _uci_real:get("network", net) == "interface" then
1440                 return network(net)
1441         end
1442 end
1443
1444 function wifinet.get_interface(self)
1445         return interface(self:ifname())
1446 end
1447
1448
1449 -- setup base protocols
1450 _M:register_protocol("static")
1451 _M:register_protocol("dhcp")
1452 _M:register_protocol("none")
1453
1454 -- load protocol extensions
1455 local exts = nfs.dir(utl.libpath() .. "/model/network")
1456 if exts then
1457         local ext
1458         for ext in exts do
1459                 if ext:match("%.lua$") then
1460                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1461                 end
1462         end
1463 end