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