luci-base: switch luci.model.network to common ubus api
[project/luci.git] / modules / luci-base / luasrc / model / network.lua
1 --[[
2 LuCI - Network model
3
4 Copyright 2009-2015 Jo-Philipp Wich <jow@openwrt.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 nxo = require "nixio"
28 local nfs = require "nixio.fs"
29 local ipc = require "luci.ip"
30 local sys = require "luci.sys"
31 local utl = require "luci.util"
32 local dsp = require "luci.dispatcher"
33 local uci = require "luci.model.uci"
34 local lng = require "luci.i18n"
35
36 module "luci.model.network"
37
38
39 IFACE_PATTERNS_VIRTUAL  = { }
40 IFACE_PATTERNS_IGNORE   = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^lo$" }
41 IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
42
43
44 protocol = utl.class()
45
46 local _protocols = { }
47
48 local _interfaces, _bridge, _switch, _tunnel
49 local _ubusnetcache, _ubusdevcache, _ubuswificache
50 local _uci_real, _uci_state
51
52 function _filter(c, s, o, r)
53         local val = _uci_real:get(c, s, o)
54         if val then
55                 local l = { }
56                 if type(val) == "string" then
57                         for val in val:gmatch("%S+") do
58                                 if val ~= r then
59                                         l[#l+1] = val
60                                 end
61                         end
62                         if #l > 0 then
63                                 _uci_real:set(c, s, o, table.concat(l, " "))
64                         else
65                                 _uci_real:delete(c, s, o)
66                         end
67                 elseif type(val) == "table" then
68                         for _, val in ipairs(val) do
69                                 if val ~= r then
70                                         l[#l+1] = val
71                                 end
72                         end
73                         if #l > 0 then
74                                 _uci_real:set(c, s, o, l)
75                         else
76                                 _uci_real:delete(c, s, o)
77                         end
78                 end
79         end
80 end
81
82 function _append(c, s, o, a)
83         local val = _uci_real:get(c, s, o) or ""
84         if type(val) == "string" then
85                 local l = { }
86                 for val in val:gmatch("%S+") do
87                         if val ~= a then
88                                 l[#l+1] = val
89                         end
90                 end
91                 l[#l+1] = a
92                 _uci_real:set(c, s, o, table.concat(l, " "))
93         elseif type(val) == "table" then
94                 local l = { }
95                 for _, val in ipairs(val) do
96                         if val ~= a then
97                                 l[#l+1] = val
98                         end
99                 end
100                 l[#l+1] = a
101                 _uci_real:set(c, s, o, l)
102         end
103 end
104
105 function _stror(s1, s2)
106         if not s1 or #s1 == 0 then
107                 return s2 and #s2 > 0 and s2
108         else
109                 return s1
110         end
111 end
112
113 function _get(c, s, o)
114         return _uci_real:get(c, s, o)
115 end
116
117 function _set(c, s, o, v)
118         if v ~= nil then
119                 if type(v) == "boolean" then v = v and "1" or "0" end
120                 return _uci_real:set(c, s, o, v)
121         else
122                 return _uci_real:delete(c, s, o)
123         end
124 end
125
126 function _wifi_iface(x)
127         local _, p
128         for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
129                 if x:match(p) then
130                         return true
131                 end
132         end
133         return false
134 end
135
136 function _wifi_state(key, val, field)
137         local radio, radiostate, ifc, ifcstate
138
139         if not next(_ubuswificache) then
140                 _ubuswificache = utl.ubus("network.wireless", "status", {}) or {}
141
142                 -- workaround extended section format
143                 for radio, radiostate in pairs(_ubuswificache) do
144                         for ifc, ifcstate in pairs(radiostate.interfaces) do
145                                 if ifcstate.section and ifcstate.section:sub(1, 1) == '@' then
146                                         local s = _uci_real:get_all('wireless.%s' % ifcstate.section)
147                                         if s then
148                                                 ifcstate.section = s['.name']
149                                         end
150                                 end
151                         end
152                 end
153         end
154
155         for radio, radiostate in pairs(_ubuswificache) do
156                 for ifc, ifcstate in pairs(radiostate.interfaces) do
157                         if ifcstate[key] == val then
158                                 return ifcstate[field]
159                         end
160                 end
161         end
162 end
163
164 function _wifi_lookup(ifn)
165         -- got a radio#.network# pseudo iface, locate the corresponding section
166         local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
167         if radio and ifnidx then
168                 local sid = nil
169                 local num = 0
170
171                 ifnidx = tonumber(ifnidx)
172                 _uci_real:foreach("wireless", "wifi-iface",
173                         function(s)
174                                 if s.device == radio then
175                                         num = num + 1
176                                         if num == ifnidx then
177                                                 sid = s['.name']
178                                                 return false
179                                         end
180                                 end
181                         end)
182
183                 return sid
184
185         -- looks like wifi, try to locate the section via state vars
186         elseif _wifi_iface(ifn) then
187                 local sid = _wifi_state("ifname", ifn, "section")
188                 if not sid then
189                         _uci_state:foreach("wireless", "wifi-iface",
190                                 function(s)
191                                         if s.ifname == ifn then
192                                                 sid = s['.name']
193                                                 return false
194                                         end
195                                 end)
196                 end
197
198                 return sid
199         end
200 end
201
202 function _iface_virtual(x)
203         local _, p
204         for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do
205                 if x:match(p) then
206                         return true
207                 end
208         end
209         return false
210 end
211
212 function _iface_ignore(x)
213         local _, p
214         for _, p in ipairs(IFACE_PATTERNS_IGNORE) do
215                 if x:match(p) then
216                         return true
217                 end
218         end
219         return _iface_virtual(x)
220 end
221
222
223 function init(cursor)
224         _uci_real  = cursor or _uci_real or uci.cursor()
225         _uci_state = _uci_real:substate()
226
227         _interfaces = { }
228         _bridge     = { }
229         _switch     = { }
230         _tunnel     = { }
231
232         _ubusnetcache  = { }
233         _ubusdevcache  = { }
234         _ubuswificache = { }
235
236         -- read interface information
237         local n, i
238         for n, i in ipairs(nxo.getifaddrs()) do
239                 local name = i.name:match("[^:]+")
240                 local prnt = name:match("^([^%.]+)%.")
241
242                 if _iface_virtual(name) then
243                         _tunnel[name] = true
244                 end
245
246                 if _tunnel[name] or not _iface_ignore(name) then
247                         _interfaces[name] = _interfaces[name] or {
248                                 idx      = i.ifindex or n,
249                                 name     = name,
250                                 rawname  = i.name,
251                                 flags    = { },
252                                 ipaddrs  = { },
253                                 ip6addrs = { }
254                         }
255
256                         if prnt then
257                                 _switch[name] = true
258                                 _switch[prnt] = true
259                         end
260
261                         if i.family == "packet" then
262                                 _interfaces[name].flags   = i.flags
263                                 _interfaces[name].stats   = i.data
264                                 _interfaces[name].macaddr = i.addr
265                         elseif i.family == "inet" then
266                                 _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
267                         elseif i.family == "inet6" then
268                                 _interfaces[name].ip6addrs[#_interfaces[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
269                         end
270                 end
271         end
272
273         -- read bridge informaton
274         local b, l
275         for l in utl.execi("brctl show") do
276                 if not l:match("STP") then
277                         local r = utl.split(l, "%s+", nil, true)
278                         if #r == 4 then
279                                 b = {
280                                         name    = r[1],
281                                         id      = r[2],
282                                         stp     = r[3] == "yes",
283                                         ifnames = { _interfaces[r[4]] }
284                                 }
285                                 if b.ifnames[1] then
286                                         b.ifnames[1].bridge = b
287                                 end
288                                 _bridge[r[1]] = b
289                         elseif b then
290                                 b.ifnames[#b.ifnames+1] = _interfaces[r[2]]
291                                 b.ifnames[#b.ifnames].bridge = b
292                         end
293                 end
294         end
295
296         return _M
297 end
298
299 function save(self, ...)
300         _uci_real:save(...)
301         _uci_real:load(...)
302 end
303
304 function commit(self, ...)
305         _uci_real:commit(...)
306         _uci_real:load(...)
307 end
308
309 function ifnameof(self, x)
310         if utl.instanceof(x, interface) then
311                 return x:name()
312         elseif utl.instanceof(x, protocol) then
313                 return x:ifname()
314         elseif type(x) == "string" then
315                 return x:match("^[^:]+")
316         end
317 end
318
319 function get_protocol(self, protoname, netname)
320         local v = _protocols[protoname]
321         if v then
322                 return v(netname or "__dummy__")
323         end
324 end
325
326 function get_protocols(self)
327         local p = { }
328         local _, v
329         for _, v in ipairs(_protocols) do
330                 p[#p+1] = v("__dummy__")
331         end
332         return p
333 end
334
335 function register_protocol(self, protoname)
336         local proto = utl.class(protocol)
337
338         function proto.__init__(self, name)
339                 self.sid = name
340         end
341
342         function proto.proto(self)
343                 return protoname
344         end
345
346         _protocols[#_protocols+1] = proto
347         _protocols[protoname]     = proto
348
349         return proto
350 end
351
352 function register_pattern_virtual(self, pat)
353         IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
354 end
355
356
357 function has_ipv6(self)
358         return nfs.access("/proc/net/ipv6_route")
359 end
360
361 function add_network(self, n, options)
362         local oldnet = self:get_network(n)
363         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
364                 if _uci_real:section("network", "interface", n, options) then
365                         return network(n)
366                 end
367         elseif oldnet and oldnet:is_empty() then
368                 if options then
369                         local k, v
370                         for k, v in pairs(options) do
371                                 oldnet:set(k, v)
372                         end
373                 end
374                 return oldnet
375         end
376 end
377
378 function get_network(self, n)
379         if n and _uci_real:get("network", n) == "interface" then
380                 return network(n)
381         end
382 end
383
384 function get_networks(self)
385         local nets = { }
386         local nls = { }
387
388         _uci_real:foreach("network", "interface",
389                 function(s)
390                         nls[s['.name']] = network(s['.name'])
391                 end)
392
393         local n
394         for n in utl.kspairs(nls) do
395                 nets[#nets+1] = nls[n]
396         end
397
398         return nets
399 end
400
401 function del_network(self, n)
402         local r = _uci_real:delete("network", n)
403         if r then
404                 _uci_real:delete_all("network", "alias",
405                         function(s) return (s.interface == n) end)
406
407                 _uci_real:delete_all("network", "route",
408                         function(s) return (s.interface == n) end)
409
410                 _uci_real:delete_all("network", "route6",
411                         function(s) return (s.interface == n) end)
412
413                 _uci_real:foreach("wireless", "wifi-iface",
414                         function(s)
415                                 local net
416                                 local rest = { }
417                                 for net in utl.imatch(s.network) do
418                                         if net ~= n then
419                                                 rest[#rest+1] = net
420                                         end
421                                 end
422                                 if #rest > 0 then
423                                         _uci_real:set("wireless", s['.name'], "network",
424                                                       table.concat(rest, " "))
425                                 else
426                                         _uci_real:delete("wireless", s['.name'], "network")
427                                 end
428                         end)
429         end
430         return r
431 end
432
433 function rename_network(self, old, new)
434         local r
435         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
436                 r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
437
438                 if r then
439                         _uci_real:foreach("network", "alias",
440                                 function(s)
441                                         if s.interface == old then
442                                                 _uci_real:set("network", s['.name'], "interface", new)
443                                         end
444                                 end)
445
446                         _uci_real:foreach("network", "route",
447                                 function(s)
448                                         if s.interface == old then
449                                                 _uci_real:set("network", s['.name'], "interface", new)
450                                         end
451                                 end)
452
453                         _uci_real:foreach("network", "route6",
454                                 function(s)
455                                         if s.interface == old then
456                                                 _uci_real:set("network", s['.name'], "interface", new)
457                                         end
458                                 end)
459
460                         _uci_real:foreach("wireless", "wifi-iface",
461                                 function(s)
462                                         local net
463                                         local list = { }
464                                         for net in utl.imatch(s.network) do
465                                                 if net == old then
466                                                         list[#list+1] = new
467                                                 else
468                                                         list[#list+1] = net
469                                                 end
470                                         end
471                                         if #list > 0 then
472                                                 _uci_real:set("wireless", s['.name'], "network",
473                                                               table.concat(list, " "))
474                                         end
475                                 end)
476
477                         _uci_real:delete("network", old)
478                 end
479         end
480         return r or false
481 end
482
483 function get_interface(self, i)
484         if _interfaces[i] or _wifi_iface(i) then
485                 return interface(i)
486         else
487                 local ifc
488                 local num = { }
489                 _uci_real:foreach("wireless", "wifi-iface",
490                         function(s)
491                                 if s.device then
492                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
493                                         if s['.name'] == i then
494                                                 ifc = interface(
495                                                         "%s.network%d" %{s.device, num[s.device] })
496                                                 return false
497                                         end
498                                 end
499                         end)
500                 return ifc
501         end
502 end
503
504 function get_interfaces(self)
505         local iface
506         local ifaces = { }
507         local seen = { }
508         local nfs = { }
509         local baseof = { }
510
511         -- find normal interfaces
512         _uci_real:foreach("network", "interface",
513                 function(s)
514                         for iface in utl.imatch(s.ifname) do
515                                 if not _iface_ignore(iface) and not _wifi_iface(iface) then
516                                         seen[iface] = true
517                                         nfs[iface] = interface(iface)
518                                 end
519                         end
520                 end)
521
522         for iface in utl.kspairs(_interfaces) do
523                 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
524                         nfs[iface] = interface(iface)
525                 end
526         end
527
528         -- find vlan interfaces
529         _uci_real:foreach("network", "switch_vlan",
530                 function(s)
531                         if not s.device then
532                                 return
533                         end
534
535                         local base = baseof[s.device]
536                         if not base then
537                                 if not s.device:match("^eth%d") then
538                                         local l
539                                         for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
540                                                 if not base then
541                                                         base = l:match("^%w+: (%w+)")
542                                                 end
543                                         end
544                                         if not base or not base:match("^eth%d") then
545                                                 base = "eth0"
546                                         end
547                                 else
548                                         base = s.device
549                                 end
550                                 baseof[s.device] = base
551                         end
552
553                         local vid = tonumber(s.vid or s.vlan)
554                         if vid ~= nil and vid >= 0 and vid <= 4095 then
555                                 local iface = "%s.%d" %{ base, vid }
556                                 if not seen[iface] then
557                                         seen[iface] = true
558                                         nfs[iface] = interface(iface)
559                                 end
560                         end
561                 end)
562
563         for iface in utl.kspairs(nfs) do
564                 ifaces[#ifaces+1] = nfs[iface]
565         end
566
567         -- find wifi interfaces
568         local num = { }
569         local wfs = { }
570         _uci_real:foreach("wireless", "wifi-iface",
571                 function(s)
572                         if s.device then
573                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
574                                 local i = "%s.network%d" %{ s.device, num[s.device] }
575                                 wfs[i] = interface(i)
576                         end
577                 end)
578
579         for iface in utl.kspairs(wfs) do
580                 ifaces[#ifaces+1] = wfs[iface]
581         end
582
583         return ifaces
584 end
585
586 function ignore_interface(self, x)
587         return _iface_ignore(x)
588 end
589
590 function get_wifidev(self, dev)
591         if _uci_real:get("wireless", dev) == "wifi-device" then
592                 return wifidev(dev)
593         end
594 end
595
596 function get_wifidevs(self)
597         local devs = { }
598         local wfd  = { }
599
600         _uci_real:foreach("wireless", "wifi-device",
601                 function(s) wfd[#wfd+1] = s['.name'] end)
602
603         local dev
604         for _, dev in utl.vspairs(wfd) do
605                 devs[#devs+1] = wifidev(dev)
606         end
607
608         return devs
609 end
610
611 function get_wifinet(self, net)
612         local wnet = _wifi_lookup(net)
613         if wnet then
614                 return wifinet(wnet)
615         end
616 end
617
618 function add_wifinet(self, net, options)
619         if type(options) == "table" and options.device and
620                 _uci_real:get("wireless", options.device) == "wifi-device"
621         then
622                 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
623                 return wifinet(wnet)
624         end
625 end
626
627 function del_wifinet(self, net)
628         local wnet = _wifi_lookup(net)
629         if wnet then
630                 _uci_real:delete("wireless", wnet)
631                 return true
632         end
633         return false
634 end
635
636 function get_status_by_route(self, addr, mask)
637         local _, object
638         for _, object in ipairs(utl.ubus()) do
639                 local net = object:match("^network%.interface%.(.+)")
640                 if net then
641                         local s = utl.ubus(object, "status", {})
642                         if s and s.route then
643                                 local rt
644                                 for _, rt in ipairs(s.route) do
645                                         if not rt.table and rt.target == addr and rt.mask == mask then
646                                                 return net, s
647                                         end
648                                 end
649                         end
650                 end
651         end
652 end
653
654 function get_status_by_address(self, addr)
655         local _, object
656         for _, object in ipairs(utl.ubus()) do
657                 local net = object:match("^network%.interface%.(.+)")
658                 if net then
659                         local s = utl.ubus(object, "status", {})
660                         if s and s['ipv4-address'] then
661                                 local a
662                                 for _, a in ipairs(s['ipv4-address']) do
663                                         if a.address == addr then
664                                                 return net, s
665                                         end
666                                 end
667                         end
668                         if s and s['ipv6-address'] then
669                                 local a
670                                 for _, a in ipairs(s['ipv6-address']) do
671                                         if a.address == addr then
672                                                 return net, s
673                                         end
674                                 end
675                         end
676                 end
677         end
678 end
679
680 function get_wannet(self)
681         local net = self:get_status_by_route("0.0.0.0", 0)
682         return net and network(net)
683 end
684
685 function get_wandev(self)
686         local _, stat = self:get_status_by_route("0.0.0.0", 0)
687         return stat and interface(stat.l3_device or stat.device)
688 end
689
690 function get_wan6net(self)
691         local net = self:get_status_by_route("::", 0)
692         return net and network(net)
693 end
694
695 function get_wan6dev(self)
696         local _, stat = self:get_status_by_route("::", 0)
697         return stat and interface(stat.l3_device or stat.device)
698 end
699
700
701 function network(name, proto)
702         if name then
703                 local p = proto or _uci_real:get("network", name, "proto")
704                 local c = p and _protocols[p] or protocol
705                 return c(name)
706         end
707 end
708
709 function protocol.__init__(self, name)
710         self.sid = name
711 end
712
713 function protocol._get(self, opt)
714         local v = _uci_real:get("network", self.sid, opt)
715         if type(v) == "table" then
716                 return table.concat(v, " ")
717         end
718         return v or ""
719 end
720
721 function protocol._ubus(self, field)
722         if not _ubusnetcache[self.sid] then
723                 _ubusnetcache[self.sid] = utl.ubus("network.interface.%s" % self.sid,
724                                                    "status", { })
725         end
726         if _ubusnetcache[self.sid] and field then
727                 return _ubusnetcache[self.sid][field]
728         end
729         return _ubusnetcache[self.sid]
730 end
731
732 function protocol.get(self, opt)
733         return _get("network", self.sid, opt)
734 end
735
736 function protocol.set(self, opt, val)
737         return _set("network", self.sid, opt, val)
738 end
739
740 function protocol.ifname(self)
741         local ifname
742         if self:is_floating() then
743                 ifname = self:_ubus("l3_device")
744         else
745                 ifname = self:_ubus("device")
746         end
747         if not ifname then
748                 local num = { }
749                 _uci_real:foreach("wireless", "wifi-iface",
750                         function(s)
751                                 if s.device then
752                                         num[s.device] = num[s.device]
753                                                 and num[s.device] + 1 or 1
754
755                                         local net
756                                         for net in utl.imatch(s.network) do
757                                                 if net == self.sid then
758                                                         ifname = "%s.network%d" %{ s.device, num[s.device] }
759                                                         return false
760                                                 end
761                                         end
762                                 end
763                         end)
764         end
765         return ifname
766 end
767
768 function protocol.proto(self)
769         return "none"
770 end
771
772 function protocol.get_i18n(self)
773         local p = self:proto()
774         if p == "none" then
775                 return lng.translate("Unmanaged")
776         elseif p == "static" then
777                 return lng.translate("Static address")
778         elseif p == "dhcp" then
779                 return lng.translate("DHCP client")
780         else
781                 return lng.translate("Unknown")
782         end
783 end
784
785 function protocol.type(self)
786         return self:_get("type")
787 end
788
789 function protocol.name(self)
790         return self.sid
791 end
792
793 function protocol.uptime(self)
794         return self:_ubus("uptime") or 0
795 end
796
797 function protocol.expires(self)
798         local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
799         local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
800         if a and l then
801                 l = l - (nxo.sysinfo().uptime - a)
802                 return l > 0 and l or 0
803         end
804         return -1
805 end
806
807 function protocol.metric(self)
808         return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
809 end
810
811 function protocol.ipaddr(self)
812         local addrs = self:_ubus("ipv4-address")
813         return addrs and #addrs > 0 and addrs[1].address
814 end
815
816 function protocol.netmask(self)
817         local addrs = self:_ubus("ipv4-address")
818         return addrs and #addrs > 0 and
819                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
820 end
821
822 function protocol.gwaddr(self)
823         local _, route
824         for _, route in ipairs(self:_ubus("route") or { }) do
825                 if route.target == "0.0.0.0" and route.mask == 0 then
826                         return route.nexthop
827                 end
828         end
829 end
830
831 function protocol.dnsaddrs(self)
832         local dns = { }
833         local _, addr
834         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
835                 if not addr:match(":") then
836                         dns[#dns+1] = addr
837                 end
838         end
839         return dns
840 end
841
842 function protocol.ip6addr(self)
843         local addrs = self:_ubus("ipv6-address")
844         if addrs and #addrs > 0 then
845                 return "%s/%d" %{ addrs[1].address, addrs[1].mask }
846         else
847                 addrs = self:_ubus("ipv6-prefix-assignment")
848                 if addrs and #addrs > 0 then
849                         return "%s/%d" %{ addrs[1].address, addrs[1].mask }
850                 end
851         end
852 end
853
854 function protocol.gw6addr(self)
855         local _, route
856         for _, route in ipairs(self:_ubus("route") or { }) do
857                 if route.target == "::" and route.mask == 0 then
858                         return ipc.IPv6(route.nexthop):string()
859                 end
860         end
861 end
862
863 function protocol.dns6addrs(self)
864         local dns = { }
865         local _, addr
866         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
867                 if addr:match(":") then
868                         dns[#dns+1] = addr
869                 end
870         end
871         return dns
872 end
873
874 function protocol.is_bridge(self)
875         return (not self:is_virtual() and self:type() == "bridge")
876 end
877
878 function protocol.opkg_package(self)
879         return nil
880 end
881
882 function protocol.is_installed(self)
883         return true
884 end
885
886 function protocol.is_virtual(self)
887         return false
888 end
889
890 function protocol.is_floating(self)
891         return false
892 end
893
894 function protocol.is_empty(self)
895         if self:is_floating() then
896                 return false
897         else
898                 local rv = true
899
900                 if (self:_get("ifname") or ""):match("%S+") then
901                         rv = false
902                 end
903
904                 _uci_real:foreach("wireless", "wifi-iface",
905                         function(s)
906                                 local n
907                                 for n in utl.imatch(s.network) do
908                                         if n == self.sid then
909                                                 rv = false
910                                                 return false
911                                         end
912                                 end
913                         end)
914
915                 return rv
916         end
917 end
918
919 function protocol.add_interface(self, ifname)
920         ifname = _M:ifnameof(ifname)
921         if ifname and not self:is_floating() then
922                 -- if its a wifi interface, change its network option
923                 local wif = _wifi_lookup(ifname)
924                 if wif then
925                         _append("wireless", wif, "network", self.sid)
926
927                 -- add iface to our iface list
928                 else
929                         _append("network", self.sid, "ifname", ifname)
930                 end
931         end
932 end
933
934 function protocol.del_interface(self, ifname)
935         ifname = _M:ifnameof(ifname)
936         if ifname and not self:is_floating() then
937                 -- if its a wireless interface, clear its network option
938                 local wif = _wifi_lookup(ifname)
939                 if wif then _filter("wireless", wif, "network", self.sid) end
940
941                 -- remove the interface
942                 _filter("network", self.sid, "ifname", ifname)
943         end
944 end
945
946 function protocol.get_interface(self)
947         if self:is_virtual() then
948                 _tunnel[self:proto() .. "-" .. self.sid] = true
949                 return interface(self:proto() .. "-" .. self.sid, self)
950         elseif self:is_bridge() then
951                 _bridge["br-" .. self.sid] = true
952                 return interface("br-" .. self.sid, self)
953         else
954                 local ifn = nil
955                 local num = { }
956                 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
957                         ifn = ifn:match("^[^:/]+")
958                         return ifn and interface(ifn, self)
959                 end
960                 ifn = nil
961                 _uci_real:foreach("wireless", "wifi-iface",
962                         function(s)
963                                 if s.device then
964                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
965
966                                         local net
967                                         for net in utl.imatch(s.network) do
968                                                 if net == self.sid then
969                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
970                                                         return false
971                                                 end
972                                         end
973                                 end
974                         end)
975                 return ifn and interface(ifn, self)
976         end
977 end
978
979 function protocol.get_interfaces(self)
980         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
981                 local ifaces = { }
982
983                 local ifn
984                 local nfs = { }
985                 for ifn in utl.imatch(self:get("ifname")) do
986                         ifn = ifn:match("^[^:/]+")
987                         nfs[ifn] = interface(ifn, self)
988                 end
989
990                 for ifn in utl.kspairs(nfs) do
991                         ifaces[#ifaces+1] = nfs[ifn]
992                 end
993
994                 local num = { }
995                 local wfs = { }
996                 _uci_real:foreach("wireless", "wifi-iface",
997                         function(s)
998                                 if s.device then
999                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
1000
1001                                         local net
1002                                         for net in utl.imatch(s.network) do
1003                                                 if net == self.sid then
1004                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
1005                                                         wfs[ifn] = interface(ifn, self)
1006                                                 end
1007                                         end
1008                                 end
1009                         end)
1010
1011                 for ifn in utl.kspairs(wfs) do
1012                         ifaces[#ifaces+1] = wfs[ifn]
1013                 end
1014
1015                 return ifaces
1016         end
1017 end
1018
1019 function protocol.contains_interface(self, ifname)
1020         ifname = _M:ifnameof(ifname)
1021         if not ifname then
1022                 return false
1023         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
1024                 return true
1025         elseif self:is_bridge() and "br-" .. self.sid == ifname then
1026                 return true
1027         else
1028                 local ifn
1029                 for ifn in utl.imatch(self:get("ifname")) do
1030                         ifn = ifn:match("[^:]+")
1031                         if ifn == ifname then
1032                                 return true
1033                         end
1034                 end
1035
1036                 local wif = _wifi_lookup(ifname)
1037                 if wif then
1038                         local n
1039                         for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
1040                                 if n == self.sid then
1041                                         return true
1042                                 end
1043                         end
1044                 end
1045         end
1046
1047         return false
1048 end
1049
1050 function protocol.adminlink(self)
1051         return dsp.build_url("admin", "network", "network", self.sid)
1052 end
1053
1054
1055 interface = utl.class()
1056
1057 function interface.__init__(self, ifname, network)
1058         local wif = _wifi_lookup(ifname)
1059         if wif then
1060                 self.wif    = wifinet(wif)
1061                 self.ifname = _wifi_state("section", wif, "ifname")
1062         end
1063
1064         self.ifname  = self.ifname or ifname
1065         self.dev     = _interfaces[self.ifname]
1066         self.network = network
1067 end
1068
1069 function interface._ubus(self, field)
1070         if not _ubusdevcache[self.ifname] then
1071                 _ubusdevcache[self.ifname] = utl.ubus("network.device", "status",
1072                                                       { name = self.ifname })
1073         end
1074         if _ubusdevcache[self.ifname] and field then
1075                 return _ubusdevcache[self.ifname][field]
1076         end
1077         return _ubusdevcache[self.ifname]
1078 end
1079
1080 function interface.name(self)
1081         return self.wif and self.wif:ifname() or self.ifname
1082 end
1083
1084 function interface.mac(self)
1085         return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
1086 end
1087
1088 function interface.ipaddrs(self)
1089         return self.dev and self.dev.ipaddrs or { }
1090 end
1091
1092 function interface.ip6addrs(self)
1093         return self.dev and self.dev.ip6addrs or { }
1094 end
1095
1096 function interface.type(self)
1097         if self.wif or _wifi_iface(self.ifname) then
1098                 return "wifi"
1099         elseif _bridge[self.ifname] then
1100                 return "bridge"
1101         elseif _tunnel[self.ifname] then
1102                 return "tunnel"
1103         elseif self.ifname:match("%.") then
1104                 return "vlan"
1105         elseif _switch[self.ifname] then
1106                 return "switch"
1107         else
1108                 return "ethernet"
1109         end
1110 end
1111
1112 function interface.shortname(self)
1113         if self.wif then
1114                 return "%s %q" %{
1115                         self.wif:active_mode(),
1116                         self.wif:active_ssid() or self.wif:active_bssid()
1117                 }
1118         else
1119                 return self.ifname
1120         end
1121 end
1122
1123 function interface.get_i18n(self)
1124         if self.wif then
1125                 return "%s: %s %q" %{
1126                         lng.translate("Wireless Network"),
1127                         self.wif:active_mode(),
1128                         self.wif:active_ssid() or self.wif:active_bssid()
1129                 }
1130         else
1131                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1132         end
1133 end
1134
1135 function interface.get_type_i18n(self)
1136         local x = self:type()
1137         if x == "wifi" then
1138                 return lng.translate("Wireless Adapter")
1139         elseif x == "bridge" then
1140                 return lng.translate("Bridge")
1141         elseif x == "switch" then
1142                 return lng.translate("Ethernet Switch")
1143         elseif x == "vlan" then
1144                 return lng.translate("VLAN Interface")
1145         elseif x == "tunnel" then
1146                 return lng.translate("Tunnel Interface")
1147         else
1148                 return lng.translate("Ethernet Adapter")
1149         end
1150 end
1151
1152 function interface.adminlink(self)
1153         if self.wif then
1154                 return self.wif:adminlink()
1155         end
1156 end
1157
1158 function interface.ports(self)
1159         local members = self:_ubus("bridge-members")
1160         if members then
1161                 local _, iface
1162                 local ifaces = { }
1163                 for _, iface in ipairs(members) do
1164                         ifaces[#ifaces+1] = interface(iface)
1165                 end
1166         end
1167 end
1168
1169 function interface.bridge_id(self)
1170         if self.br then
1171                 return self.br.id
1172         else
1173                 return nil
1174         end
1175 end
1176
1177 function interface.bridge_stp(self)
1178         if self.br then
1179                 return self.br.stp
1180         else
1181                 return false
1182         end
1183 end
1184
1185 function interface.is_up(self)
1186         return self:_ubus("up") or false
1187 end
1188
1189 function interface.is_bridge(self)
1190         return (self:type() == "bridge")
1191 end
1192
1193 function interface.is_bridgeport(self)
1194         return self.dev and self.dev.bridge and true or false
1195 end
1196
1197 function interface.tx_bytes(self)
1198         local stat = self:_ubus("statistics")
1199         return stat and stat.tx_bytes or 0
1200 end
1201
1202 function interface.rx_bytes(self)
1203         local stat = self:_ubus("statistics")
1204         return stat and stat.rx_bytes or 0
1205 end
1206
1207 function interface.tx_packets(self)
1208         local stat = self:_ubus("statistics")
1209         return stat and stat.tx_packets or 0
1210 end
1211
1212 function interface.rx_packets(self)
1213         local stat = self:_ubus("statistics")
1214         return stat and stat.rx_packets or 0
1215 end
1216
1217 function interface.get_network(self)
1218         return self:get_networks()[1]
1219 end
1220
1221 function interface.get_networks(self)
1222         if not self.networks then
1223                 local nets = { }
1224                 local _, net
1225                 for _, net in ipairs(_M:get_networks()) do
1226                         if net:contains_interface(self.ifname) or
1227                            net:ifname() == self.ifname
1228                         then
1229                                 nets[#nets+1] = net
1230                         end
1231                 end
1232                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1233                 self.networks = nets
1234                 return nets
1235         else
1236                 return self.networks
1237         end
1238 end
1239
1240 function interface.get_wifinet(self)
1241         return self.wif
1242 end
1243
1244
1245 wifidev = utl.class()
1246
1247 function wifidev.__init__(self, dev)
1248         self.sid    = dev
1249         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1250 end
1251
1252 function wifidev.get(self, opt)
1253         return _get("wireless", self.sid, opt)
1254 end
1255
1256 function wifidev.set(self, opt, val)
1257         return _set("wireless", self.sid, opt, val)
1258 end
1259
1260 function wifidev.name(self)
1261         return self.sid
1262 end
1263
1264 function wifidev.hwmodes(self)
1265         local l = self.iwinfo.hwmodelist
1266         if l and next(l) then
1267                 return l
1268         else
1269                 return { b = true, g = true }
1270         end
1271 end
1272
1273 function wifidev.get_i18n(self)
1274         local t = "Generic"
1275         if self.iwinfo.type == "wl" then
1276                 t = "Broadcom"
1277         elseif self.iwinfo.type == "madwifi" then
1278                 t = "Atheros"
1279         end
1280
1281         local m = ""
1282         local l = self:hwmodes()
1283         if l.a then m = m .. "a" end
1284         if l.b then m = m .. "b" end
1285         if l.g then m = m .. "g" end
1286         if l.n then m = m .. "n" end
1287         if l.ac then m = "ac" end
1288
1289         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1290 end
1291
1292 function wifidev.is_up(self)
1293         if _ubuswificache[self.sid] then
1294                 return (_ubuswificache[self.sid].up == true)
1295         end
1296
1297         local up = false
1298         _uci_state:foreach("wireless", "wifi-iface",
1299                 function(s)
1300                         if s.device == self.sid then
1301                                 if s.up == "1" then
1302                                         up = true
1303                                         return false
1304                                 end
1305                         end
1306                 end)
1307
1308         return up
1309 end
1310
1311 function wifidev.get_wifinet(self, net)
1312         if _uci_real:get("wireless", net) == "wifi-iface" then
1313                 return wifinet(net)
1314         else
1315                 local wnet = _wifi_lookup(net)
1316                 if wnet then
1317                         return wifinet(wnet)
1318                 end
1319         end
1320 end
1321
1322 function wifidev.get_wifinets(self)
1323         local nets = { }
1324
1325         _uci_real:foreach("wireless", "wifi-iface",
1326                 function(s)
1327                         if s.device == self.sid then
1328                                 nets[#nets+1] = wifinet(s['.name'])
1329                         end
1330                 end)
1331
1332         return nets
1333 end
1334
1335 function wifidev.add_wifinet(self, options)
1336         options = options or { }
1337         options.device = self.sid
1338
1339         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1340         if wnet then
1341                 return wifinet(wnet, options)
1342         end
1343 end
1344
1345 function wifidev.del_wifinet(self, net)
1346         if utl.instanceof(net, wifinet) then
1347                 net = net.sid
1348         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1349                 net = _wifi_lookup(net)
1350         end
1351
1352         if net and _uci_real:get("wireless", net, "device") == self.sid then
1353                 _uci_real:delete("wireless", net)
1354                 return true
1355         end
1356
1357         return false
1358 end
1359
1360
1361 wifinet = utl.class()
1362
1363 function wifinet.__init__(self, net, data)
1364         self.sid = net
1365
1366         local num = { }
1367         local netid
1368         _uci_real:foreach("wireless", "wifi-iface",
1369                 function(s)
1370                         if s.device then
1371                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1372                                 if s['.name'] == self.sid then
1373                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1374                                         return false
1375                                 end
1376                         end
1377                 end)
1378
1379         local dev = _wifi_state("section", self.sid, "ifname") or netid
1380
1381         self.netid  = netid
1382         self.wdev   = dev
1383         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1384         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1385                 _uci_real:get_all("wireless", self.sid) or { }
1386 end
1387
1388 function wifinet.get(self, opt)
1389         return _get("wireless", self.sid, opt)
1390 end
1391
1392 function wifinet.set(self, opt, val)
1393         return _set("wireless", self.sid, opt, val)
1394 end
1395
1396 function wifinet.mode(self)
1397         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1398 end
1399
1400 function wifinet.ssid(self)
1401         return _uci_state:get("wireless", self.sid, "ssid")
1402 end
1403
1404 function wifinet.bssid(self)
1405         return _uci_state:get("wireless", self.sid, "bssid")
1406 end
1407
1408 function wifinet.network(self)
1409         return _uci_state:get("wifinet", self.sid, "network")
1410 end
1411
1412 function wifinet.id(self)
1413         return self.netid
1414 end
1415
1416 function wifinet.name(self)
1417         return self.sid
1418 end
1419
1420 function wifinet.ifname(self)
1421         local ifname = self.iwinfo.ifname
1422         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1423                 ifname = self.wdev
1424         end
1425         return ifname
1426 end
1427
1428 function wifinet.get_device(self)
1429         if self.iwdata.device then
1430                 return wifidev(self.iwdata.device)
1431         end
1432 end
1433
1434 function wifinet.is_up(self)
1435         local ifc = self:get_interface()
1436         return (ifc and ifc:is_up() or false)
1437 end
1438
1439 function wifinet.active_mode(self)
1440         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1441
1442         if     m == "ap"      then m = "Master"
1443         elseif m == "sta"     then m = "Client"
1444         elseif m == "adhoc"   then m = "Ad-Hoc"
1445         elseif m == "mesh"    then m = "Mesh"
1446         elseif m == "monitor" then m = "Monitor"
1447         end
1448
1449         return m
1450 end
1451
1452 function wifinet.active_mode_i18n(self)
1453         return lng.translate(self:active_mode())
1454 end
1455
1456 function wifinet.active_ssid(self)
1457         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1458 end
1459
1460 function wifinet.active_bssid(self)
1461         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1462 end
1463
1464 function wifinet.active_encryption(self)
1465         local enc = self.iwinfo and self.iwinfo.encryption
1466         return enc and enc.description or "-"
1467 end
1468
1469 function wifinet.assoclist(self)
1470         return self.iwinfo.assoclist or { }
1471 end
1472
1473 function wifinet.frequency(self)
1474         local freq = self.iwinfo.frequency
1475         if freq and freq > 0 then
1476                 return "%.03f" % (freq / 1000)
1477         end
1478 end
1479
1480 function wifinet.bitrate(self)
1481         local rate = self.iwinfo.bitrate
1482         if rate and rate > 0 then
1483                 return (rate / 1000)
1484         end
1485 end
1486
1487 function wifinet.channel(self)
1488         return self.iwinfo.channel or
1489                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1490 end
1491
1492 function wifinet.signal(self)
1493         return self.iwinfo.signal or 0
1494 end
1495
1496 function wifinet.noise(self)
1497         return self.iwinfo.noise or 0
1498 end
1499
1500 function wifinet.country(self)
1501         return self.iwinfo.country or "00"
1502 end
1503
1504 function wifinet.txpower(self)
1505         local pwr = (self.iwinfo.txpower or 0)
1506         return pwr + self:txpower_offset()
1507 end
1508
1509 function wifinet.txpower_offset(self)
1510         return self.iwinfo.txpower_offset or 0
1511 end
1512
1513 function wifinet.signal_level(self, s, n)
1514         if self:active_bssid() ~= "00:00:00:00:00:00" then
1515                 local signal = s or self:signal()
1516                 local noise  = n or self:noise()
1517
1518                 if signal < 0 and noise < 0 then
1519                         local snr = -1 * (noise - signal)
1520                         return math.floor(snr / 5)
1521                 else
1522                         return 0
1523                 end
1524         else
1525                 return -1
1526         end
1527 end
1528
1529 function wifinet.signal_percent(self)
1530         local qc = self.iwinfo.quality or 0
1531         local qm = self.iwinfo.quality_max or 0
1532
1533         if qc > 0 and qm > 0 then
1534                 return math.floor((100 / qm) * qc)
1535         else
1536                 return 0
1537         end
1538 end
1539
1540 function wifinet.shortname(self)
1541         return "%s %q" %{
1542                 lng.translate(self:active_mode()),
1543                 self:active_ssid() or self:active_bssid()
1544         }
1545 end
1546
1547 function wifinet.get_i18n(self)
1548         return "%s: %s %q (%s)" %{
1549                 lng.translate("Wireless Network"),
1550                 lng.translate(self:active_mode()),
1551                 self:active_ssid() or self:active_bssid(),
1552                 self:ifname()
1553         }
1554 end
1555
1556 function wifinet.adminlink(self)
1557         return dsp.build_url("admin", "network", "wireless", self.netid)
1558 end
1559
1560 function wifinet.get_network(self)
1561         return self:get_networks()[1]
1562 end
1563
1564 function wifinet.get_networks(self)
1565         local nets = { }
1566         local net
1567         for net in utl.imatch(tostring(self.iwdata.network)) do
1568                 if _uci_real:get("network", net) == "interface" then
1569                         nets[#nets+1] = network(net)
1570                 end
1571         end
1572         table.sort(nets, function(a, b) return a.sid < b.sid end)
1573         return nets
1574 end
1575
1576 function wifinet.get_interface(self)
1577         return interface(self:ifname())
1578 end
1579
1580
1581 -- setup base protocols
1582 _M:register_protocol("static")
1583 _M:register_protocol("dhcp")
1584 _M:register_protocol("none")
1585
1586 -- load protocol extensions
1587 local exts = nfs.dir(utl.libpath() .. "/model/network")
1588 if exts then
1589         local ext
1590         for ext in exts do
1591                 if ext:match("%.lua$") then
1592                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1593                 end
1594         end
1595 end