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