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