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