f251411ec776cd04768acef8975e1521108a238c
[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 = tonumber, tostring, math
24
25 local require = require
26
27 local bus = require "ubus"
28 local nxo = require "nixio"
29 local nfs = require "nixio.fs"
30 local ipc = require "luci.ip"
31 local sys = require "luci.sys"
32 local utl = require "luci.util"
33 local dsp = require "luci.dispatcher"
34 local uci = require "luci.model.uci"
35 local lng = require "luci.i18n"
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", "^gre%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                                 local net
388                                 local rest = { }
389                                 for net in utl.imatch(s.network) do
390                                         if net ~= n then
391                                                 rest[#rest+1] = net
392                                         end
393                                 end
394                                 if #rest > 0 then
395                                         _uci_real:set("wireless", s['.name'], "network",
396                                                       table.concat(rest, " "))
397                                 else
398                                         _uci_real:delete("wireless", s['.name'], "network")
399                                 end
400                         end)
401         end
402         return r
403 end
404
405 function rename_network(self, old, new)
406         local r
407         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
408                 r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
409
410                 if r then
411                         _uci_real:foreach("network", "alias",
412                                 function(s)
413                                         if s.interface == old then
414                                                 _uci_real:set("network", s['.name'], "interface", new)
415                                         end
416                                 end)
417
418                         _uci_real:foreach("network", "route",
419                                 function(s)
420                                         if s.interface == old then
421                                                 _uci_real:set("network", s['.name'], "interface", new)
422                                         end
423                                 end)
424
425                         _uci_real:foreach("network", "route6",
426                                 function(s)
427                                         if s.interface == old then
428                                                 _uci_real:set("network", s['.name'], "interface", new)
429                                         end
430                                 end)
431
432                         _uci_real:foreach("wireless", "wifi-iface",
433                                 function(s)
434                                         local net
435                                         local list = { }
436                                         for net in utl.imatch(s.network) do
437                                                 if net == old then
438                                                         list[#list+1] = new
439                                                 else
440                                                         list[#list+1] = net
441                                                 end
442                                         end
443                                         if #list > 0 then
444                                                 _uci_real:set("wireless", s['.name'], "network",
445                                                               table.concat(list, " "))
446                                         end
447                                 end)
448
449                         _uci_real:delete("network", old)
450                 end
451         end
452         return r or false
453 end
454
455 function get_interface(self, i)
456         if _interfaces[i] or _wifi_iface(i) then
457                 return interface(i)
458         else
459                 local ifc
460                 local num = { }
461                 _uci_real:foreach("wireless", "wifi-iface",
462                         function(s)
463                                 if s.device then
464                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
465                                         if s['.name'] == i then
466                                                 ifc = interface(
467                                                         "%s.network%d" %{s.device, num[s.device] })
468                                                 return false
469                                         end
470                                 end
471                         end)
472                 return ifc
473         end
474 end
475
476 function get_interfaces(self)
477         local iface
478         local ifaces = { }
479         local seen = { }
480         local nfs = { }
481         local baseof = { }
482
483         -- find normal interfaces
484         _uci_real:foreach("network", "interface",
485                 function(s)
486                         for iface in utl.imatch(s.ifname) do
487                                 if not _iface_ignore(iface) and not _wifi_iface(iface) then
488                                         seen[iface] = true
489                                         nfs[iface] = interface(iface)
490                                 end
491                         end
492                 end)
493
494         for iface in utl.kspairs(_interfaces) do
495                 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
496                         nfs[iface] = interface(iface)
497                 end
498         end
499
500         -- find vlan interfaces
501         _uci_real:foreach("network", "switch_vlan",
502                 function(s)
503                         if not s.device then
504                                 return
505                         end
506
507                         local base = baseof[s.device]
508                         if not base then
509                                 if not s.device:match("^eth%d") then
510                                         local l
511                                         for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
512                                                 if not base then
513                                                         base = l:match("^%w+: (%w+)")
514                                                 end
515                                         end
516                                         if not base or not base:match("^eth%d") then
517                                                 base = "eth0"
518                                         end
519                                 else
520                                         base = s.device
521                                 end
522                                 baseof[s.device] = base
523                         end
524
525                         local vid = tonumber(s.vid or s.vlan)
526                         if vid ~= nil and vid >= 0 and vid <= 4095 then
527                                 local iface = "%s.%d" %{ base, vid }
528                                 if not seen[iface] then
529                                         seen[iface] = true
530                                         nfs[iface] = interface(iface)
531                                 end
532                         end
533                 end)
534
535         for iface in utl.kspairs(nfs) do
536                 ifaces[#ifaces+1] = nfs[iface]
537         end
538
539         -- find wifi interfaces
540         local num = { }
541         local wfs = { }
542         _uci_real:foreach("wireless", "wifi-iface",
543                 function(s)
544                         if s.device then
545                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
546                                 local i = "%s.network%d" %{ s.device, num[s.device] }
547                                 wfs[i] = interface(i)
548                         end
549                 end)
550
551         for iface in utl.kspairs(wfs) do
552                 ifaces[#ifaces+1] = wfs[iface]
553         end
554
555         return ifaces
556 end
557
558 function ignore_interface(self, x)
559         return _iface_ignore(x)
560 end
561
562 function get_wifidev(self, dev)
563         if _uci_real:get("wireless", dev) == "wifi-device" then
564                 return wifidev(dev)
565         end
566 end
567
568 function get_wifidevs(self)
569         local devs = { }
570         local wfd  = { }
571
572         _uci_real:foreach("wireless", "wifi-device",
573                 function(s) wfd[#wfd+1] = s['.name'] end)
574
575         local dev
576         for _, dev in utl.vspairs(wfd) do
577                 devs[#devs+1] = wifidev(dev)
578         end
579
580         return devs
581 end
582
583 function get_wifinet(self, net)
584         local wnet = _wifi_lookup(net)
585         if wnet then
586                 return wifinet(wnet)
587         end
588 end
589
590 function add_wifinet(self, net, options)
591         if type(options) == "table" and options.device and
592                 _uci_real:get("wireless", options.device) == "wifi-device"
593         then
594                 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
595                 return wifinet(wnet)
596         end
597 end
598
599 function del_wifinet(self, net)
600         local wnet = _wifi_lookup(net)
601         if wnet then
602                 _uci_real:delete("wireless", wnet)
603                 return true
604         end
605         return false
606 end
607
608 function get_status_by_route(self, addr, mask)
609         local _, object
610         for _, object in ipairs(_ubus:objects()) do
611                 local net = object:match("^network%.interface%.(.+)")
612                 if net then
613                         local s = _ubus:call(object, "status", {})
614                         if s and s.route then
615                                 local rt
616                                 for _, rt in ipairs(s.route) do
617                                         if rt.target == addr and rt.mask == mask then
618                                                 return net, s
619                                         end
620                                 end
621                         end
622                 end
623         end
624 end
625
626 function get_status_by_address(self, addr)
627         local _, object
628         for _, object in ipairs(_ubus:objects()) do
629                 local net = object:match("^network%.interface%.(.+)")
630                 if net then
631                         local s = _ubus:call(object, "status", {})
632                         if s and s['ipv4-address'] then
633                                 local a
634                                 for _, a in ipairs(s['ipv4-address']) do
635                                         if a.address == addr then
636                                                 return net, s
637                                         end
638                                 end
639                         end
640                         if s and s['ipv6-address'] then
641                                 local a
642                                 for _, a in ipairs(s['ipv6-address']) do
643                                         if a.address == addr then
644                                                 return net, s
645                                         end
646                                 end
647                         end
648                 end
649         end
650 end
651
652 function get_wannet(self)
653         local net = self:get_status_by_route("0.0.0.0", 0)
654         return net and network(net)
655 end
656
657 function get_wandev(self)
658         local _, stat = self:get_status_by_route("0.0.0.0", 0)
659         return stat and interface(stat.l3_device or stat.device)
660 end
661
662 function get_wan6net(self)
663         local net = self:get_status_by_route("::", 0)
664         return net and network(net)
665 end
666
667 function get_wan6dev(self)
668         local _, stat = self:get_status_by_route("::", 0)
669         return stat and interface(stat.l3_device or stat.device)
670 end
671
672
673 function network(name, proto)
674         if name then
675                 local p = proto or _uci_real:get("network", name, "proto")
676                 local c = p and _protocols[p] or protocol
677                 return c(name)
678         end
679 end
680
681 function protocol.__init__(self, name)
682         self.sid = name
683 end
684
685 function protocol._get(self, opt)
686         local v = _uci_real:get("network", self.sid, opt)
687         if type(v) == "table" then
688                 return table.concat(v, " ")
689         end
690         return v or ""
691 end
692
693 function protocol._ubus(self, field)
694         if not _ubusnetcache[self.sid] then
695                 _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid,
696                                                      "status", { })
697         end
698         if _ubusnetcache[self.sid] and field then
699                 return _ubusnetcache[self.sid][field]
700         end
701         return _ubusnetcache[self.sid]
702 end
703
704 function protocol.get(self, opt)
705         return _get("network", self.sid, opt)
706 end
707
708 function protocol.set(self, opt, val)
709         return _set("network", self.sid, opt, val)
710 end
711
712 function protocol.ifname(self)
713         local ifname
714         if self:is_floating() then
715                 ifname = self:_ubus("l3_device")
716         else
717                 ifname = self:_ubus("device")
718         end
719         if not ifname then
720                 local num = { }
721                 _uci_real:foreach("wireless", "wifi-iface",
722                         function(s)
723                                 if s.device then
724                                         num[s.device] = num[s.device]
725                                                 and num[s.device] + 1 or 1
726
727                                         local net
728                                         for net in utl.imatch(s.network) do
729                                                 if net == self.sid then
730                                                         ifname = "%s.network%d" %{ s.device, num[s.device] }
731                                                         return false
732                                                 end
733                                         end
734                                 end
735                         end)
736         end
737         return ifname
738 end
739
740 function protocol.proto(self)
741         return "none"
742 end
743
744 function protocol.get_i18n(self)
745         local p = self:proto()
746         if p == "none" then
747                 return lng.translate("Unmanaged")
748         elseif p == "static" then
749                 return lng.translate("Static address")
750         elseif p == "dhcp" then
751                 return lng.translate("DHCP client")
752         elseif p == "dhcpv6" then
753                 return lng.translate("DHCPv6 client")
754         else
755                 return lng.translate("Unknown")
756         end
757 end
758
759 function protocol.type(self)
760         return self:_get("type")
761 end
762
763 function protocol.name(self)
764         return self.sid
765 end
766
767 function protocol.uptime(self)
768         return self:_ubus("uptime") or 0
769 end
770
771 function protocol.expires(self)
772         local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
773         local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
774         if a and l then
775                 l = l - (nxo.sysinfo().uptime - a)
776                 return l > 0 and l or 0
777         end
778         return -1
779 end
780
781 function protocol.metric(self)
782         return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
783 end
784
785 function protocol.ipaddr(self)
786         local addrs = self:_ubus("ipv4-address")
787         return addrs and #addrs > 0 and addrs[1].address
788 end
789
790 function protocol.netmask(self)
791         local addrs = self:_ubus("ipv4-address")
792         return addrs and #addrs > 0 and
793                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
794 end
795
796 function protocol.gwaddr(self)
797         local _, route
798         for _, route in ipairs(self:_ubus("route") or { }) do
799                 if route.target == "0.0.0.0" and route.mask == 0 then
800                         return route.nexthop
801                 end
802         end
803 end
804
805 function protocol.dnsaddrs(self)
806         local dns = { }
807         local _, addr
808         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
809                 if not addr:match(":") then
810                         dns[#dns+1] = addr
811                 end
812         end
813         return dns
814 end
815
816 function protocol.ip6addr(self)
817         local addrs = self:_ubus("ipv6-address")
818         return addrs and #addrs > 0
819                 and "%s/%d" %{ addrs[1].address, addrs[1].mask }
820 end
821
822 function protocol.gw6addr(self)
823         local _, route
824         for _, route in ipairs(self:_ubus("route") or { }) do
825                 if route.target == "::" and route.mask == 0 then
826                         return ipc.IPv6(route.nexthop):string()
827                 end
828         end
829 end
830
831 function protocol.dns6addrs(self)
832         local dns = { }
833         local _, addr
834         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
835                 if addr:match(":") then
836                         dns[#dns+1] = addr
837                 end
838         end
839         return dns
840 end
841
842 function protocol.is_bridge(self)
843         return (not self:is_virtual() and self:type() == "bridge")
844 end
845
846 function protocol.opkg_package(self)
847         return nil
848 end
849
850 function protocol.is_installed(self)
851         return true
852 end
853
854 function protocol.is_virtual(self)
855         return false
856 end
857
858 function protocol.is_floating(self)
859         return false
860 end
861
862 function protocol.is_empty(self)
863         if self:is_floating() then
864                 return false
865         else
866                 local rv = true
867
868                 if (self:_get("ifname") or ""):match("%S+") then
869                         rv = false
870                 end
871
872                 _uci_real:foreach("wireless", "wifi-iface",
873                         function(s)
874                                 local n
875                                 for n in utl.imatch(s.network) do
876                                         if n == self.sid then
877                                                 rv = false
878                                                 return false
879                                         end
880                                 end
881                         end)
882
883                 return rv
884         end
885 end
886
887 function protocol.add_interface(self, ifname)
888         ifname = _M:ifnameof(ifname)
889         if ifname and not self:is_floating() then
890                 -- if its a wifi interface, change its network option
891                 local wif = _wifi_lookup(ifname)
892                 if wif then
893                         _append("wireless", wif, "network", self.sid)
894
895                 -- add iface to our iface list
896                 else
897                         _append("network", self.sid, "ifname", ifname)
898                 end
899         end
900 end
901
902 function protocol.del_interface(self, ifname)
903         ifname = _M:ifnameof(ifname)
904         if ifname and not self:is_floating() then
905                 -- if its a wireless interface, clear its network option
906                 local wif = _wifi_lookup(ifname)
907                 if wif then _filter("wireless", wif, "network", self.sid) end
908
909                 -- remove the interface
910                 _filter("network", self.sid, "ifname", ifname)
911         end
912 end
913
914 function protocol.get_interface(self)
915         if self:is_virtual() then
916                 _tunnel[self:proto() .. "-" .. self.sid] = true
917                 return interface(self:proto() .. "-" .. self.sid, self)
918         elseif self:is_bridge() then
919                 _bridge["br-" .. self.sid] = true
920                 return interface("br-" .. self.sid, self)
921         else
922                 local ifn = nil
923                 local num = { }
924                 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
925                         ifn = ifn:match("^[^:/]+")
926                         return ifn and interface(ifn, self)
927                 end
928                 ifn = nil
929                 _uci_real:foreach("wireless", "wifi-iface",
930                         function(s)
931                                 if s.device then
932                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
933
934                                         local net
935                                         for net in utl.imatch(s.network) do
936                                                 if net == self.sid then
937                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
938                                                         return false
939                                                 end
940                                         end
941                                 end
942                         end)
943                 return ifn and interface(ifn, self)
944         end
945 end
946
947 function protocol.get_interfaces(self)
948         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
949                 local ifaces = { }
950
951                 local ifn
952                 local nfs = { }
953                 for ifn in utl.imatch(self:get("ifname")) do
954                         ifn = ifn:match("^[^:/]+")
955                         nfs[ifn] = interface(ifn, self)
956                 end
957
958                 for ifn in utl.kspairs(nfs) do
959                         ifaces[#ifaces+1] = nfs[ifn]
960                 end
961
962                 local num = { }
963                 local wfs = { }
964                 _uci_real:foreach("wireless", "wifi-iface",
965                         function(s)
966                                 if s.device then
967                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
968
969                                         local net
970                                         for net in utl.imatch(s.network) do
971                                                 if net == self.sid then
972                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
973                                                         wfs[ifn] = interface(ifn, self)
974                                                 end
975                                         end
976                                 end
977                         end)
978
979                 for ifn in utl.kspairs(wfs) do
980                         ifaces[#ifaces+1] = wfs[ifn]
981                 end
982
983                 return ifaces
984         end
985 end
986
987 function protocol.contains_interface(self, ifname)
988         ifname = _M:ifnameof(ifname)
989         if not ifname then
990                 return false
991         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
992                 return true
993         elseif self:is_bridge() and "br-" .. self.sid == ifname then
994                 return true
995         else
996                 local ifn
997                 for ifn in utl.imatch(self:get("ifname")) do
998                         ifn = ifn:match("[^:]+")
999                         if ifn == ifname then
1000                                 return true
1001                         end
1002                 end
1003
1004                 local wif = _wifi_lookup(ifname)
1005                 if wif then
1006                         local n
1007                         for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
1008                                 if n == self.sid then
1009                                         return true
1010                                 end
1011                         end
1012                 end
1013         end
1014
1015         return false
1016 end
1017
1018 function protocol.adminlink(self)
1019         return dsp.build_url("admin", "network", "network", self.sid)
1020 end
1021
1022
1023 interface = utl.class()
1024
1025 function interface.__init__(self, ifname, network)
1026         local wif = _wifi_lookup(ifname)
1027         if wif then
1028                 self.wif    = wifinet(wif)
1029                 self.ifname = _uci_state:get("wireless", wif, "ifname")
1030         end
1031
1032         self.ifname  = self.ifname or ifname
1033         self.dev     = _interfaces[self.ifname]
1034         self.network = network
1035 end
1036
1037 function interface._ubus(self, field)
1038         if not _ubusdevcache[self.ifname] then
1039                 _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
1040                                                         { name = self.ifname })
1041         end
1042         if _ubusdevcache[self.ifname] and field then
1043                 return _ubusdevcache[self.ifname][field]
1044         end
1045         return _ubusdevcache[self.ifname]
1046 end
1047
1048 function interface.name(self)
1049         return self.wif and self.wif:ifname() or self.ifname
1050 end
1051
1052 function interface.mac(self)
1053         return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
1054 end
1055
1056 function interface.ipaddrs(self)
1057         return self.dev and self.dev.ipaddrs or { }
1058 end
1059
1060 function interface.ip6addrs(self)
1061         return self.dev and self.dev.ip6addrs or { }
1062 end
1063
1064 function interface.type(self)
1065         if self.wif or _wifi_iface(self.ifname) then
1066                 return "wifi"
1067         elseif _bridge[self.ifname] then
1068                 return "bridge"
1069         elseif _tunnel[self.ifname] then
1070                 return "tunnel"
1071         elseif self.ifname:match("%.") then
1072                 return "vlan"
1073         elseif _switch[self.ifname] then
1074                 return "switch"
1075         else
1076                 return "ethernet"
1077         end
1078 end
1079
1080 function interface.shortname(self)
1081         if self.wif then
1082                 return "%s %q" %{
1083                         self.wif:active_mode(),
1084                         self.wif:active_ssid() or self.wif:active_bssid()
1085                 }
1086         else
1087                 return self.ifname
1088         end
1089 end
1090
1091 function interface.get_i18n(self)
1092         if self.wif then
1093                 return "%s: %s %q" %{
1094                         lng.translate("Wireless Network"),
1095                         self.wif:active_mode(),
1096                         self.wif:active_ssid() or self.wif:active_bssid()
1097                 }
1098         else
1099                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1100         end
1101 end
1102
1103 function interface.get_type_i18n(self)
1104         local x = self:type()
1105         if x == "wifi" then
1106                 return lng.translate("Wireless Adapter")
1107         elseif x == "bridge" then
1108                 return lng.translate("Bridge")
1109         elseif x == "switch" then
1110                 return lng.translate("Ethernet Switch")
1111         elseif x == "vlan" then
1112                 return lng.translate("VLAN Interface")
1113         elseif x == "tunnel" then
1114                 return lng.translate("Tunnel Interface")
1115         else
1116                 return lng.translate("Ethernet Adapter")
1117         end
1118 end
1119
1120 function interface.adminlink(self)
1121         if self.wif then
1122                 return self.wif:adminlink()
1123         end
1124 end
1125
1126 function interface.ports(self)
1127         local members = self:_ubus("bridge-members")
1128         if members then
1129                 local _, iface
1130                 local ifaces = { }
1131                 for _, iface in ipairs(members) do
1132                         ifaces[#ifaces+1] = interface(iface)
1133                 end
1134         end
1135 end
1136
1137 function interface.bridge_id(self)
1138         if self.br then
1139                 return self.br.id
1140         else
1141                 return nil
1142         end
1143 end
1144
1145 function interface.bridge_stp(self)
1146         if self.br then
1147                 return self.br.stp
1148         else
1149                 return false
1150         end
1151 end
1152
1153 function interface.is_up(self)
1154         if self.wif then
1155                 return self.wif:is_up()
1156         else
1157                 return self:_ubus("up") or false
1158         end
1159 end
1160
1161 function interface.is_bridge(self)
1162         return (self:type() == "bridge")
1163 end
1164
1165 function interface.is_bridgeport(self)
1166         return self.dev and self.dev.bridge and true or false
1167 end
1168
1169 function interface.tx_bytes(self)
1170         local stat = self:_ubus("statistics")
1171         return stat and stat.tx_bytes or 0
1172 end
1173
1174 function interface.rx_bytes(self)
1175         local stat = self:_ubus("statistics")
1176         return stat and stat.rx_bytes or 0
1177 end
1178
1179 function interface.tx_packets(self)
1180         local stat = self:_ubus("statistics")
1181         return stat and stat.tx_packets or 0
1182 end
1183
1184 function interface.rx_packets(self)
1185         local stat = self:_ubus("statistics")
1186         return stat and stat.rx_packets or 0
1187 end
1188
1189 function interface.get_network(self)
1190         return self:get_networks()[1]
1191 end
1192
1193 function interface.get_networks(self)
1194         if not self.networks then
1195                 local nets = { }
1196                 local _, net
1197                 for _, net in ipairs(_M:get_networks()) do
1198                         if net:contains_interface(self.ifname) or
1199                            net:ifname() == self.ifname
1200                         then
1201                                 nets[#nets+1] = net
1202                         end
1203                 end
1204                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1205                 self.networks = nets
1206                 return nets
1207         else
1208                 return self.networks
1209         end
1210 end
1211
1212 function interface.get_wifinet(self)
1213         return self.wif
1214 end
1215
1216
1217 wifidev = utl.class()
1218
1219 function wifidev.__init__(self, dev)
1220         self.sid    = dev
1221         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1222 end
1223
1224 function wifidev.get(self, opt)
1225         return _get("wireless", self.sid, opt)
1226 end
1227
1228 function wifidev.set(self, opt, val)
1229         return _set("wireless", self.sid, opt, val)
1230 end
1231
1232 function wifidev.name(self)
1233         return self.sid
1234 end
1235
1236 function wifidev.hwmodes(self)
1237         local l = self.iwinfo.hwmodelist
1238         if l and next(l) then
1239                 return l
1240         else
1241                 return { b = true, g = true }
1242         end
1243 end
1244
1245 function wifidev.get_i18n(self)
1246         local t = "Generic"
1247         if self.iwinfo.type == "wl" then
1248                 t = "Broadcom"
1249         elseif self.iwinfo.type == "madwifi" then
1250                 t = "Atheros"
1251         end
1252
1253         local m = ""
1254         local l = self:hwmodes()
1255         if l.a then m = m .. "a" end
1256         if l.b then m = m .. "b" end
1257         if l.g then m = m .. "g" end
1258         if l.n then m = m .. "n" end
1259
1260         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1261 end
1262
1263 function wifidev.is_up(self)
1264         local up = false
1265
1266         _uci_state:foreach("wireless", "wifi-iface",
1267                 function(s)
1268                         if s.device == self.sid then
1269                                 if s.up == "1" then
1270                                         up = true
1271                                         return false
1272                                 end
1273                         end
1274                 end)
1275
1276         return up
1277 end
1278
1279 function wifidev.get_wifinet(self, net)
1280         if _uci_real:get("wireless", net) == "wifi-iface" then
1281                 return wifinet(net)
1282         else
1283                 local wnet = _wifi_lookup(net)
1284                 if wnet then
1285                         return wifinet(wnet)
1286                 end
1287         end
1288 end
1289
1290 function wifidev.get_wifinets(self)
1291         local nets = { }
1292
1293         _uci_real:foreach("wireless", "wifi-iface",
1294                 function(s)
1295                         if s.device == self.sid then
1296                                 nets[#nets+1] = wifinet(s['.name'])
1297                         end
1298                 end)
1299
1300         return nets
1301 end
1302
1303 function wifidev.add_wifinet(self, options)
1304         options = options or { }
1305         options.device = self.sid
1306
1307         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1308         if wnet then
1309                 return wifinet(wnet, options)
1310         end
1311 end
1312
1313 function wifidev.del_wifinet(self, net)
1314         if utl.instanceof(net, wifinet) then
1315                 net = net.sid
1316         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1317                 net = _wifi_lookup(net)
1318         end
1319
1320         if net and _uci_real:get("wireless", net, "device") == self.sid then
1321                 _uci_real:delete("wireless", net)
1322                 return true
1323         end
1324
1325         return false
1326 end
1327
1328
1329 wifinet = utl.class()
1330
1331 function wifinet.__init__(self, net, data)
1332         self.sid = net
1333
1334         local num = { }
1335         local netid
1336         _uci_real:foreach("wireless", "wifi-iface",
1337                 function(s)
1338                         if s.device then
1339                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1340                                 if s['.name'] == self.sid then
1341                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1342                                         return false
1343                                 end
1344                         end
1345                 end)
1346
1347         local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1348
1349         self.netid  = netid
1350         self.wdev   = dev
1351         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1352         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1353                 _uci_real:get_all("wireless", self.sid) or { }
1354 end
1355
1356 function wifinet.get(self, opt)
1357         return _get("wireless", self.sid, opt)
1358 end
1359
1360 function wifinet.set(self, opt, val)
1361         return _set("wireless", self.sid, opt, val)
1362 end
1363
1364 function wifinet.mode(self)
1365         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1366 end
1367
1368 function wifinet.ssid(self)
1369         return _uci_state:get("wireless", self.sid, "ssid")
1370 end
1371
1372 function wifinet.bssid(self)
1373         return _uci_state:get("wireless", self.sid, "bssid")
1374 end
1375
1376 function wifinet.network(self)
1377         return _uci_state:get("wifinet", self.sid, "network")
1378 end
1379
1380 function wifinet.id(self)
1381         return self.netid
1382 end
1383
1384 function wifinet.name(self)
1385         return self.sid
1386 end
1387
1388 function wifinet.ifname(self)
1389         local ifname = self.iwinfo.ifname
1390         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1391                 ifname = self.wdev
1392         end
1393         return ifname
1394 end
1395
1396 function wifinet.get_device(self)
1397         if self.iwdata.device then
1398                 return wifidev(self.iwdata.device)
1399         end
1400 end
1401
1402 function wifinet.is_up(self)
1403         return (self.iwdata.up == "1")
1404 end
1405
1406 function wifinet.active_mode(self)
1407         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1408
1409         if     m == "ap"      then m = "Master"
1410         elseif m == "sta"     then m = "Client"
1411         elseif m == "adhoc"   then m = "Ad-Hoc"
1412         elseif m == "mesh"    then m = "Mesh"
1413         elseif m == "monitor" then m = "Monitor"
1414         end
1415
1416         return m
1417 end
1418
1419 function wifinet.active_mode_i18n(self)
1420         return lng.translate(self:active_mode())
1421 end
1422
1423 function wifinet.active_ssid(self)
1424         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1425 end
1426
1427 function wifinet.active_bssid(self)
1428         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1429 end
1430
1431 function wifinet.active_encryption(self)
1432         local enc = self.iwinfo and self.iwinfo.encryption
1433         return enc and enc.description or "-"
1434 end
1435
1436 function wifinet.assoclist(self)
1437         return self.iwinfo.assoclist or { }
1438 end
1439
1440 function wifinet.frequency(self)
1441         local freq = self.iwinfo.frequency
1442         if freq and freq > 0 then
1443                 return "%.03f" % (freq / 1000)
1444         end
1445 end
1446
1447 function wifinet.bitrate(self)
1448         local rate = self.iwinfo.bitrate
1449         if rate and rate > 0 then
1450                 return (rate / 1000)
1451         end
1452 end
1453
1454 function wifinet.channel(self)
1455         return self.iwinfo.channel or
1456                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1457 end
1458
1459 function wifinet.signal(self)
1460         return self.iwinfo.signal or 0
1461 end
1462
1463 function wifinet.noise(self)
1464         return self.iwinfo.noise or 0
1465 end
1466
1467 function wifinet.country(self)
1468         return self.iwinfo.country or "00"
1469 end
1470
1471 function wifinet.txpower(self)
1472         local pwr = (self.iwinfo.txpower or 0)
1473         return pwr + self:txpower_offset()
1474 end
1475
1476 function wifinet.txpower_offset(self)
1477         return self.iwinfo.txpower_offset or 0
1478 end
1479
1480 function wifinet.signal_level(self, s, n)
1481         if self:active_bssid() ~= "00:00:00:00:00:00" then
1482                 local signal = s or self:signal()
1483                 local noise  = n or self:noise()
1484
1485                 if signal < 0 and noise < 0 then
1486                         local snr = -1 * (noise - signal)
1487                         return math.floor(snr / 5)
1488                 else
1489                         return 0
1490                 end
1491         else
1492                 return -1
1493         end
1494 end
1495
1496 function wifinet.signal_percent(self)
1497         local qc = self.iwinfo.quality or 0
1498         local qm = self.iwinfo.quality_max or 0
1499
1500         if qc > 0 and qm > 0 then
1501                 return math.floor((100 / qm) * qc)
1502         else
1503                 return 0
1504         end
1505 end
1506
1507 function wifinet.shortname(self)
1508         return "%s %q" %{
1509                 lng.translate(self:active_mode()),
1510                 self:active_ssid() or self:active_bssid()
1511         }
1512 end
1513
1514 function wifinet.get_i18n(self)
1515         return "%s: %s %q (%s)" %{
1516                 lng.translate("Wireless Network"),
1517                 lng.translate(self:active_mode()),
1518                 self:active_ssid() or self:active_bssid(),
1519                 self:ifname()
1520         }
1521 end
1522
1523 function wifinet.adminlink(self)
1524         return dsp.build_url("admin", "network", "wireless", self.netid)
1525 end
1526
1527 function wifinet.get_network(self)
1528         return self:get_networks()[1]
1529 end
1530
1531 function wifinet.get_networks(self)
1532         local nets = { }
1533         local net
1534         for net in utl.imatch(tostring(self.iwdata.network)) do
1535                 if _uci_real:get("network", net) == "interface" then
1536                         nets[#nets+1] = network(net)
1537                 end
1538         end
1539         table.sort(nets, function(a, b) return a.sid < b.sid end)
1540         return nets
1541 end
1542
1543 function wifinet.get_interface(self)
1544         return interface(self:ifname())
1545 end
1546
1547
1548 -- setup base protocols
1549 _M:register_protocol("static")
1550 _M:register_protocol("dhcp")
1551
1552 local dhcpv6 = _M:register_protocol("dhcpv6")
1553 function dhcpv6.is_installed(self)
1554         return nfs.access("/lib/netifd/proto/dhcpv6.sh")
1555 end
1556
1557 _M:register_protocol("none")
1558
1559
1560 -- load protocol extensions
1561 local exts = nfs.dir(utl.libpath() .. "/model/network")
1562 if exts then
1563         local ext
1564         for ext in exts do
1565                 if ext:match("%.lua$") then
1566                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1567                 end
1568         end
1569 end