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