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