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