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