libs/core: fix various problems in the network model
[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                 -- remove the interface from all ifaces
799                 _uci_real:foreach("network", "interface",
800                         function(s)
801                                 _filter("network", s['.name'], "ifname", ifname)
802                         end)
803
804                 -- if its a wifi interface, change its network option
805                 local wif = _wifi_lookup(ifname)
806                 if wif then
807                         _uci_real:set("wireless", wif, "network", self.sid)
808
809                 -- add iface to our iface list
810                 else
811                         _append("network", self.sid, "ifname", ifname)
812                 end
813         end
814 end
815
816 function protocol.del_interface(self, ifname)
817         ifname = _M:ifnameof(ifname)
818         if ifname and not self:is_floating() then
819                 -- if its a wireless interface, clear its network option
820                 local wif = _wifi_lookup(ifname)
821                 if wif then     _uci_real:delete("wireless", wif, "network") end
822
823                 -- remove the interface
824                 _filter("network", self.sid, "ifname", ifname)
825         end
826 end
827
828 function protocol.get_interface(self)
829         if self:is_virtual() then
830                 _tunnel[self:proto() .. "-" .. self.sid] = true
831                 return interface(self:proto() .. "-" .. self.sid, self)
832         elseif self:is_bridge() then
833                 _bridge["br-" .. self.sid] = true
834                 return interface("br-" .. self.sid, self)
835         else
836                 local ifn = nil
837                 local num = { }
838                 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
839                         ifn = ifn:match("^[^:/]+")
840                         return ifn and interface(ifn, self)
841                 end
842                 ifn = nil
843                 _uci_real:foreach("wireless", "wifi-iface",
844                         function(s)
845                                 if s.device then
846                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
847                                         if s.network == self.sid then
848                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
849                                                 return false
850                                         end
851                                 end
852                         end)
853                 return ifn and interface(ifn, self)
854         end
855 end
856
857 function protocol.get_interfaces(self)
858         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
859                 local ifaces = { }
860
861                 local ifn
862                 local nfs = { }
863                 for ifn in utl.imatch(self:get("ifname")) do
864                         ifn = ifn:match("^[^:/]+")
865                         nfs[ifn] = interface(ifn, self)
866                 end
867
868                 for ifn in utl.kspairs(nfs) do
869                         ifaces[#ifaces+1] = nfs[ifn]
870                 end
871
872                 local num = { }
873                 local wfs = { }
874                 _uci_real:foreach("wireless", "wifi-iface",
875                         function(s)
876                                 if s.device then
877                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
878                                         if s.network == self.sid then
879                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
880                                                 wfs[ifn] = interface(ifn, self)
881                                         end
882                                 end
883                         end)
884
885                 for ifn in utl.kspairs(wfs) do
886                         ifaces[#ifaces+1] = wfs[ifn]
887                 end
888
889                 return ifaces
890         end
891 end
892
893 function protocol.contains_interface(self, ifname)
894         ifname = _M:ifnameof(ifname)
895         if not ifname then
896                 return false
897         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
898                 return true
899         elseif self:is_bridge() and "br-" .. self.sid == ifname then
900                 return true
901         else
902                 local ifn
903                 for ifn in utl.imatch(self:get("ifname")) do
904                         ifn = ifn:match("[^:]+")
905                         if ifn == ifname then
906                                 return true
907                         end
908                 end
909
910                 local wif = _wifi_lookup(ifname)
911                 if wif then
912                         return (_uci_real:get("wireless", wif, "network") == self.sid)
913                 end
914         end
915
916         return false
917 end
918
919 function protocol.adminlink(self)
920         return dsp.build_url("admin", "network", "network", self.sid)
921 end
922
923
924 interface = utl.class()
925
926 function interface.__init__(self, ifname, network)
927         local wif = _wifi_lookup(ifname)
928         if wif then
929                 self.wif    = wifinet(wif)
930                 self.ifname = _uci_state:get("wireless", wif, "ifname")
931         end
932
933         self.ifname  = self.ifname or ifname
934         self.dev     = _interfaces[self.ifname]
935         self.network = network
936 end
937
938 function interface._ubus(self, field)
939         if not _ubusdevcache[self.ifname] then
940                 _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
941                                                         { name = self.ifname })
942         end
943         if _ubusdevcache[self.ifname] and field then
944                 return _ubusdevcache[self.ifname][field]
945         end
946         return _ubusdevcache[self.ifname]
947 end
948
949 function interface.name(self)
950         return self.wif and self.wif:ifname() or self.ifname
951 end
952
953 function interface.mac(self)
954         return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
955 end
956
957 function interface.ipaddrs(self)
958         return self.dev and self.dev.ipaddrs or { }
959 end
960
961 function interface.ip6addrs(self)
962         return self.dev and self.dev.ip6addrs or { }
963 end
964
965 function interface.type(self)
966         if self.wif or _wifi_iface(self.ifname) then
967                 return "wifi"
968         elseif _bridge[self.ifname] then
969                 return "bridge"
970         elseif _tunnel[self.ifname] then
971                 return "tunnel"
972         elseif self.ifname:match("%.") then
973                 return "vlan"
974         elseif _switch[self.ifname] then
975                 return "switch"
976         else
977                 return "ethernet"
978         end
979 end
980
981 function interface.shortname(self)
982         if self.wif then
983                 return "%s %q" %{
984                         self.wif:active_mode(),
985                         self.wif:active_ssid() or self.wif:active_bssid()
986                 }
987         else
988                 return self.ifname
989         end
990 end
991
992 function interface.get_i18n(self)
993         if self.wif then
994                 return "%s: %s %q" %{
995                         i18n.translate("Wireless Network"),
996                         self.wif:active_mode(),
997                         self.wif:active_ssid() or self.wif:active_bssid()
998                 }
999         else
1000                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1001         end
1002 end
1003
1004 function interface.get_type_i18n(self)
1005         local x = self:type()
1006         if x == "wifi" then
1007                 return i18n.translate("Wireless Adapter")
1008         elseif x == "bridge" then
1009                 return i18n.translate("Bridge")
1010         elseif x == "switch" then
1011                 return i18n.translate("Ethernet Switch")
1012         elseif x == "vlan" then
1013                 return i18n.translate("VLAN Interface")
1014         elseif x == "tunnel" then
1015                 return i18n.translate("Tunnel Interface")
1016         else
1017                 return i18n.translate("Ethernet Adapter")
1018         end
1019 end
1020
1021 function interface.adminlink(self)
1022         if self.wif then
1023                 return self.wif:adminlink()
1024         end
1025 end
1026
1027 function interface.ports(self)
1028         local members = self:_ubus("bridge-members")
1029         if members then
1030                 local _, iface
1031                 local ifaces = { }
1032                 for _, iface in ipairs(members) do
1033                         ifaces[#ifaces+1] = interface(iface)
1034                 end
1035                 return ifaces
1036         end
1037 end
1038
1039 function interface.bridge_id(self)
1040         if self.br then
1041                 return self.br.id
1042         else
1043                 return nil
1044         end
1045 end
1046
1047 function interface.bridge_stp(self)
1048         if self.br then
1049                 return self.br.stp
1050         else
1051                 return false
1052         end
1053 end
1054
1055 function interface.is_up(self)
1056         if self.wif then
1057                 return self.wif:is_up()
1058         else
1059                 return self:_ubus("up") or false
1060         end
1061 end
1062
1063 function interface.is_bridge(self)
1064         return (self:type() == "bridge")
1065 end
1066
1067 function interface.is_bridgeport(self)
1068         return self.dev and self.dev.bridge and true or false
1069 end
1070
1071 local function uint(x)
1072         if x then
1073                 return (x < 0) and ((2^32) + x) or x
1074         end
1075         return 0
1076 end
1077
1078 function interface.tx_bytes(self)
1079         local stat = self:_ubus("statistics")
1080         return stat and uint(stat.tx_bytes) or 0
1081 end
1082
1083 function interface.rx_bytes(self)
1084         local stat = self:_ubus("statistics")
1085         return stat and uint(stat.rx_bytes) or 0
1086 end
1087
1088 function interface.tx_packets(self)
1089         local stat = self:_ubus("statistics")
1090         return stat and uint(stat.tx_packets) or 0
1091 end
1092
1093 function interface.rx_packets(self)
1094         local stat = self:_ubus("statistics")
1095         return stat and uint(stat.rx_packets) or 0
1096 end
1097
1098 function interface.get_network(self)
1099         if not self.network then
1100                 if self.dev and self.dev.network then
1101                         self.network = _M:get_network(self.dev.network)
1102                 end
1103         end
1104
1105         if not self.network then
1106                 local net
1107                 for _, net in ipairs(_M:get_networks()) do
1108                         if net:contains_interface(self.ifname) or
1109                            net:ifname() == self.ifname
1110                         then
1111                                 self.network = net
1112                                 return net
1113                         end
1114                 end
1115         else
1116                 return self.network
1117         end
1118 end
1119
1120 function interface.get_wifinet(self)
1121         return self.wif
1122 end
1123
1124
1125 wifidev = utl.class()
1126
1127 function wifidev.__init__(self, dev)
1128         self.sid    = dev
1129         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1130 end
1131
1132 function wifidev.get(self, opt)
1133         return _get("wireless", self.sid, opt)
1134 end
1135
1136 function wifidev.set(self, opt, val)
1137         return _set("wireless", self.sid, opt, val)
1138 end
1139
1140 function wifidev.name(self)
1141         return self.sid
1142 end
1143
1144 function wifidev.hwmodes(self)
1145         local l = self.iwinfo.hwmodelist
1146         if l and next(l) then
1147                 return l
1148         else
1149                 return { b = true, g = true }
1150         end
1151 end
1152
1153 function wifidev.get_i18n(self)
1154         local t = "Generic"
1155         if self.iwinfo.type == "wl" then
1156                 t = "Broadcom"
1157         elseif self.iwinfo.type == "madwifi" then
1158                 t = "Atheros"
1159         end
1160
1161         local m = ""
1162         local l = self:hwmodes()
1163         if l.a then m = m .. "a" end
1164         if l.b then m = m .. "b" end
1165         if l.g then m = m .. "g" end
1166         if l.n then m = m .. "n" end
1167
1168         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1169 end
1170
1171 function wifidev.is_up(self)
1172         local up = false
1173
1174         _uci_state:foreach("wireless", "wifi-iface",
1175                 function(s)
1176                         if s.device == self.sid then
1177                                 if s.up == "1" then
1178                                         up = true
1179                                         return false
1180                                 end
1181                         end
1182                 end)
1183
1184         return up
1185 end
1186
1187 function wifidev.get_wifinet(self, net)
1188         if _uci_real:get("wireless", net) == "wifi-iface" then
1189                 return wifinet(net)
1190         else
1191                 local wnet = _wifi_lookup(net)
1192                 if wnet then
1193                         return wifinet(wnet)
1194                 end
1195         end
1196 end
1197
1198 function wifidev.get_wifinets(self)
1199         local nets = { }
1200
1201         _uci_real:foreach("wireless", "wifi-iface",
1202                 function(s)
1203                         if s.device == self.sid then
1204                                 nets[#nets+1] = wifinet(s['.name'])
1205                         end
1206                 end)
1207
1208         return nets
1209 end
1210
1211 function wifidev.add_wifinet(self, options)
1212         options = options or { }
1213         options.device = self.sid
1214
1215         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1216         if wnet then
1217                 return wifinet(wnet, options)
1218         end
1219 end
1220
1221 function wifidev.del_wifinet(self, net)
1222         if utl.instanceof(net, wifinet) then
1223                 net = net.sid
1224         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1225                 net = _wifi_lookup(net)
1226         end
1227
1228         if net and _uci_real:get("wireless", net, "device") == self.sid then
1229                 _uci_real:delete("wireless", net)
1230                 return true
1231         end
1232
1233         return false
1234 end
1235
1236
1237 wifinet = utl.class()
1238
1239 function wifinet.__init__(self, net, data)
1240         self.sid = net
1241
1242         local num = { }
1243         local netid
1244         _uci_real:foreach("wireless", "wifi-iface",
1245                 function(s)
1246                         if s.device then
1247                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1248                                 if s['.name'] == self.sid then
1249                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1250                                         return false
1251                                 end
1252                         end
1253                 end)
1254
1255         local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1256
1257         self.netid  = netid
1258         self.wdev   = dev
1259         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1260         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1261                 _uci_real:get_all("wireless", self.sid) or { }
1262 end
1263
1264 function wifinet.get(self, opt)
1265         return _get("wireless", self.sid, opt)
1266 end
1267
1268 function wifinet.set(self, opt, val)
1269         return _set("wireless", self.sid, opt, val)
1270 end
1271
1272 function wifinet.mode(self)
1273         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1274 end
1275
1276 function wifinet.ssid(self)
1277         return _uci_state:get("wireless", self.sid, "ssid")
1278 end
1279
1280 function wifinet.bssid(self)
1281         return _uci_state:get("wireless", self.sid, "bssid")
1282 end
1283
1284 function wifinet.network(self)
1285         return _uci_state:get("wifinet", self.sid, "network")
1286 end
1287
1288 function wifinet.id(self)
1289         return self.netid
1290 end
1291
1292 function wifinet.name(self)
1293         return self.sid
1294 end
1295
1296 function wifinet.ifname(self)
1297         local ifname = self.iwinfo.ifname
1298         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1299                 ifname = self.wdev
1300         end
1301         return ifname
1302 end
1303
1304 function wifinet.get_device(self)
1305         if self.iwdata.device then
1306                 return wifidev(self.iwdata.device)
1307         end
1308 end
1309
1310 function wifinet.is_up(self)
1311         return (self.iwdata.up == "1")
1312 end
1313
1314 function wifinet.active_mode(self)
1315         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1316
1317         if     m == "ap"      then m = "Master"
1318         elseif m == "sta"     then m = "Client"
1319         elseif m == "adhoc"   then m = "Ad-Hoc"
1320         elseif m == "mesh"    then m = "Mesh"
1321         elseif m == "monitor" then m = "Monitor"
1322         end
1323
1324         return m
1325 end
1326
1327 function wifinet.active_mode_i18n(self)
1328         return i18n.translate(self:active_mode())
1329 end
1330
1331 function wifinet.active_ssid(self)
1332         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1333 end
1334
1335 function wifinet.active_bssid(self)
1336         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1337 end
1338
1339 function wifinet.active_encryption(self)
1340         local enc = self.iwinfo and self.iwinfo.encryption
1341         return enc and enc.description or "-"
1342 end
1343
1344 function wifinet.assoclist(self)
1345         return self.iwinfo.assoclist or { }
1346 end
1347
1348 function wifinet.frequency(self)
1349         local freq = self.iwinfo.frequency
1350         if freq and freq > 0 then
1351                 return "%.03f" % (freq / 1000)
1352         end
1353 end
1354
1355 function wifinet.bitrate(self)
1356         local rate = self.iwinfo.bitrate
1357         if rate and rate > 0 then
1358                 return (rate / 1000)
1359         end
1360 end
1361
1362 function wifinet.channel(self)
1363         return self.iwinfo.channel or
1364                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1365 end
1366
1367 function wifinet.signal(self)
1368         return self.iwinfo.signal or 0
1369 end
1370
1371 function wifinet.noise(self)
1372         return self.iwinfo.noise or 0
1373 end
1374
1375 function wifinet.country(self)
1376         return self.iwinfo.country or "00"
1377 end
1378
1379 function wifinet.txpower(self)
1380         local pwr = (self.iwinfo.txpower or 0)
1381         return pwr + self:txpower_offset()
1382 end
1383
1384 function wifinet.txpower_offset(self)
1385         return self.iwinfo.txpower_offset or 0
1386 end
1387
1388 function wifinet.signal_level(self, s, n)
1389         if self:active_bssid() ~= "00:00:00:00:00:00" then
1390                 local signal = s or self:signal()
1391                 local noise  = n or self:noise()
1392
1393                 if signal < 0 and noise < 0 then
1394                         local snr = -1 * (noise - signal)
1395                         return math.floor(snr / 5)
1396                 else
1397                         return 0
1398                 end
1399         else
1400                 return -1
1401         end
1402 end
1403
1404 function wifinet.signal_percent(self)
1405         local qc = self.iwinfo.quality or 0
1406         local qm = self.iwinfo.quality_max or 0
1407
1408         if qc > 0 and qm > 0 then
1409                 return math.floor((100 / qm) * qc)
1410         else
1411                 return 0
1412         end
1413 end
1414
1415 function wifinet.shortname(self)
1416         return "%s %q" %{
1417                 i18n.translate(self:active_mode()),
1418                 self:active_ssid() or self:active_bssid()
1419         }
1420 end
1421
1422 function wifinet.get_i18n(self)
1423         return "%s: %s %q (%s)" %{
1424                 i18n.translate("Wireless Network"),
1425                 i18n.translate(self:active_mode()),
1426                 self:active_ssid() or self:active_bssid(),
1427                 self:ifname()
1428         }
1429 end
1430
1431 function wifinet.adminlink(self)
1432         return dsp.build_url("admin", "network", "wireless", self.netid)
1433 end
1434
1435 function wifinet.get_network(self)
1436         local net = tostring(self.iwdata.network)
1437         if net and _uci_real:get("network", net) == "interface" then
1438                 return network(net)
1439         end
1440 end
1441
1442 function wifinet.get_interface(self)
1443         return interface(self:ifname())
1444 end
1445
1446
1447 -- setup base protocols
1448 _M:register_protocol("static")
1449 _M:register_protocol("dhcp")
1450 _M:register_protocol("none")
1451
1452 -- load protocol extensions
1453 local exts = nfs.dir(utl.libpath() .. "/model/network")
1454 if exts then
1455         local ext
1456         for ext in exts do
1457                 if ext:match("%.lua$") then
1458                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1459                 end
1460         end
1461 end