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