3e59ed5c4ead412cad89fa0b42e33544146b5b3d
[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, i18n
24         = tonumber, tostring, math, luci.i18n
25
26 local require = require
27
28 local bus = require "ubus"
29 local nxo = require "nixio"
30 local nfs = require "nixio.fs"
31 local ipc = require "luci.ip"
32 local sys = require "luci.sys"
33 local utl = require "luci.util"
34 local dsp = require "luci.dispatcher"
35 local uci = require "luci.model.uci"
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", "^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                                 if s.network == n then
388                                         _uci_real:delete("wireless", s['.name'], "network")
389                                 end
390                         end)
391         end
392         return r
393 end
394
395 function rename_network(self, old, new)
396         local r
397         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
398                 r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
399
400                 if r then
401                         _uci_real:foreach("network", "alias",
402                                 function(s)
403                                         if s.interface == old then
404                                                 _uci_real:set("network", s['.name'], "interface", new)
405                                         end
406                                 end)
407
408                         _uci_real:foreach("network", "route",
409                                 function(s)
410                                         if s.interface == old then
411                                                 _uci_real:set("network", s['.name'], "interface", new)
412                                         end
413                                 end)
414
415                         _uci_real:foreach("network", "route6",
416                                 function(s)
417                                         if s.interface == old then
418                                                 _uci_real:set("network", s['.name'], "interface", new)
419                                         end
420                                 end)
421
422                         _uci_real:foreach("wireless", "wifi-iface",
423                                 function(s)
424                                         if s.network == old then
425                                                 _uci_real:set("wireless", s['.name'], "network", new)
426                                         end
427                                 end)
428
429                         _uci_real:delete("network", old)
430                 end
431         end
432         return r or false
433 end
434
435 function get_interface(self, i)
436         if _interfaces[i] or _wifi_iface(i) then
437                 return interface(i)
438         else
439                 local ifc
440                 local num = { }
441                 _uci_real:foreach("wireless", "wifi-iface",
442                         function(s)
443                                 if s.device then
444                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
445                                         if s['.name'] == i then
446                                                 ifc = interface(
447                                                         "%s.network%d" %{s.device, num[s.device] })
448                                                 return false
449                                         end
450                                 end
451                         end)
452                 return ifc
453         end
454 end
455
456 function get_interfaces(self)
457         local iface
458         local ifaces = { }
459         local seen = { }
460         local nfs = { }
461         local baseof = { }
462
463         -- find normal interfaces
464         _uci_real:foreach("network", "interface",
465                 function(s)
466                         for iface in utl.imatch(s.ifname) do
467                                 if not _iface_ignore(iface) and not _wifi_iface(iface) then
468                                         seen[iface] = true
469                                         nfs[iface] = interface(iface)
470                                 end
471                         end
472                 end)
473
474         for iface in utl.kspairs(_interfaces) do
475                 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
476                         nfs[iface] = interface(iface)
477                 end
478         end
479
480         -- find vlan interfaces
481         _uci_real:foreach("network", "switch_vlan",
482                 function(s)
483                         if not s.device then
484                                 return
485                         end
486
487                         local base = baseof[s.device]
488                         if not base then
489                                 if not s.device:match("^eth%d") then
490                                         local l
491                                         for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
492                                                 if not base then
493                                                         base = l:match("^%w+: (%w+)")
494                                                 end
495                                         end
496                                         if not base or not base:match("^eth%d") then
497                                                 base = "eth0"
498                                         end
499                                 else
500                                         base = s.device
501                                 end
502                                 baseof[s.device] = base
503                         end
504
505                         local vid = tonumber(s.vid or s.vlan)
506                         if vid ~= nil and vid >= 0 and vid <= 4095 then
507                                 local iface = "%s.%d" %{ base, vid }
508                                 if not seen[iface] then
509                                         seen[iface] = true
510                                         nfs[iface] = interface(iface)
511                                 end
512                         end
513                 end)
514
515         for iface in utl.kspairs(nfs) do
516                 ifaces[#ifaces+1] = nfs[iface]
517         end
518
519         -- find wifi interfaces
520         local num = { }
521         local wfs = { }
522         _uci_real:foreach("wireless", "wifi-iface",
523                 function(s)
524                         if s.device then
525                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
526                                 local i = "%s.network%d" %{ s.device, num[s.device] }
527                                 wfs[i] = interface(i)
528                         end
529                 end)
530
531         for iface in utl.kspairs(wfs) do
532                 ifaces[#ifaces+1] = wfs[iface]
533         end
534
535         return ifaces
536 end
537
538 function ignore_interface(self, x)
539         return _iface_ignore(x)
540 end
541
542 function get_wifidev(self, dev)
543         if _uci_real:get("wireless", dev) == "wifi-device" then
544                 return wifidev(dev)
545         end
546 end
547
548 function get_wifidevs(self)
549         local devs = { }
550         local wfd  = { }
551
552         _uci_real:foreach("wireless", "wifi-device",
553                 function(s) wfd[#wfd+1] = s['.name'] end)
554
555         local dev
556         for _, dev in utl.vspairs(wfd) do
557                 devs[#devs+1] = wifidev(dev)
558         end
559
560         return devs
561 end
562
563 function get_wifinet(self, net)
564         local wnet = _wifi_lookup(net)
565         if wnet then
566                 return wifinet(wnet)
567         end
568 end
569
570 function add_wifinet(self, net, options)
571         if type(options) == "table" and options.device and
572                 _uci_real:get("wireless", options.device) == "wifi-device"
573         then
574                 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
575                 return wifinet(wnet)
576         end
577 end
578
579 function del_wifinet(self, net)
580         local wnet = _wifi_lookup(net)
581         if wnet then
582                 _uci_real:delete("wireless", wnet)
583                 return true
584         end
585         return false
586 end
587
588
589 function network(name, proto)
590         if name then
591                 local p = proto or _uci_real:get("network", name, "proto")
592                 local c = p and _protocols[p] or protocol
593                 return c(name)
594         end
595 end
596
597 function protocol.__init__(self, name)
598         self.sid = name
599 end
600
601 function protocol._get(self, opt)
602         local v = _uci_real:get("network", self.sid, opt)
603         if type(v) == "table" then
604                 return table.concat(v, " ")
605         end
606         return v or ""
607 end
608
609 function protocol._ubus(self, field)
610         if not _ubusnetcache[self.sid] then
611                 _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid,
612                                                      "status", { })
613         end
614         if _ubusnetcache[self.sid] and field then
615                 return _ubusnetcache[self.sid][field]
616         end
617         return _ubusnetcache[self.sid]
618 end
619
620 function protocol.get(self, opt)
621         return _get("network", self.sid, opt)
622 end
623
624 function protocol.set(self, opt, val)
625         return _set("network", self.sid, opt, val)
626 end
627
628 function protocol.ifname(self)
629         local ifname
630         if self:is_floating() then
631                 ifname = self:_ubus("l3_device")
632         else
633                 ifname = self:_ubus("device")
634         end
635         if not ifname then
636                 _uci_real:foreach("wireless", "wifi-iface",
637                         function(s)
638                                 if s.device then
639                                         num[s.device] = num[s.device]
640                                                 and num[s.device] + 1 or 1
641
642                                         if s.network == self.sid then
643                                                 ifname = "%s.network%d" %{ s.device, num[s.device] }
644                                                 return false
645                                         end
646                                 end
647                         end)
648         end
649         return ifname
650 end
651
652 function protocol.proto(self)
653         return "none"
654 end
655
656 function protocol.get_i18n(self)
657         local p = self:proto()
658         if p == "none" then
659                 return i18n.translate("Unmanaged")
660         elseif p == "static" then
661                 return i18n.translate("Static address")
662         elseif p == "dhcp" then
663                 return i18n.translate("DHCP client")
664         else
665                 return i18n.translate("Unknown")
666         end
667 end
668
669 function protocol.type(self)
670         return self:_get("type")
671 end
672
673 function protocol.name(self)
674         return self.sid
675 end
676
677 function protocol.uptime(self)
678         return self:_ubus("uptime") or 0
679 end
680
681 function protocol.expires(self)
682         local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
683         local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
684         if a and l then
685                 l = l - (nxo.sysinfo().uptime - a)
686                 return l > 0 and l or 0
687         end
688         return -1
689 end
690
691 function protocol.metric(self)
692         return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
693 end
694
695 function protocol.ipaddr(self)
696         local addrs = self:_ubus("ipv4-address")
697         return addrs and #addrs > 0 and addrs[1].address
698 end
699
700 function protocol.netmask(self)
701         local addrs = self:_ubus("ipv4-address")
702         return addrs and #addrs > 0 and
703                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
704 end
705
706 function protocol.gwaddr(self)
707         local _, route
708         for _, route in ipairs(self:_ubus("route") or { }) do
709                 if route.target == "0.0.0.0" and route.mask == 0 then
710                         return route.nexthop
711                 end
712         end
713 end
714
715 function protocol.dnsaddrs(self)
716         local dns = { }
717         local _, addr
718         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
719                 if not addr:match(":") then
720                         dns[#dns+1] = addr
721                 end
722         end
723         return dns
724 end
725
726 function protocol.ip6addr(self)
727         local addrs = self:_ubus("ipv6-address")
728         return addrs and #addrs > 0
729                 and "%s/%d" %{ addrs[1].address, addrs[1].mask }
730 end
731
732 function protocol.gw6addr(self)
733         local _, route
734         for _, route in ipairs(self:_ubus("route")) do
735                 if route.target == "::" and route.mask == 0 then
736                         return ipc.IPv6(route.nexthop):string()
737                 end
738         end
739 end
740
741 function protocol.dns6addrs(self)
742         local dns = { }
743         local _, addr
744         for _, addr in ipairs(self:_ubus("dns-server")) do
745                 if addr:match(":") then
746                         dns[#dns+1] = addr
747                 end
748         end
749         return dns
750 end
751
752 function protocol.is_bridge(self)
753         return (not self:is_virtual() and self:type() == "bridge")
754 end
755
756 function protocol.opkg_package(self)
757         return nil
758 end
759
760 function protocol.is_installed(self)
761         return true
762 end
763
764 function protocol.is_virtual(self)
765         return false
766 end
767
768 function protocol.is_floating(self)
769         return false
770 end
771
772 function protocol.is_empty(self)
773         if self:is_floating() then
774                 return false
775         else
776                 local rv = true
777
778                 if (self:_get("ifname") or ""):match("%S+") then
779                         rv = false
780                 end
781
782                 _uci_real:foreach("wireless", "wifi-iface",
783                         function(s)
784                                 if s.network == self.sid then
785                                         rv = false
786                                         return false
787                                 end
788                         end)
789
790                 return rv
791         end
792 end
793
794 function protocol.add_interface(self, ifname)
795         ifname = _M:ifnameof(ifname)
796         if ifname and not self:is_floating() then
797                 -- remove the interface from all ifaces
798                 _uci_real:foreach("network", "interface",
799                         function(s)
800                                 _filter("network", s['.name'], "ifname", ifname)
801                         end)
802
803                 -- if its a wifi interface, change its network option
804                 local wif = _wifi_lookup(ifname)
805                 if wif then
806                         _uci_real:set("wireless", wif, "network", self.sid)
807
808                 -- add iface to our iface list
809                 else
810                         _append("network", self.sid, "ifname", ifname)
811                 end
812         end
813 end
814
815 function protocol.del_interface(self, ifname)
816         ifname = _M:ifnameof(ifname)
817         if ifname and not self:is_floating() then
818                 -- if its a wireless interface, clear its network option
819                 local wif = _wifi_lookup(ifname)
820                 if wif then     _uci_real:delete("wireless", wif, "network") end
821
822                 -- remove the interface
823                 _filter("network", self.sid, "ifname", ifname)
824         end
825 end
826
827 function protocol.get_interface(self)
828         if self:is_virtual() then
829                 _tunnel[self:proto() .. "-" .. self.sid] = true
830                 return interface(self:proto() .. "-" .. self.sid, self)
831         elseif self:is_bridge() then
832                 _bridge["br-" .. self.sid] = true
833                 return interface("br-" .. self.sid, self)
834         else
835                 local ifn = nil
836                 local num = { }
837                 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
838                         ifn = ifn:match("^[^:/]+")
839                         return ifn and interface(ifn, self)
840                 end
841                 ifn = nil
842                 _uci_real:foreach("wireless", "wifi-iface",
843                         function(s)
844                                 if s.device then
845                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
846                                         if s.network == self.sid then
847                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
848                                                 return false
849                                         end
850                                 end
851                         end)
852                 return ifn and interface(ifn, self)
853         end
854 end
855
856 function protocol.get_interfaces(self)
857         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
858                 local ifaces = { }
859
860                 local ifn
861                 local nfs = { }
862                 for ifn in utl.imatch(self:get("ifname")) do
863                         ifn = ifn:match("^[^:/]+")
864                         nfs[ifn] = interface(ifn, self)
865                 end
866
867                 for ifn in utl.kspairs(nfs) do
868                         ifaces[#ifaces+1] = nfs[ifn]
869                 end
870
871                 local num = { }
872                 local wfs = { }
873                 _uci_real:foreach("wireless", "wifi-iface",
874                         function(s)
875                                 if s.device then
876                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
877                                         if s.network == self.sid then
878                                                 ifn = "%s.network%d" %{ s.device, num[s.device] }
879                                                 wfs[ifn] = interface(ifn, self)
880                                         end
881                                 end
882                         end)
883
884                 for ifn in utl.kspairs(wfs) do
885                         ifaces[#ifaces+1] = wfs[ifn]
886                 end
887
888                 return ifaces
889         end
890 end
891
892 function protocol.contains_interface(self, ifname)
893         ifname = _M:ifnameof(ifname)
894         if not ifname then
895                 return false
896         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
897                 return true
898         elseif self:is_bridge() and "br-" .. self.sid == ifname then
899                 return true
900         else
901                 local ifn
902                 for ifn in utl.imatch(self:get("ifname")) do
903                         ifn = ifn:match("[^:]+")
904                         if ifn == ifname then
905                                 return true
906                         end
907                 end
908
909                 local wif = _wifi_lookup(ifname)
910                 if wif then
911                         return (_uci_real:get("wireless", wif, "network") == self.sid)
912                 end
913         end
914
915         return false
916 end
917
918 function protocol.adminlink(self)
919         return dsp.build_url("admin", "network", "network", self.sid)
920 end
921
922
923 interface = utl.class()
924
925 function interface.__init__(self, ifname, network)
926         local wif = _wifi_lookup(ifname)
927         if wif then
928                 self.wif    = wifinet(wif)
929                 self.ifname = _uci_state:get("wireless", wif, "ifname")
930         end
931
932         self.ifname  = self.ifname or ifname
933         self.dev     = _interfaces[self.ifname]
934         self.network = network
935 end
936
937 function interface._ubus(self, field)
938         if not _ubusdevcache[self.ifname] then
939                 _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
940                                                         { name = self.ifname })
941         end
942         if _ubusdevcache[self.ifname] and field then
943                 return _ubusdevcache[self.ifname][field]
944         end
945         return _ubusdevcache[self.ifname]
946 end
947
948 function interface.name(self)
949         return self.wif and self.wif:ifname() or self.ifname
950 end
951
952 function interface.mac(self)
953         return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
954 end
955
956 function interface.ipaddrs(self)
957         return self.dev and self.dev.ipaddrs or { }
958 end
959
960 function interface.ip6addrs(self)
961         return self.dev and self.dev.ip6addrs or { }
962 end
963
964 function interface.type(self)
965         if self.wif or _wifi_iface(self.ifname) then
966                 return "wifi"
967         elseif _bridge[self.ifname] then
968                 return "bridge"
969         elseif _tunnel[self.ifname] then
970                 return "tunnel"
971         elseif self.ifname:match("%.") then
972                 return "vlan"
973         elseif _switch[self.ifname] then
974                 return "switch"
975         else
976                 return "ethernet"
977         end
978 end
979
980 function interface.shortname(self)
981         if self.wif then
982                 return "%s %q" %{
983                         self.wif:active_mode(),
984                         self.wif:active_ssid() or self.wif:active_bssid()
985                 }
986         else
987                 return self.ifname
988         end
989 end
990
991 function interface.get_i18n(self)
992         if self.wif then
993                 return "%s: %s %q" %{
994                         i18n.translate("Wireless Network"),
995                         self.wif:active_mode(),
996                         self.wif:active_ssid() or self.wif:active_bssid()
997                 }
998         else
999                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1000         end
1001 end
1002
1003 function interface.get_type_i18n(self)
1004         local x = self:type()
1005         if x == "wifi" then
1006                 return i18n.translate("Wireless Adapter")
1007         elseif x == "bridge" then
1008                 return i18n.translate("Bridge")
1009         elseif x == "switch" then
1010                 return i18n.translate("Ethernet Switch")
1011         elseif x == "vlan" then
1012                 return i18n.translate("VLAN Interface")
1013         elseif x == "tunnel" then
1014                 return i18n.translate("Tunnel Interface")
1015         else
1016                 return i18n.translate("Ethernet Adapter")
1017         end
1018 end
1019
1020 function interface.adminlink(self)
1021         if self.wif then
1022                 return self.wif:adminlink()
1023         end
1024 end
1025
1026 function interface.ports(self)
1027         local members = self:_ubus("bridge-members")
1028         if members then
1029                 local _, iface
1030                 local ifaces = { }
1031                 for _, iface in ipairs(members) do
1032                         ifaces[#ifaces+1] = interface(iface)
1033                 end
1034         end
1035 end
1036
1037 function interface.bridge_id(self)
1038         if self.br then
1039                 return self.br.id
1040         else
1041                 return nil
1042         end
1043 end
1044
1045 function interface.bridge_stp(self)
1046         if self.br then
1047                 return self.br.stp
1048         else
1049                 return false
1050         end
1051 end
1052
1053 function interface.is_up(self)
1054         if self.wif then
1055                 return self.wif:is_up()
1056         else
1057                 return self:_ubus("up") or false
1058         end
1059 end
1060
1061 function interface.is_bridge(self)
1062         return (self:type() == "bridge")
1063 end
1064
1065 function interface.is_bridgeport(self)
1066         return self.dev and self.dev.bridge and true or false
1067 end
1068
1069 local function uint(x)
1070         if x then
1071                 return (x < 0) and ((2^32) + x) or x
1072         end
1073         return 0
1074 end
1075
1076 function interface.tx_bytes(self)
1077         local stat = self:_ubus("statistics")
1078         return stat and uint(stat.tx_bytes) or 0
1079 end
1080
1081 function interface.rx_bytes(self)
1082         local stat = self:_ubus("statistics")
1083         return stat and uint(stat.rx_bytes) or 0
1084 end
1085
1086 function interface.tx_packets(self)
1087         local stat = self:_ubus("statistics")
1088         return stat and uint(stat.tx_packets) or 0
1089 end
1090
1091 function interface.rx_packets(self)
1092         local stat = self:_ubus("statistics")
1093         return stat and uint(stat.rx_packets) or 0
1094 end
1095
1096 function interface.get_network(self)
1097         if not self.network then
1098                 if self.dev and self.dev.network then
1099                         self.network = _M:get_network(self.dev.network)
1100                 end
1101         end
1102
1103         if not self.network then
1104                 local net
1105                 for _, net in ipairs(_M:get_networks()) do
1106                         if net:contains_interface(self.ifname) or
1107                            net:ifname() == self.ifname
1108                         then
1109                                 self.network = net
1110                                 return net
1111                         end
1112                 end
1113         else
1114                 return self.network
1115         end
1116 end
1117
1118 function interface.get_wifinet(self)
1119         return self.wif
1120 end
1121
1122
1123 wifidev = utl.class()
1124
1125 function wifidev.__init__(self, dev)
1126         self.sid    = dev
1127         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1128 end
1129
1130 function wifidev.get(self, opt)
1131         return _get("wireless", self.sid, opt)
1132 end
1133
1134 function wifidev.set(self, opt, val)
1135         return _set("wireless", self.sid, opt, val)
1136 end
1137
1138 function wifidev.name(self)
1139         return self.sid
1140 end
1141
1142 function wifidev.hwmodes(self)
1143         local l = self.iwinfo.hwmodelist
1144         if l and next(l) then
1145                 return l
1146         else
1147                 return { b = true, g = true }
1148         end
1149 end
1150
1151 function wifidev.get_i18n(self)
1152         local t = "Generic"
1153         if self.iwinfo.type == "wl" then
1154                 t = "Broadcom"
1155         elseif self.iwinfo.type == "madwifi" then
1156                 t = "Atheros"
1157         end
1158
1159         local m = ""
1160         local l = self:hwmodes()
1161         if l.a then m = m .. "a" end
1162         if l.b then m = m .. "b" end
1163         if l.g then m = m .. "g" end
1164         if l.n then m = m .. "n" end
1165
1166         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1167 end
1168
1169 function wifidev.is_up(self)
1170         local up = false
1171
1172         _uci_state:foreach("wireless", "wifi-iface",
1173                 function(s)
1174                         if s.device == self.sid then
1175                                 if s.up == "1" then
1176                                         up = true
1177                                         return false
1178                                 end
1179                         end
1180                 end)
1181
1182         return up
1183 end
1184
1185 function wifidev.get_wifinet(self, net)
1186         if _uci_real:get("wireless", net) == "wifi-iface" then
1187                 return wifinet(net)
1188         else
1189                 local wnet = _wifi_lookup(net)
1190                 if wnet then
1191                         return wifinet(wnet)
1192                 end
1193         end
1194 end
1195
1196 function wifidev.get_wifinets(self)
1197         local nets = { }
1198
1199         _uci_real:foreach("wireless", "wifi-iface",
1200                 function(s)
1201                         if s.device == self.sid then
1202                                 nets[#nets+1] = wifinet(s['.name'])
1203                         end
1204                 end)
1205
1206         return nets
1207 end
1208
1209 function wifidev.add_wifinet(self, options)
1210         options = options or { }
1211         options.device = self.sid
1212
1213         local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1214         if wnet then
1215                 return wifinet(wnet, options)
1216         end
1217 end
1218
1219 function wifidev.del_wifinet(self, net)
1220         if utl.instanceof(net, wifinet) then
1221                 net = net.sid
1222         elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1223                 net = _wifi_lookup(net)
1224         end
1225
1226         if net and _uci_real:get("wireless", net, "device") == self.sid then
1227                 _uci_real:delete("wireless", net)
1228                 return true
1229         end
1230
1231         return false
1232 end
1233
1234
1235 wifinet = utl.class()
1236
1237 function wifinet.__init__(self, net, data)
1238         self.sid = net
1239
1240         local num = { }
1241         local netid
1242         _uci_real:foreach("wireless", "wifi-iface",
1243                 function(s)
1244                         if s.device then
1245                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1246                                 if s['.name'] == self.sid then
1247                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1248                                         return false
1249                                 end
1250                         end
1251                 end)
1252
1253         local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1254
1255         self.netid  = netid
1256         self.wdev   = dev
1257         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1258         self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1259                 _uci_real:get_all("wireless", self.sid) or { }
1260 end
1261
1262 function wifinet.get(self, opt)
1263         return _get("wireless", self.sid, opt)
1264 end
1265
1266 function wifinet.set(self, opt, val)
1267         return _set("wireless", self.sid, opt, val)
1268 end
1269
1270 function wifinet.mode(self)
1271         return _uci_state:get("wireless", self.sid, "mode") or "ap"
1272 end
1273
1274 function wifinet.ssid(self)
1275         return _uci_state:get("wireless", self.sid, "ssid")
1276 end
1277
1278 function wifinet.bssid(self)
1279         return _uci_state:get("wireless", self.sid, "bssid")
1280 end
1281
1282 function wifinet.network(self)
1283         return _uci_state:get("wifinet", self.sid, "network")
1284 end
1285
1286 function wifinet.id(self)
1287         return self.netid
1288 end
1289
1290 function wifinet.name(self)
1291         return self.sid
1292 end
1293
1294 function wifinet.ifname(self)
1295         local ifname = self.iwinfo.ifname
1296         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1297                 ifname = self.wdev
1298         end
1299         return ifname
1300 end
1301
1302 function wifinet.get_device(self)
1303         if self.iwdata.device then
1304                 return wifidev(self.iwdata.device)
1305         end
1306 end
1307
1308 function wifinet.is_up(self)
1309         return (self.iwdata.up == "1")
1310 end
1311
1312 function wifinet.active_mode(self)
1313         local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1314
1315         if     m == "ap"      then m = "Master"
1316         elseif m == "sta"     then m = "Client"
1317         elseif m == "adhoc"   then m = "Ad-Hoc"
1318         elseif m == "mesh"    then m = "Mesh"
1319         elseif m == "monitor" then m = "Monitor"
1320         end
1321
1322         return m
1323 end
1324
1325 function wifinet.active_mode_i18n(self)
1326         return i18n.translate(self:active_mode())
1327 end
1328
1329 function wifinet.active_ssid(self)
1330         return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1331 end
1332
1333 function wifinet.active_bssid(self)
1334         return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1335 end
1336
1337 function wifinet.active_encryption(self)
1338         local enc = self.iwinfo and self.iwinfo.encryption
1339         return enc and enc.description or "-"
1340 end
1341
1342 function wifinet.assoclist(self)
1343         return self.iwinfo.assoclist or { }
1344 end
1345
1346 function wifinet.frequency(self)
1347         local freq = self.iwinfo.frequency
1348         if freq and freq > 0 then
1349                 return "%.03f" % (freq / 1000)
1350         end
1351 end
1352
1353 function wifinet.bitrate(self)
1354         local rate = self.iwinfo.bitrate
1355         if rate and rate > 0 then
1356                 return (rate / 1000)
1357         end
1358 end
1359
1360 function wifinet.channel(self)
1361         return self.iwinfo.channel or
1362                 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1363 end
1364
1365 function wifinet.signal(self)
1366         return self.iwinfo.signal or 0
1367 end
1368
1369 function wifinet.noise(self)
1370         return self.iwinfo.noise or 0
1371 end
1372
1373 function wifinet.country(self)
1374         return self.iwinfo.country or "00"
1375 end
1376
1377 function wifinet.txpower(self)
1378         local pwr = (self.iwinfo.txpower or 0)
1379         return pwr + self:txpower_offset()
1380 end
1381
1382 function wifinet.txpower_offset(self)
1383         return self.iwinfo.txpower_offset or 0
1384 end
1385
1386 function wifinet.signal_level(self, s, n)
1387         if self:active_bssid() ~= "00:00:00:00:00:00" then
1388                 local signal = s or self:signal()
1389                 local noise  = n or self:noise()
1390
1391                 if signal < 0 and noise < 0 then
1392                         local snr = -1 * (noise - signal)
1393                         return math.floor(snr / 5)
1394                 else
1395                         return 0
1396                 end
1397         else
1398                 return -1
1399         end
1400 end
1401
1402 function wifinet.signal_percent(self)
1403         local qc = self.iwinfo.quality or 0
1404         local qm = self.iwinfo.quality_max or 0
1405
1406         if qc > 0 and qm > 0 then
1407                 return math.floor((100 / qm) * qc)
1408         else
1409                 return 0
1410         end
1411 end
1412
1413 function wifinet.shortname(self)
1414         return "%s %q" %{
1415                 i18n.translate(self:active_mode()),
1416                 self:active_ssid() or self:active_bssid()
1417         }
1418 end
1419
1420 function wifinet.get_i18n(self)
1421         return "%s: %s %q (%s)" %{
1422                 i18n.translate("Wireless Network"),
1423                 i18n.translate(self:active_mode()),
1424                 self:active_ssid() or self:active_bssid(),
1425                 self:ifname()
1426         }
1427 end
1428
1429 function wifinet.adminlink(self)
1430         return dsp.build_url("admin", "network", "wireless", self.netid)
1431 end
1432
1433 function wifinet.get_network(self)
1434         local net = tostring(self.iwdata.network)
1435         if net and _uci_real:get("network", net) == "interface" then
1436                 return network(net)
1437         end
1438 end
1439
1440 function wifinet.get_interface(self)
1441         return interface(self:ifname())
1442 end
1443
1444
1445 -- setup base protocols
1446 _M:register_protocol("static")
1447 _M:register_protocol("dhcp")
1448 _M:register_protocol("none")
1449
1450 -- load protocol extensions
1451 local exts = nfs.dir(utl.libpath() .. "/model/network")
1452 if exts then
1453         local ext
1454         for ext in exts do
1455                 if ext:match("%.lua$") then
1456                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1457                 end
1458         end
1459 end