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