New IPv6 integration
[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 = tonumber, tostring, math
24
25 local require = require
26
27 local bus = require "ubus"
28 local nxo = require "nixio"
29 local nfs = require "nixio.fs"
30 local ipc = require "luci.ip"
31 local sys = require "luci.sys"
32 local utl = require "luci.util"
33 local dsp = require "luci.dispatcher"
34 local uci = require "luci.model.uci"
35 local lng = require "luci.i18n"
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", "^gre%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 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_real:set("wireless", s['.name'], "network",
396 table.concat(rest, " "))
397 else
398 _uci_real: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_real:section("network", "interface", new, _uci_real:get_all("network", old))
409
410 if r then
411 _uci_real:foreach("network", "alias",
412 function(s)
413 if s.interface == old then
414 _uci_real:set("network", s['.name'], "interface", new)
415 end
416 end)
417
418 _uci_real:foreach("network", "route",
419 function(s)
420 if s.interface == old then
421 _uci_real:set("network", s['.name'], "interface", new)
422 end
423 end)
424
425 _uci_real:foreach("network", "route6",
426 function(s)
427 if s.interface == old then
428 _uci_real:set("network", s['.name'], "interface", new)
429 end
430 end)
431
432 _uci_real: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_real:set("wireless", s['.name'], "network",
445 table.concat(list, " "))
446 end
447 end)
448
449 _uci_real: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_real: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_real: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_real: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_real: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_real: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_real: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_real:get("wireless", options.device) == "wifi-device"
593 then
594 local wnet = _uci_real: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_real: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(_ubus:objects()) do
611 local net = object:match("^network%.interface%.(.+)")
612 if net then
613 local s = _ubus:call(object, "status", {})
614 if s and s.route then
615 local rt
616 for _, rt in ipairs(s.route) do
617 if 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(_ubus:objects()) do
629 local net = object:match("^network%.interface%.(.+)")
630 if net then
631 local s = _ubus:call(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_real: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_real: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] = _ubus:call("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_real: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 elseif p == "dhcpv6" then
753 return lng.translate("DHCPv6 client")
754 else
755 return lng.translate("Unknown")
756 end
757 end
758
759 function protocol.type(self)
760 return self:_get("type")
761 end
762
763 function protocol.name(self)
764 return self.sid
765 end
766
767 function protocol.uptime(self)
768 return self:_ubus("uptime") or 0
769 end
770
771 function protocol.expires(self)
772 local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
773 local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
774 if a and l then
775 l = l - (nxo.sysinfo().uptime - a)
776 return l > 0 and l or 0
777 end
778 return -1
779 end
780
781 function protocol.metric(self)
782 return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
783 end
784
785 function protocol.ipaddr(self)
786 local addrs = self:_ubus("ipv4-address")
787 return addrs and #addrs > 0 and addrs[1].address
788 end
789
790 function protocol.netmask(self)
791 local addrs = self:_ubus("ipv4-address")
792 return addrs and #addrs > 0 and
793 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
794 end
795
796 function protocol.gwaddr(self)
797 local _, route
798 for _, route in ipairs(self:_ubus("route") or { }) do
799 if route.target == "0.0.0.0" and route.mask == 0 then
800 return route.nexthop
801 end
802 end
803 end
804
805 function protocol.dnsaddrs(self)
806 local dns = { }
807 local _, addr
808 for _, addr in ipairs(self:_ubus("dns-server") or { }) do
809 if not addr:match(":") then
810 dns[#dns+1] = addr
811 end
812 end
813 return dns
814 end
815
816 function protocol.ip6addr(self)
817 local addrs = self:_ubus("ipv6-address")
818 return addrs and #addrs > 0
819 and "%s/%d" %{ addrs[1].address, addrs[1].mask }
820 end
821
822 function protocol.gw6addr(self)
823 local _, route
824 for _, route in ipairs(self:_ubus("route") or { }) do
825 if route.target == "::" and route.mask == 0 then
826 return ipc.IPv6(route.nexthop):string()
827 end
828 end
829 end
830
831 function protocol.dns6addrs(self)
832 local dns = { }
833 local _, addr
834 for _, addr in ipairs(self:_ubus("dns-server") or { }) do
835 if addr:match(":") then
836 dns[#dns+1] = addr
837 end
838 end
839 return dns
840 end
841
842 function protocol.is_bridge(self)
843 return (not self:is_virtual() and self:type() == "bridge")
844 end
845
846 function protocol.opkg_package(self)
847 return nil
848 end
849
850 function protocol.is_installed(self)
851 return true
852 end
853
854 function protocol.is_virtual(self)
855 return false
856 end
857
858 function protocol.is_floating(self)
859 return false
860 end
861
862 function protocol.is_empty(self)
863 if self:is_floating() then
864 return false
865 else
866 local rv = true
867
868 if (self:_get("ifname") or ""):match("%S+") then
869 rv = false
870 end
871
872 _uci_real:foreach("wireless", "wifi-iface",
873 function(s)
874 local n
875 for n in utl.imatch(s.network) do
876 if n == self.sid then
877 rv = false
878 return false
879 end
880 end
881 end)
882
883 return rv
884 end
885 end
886
887 function protocol.add_interface(self, ifname)
888 ifname = _M:ifnameof(ifname)
889 if ifname and not self:is_floating() then
890 -- if its a wifi interface, change its network option
891 local wif = _wifi_lookup(ifname)
892 if wif then
893 _append("wireless", wif, "network", self.sid)
894
895 -- add iface to our iface list
896 else
897 _append("network", self.sid, "ifname", ifname)
898 end
899 end
900 end
901
902 function protocol.del_interface(self, ifname)
903 ifname = _M:ifnameof(ifname)
904 if ifname and not self:is_floating() then
905 -- if its a wireless interface, clear its network option
906 local wif = _wifi_lookup(ifname)
907 if wif then _filter("wireless", wif, "network", self.sid) end
908
909 -- remove the interface
910 _filter("network", self.sid, "ifname", ifname)
911 end
912 end
913
914 function protocol.get_interface(self)
915 if self:is_virtual() then
916 _tunnel[self:proto() .. "-" .. self.sid] = true
917 return interface(self:proto() .. "-" .. self.sid, self)
918 elseif self:is_bridge() then
919 _bridge["br-" .. self.sid] = true
920 return interface("br-" .. self.sid, self)
921 else
922 local ifn = nil
923 local num = { }
924 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
925 ifn = ifn:match("^[^:/]+")
926 return ifn and interface(ifn, self)
927 end
928 ifn = nil
929 _uci_real:foreach("wireless", "wifi-iface",
930 function(s)
931 if s.device then
932 num[s.device] = num[s.device] and num[s.device] + 1 or 1
933
934 local net
935 for net in utl.imatch(s.network) do
936 if net == self.sid then
937 ifn = "%s.network%d" %{ s.device, num[s.device] }
938 return false
939 end
940 end
941 end
942 end)
943 return ifn and interface(ifn, self)
944 end
945 end
946
947 function protocol.get_interfaces(self)
948 if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
949 local ifaces = { }
950
951 local ifn
952 local nfs = { }
953 for ifn in utl.imatch(self:get("ifname")) do
954 ifn = ifn:match("^[^:/]+")
955 nfs[ifn] = interface(ifn, self)
956 end
957
958 for ifn in utl.kspairs(nfs) do
959 ifaces[#ifaces+1] = nfs[ifn]
960 end
961
962 local num = { }
963 local wfs = { }
964 _uci_real:foreach("wireless", "wifi-iface",
965 function(s)
966 if s.device then
967 num[s.device] = num[s.device] and num[s.device] + 1 or 1
968
969 local net
970 for net in utl.imatch(s.network) do
971 if net == self.sid then
972 ifn = "%s.network%d" %{ s.device, num[s.device] }
973 wfs[ifn] = interface(ifn, self)
974 end
975 end
976 end
977 end)
978
979 for ifn in utl.kspairs(wfs) do
980 ifaces[#ifaces+1] = wfs[ifn]
981 end
982
983 return ifaces
984 end
985 end
986
987 function protocol.contains_interface(self, ifname)
988 ifname = _M:ifnameof(ifname)
989 if not ifname then
990 return false
991 elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
992 return true
993 elseif self:is_bridge() and "br-" .. self.sid == ifname then
994 return true
995 else
996 local ifn
997 for ifn in utl.imatch(self:get("ifname")) do
998 ifn = ifn:match("[^:]+")
999 if ifn == ifname then
1000 return true
1001 end
1002 end
1003
1004 local wif = _wifi_lookup(ifname)
1005 if wif then
1006 local n
1007 for n in utl.imatch(_uci_real:get("wireless", wif, "network")) do
1008 if n == self.sid then
1009 return true
1010 end
1011 end
1012 end
1013 end
1014
1015 return false
1016 end
1017
1018 function protocol.adminlink(self)
1019 return dsp.build_url("admin", "network", "network", self.sid)
1020 end
1021
1022
1023 interface = utl.class()
1024
1025 function interface.__init__(self, ifname, network)
1026 local wif = _wifi_lookup(ifname)
1027 if wif then
1028 self.wif = wifinet(wif)
1029 self.ifname = _uci_state:get("wireless", wif, "ifname")
1030 end
1031
1032 self.ifname = self.ifname or ifname
1033 self.dev = _interfaces[self.ifname]
1034 self.network = network
1035 end
1036
1037 function interface._ubus(self, field)
1038 if not _ubusdevcache[self.ifname] then
1039 _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
1040 { name = self.ifname })
1041 end
1042 if _ubusdevcache[self.ifname] and field then
1043 return _ubusdevcache[self.ifname][field]
1044 end
1045 return _ubusdevcache[self.ifname]
1046 end
1047
1048 function interface.name(self)
1049 return self.wif and self.wif:ifname() or self.ifname
1050 end
1051
1052 function interface.mac(self)
1053 return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
1054 end
1055
1056 function interface.ipaddrs(self)
1057 return self.dev and self.dev.ipaddrs or { }
1058 end
1059
1060 function interface.ip6addrs(self)
1061 return self.dev and self.dev.ip6addrs or { }
1062 end
1063
1064 function interface.type(self)
1065 if self.wif or _wifi_iface(self.ifname) then
1066 return "wifi"
1067 elseif _bridge[self.ifname] then
1068 return "bridge"
1069 elseif _tunnel[self.ifname] then
1070 return "tunnel"
1071 elseif self.ifname:match("%.") then
1072 return "vlan"
1073 elseif _switch[self.ifname] then
1074 return "switch"
1075 else
1076 return "ethernet"
1077 end
1078 end
1079
1080 function interface.shortname(self)
1081 if self.wif then
1082 return "%s %q" %{
1083 self.wif:active_mode(),
1084 self.wif:active_ssid() or self.wif:active_bssid()
1085 }
1086 else
1087 return self.ifname
1088 end
1089 end
1090
1091 function interface.get_i18n(self)
1092 if self.wif then
1093 return "%s: %s %q" %{
1094 lng.translate("Wireless Network"),
1095 self.wif:active_mode(),
1096 self.wif:active_ssid() or self.wif:active_bssid()
1097 }
1098 else
1099 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1100 end
1101 end
1102
1103 function interface.get_type_i18n(self)
1104 local x = self:type()
1105 if x == "wifi" then
1106 return lng.translate("Wireless Adapter")
1107 elseif x == "bridge" then
1108 return lng.translate("Bridge")
1109 elseif x == "switch" then
1110 return lng.translate("Ethernet Switch")
1111 elseif x == "vlan" then
1112 return lng.translate("VLAN Interface")
1113 elseif x == "tunnel" then
1114 return lng.translate("Tunnel Interface")
1115 else
1116 return lng.translate("Ethernet Adapter")
1117 end
1118 end
1119
1120 function interface.adminlink(self)
1121 if self.wif then
1122 return self.wif:adminlink()
1123 end
1124 end
1125
1126 function interface.ports(self)
1127 local members = self:_ubus("bridge-members")
1128 if members then
1129 local _, iface
1130 local ifaces = { }
1131 for _, iface in ipairs(members) do
1132 ifaces[#ifaces+1] = interface(iface)
1133 end
1134 end
1135 end
1136
1137 function interface.bridge_id(self)
1138 if self.br then
1139 return self.br.id
1140 else
1141 return nil
1142 end
1143 end
1144
1145 function interface.bridge_stp(self)
1146 if self.br then
1147 return self.br.stp
1148 else
1149 return false
1150 end
1151 end
1152
1153 function interface.is_up(self)
1154 if self.wif then
1155 return self.wif:is_up()
1156 else
1157 return self:_ubus("up") or false
1158 end
1159 end
1160
1161 function interface.is_bridge(self)
1162 return (self:type() == "bridge")
1163 end
1164
1165 function interface.is_bridgeport(self)
1166 return self.dev and self.dev.bridge and true or false
1167 end
1168
1169 function interface.tx_bytes(self)
1170 local stat = self:_ubus("statistics")
1171 return stat and stat.tx_bytes or 0
1172 end
1173
1174 function interface.rx_bytes(self)
1175 local stat = self:_ubus("statistics")
1176 return stat and stat.rx_bytes or 0
1177 end
1178
1179 function interface.tx_packets(self)
1180 local stat = self:_ubus("statistics")
1181 return stat and stat.tx_packets or 0
1182 end
1183
1184 function interface.rx_packets(self)
1185 local stat = self:_ubus("statistics")
1186 return stat and stat.rx_packets or 0
1187 end
1188
1189 function interface.get_network(self)
1190 return self:get_networks()[1]
1191 end
1192
1193 function interface.get_networks(self)
1194 if not self.networks then
1195 local nets = { }
1196 local _, net
1197 for _, net in ipairs(_M:get_networks()) do
1198 if net:contains_interface(self.ifname) or
1199 net:ifname() == self.ifname
1200 then
1201 nets[#nets+1] = net
1202 end
1203 end
1204 table.sort(nets, function(a, b) return a.sid < b.sid end)
1205 self.networks = nets
1206 return nets
1207 else
1208 return self.networks
1209 end
1210 end
1211
1212 function interface.get_wifinet(self)
1213 return self.wif
1214 end
1215
1216
1217 wifidev = utl.class()
1218
1219 function wifidev.__init__(self, dev)
1220 self.sid = dev
1221 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1222 end
1223
1224 function wifidev.get(self, opt)
1225 return _get("wireless", self.sid, opt)
1226 end
1227
1228 function wifidev.set(self, opt, val)
1229 return _set("wireless", self.sid, opt, val)
1230 end
1231
1232 function wifidev.name(self)
1233 return self.sid
1234 end
1235
1236 function wifidev.hwmodes(self)
1237 local l = self.iwinfo.hwmodelist
1238 if l and next(l) then
1239 return l
1240 else
1241 return { b = true, g = true }
1242 end
1243 end
1244
1245 function wifidev.get_i18n(self)
1246 local t = "Generic"
1247 if self.iwinfo.type == "wl" then
1248 t = "Broadcom"
1249 elseif self.iwinfo.type == "madwifi" then
1250 t = "Atheros"
1251 end
1252
1253 local m = ""
1254 local l = self:hwmodes()
1255 if l.a then m = m .. "a" end
1256 if l.b then m = m .. "b" end
1257 if l.g then m = m .. "g" end
1258 if l.n then m = m .. "n" end
1259
1260 return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1261 end
1262
1263 function wifidev.is_up(self)
1264 local up = false
1265
1266 _uci_state:foreach("wireless", "wifi-iface",
1267 function(s)
1268 if s.device == self.sid then
1269 if s.up == "1" then
1270 up = true
1271 return false
1272 end
1273 end
1274 end)
1275
1276 return up
1277 end
1278
1279 function wifidev.get_wifinet(self, net)
1280 if _uci_real:get("wireless", net) == "wifi-iface" then
1281 return wifinet(net)
1282 else
1283 local wnet = _wifi_lookup(net)
1284 if wnet then
1285 return wifinet(wnet)
1286 end
1287 end
1288 end
1289
1290 function wifidev.get_wifinets(self)
1291 local nets = { }
1292
1293 _uci_real:foreach("wireless", "wifi-iface",
1294 function(s)
1295 if s.device == self.sid then
1296 nets[#nets+1] = wifinet(s['.name'])
1297 end
1298 end)
1299
1300 return nets
1301 end
1302
1303 function wifidev.add_wifinet(self, options)
1304 options = options or { }
1305 options.device = self.sid
1306
1307 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1308 if wnet then
1309 return wifinet(wnet, options)
1310 end
1311 end
1312
1313 function wifidev.del_wifinet(self, net)
1314 if utl.instanceof(net, wifinet) then
1315 net = net.sid
1316 elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1317 net = _wifi_lookup(net)
1318 end
1319
1320 if net and _uci_real:get("wireless", net, "device") == self.sid then
1321 _uci_real:delete("wireless", net)
1322 return true
1323 end
1324
1325 return false
1326 end
1327
1328
1329 wifinet = utl.class()
1330
1331 function wifinet.__init__(self, net, data)
1332 self.sid = net
1333
1334 local num = { }
1335 local netid
1336 _uci_real:foreach("wireless", "wifi-iface",
1337 function(s)
1338 if s.device then
1339 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1340 if s['.name'] == self.sid then
1341 netid = "%s.network%d" %{ s.device, num[s.device] }
1342 return false
1343 end
1344 end
1345 end)
1346
1347 local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1348
1349 self.netid = netid
1350 self.wdev = dev
1351 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1352 self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1353 _uci_real:get_all("wireless", self.sid) or { }
1354 end
1355
1356 function wifinet.get(self, opt)
1357 return _get("wireless", self.sid, opt)
1358 end
1359
1360 function wifinet.set(self, opt, val)
1361 return _set("wireless", self.sid, opt, val)
1362 end
1363
1364 function wifinet.mode(self)
1365 return _uci_state:get("wireless", self.sid, "mode") or "ap"
1366 end
1367
1368 function wifinet.ssid(self)
1369 return _uci_state:get("wireless", self.sid, "ssid")
1370 end
1371
1372 function wifinet.bssid(self)
1373 return _uci_state:get("wireless", self.sid, "bssid")
1374 end
1375
1376 function wifinet.network(self)
1377 return _uci_state:get("wifinet", self.sid, "network")
1378 end
1379
1380 function wifinet.id(self)
1381 return self.netid
1382 end
1383
1384 function wifinet.name(self)
1385 return self.sid
1386 end
1387
1388 function wifinet.ifname(self)
1389 local ifname = self.iwinfo.ifname
1390 if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1391 ifname = self.wdev
1392 end
1393 return ifname
1394 end
1395
1396 function wifinet.get_device(self)
1397 if self.iwdata.device then
1398 return wifidev(self.iwdata.device)
1399 end
1400 end
1401
1402 function wifinet.is_up(self)
1403 return (self.iwdata.up == "1")
1404 end
1405
1406 function wifinet.active_mode(self)
1407 local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1408
1409 if m == "ap" then m = "Master"
1410 elseif m == "sta" then m = "Client"
1411 elseif m == "adhoc" then m = "Ad-Hoc"
1412 elseif m == "mesh" then m = "Mesh"
1413 elseif m == "monitor" then m = "Monitor"
1414 end
1415
1416 return m
1417 end
1418
1419 function wifinet.active_mode_i18n(self)
1420 return lng.translate(self:active_mode())
1421 end
1422
1423 function wifinet.active_ssid(self)
1424 return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1425 end
1426
1427 function wifinet.active_bssid(self)
1428 return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1429 end
1430
1431 function wifinet.active_encryption(self)
1432 local enc = self.iwinfo and self.iwinfo.encryption
1433 return enc and enc.description or "-"
1434 end
1435
1436 function wifinet.assoclist(self)
1437 return self.iwinfo.assoclist or { }
1438 end
1439
1440 function wifinet.frequency(self)
1441 local freq = self.iwinfo.frequency
1442 if freq and freq > 0 then
1443 return "%.03f" % (freq / 1000)
1444 end
1445 end
1446
1447 function wifinet.bitrate(self)
1448 local rate = self.iwinfo.bitrate
1449 if rate and rate > 0 then
1450 return (rate / 1000)
1451 end
1452 end
1453
1454 function wifinet.channel(self)
1455 return self.iwinfo.channel or
1456 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1457 end
1458
1459 function wifinet.signal(self)
1460 return self.iwinfo.signal or 0
1461 end
1462
1463 function wifinet.noise(self)
1464 return self.iwinfo.noise or 0
1465 end
1466
1467 function wifinet.country(self)
1468 return self.iwinfo.country or "00"
1469 end
1470
1471 function wifinet.txpower(self)
1472 local pwr = (self.iwinfo.txpower or 0)
1473 return pwr + self:txpower_offset()
1474 end
1475
1476 function wifinet.txpower_offset(self)
1477 return self.iwinfo.txpower_offset or 0
1478 end
1479
1480 function wifinet.signal_level(self, s, n)
1481 if self:active_bssid() ~= "00:00:00:00:00:00" then
1482 local signal = s or self:signal()
1483 local noise = n or self:noise()
1484
1485 if signal < 0 and noise < 0 then
1486 local snr = -1 * (noise - signal)
1487 return math.floor(snr / 5)
1488 else
1489 return 0
1490 end
1491 else
1492 return -1
1493 end
1494 end
1495
1496 function wifinet.signal_percent(self)
1497 local qc = self.iwinfo.quality or 0
1498 local qm = self.iwinfo.quality_max or 0
1499
1500 if qc > 0 and qm > 0 then
1501 return math.floor((100 / qm) * qc)
1502 else
1503 return 0
1504 end
1505 end
1506
1507 function wifinet.shortname(self)
1508 return "%s %q" %{
1509 lng.translate(self:active_mode()),
1510 self:active_ssid() or self:active_bssid()
1511 }
1512 end
1513
1514 function wifinet.get_i18n(self)
1515 return "%s: %s %q (%s)" %{
1516 lng.translate("Wireless Network"),
1517 lng.translate(self:active_mode()),
1518 self:active_ssid() or self:active_bssid(),
1519 self:ifname()
1520 }
1521 end
1522
1523 function wifinet.adminlink(self)
1524 return dsp.build_url("admin", "network", "wireless", self.netid)
1525 end
1526
1527 function wifinet.get_network(self)
1528 return self:get_networks()[1]
1529 end
1530
1531 function wifinet.get_networks(self)
1532 local nets = { }
1533 local net
1534 for net in utl.imatch(tostring(self.iwdata.network)) do
1535 if _uci_real:get("network", net) == "interface" then
1536 nets[#nets+1] = network(net)
1537 end
1538 end
1539 table.sort(nets, function(a, b) return a.sid < b.sid end)
1540 return nets
1541 end
1542
1543 function wifinet.get_interface(self)
1544 return interface(self:ifname())
1545 end
1546
1547
1548 -- setup base protocols
1549 _M:register_protocol("static")
1550 _M:register_protocol("dhcp")
1551
1552 local dhcpv6 = _M:register_protocol("dhcpv6")
1553 function dhcpv6.is_installed(self)
1554 return nfs.access("/lib/netifd/proto/dhcpv6.sh")
1555 end
1556
1557 _M:register_protocol("none")
1558
1559
1560 -- load protocol extensions
1561 local exts = nfs.dir(utl.libpath() .. "/model/network")
1562 if exts then
1563 local ext
1564 for ext in exts do
1565 if ext:match("%.lua$") then
1566 require("luci.model.network." .. ext:gsub("%.lua$", ""))
1567 end
1568 end
1569 end