libs/core: allow ifaces to be part of multiple networks in luci.model.network
[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 local num = { }
637 _uci_real:foreach("wireless", "wifi-iface",
638 function(s)
639 if s.device then
640 num[s.device] = num[s.device]
641 and num[s.device] + 1 or 1
642
643 if s.network == self.sid then
644 ifname = "%s.network%d" %{ s.device, num[s.device] }
645 return false
646 end
647 end
648 end)
649 end
650 return ifname
651 end
652
653 function protocol.proto(self)
654 return "none"
655 end
656
657 function protocol.get_i18n(self)
658 local p = self:proto()
659 if p == "none" then
660 return i18n.translate("Unmanaged")
661 elseif p == "static" then
662 return i18n.translate("Static address")
663 elseif p == "dhcp" then
664 return i18n.translate("DHCP client")
665 else
666 return i18n.translate("Unknown")
667 end
668 end
669
670 function protocol.type(self)
671 return self:_get("type")
672 end
673
674 function protocol.name(self)
675 return self.sid
676 end
677
678 function protocol.uptime(self)
679 return self:_ubus("uptime") or 0
680 end
681
682 function protocol.expires(self)
683 local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
684 local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
685 if a and l then
686 l = l - (nxo.sysinfo().uptime - a)
687 return l > 0 and l or 0
688 end
689 return -1
690 end
691
692 function protocol.metric(self)
693 return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
694 end
695
696 function protocol.ipaddr(self)
697 local addrs = self:_ubus("ipv4-address")
698 return addrs and #addrs > 0 and addrs[1].address
699 end
700
701 function protocol.netmask(self)
702 local addrs = self:_ubus("ipv4-address")
703 return addrs and #addrs > 0 and
704 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
705 end
706
707 function protocol.gwaddr(self)
708 local _, route
709 for _, route in ipairs(self:_ubus("route") or { }) do
710 if route.target == "0.0.0.0" and route.mask == 0 then
711 return route.nexthop
712 end
713 end
714 end
715
716 function protocol.dnsaddrs(self)
717 local dns = { }
718 local _, addr
719 for _, addr in ipairs(self:_ubus("dns-server") or { }) do
720 if not addr:match(":") then
721 dns[#dns+1] = addr
722 end
723 end
724 return dns
725 end
726
727 function protocol.ip6addr(self)
728 local addrs = self:_ubus("ipv6-address")
729 return addrs and #addrs > 0
730 and "%s/%d" %{ addrs[1].address, addrs[1].mask }
731 end
732
733 function protocol.gw6addr(self)
734 local _, route
735 for _, route in ipairs(self:_ubus("route") or { }) do
736 if route.target == "::" and route.mask == 0 then
737 return ipc.IPv6(route.nexthop):string()
738 end
739 end
740 end
741
742 function protocol.dns6addrs(self)
743 local dns = { }
744 local _, addr
745 for _, addr in ipairs(self:_ubus("dns-server") or { }) do
746 if addr:match(":") then
747 dns[#dns+1] = addr
748 end
749 end
750 return dns
751 end
752
753 function protocol.is_bridge(self)
754 return (not self:is_virtual() and self:type() == "bridge")
755 end
756
757 function protocol.opkg_package(self)
758 return nil
759 end
760
761 function protocol.is_installed(self)
762 return true
763 end
764
765 function protocol.is_virtual(self)
766 return false
767 end
768
769 function protocol.is_floating(self)
770 return false
771 end
772
773 function protocol.is_empty(self)
774 if self:is_floating() then
775 return false
776 else
777 local rv = true
778
779 if (self:_get("ifname") or ""):match("%S+") then
780 rv = false
781 end
782
783 _uci_real:foreach("wireless", "wifi-iface",
784 function(s)
785 if s.network == self.sid then
786 rv = false
787 return false
788 end
789 end)
790
791 return rv
792 end
793 end
794
795 function protocol.add_interface(self, ifname)
796 ifname = _M:ifnameof(ifname)
797 if ifname and not self:is_floating() then
798 -- if its a wifi interface, change its network option
799 local wif = _wifi_lookup(ifname)
800 if wif then
801 _append("wireless", wif, "network", self.sid)
802
803 -- add iface to our iface list
804 else
805 _append("network", self.sid, "ifname", ifname)
806 end
807 end
808 end
809
810 function protocol.del_interface(self, ifname)
811 ifname = _M:ifnameof(ifname)
812 if ifname and not self:is_floating() then
813 -- if its a wireless interface, clear its network option
814 local wif = _wifi_lookup(ifname)
815 if wif then _filter("wireless", wif, "network", self.sid) end
816
817 -- remove the interface
818 _filter("network", self.sid, "ifname", ifname)
819 end
820 end
821
822 function protocol.get_interface(self)
823 if self:is_virtual() then
824 _tunnel[self:proto() .. "-" .. self.sid] = true
825 return interface(self:proto() .. "-" .. self.sid, self)
826 elseif self:is_bridge() then
827 _bridge["br-" .. self.sid] = true
828 return interface("br-" .. self.sid, self)
829 else
830 local ifn = nil
831 local num = { }
832 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
833 ifn = ifn:match("^[^:/]+")
834 return ifn and interface(ifn, self)
835 end
836 ifn = nil
837 _uci_real:foreach("wireless", "wifi-iface",
838 function(s)
839 if s.device then
840 num[s.device] = num[s.device] and num[s.device] + 1 or 1
841 if s.network == self.sid then
842 ifn = "%s.network%d" %{ s.device, num[s.device] }
843 return false
844 end
845 end
846 end)
847 return ifn and interface(ifn, self)
848 end
849 end
850
851 function protocol.get_interfaces(self)
852 if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
853 local ifaces = { }
854
855 local ifn
856 local nfs = { }
857 for ifn in utl.imatch(self:get("ifname")) do
858 ifn = ifn:match("^[^:/]+")
859 nfs[ifn] = interface(ifn, self)
860 end
861
862 for ifn in utl.kspairs(nfs) do
863 ifaces[#ifaces+1] = nfs[ifn]
864 end
865
866 local num = { }
867 local wfs = { }
868 _uci_real:foreach("wireless", "wifi-iface",
869 function(s)
870 if s.device then
871 num[s.device] = num[s.device] and num[s.device] + 1 or 1
872 if s.network == self.sid then
873 ifn = "%s.network%d" %{ s.device, num[s.device] }
874 wfs[ifn] = interface(ifn, self)
875 end
876 end
877 end)
878
879 for ifn in utl.kspairs(wfs) do
880 ifaces[#ifaces+1] = wfs[ifn]
881 end
882
883 return ifaces
884 end
885 end
886
887 function protocol.contains_interface(self, ifname)
888 ifname = _M:ifnameof(ifname)
889 if not ifname then
890 return false
891 elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
892 return true
893 elseif self:is_bridge() and "br-" .. self.sid == ifname then
894 return true
895 else
896 local ifn
897 for ifn in utl.imatch(self:get("ifname")) do
898 ifn = ifn:match("[^:]+")
899 if ifn == ifname then
900 return true
901 end
902 end
903
904 local wif = _wifi_lookup(ifname)
905 if wif then
906 local n
907 for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
908 if n == self.sid then
909 return true
910 end
911 end
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 return self:get_networks()[1]
1098 end
1099
1100 function interface.get_networks(self)
1101 if not self.networks then
1102 local nets = { }
1103 local _, net
1104 for _, net in ipairs(_M:get_networks()) do
1105 if net:contains_interface(self.ifname) or
1106 net:ifname() == self.ifname
1107 then
1108 nets[#nets+1] = net
1109 end
1110 end
1111 table.sort(nets, function(a, b) return a.sid < b.sid end)
1112 self.networks = nets
1113 return nets
1114 else
1115 return self.networks
1116 end
1117 end
1118
1119 function interface.get_wifinet(self)
1120 return self.wif
1121 end
1122
1123
1124 wifidev = utl.class()
1125
1126 function wifidev.__init__(self, dev)
1127 self.sid = dev
1128 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1129 end
1130
1131 function wifidev.get(self, opt)
1132 return _get("wireless", self.sid, opt)
1133 end
1134
1135 function wifidev.set(self, opt, val)
1136 return _set("wireless", self.sid, opt, val)
1137 end
1138
1139 function wifidev.name(self)
1140 return self.sid
1141 end
1142
1143 function wifidev.hwmodes(self)
1144 local l = self.iwinfo.hwmodelist
1145 if l and next(l) then
1146 return l
1147 else
1148 return { b = true, g = true }
1149 end
1150 end
1151
1152 function wifidev.get_i18n(self)
1153 local t = "Generic"
1154 if self.iwinfo.type == "wl" then
1155 t = "Broadcom"
1156 elseif self.iwinfo.type == "madwifi" then
1157 t = "Atheros"
1158 end
1159
1160 local m = ""
1161 local l = self:hwmodes()
1162 if l.a then m = m .. "a" end
1163 if l.b then m = m .. "b" end
1164 if l.g then m = m .. "g" end
1165 if l.n then m = m .. "n" end
1166
1167 return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1168 end
1169
1170 function wifidev.is_up(self)
1171 local up = false
1172
1173 _uci_state:foreach("wireless", "wifi-iface",
1174 function(s)
1175 if s.device == self.sid then
1176 if s.up == "1" then
1177 up = true
1178 return false
1179 end
1180 end
1181 end)
1182
1183 return up
1184 end
1185
1186 function wifidev.get_wifinet(self, net)
1187 if _uci_real:get("wireless", net) == "wifi-iface" then
1188 return wifinet(net)
1189 else
1190 local wnet = _wifi_lookup(net)
1191 if wnet then
1192 return wifinet(wnet)
1193 end
1194 end
1195 end
1196
1197 function wifidev.get_wifinets(self)
1198 local nets = { }
1199
1200 _uci_real:foreach("wireless", "wifi-iface",
1201 function(s)
1202 if s.device == self.sid then
1203 nets[#nets+1] = wifinet(s['.name'])
1204 end
1205 end)
1206
1207 return nets
1208 end
1209
1210 function wifidev.add_wifinet(self, options)
1211 options = options or { }
1212 options.device = self.sid
1213
1214 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1215 if wnet then
1216 return wifinet(wnet, options)
1217 end
1218 end
1219
1220 function wifidev.del_wifinet(self, net)
1221 if utl.instanceof(net, wifinet) then
1222 net = net.sid
1223 elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1224 net = _wifi_lookup(net)
1225 end
1226
1227 if net and _uci_real:get("wireless", net, "device") == self.sid then
1228 _uci_real:delete("wireless", net)
1229 return true
1230 end
1231
1232 return false
1233 end
1234
1235
1236 wifinet = utl.class()
1237
1238 function wifinet.__init__(self, net, data)
1239 self.sid = net
1240
1241 local num = { }
1242 local netid
1243 _uci_real:foreach("wireless", "wifi-iface",
1244 function(s)
1245 if s.device then
1246 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1247 if s['.name'] == self.sid then
1248 netid = "%s.network%d" %{ s.device, num[s.device] }
1249 return false
1250 end
1251 end
1252 end)
1253
1254 local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1255
1256 self.netid = netid
1257 self.wdev = dev
1258 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1259 self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1260 _uci_real:get_all("wireless", self.sid) or { }
1261 end
1262
1263 function wifinet.get(self, opt)
1264 return _get("wireless", self.sid, opt)
1265 end
1266
1267 function wifinet.set(self, opt, val)
1268 return _set("wireless", self.sid, opt, val)
1269 end
1270
1271 function wifinet.mode(self)
1272 return _uci_state:get("wireless", self.sid, "mode") or "ap"
1273 end
1274
1275 function wifinet.ssid(self)
1276 return _uci_state:get("wireless", self.sid, "ssid")
1277 end
1278
1279 function wifinet.bssid(self)
1280 return _uci_state:get("wireless", self.sid, "bssid")
1281 end
1282
1283 function wifinet.network(self)
1284 return _uci_state:get("wifinet", self.sid, "network")
1285 end
1286
1287 function wifinet.id(self)
1288 return self.netid
1289 end
1290
1291 function wifinet.name(self)
1292 return self.sid
1293 end
1294
1295 function wifinet.ifname(self)
1296 local ifname = self.iwinfo.ifname
1297 if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1298 ifname = self.wdev
1299 end
1300 return ifname
1301 end
1302
1303 function wifinet.get_device(self)
1304 if self.iwdata.device then
1305 return wifidev(self.iwdata.device)
1306 end
1307 end
1308
1309 function wifinet.is_up(self)
1310 return (self.iwdata.up == "1")
1311 end
1312
1313 function wifinet.active_mode(self)
1314 local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1315
1316 if m == "ap" then m = "Master"
1317 elseif m == "sta" then m = "Client"
1318 elseif m == "adhoc" then m = "Ad-Hoc"
1319 elseif m == "mesh" then m = "Mesh"
1320 elseif m == "monitor" then m = "Monitor"
1321 end
1322
1323 return m
1324 end
1325
1326 function wifinet.active_mode_i18n(self)
1327 return i18n.translate(self:active_mode())
1328 end
1329
1330 function wifinet.active_ssid(self)
1331 return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1332 end
1333
1334 function wifinet.active_bssid(self)
1335 return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1336 end
1337
1338 function wifinet.active_encryption(self)
1339 local enc = self.iwinfo and self.iwinfo.encryption
1340 return enc and enc.description or "-"
1341 end
1342
1343 function wifinet.assoclist(self)
1344 return self.iwinfo.assoclist or { }
1345 end
1346
1347 function wifinet.frequency(self)
1348 local freq = self.iwinfo.frequency
1349 if freq and freq > 0 then
1350 return "%.03f" % (freq / 1000)
1351 end
1352 end
1353
1354 function wifinet.bitrate(self)
1355 local rate = self.iwinfo.bitrate
1356 if rate and rate > 0 then
1357 return (rate / 1000)
1358 end
1359 end
1360
1361 function wifinet.channel(self)
1362 return self.iwinfo.channel or
1363 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1364 end
1365
1366 function wifinet.signal(self)
1367 return self.iwinfo.signal or 0
1368 end
1369
1370 function wifinet.noise(self)
1371 return self.iwinfo.noise or 0
1372 end
1373
1374 function wifinet.country(self)
1375 return self.iwinfo.country or "00"
1376 end
1377
1378 function wifinet.txpower(self)
1379 local pwr = (self.iwinfo.txpower or 0)
1380 return pwr + self:txpower_offset()
1381 end
1382
1383 function wifinet.txpower_offset(self)
1384 return self.iwinfo.txpower_offset or 0
1385 end
1386
1387 function wifinet.signal_level(self, s, n)
1388 if self:active_bssid() ~= "00:00:00:00:00:00" then
1389 local signal = s or self:signal()
1390 local noise = n or self:noise()
1391
1392 if signal < 0 and noise < 0 then
1393 local snr = -1 * (noise - signal)
1394 return math.floor(snr / 5)
1395 else
1396 return 0
1397 end
1398 else
1399 return -1
1400 end
1401 end
1402
1403 function wifinet.signal_percent(self)
1404 local qc = self.iwinfo.quality or 0
1405 local qm = self.iwinfo.quality_max or 0
1406
1407 if qc > 0 and qm > 0 then
1408 return math.floor((100 / qm) * qc)
1409 else
1410 return 0
1411 end
1412 end
1413
1414 function wifinet.shortname(self)
1415 return "%s %q" %{
1416 i18n.translate(self:active_mode()),
1417 self:active_ssid() or self:active_bssid()
1418 }
1419 end
1420
1421 function wifinet.get_i18n(self)
1422 return "%s: %s %q (%s)" %{
1423 i18n.translate("Wireless Network"),
1424 i18n.translate(self:active_mode()),
1425 self:active_ssid() or self:active_bssid(),
1426 self:ifname()
1427 }
1428 end
1429
1430 function wifinet.adminlink(self)
1431 return dsp.build_url("admin", "network", "wireless", self.netid)
1432 end
1433
1434 function wifinet.get_network(self)
1435 return self:get_networks()[1]
1436 end
1437
1438 function wifinet.get_networks(self)
1439 local nets = { }
1440 local net
1441 for net in utl.imatch(tostring(self.iwdata.network)) do
1442 if _uci_real:get("network", net) == "interface" then
1443 nets[#nets+1] = network(net)
1444 end
1445 end
1446 table.sort(nets, function(a, b) return a.sid < b.sid end)
1447 return nets
1448 end
1449
1450 function wifinet.get_interface(self)
1451 return interface(self:ifname())
1452 end
1453
1454
1455 -- setup base protocols
1456 _M:register_protocol("static")
1457 _M:register_protocol("dhcp")
1458 _M:register_protocol("none")
1459
1460 -- load protocol extensions
1461 local exts = nfs.dir(utl.libpath() .. "/model/network")
1462 if exts then
1463 local ext
1464 for ext in exts do
1465 if ext:match("%.lua$") then
1466 require("luci.model.network." .. ext:gsub("%.lua$", ""))
1467 end
1468 end
1469 end