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