libs/core: implement get_wandev(), get_wan6dev(), get_wannet() and get_wan6net()...
[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 function get_status_by_route(self, addr, mask)
589         local _, object
590         for _, object in ipairs(_ubus:objects()) do
591                 local net = object:match("^network%.interface%.(.+)")
592                 if net then
593                         local s = _ubus:call(object, "status", {})
594                         if s and s.route then
595                                 local rt
596                                 for _, rt in ipairs(s.route) do
597                                         if rt.target == addr and rt.mask == mask then
598                                                 return net, s
599                                         end
600                                 end
601                         end
602                 end
603         end
604 end
605
606 function get_wannet(self)
607         local net = self:get_status_by_route("0.0.0.0", 0)
608         return net and network(net)
609 end
610
611 function get_wandev(self)
612         local _, stat = self:get_status_by_route("0.0.0.0", 0)
613         return stat and interface(stat.l3_device or stat.device)
614 end
615
616 function get_wan6net(self)
617         local net = self:get_status_by_route("::", 0)
618         return net and network(net)
619 end
620
621 function get_wan6dev(self)
622         local _, stat = self:get_status_by_route("::", 0)
623         return stat and interface(stat.l3_device or stat.device)
624 end
625
626
627 function network(name, proto)
628         if name then
629                 local p = proto or _uci_real:get("network", name, "proto")
630                 local c = p and _protocols[p] or protocol
631                 return c(name)
632         end
633 end
634
635 function protocol.__init__(self, name)
636         self.sid = name
637 end
638
639 function protocol._get(self, opt)
640         local v = _uci_real:get("network", self.sid, opt)
641         if type(v) == "table" then
642                 return table.concat(v, " ")
643         end
644         return v or ""
645 end
646
647 function protocol._ubus(self, field)
648         if not _ubusnetcache[self.sid] then
649                 _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid,
650                                                      "status", { })
651         end
652         if _ubusnetcache[self.sid] and field then
653                 return _ubusnetcache[self.sid][field]
654         end
655         return _ubusnetcache[self.sid]
656 end
657
658 function protocol.get(self, opt)
659         return _get("network", self.sid, opt)
660 end
661
662 function protocol.set(self, opt, val)
663         return _set("network", self.sid, opt, val)
664 end
665
666 function protocol.ifname(self)
667         local ifname
668         if self:is_floating() then
669                 ifname = self:_ubus("l3_device")
670         else
671                 ifname = self:_ubus("device")
672         end
673         if not ifname then
674                 local num = { }
675                 _uci_real:foreach("wireless", "wifi-iface",
676                         function(s)
677                                 if s.device then
678                                         num[s.device] = num[s.device]
679                                                 and num[s.device] + 1 or 1
680
681                                         if s.network == self.sid then
682                                                 ifname = "%s.network%d" %{ s.device, num[s.device] }
683                                                 return false
684                                         end
685                                 end
686                         end)
687         end
688         return ifname
689 end
690
691 function protocol.proto(self)
692         return "none"
693 end
694
695 function protocol.get_i18n(self)
696         local p = self:proto()
697         if p == "none" then
698                 return i18n.translate("Unmanaged")
699         elseif p == "static" then
700                 return i18n.translate("Static address")
701         elseif p == "dhcp" then
702                 return i18n.translate("DHCP client")
703         else
704                 return i18n.translate("Unknown")
705         end
706 end
707
708 function protocol.type(self)
709         return self:_get("type")
710 end
711
712 function protocol.name(self)
713         return self.sid
714 end
715
716 function protocol.uptime(self)
717         return self:_ubus("uptime") or 0
718 end
719
720 function protocol.expires(self)
721         local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
722         local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
723         if a and l then
724                 l = l - (nxo.sysinfo().uptime - a)
725                 return l > 0 and l or 0
726         end
727         return -1
728 end
729
730 function protocol.metric(self)
731         return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
732 end
733
734 function protocol.ipaddr(self)
735         local addrs = self:_ubus("ipv4-address")
736         return addrs and #addrs > 0 and addrs[1].address
737 end
738
739 function protocol.netmask(self)
740         local addrs = self:_ubus("ipv4-address")
741         return addrs and #addrs > 0 and
742                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
743 end
744
745 function protocol.gwaddr(self)
746         local _, route
747         for _, route in ipairs(self:_ubus("route") or { }) do
748                 if route.target == "0.0.0.0" and route.mask == 0 then
749                         return route.nexthop
750                 end
751         end
752 end
753
754 function protocol.dnsaddrs(self)
755         local dns = { }
756         local _, addr
757         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
758                 if not addr:match(":") then
759                         dns[#dns+1] = addr
760                 end
761         end
762         return dns
763 end
764
765 function protocol.ip6addr(self)
766         local addrs = self:_ubus("ipv6-address")
767         return addrs and #addrs > 0
768                 and "%s/%d" %{ addrs[1].address, addrs[1].mask }
769 end
770
771 function protocol.gw6addr(self)
772         local _, route
773         for _, route in ipairs(self:_ubus("route") or { }) do
774                 if route.target == "::" and route.mask == 0 then
775                         return ipc.IPv6(route.nexthop):string()
776                 end
777         end
778 end
779
780 function protocol.dns6addrs(self)
781         local dns = { }
782         local _, addr
783         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
784                 if addr:match(":") then
785                         dns[#dns+1] = addr
786                 end
787         end
788         return dns
789 end
790
791 function protocol.is_bridge(self)
792         return (not self:is_virtual() and self:type() == "bridge")
793 end
794
795 function protocol.opkg_package(self)
796         return nil
797 end
798
799 function protocol.is_installed(self)
800         return true
801 end
802
803 function protocol.is_virtual(self)
804         return false
805 end
806
807 function protocol.is_floating(self)
808         return false
809 end
810
811 function protocol.is_empty(self)
812         if self:is_floating() then
813                 return false
814         else
815                 local rv = true
816
817                 if (self:_get("ifname") or ""):match("%S+") then
818                         rv = false
819                 end
820
821                 _uci_real:foreach("wireless", "wifi-iface",
822                         function(s)
823                                 local n
824                                 for n in utl.imatch(s.network) do
825                                         if n == self.sid then
826                                                 rv = false
827                                                 return false
828                                         end
829                                 end
830                         end)
831
832                 return rv
833         end
834 end
835
836 function protocol.add_interface(self, ifname)
837         ifname = _M:ifnameof(ifname)
838         if ifname and not self:is_floating() then
839                 -- if its a wifi interface, change its network option
840                 local wif = _wifi_lookup(ifname)
841                 if wif then
842                         _append("wireless", wif, "network", self.sid)
843
844                 -- add iface to our iface list
845                 else
846                         _append("network", self.sid, "ifname", ifname)
847                 end
848         end
849 end
850
851 function protocol.del_interface(self, ifname)
852         ifname = _M:ifnameof(ifname)
853         if ifname and not self:is_floating() then
854                 -- if its a wireless interface, clear its network option
855                 local wif = _wifi_lookup(ifname)
856                 if wif then _filter("wireless", wif, "network", self.sid) end
857
858                 -- remove the interface
859                 _filter("network", self.sid, "ifname", ifname)
860         end
861 end
862
863 function protocol.get_interface(self)
864         if self:is_virtual() then
865                 _tunnel[self:proto() .. "-" .. self.sid] = true
866                 return interface(self:proto() .. "-" .. self.sid, self)
867         elseif self:is_bridge() then
868                 _bridge["br-" .. self.sid] = true
869                 return interface("br-" .. self.sid, self)
870         else
871                 local ifn = nil
872                 local num = { }
873                 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
874                         ifn = ifn:match("^[^:/]+")
875                         return ifn and interface(ifn, self)
876                 end
877                 ifn = nil
878                 _uci_real:foreach("wireless", "wifi-iface",
879                         function(s)
880                                 if s.device then
881                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
882                                         if s.network == self.sid then
883                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
884                                                 return false
885                                         end
886                                 end
887                         end)
888                 return ifn and interface(ifn, self)
889         end
890 end
891
892 function protocol.get_interfaces(self)
893         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
894                 local ifaces = { }
895
896                 local ifn
897                 local nfs = { }
898                 for ifn in utl.imatch(self:get("ifname")) do
899                         ifn = ifn:match("^[^:/]+")
900                         nfs[ifn] = interface(ifn, self)
901                 end
902
903                 for ifn in utl.kspairs(nfs) do
904                         ifaces[#ifaces+1] = nfs[ifn]
905                 end
906
907                 local num = { }
908                 local wfs = { }
909                 _uci_real:foreach("wireless", "wifi-iface",
910                         function(s)
911                                 if s.device then
912                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
913                                         if s.network == self.sid then
914                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
915                                                 wfs[ifn] = interface(ifn, self)
916                                         end
917                                 end
918                         end)
919
920                 for ifn in utl.kspairs(wfs) do
921                         ifaces[#ifaces+1] = wfs[ifn]
922                 end
923
924                 return ifaces
925         end
926 end
927
928 function protocol.contains_interface(self, ifname)
929         ifname = _M:ifnameof(ifname)
930         if not ifname then
931                 return false
932         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
933                 return true
934         elseif self:is_bridge() and "br-" .. self.sid == ifname then
935                 return true
936         else
937                 local ifn
938                 for ifn in utl.imatch(self:get("ifname")) do
939                         ifn = ifn:match("[^:]+")
940                         if ifn == ifname then
941                                 return true
942                         end
943                 end
944
945                 local wif = _wifi_lookup(ifname)
946                 if wif then
947                         local n
948                         for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
949                                 if n == self.sid then
950                                         return true
951                                 end
952                         end
953                 end
954         end
955
956         return false
957 end
958
959 function protocol.adminlink(self)
960         return dsp.build_url("admin", "network", "network", self.sid)
961 end
962
963
964 interface = utl.class()
965
966 function interface.__init__(self, ifname, network)
967         local wif = _wifi_lookup(ifname)
968         if wif then
969                 self.wif    = wifinet(wif)
970                 self.ifname = _uci_state:get("wireless", wif, "ifname")
971         end
972
973         self.ifname  = self.ifname or ifname
974         self.dev     = _interfaces[self.ifname]
975         self.network = network
976 end
977
978 function interface._ubus(self, field)
979         if not _ubusdevcache[self.ifname] then
980                 _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
981                                                         { name = self.ifname })
982         end
983         if _ubusdevcache[self.ifname] and field then
984                 return _ubusdevcache[self.ifname][field]
985         end
986         return _ubusdevcache[self.ifname]
987 end
988
989 function interface.name(self)
990         return self.wif and self.wif:ifname() or self.ifname
991 end
992
993 function interface.mac(self)
994         return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
995 end
996
997 function interface.ipaddrs(self)
998         return self.dev and self.dev.ipaddrs or { }
999 end
1000
1001 function interface.ip6addrs(self)
1002         return self.dev and self.dev.ip6addrs or { }
1003 end
1004
1005 function interface.type(self)
1006         if self.wif or _wifi_iface(self.ifname) then
1007                 return "wifi"
1008         elseif _bridge[self.ifname] then
1009                 return "bridge"
1010         elseif _tunnel[self.ifname] then
1011                 return "tunnel"
1012         elseif self.ifname:match("%.") then
1013                 return "vlan"
1014         elseif _switch[self.ifname] then
1015                 return "switch"
1016         else
1017                 return "ethernet"
1018         end
1019 end
1020
1021 function interface.shortname(self)
1022         if self.wif then
1023                 return "%s %q" %{
1024                         self.wif:active_mode(),
1025                         self.wif:active_ssid() or self.wif:active_bssid()
1026                 }
1027         else
1028                 return self.ifname
1029         end
1030 end
1031
1032 function interface.get_i18n(self)
1033         if self.wif then
1034                 return "%s: %s %q" %{
1035                         i18n.translate("Wireless Network"),
1036                         self.wif:active_mode(),
1037                         self.wif:active_ssid() or self.wif:active_bssid()
1038                 }
1039         else
1040                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1041         end
1042 end
1043
1044 function interface.get_type_i18n(self)
1045         local x = self:type()
1046         if x == "wifi" then
1047                 return i18n.translate("Wireless Adapter")
1048         elseif x == "bridge" then
1049                 return i18n.translate("Bridge")
1050         elseif x == "switch" then
1051                 return i18n.translate("Ethernet Switch")
1052         elseif x == "vlan" then
1053                 return i18n.translate("VLAN Interface")
1054         elseif x == "tunnel" then
1055                 return i18n.translate("Tunnel Interface")
1056         else
1057                 return i18n.translate("Ethernet Adapter")
1058         end
1059 end
1060
1061 function interface.adminlink(self)
1062         if self.wif then
1063                 return self.wif:adminlink()
1064         end
1065 end
1066
1067 function interface.ports(self)
1068         local members = self:_ubus("bridge-members")
1069         if members then
1070                 local _, iface
1071                 local ifaces = { }
1072                 for _, iface in ipairs(members) do
1073                         ifaces[#ifaces+1] = interface(iface)
1074                 end
1075         end
1076 end
1077
1078 function interface.bridge_id(self)
1079         if self.br then
1080                 return self.br.id
1081         else
1082                 return nil
1083         end
1084 end
1085
1086 function interface.bridge_stp(self)
1087         if self.br then
1088                 return self.br.stp
1089         else
1090                 return false
1091         end
1092 end
1093
1094 function interface.is_up(self)
1095         if self.wif then
1096                 return self.wif:is_up()
1097         else
1098                 return self:_ubus("up") or false
1099         end
1100 end
1101
1102 function interface.is_bridge(self)
1103         return (self:type() == "bridge")
1104 end
1105
1106 function interface.is_bridgeport(self)
1107         return self.dev and self.dev.bridge and true or false
1108 end
1109
1110 local function uint(x)
1111         if x then
1112                 return (x < 0) and ((2^32) + x) or x
1113         end
1114         return 0
1115 end
1116
1117 function interface.tx_bytes(self)
1118         local stat = self:_ubus("statistics")
1119         return stat and uint(stat.tx_bytes) or 0
1120 end
1121
1122 function interface.rx_bytes(self)
1123         local stat = self:_ubus("statistics")
1124         return stat and uint(stat.rx_bytes) or 0
1125 end
1126
1127 function interface.tx_packets(self)
1128         local stat = self:_ubus("statistics")
1129         return stat and uint(stat.tx_packets) or 0
1130 end
1131
1132 function interface.rx_packets(self)
1133         local stat = self:_ubus("statistics")
1134         return stat and uint(stat.rx_packets) or 0
1135 end
1136
1137 function interface.get_network(self)
1138         return self:get_networks()[1]
1139 end
1140
1141 function interface.get_networks(self)
1142         if not self.networks then
1143                 local nets = { }
1144                 local _, net
1145                 for _, net in ipairs(_M:get_networks()) do
1146                         if net:contains_interface(self.ifname) or
1147                            net:ifname() == self.ifname
1148                         then
1149                                 nets[#nets+1] = net
1150                         end
1151                 end
1152                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1153                 self.networks = nets
1154                 return nets
1155         else
1156                 return self.networks
1157         end
1158 end
1159
1160 function interface.get_wifinet(self)
1161         return self.wif
1162 end
1163
1164
1165 wifidev = utl.class()
1166
1167 function wifidev.__init__(self, dev)
1168         self.sid    = dev
1169         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1170 end
1171
1172 function wifidev.get(self, opt)
1173         return _get("wireless", self.sid, opt)
1174 end
1175
1176 function wifidev.set(self, opt, val)
1177         return _set("wireless", self.sid, opt, val)
1178 end
1179
1180 function wifidev.name(self)
1181         return self.sid
1182 end
1183
1184 function wifidev.hwmodes(self)
1185         local l = self.iwinfo.hwmodelist
1186         if l and next(l) then
1187                 return l
1188         else
1189                 return { b = true, g = true }
1190         end
1191 end
1192
1193 function wifidev.get_i18n(self)
1194         local t = "Generic"
1195         if self.iwinfo.type == "wl" then
1196                 t = "Broadcom"
1197         elseif self.iwinfo.type == "madwifi" then
1198                 t = "Atheros"
1199         end
1200
1201         local m = ""
1202         local l = self:hwmodes()
1203         if l.a then m = m .. "a" end
1204         if l.b then m = m .. "b" end
1205         if l.g then m = m .. "g" end
1206         if l.n then m = m .. "n" end
1207
1208         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1209 end
1210
1211 function wifidev.is_up(self)
1212         local up = false
1213
1214         _uci_state:foreach("wireless", "wifi-iface",
1215                 function(s)
1216                         if s.device == self.sid then
1217                                 if s.up == "1" then
1218                                         up = true
1219                                         return false
1220                                 end
1221                         end
1222                 end)
1223
1224         return up
1225 end
1226
1227 function wifidev.get_wifinet(self, net)
1228         if _uci_real:get("wireless", net) == "wifi-iface" then
1229                 return wifinet(net)
1230         else
1231                 local wnet = _wifi_lookup(net)
1232                 if wnet then
1233                         return wifinet(wnet)
1234                 end
1235         end
1236 end
1237
1238 function wifidev.get_wifinets(self)
1239         local nets = { }
1240
1241         _uci_real:foreach("wireless", "wifi-iface",
1242                 function(s)
1243                         if s.device == self.sid then
1244                                 nets[#nets+1] = wifinet(s['.name'])
1245                         end
1246                 end)
1247
1248         return nets
1249 end
1250
1251 function wifidev.add_wifinet(self, options)
1252         options = options or { }
1253         options.device = self.sid
1254
1255         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1256         if wnet then
1257                 return wifinet(wnet, options)
1258         end
1259 end
1260
1261 function wifidev.del_wifinet(self, net)
1262         if utl.instanceof(net, wifinet) then
1263                 net = net.sid
1264         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1265                 net = _wifi_lookup(net)
1266         end
1267
1268         if net and _uci_real:get("wireless", net, "device") == self.sid then
1269                 _uci_real:delete("wireless", net)
1270                 return true
1271         end
1272
1273         return false
1274 end
1275
1276
1277 wifinet = utl.class()
1278
1279 function wifinet.__init__(self, net, data)
1280         self.sid = net
1281
1282         local num = { }
1283         local netid
1284         _uci_real:foreach("wireless", "wifi-iface",
1285                 function(s)
1286                         if s.device then
1287                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1288                                 if s['.name'] == self.sid then
1289                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1290                                         return false
1291                                 end
1292                         end
1293                 end)
1294
1295         local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1296
1297         self.netid  = netid
1298         self.wdev   = dev
1299         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1300         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1301                 _uci_real:get_all("wireless", self.sid) or { }
1302 end
1303
1304 function wifinet.get(self, opt)
1305         return _get("wireless", self.sid, opt)
1306 end
1307
1308 function wifinet.set(self, opt, val)
1309         return _set("wireless", self.sid, opt, val)
1310 end
1311
1312 function wifinet.mode(self)
1313         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1314 end
1315
1316 function wifinet.ssid(self)
1317         return _uci_state:get("wireless", self.sid, "ssid")
1318 end
1319
1320 function wifinet.bssid(self)
1321         return _uci_state:get("wireless", self.sid, "bssid")
1322 end
1323
1324 function wifinet.network(self)
1325         return _uci_state:get("wifinet", self.sid, "network")
1326 end
1327
1328 function wifinet.id(self)
1329         return self.netid
1330 end
1331
1332 function wifinet.name(self)
1333         return self.sid
1334 end
1335
1336 function wifinet.ifname(self)
1337         local ifname = self.iwinfo.ifname
1338         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1339                 ifname = self.wdev
1340         end
1341         return ifname
1342 end
1343
1344 function wifinet.get_device(self)
1345         if self.iwdata.device then
1346                 return wifidev(self.iwdata.device)
1347         end
1348 end
1349
1350 function wifinet.is_up(self)
1351         return (self.iwdata.up == "1")
1352 end
1353
1354 function wifinet.active_mode(self)
1355         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1356
1357         if     m == "ap"      then m = "Master"
1358         elseif m == "sta"     then m = "Client"
1359         elseif m == "adhoc"   then m = "Ad-Hoc"
1360         elseif m == "mesh"    then m = "Mesh"
1361         elseif m == "monitor" then m = "Monitor"
1362         end
1363
1364         return m
1365 end
1366
1367 function wifinet.active_mode_i18n(self)
1368         return i18n.translate(self:active_mode())
1369 end
1370
1371 function wifinet.active_ssid(self)
1372         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1373 end
1374
1375 function wifinet.active_bssid(self)
1376         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1377 end
1378
1379 function wifinet.active_encryption(self)
1380         local enc = self.iwinfo and self.iwinfo.encryption
1381         return enc and enc.description or "-"
1382 end
1383
1384 function wifinet.assoclist(self)
1385         return self.iwinfo.assoclist or { }
1386 end
1387
1388 function wifinet.frequency(self)
1389         local freq = self.iwinfo.frequency
1390         if freq and freq > 0 then
1391                 return "%.03f" % (freq / 1000)
1392         end
1393 end
1394
1395 function wifinet.bitrate(self)
1396         local rate = self.iwinfo.bitrate
1397         if rate and rate > 0 then
1398                 return (rate / 1000)
1399         end
1400 end
1401
1402 function wifinet.channel(self)
1403         return self.iwinfo.channel or
1404                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1405 end
1406
1407 function wifinet.signal(self)
1408         return self.iwinfo.signal or 0
1409 end
1410
1411 function wifinet.noise(self)
1412         return self.iwinfo.noise or 0
1413 end
1414
1415 function wifinet.country(self)
1416         return self.iwinfo.country or "00"
1417 end
1418
1419 function wifinet.txpower(self)
1420         local pwr = (self.iwinfo.txpower or 0)
1421         return pwr + self:txpower_offset()
1422 end
1423
1424 function wifinet.txpower_offset(self)
1425         return self.iwinfo.txpower_offset or 0
1426 end
1427
1428 function wifinet.signal_level(self, s, n)
1429         if self:active_bssid() ~= "00:00:00:00:00:00" then
1430                 local signal = s or self:signal()
1431                 local noise  = n or self:noise()
1432
1433                 if signal < 0 and noise < 0 then
1434                         local snr = -1 * (noise - signal)
1435                         return math.floor(snr / 5)
1436                 else
1437                         return 0
1438                 end
1439         else
1440                 return -1
1441         end
1442 end
1443
1444 function wifinet.signal_percent(self)
1445         local qc = self.iwinfo.quality or 0
1446         local qm = self.iwinfo.quality_max or 0
1447
1448         if qc > 0 and qm > 0 then
1449                 return math.floor((100 / qm) * qc)
1450         else
1451                 return 0
1452         end
1453 end
1454
1455 function wifinet.shortname(self)
1456         return "%s %q" %{
1457                 i18n.translate(self:active_mode()),
1458                 self:active_ssid() or self:active_bssid()
1459         }
1460 end
1461
1462 function wifinet.get_i18n(self)
1463         return "%s: %s %q (%s)" %{
1464                 i18n.translate("Wireless Network"),
1465                 i18n.translate(self:active_mode()),
1466                 self:active_ssid() or self:active_bssid(),
1467                 self:ifname()
1468         }
1469 end
1470
1471 function wifinet.adminlink(self)
1472         return dsp.build_url("admin", "network", "wireless", self.netid)
1473 end
1474
1475 function wifinet.get_network(self)
1476         return self:get_networks()[1]
1477 end
1478
1479 function wifinet.get_networks(self)
1480         local nets = { }
1481         local net
1482         for net in utl.imatch(tostring(self.iwdata.network)) do
1483                 if _uci_real:get("network", net) == "interface" then
1484                         nets[#nets+1] = network(net)
1485                 end
1486         end
1487         table.sort(nets, function(a, b) return a.sid < b.sid end)
1488         return nets
1489 end
1490
1491 function wifinet.get_interface(self)
1492         return interface(self:ifname())
1493 end
1494
1495
1496 -- setup base protocols
1497 _M:register_protocol("static")
1498 _M:register_protocol("dhcp")
1499 _M:register_protocol("none")
1500
1501 -- load protocol extensions
1502 local exts = nfs.dir(utl.libpath() .. "/model/network")
1503 if exts then
1504         local ext
1505         for ext in exts do
1506                 if ext:match("%.lua$") then
1507                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1508                 end
1509         end
1510 end