39145748f79352db51a87240ec2c157c4bc46141
[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, i18n
24 = tonumber, tostring, math, luci.i18n
25
26 local require = require
27
28 local bus = require "ubus"
29 local nxo = require "nixio"
30 local nfs = require "nixio.fs"
31 local ipc = require "luci.ip"
32 local sys = require "luci.sys"
33 local utl = require "luci.util"
34 local dsp = require "luci.dispatcher"
35 local uci = require "luci.model.uci"
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", "^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 if s.network == n then
388 _uci_real:delete("wireless", s['.name'], "network")
389 end
390 end)
391 end
392 return r
393 end
394
395 function rename_network(self, old, new)
396 local r
397 if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
398 r = _uci_real:section("network", "interface", new, _uci_real:get_all("network", old))
399
400 if r then
401 _uci_real:foreach("network", "alias",
402 function(s)
403 if s.interface == old then
404 _uci_real:set("network", s['.name'], "interface", new)
405 end
406 end)
407
408 _uci_real:foreach("network", "route",
409 function(s)
410 if s.interface == old then
411 _uci_real:set("network", s['.name'], "interface", new)
412 end
413 end)
414
415 _uci_real:foreach("network", "route6",
416 function(s)
417 if s.interface == old then
418 _uci_real:set("network", s['.name'], "interface", new)
419 end
420 end)
421
422 _uci_real:foreach("wireless", "wifi-iface",
423 function(s)
424 if s.network == old then
425 _uci_real:set("wireless", s['.name'], "network", new)
426 end
427 end)
428
429 _uci_real:delete("network", old)
430 end
431 end
432 return r or false
433 end
434
435 function get_interface(self, i)
436 if _interfaces[i] or _wifi_iface(i) then
437 return interface(i)
438 else
439 local ifc
440 local num = { }
441 _uci_real:foreach("wireless", "wifi-iface",
442 function(s)
443 if s.device then
444 num[s.device] = num[s.device] and num[s.device] + 1 or 1
445 if s['.name'] == i then
446 ifc = interface(
447 "%s.network%d" %{s.device, num[s.device] })
448 return false
449 end
450 end
451 end)
452 return ifc
453 end
454 end
455
456 function get_interfaces(self)
457 local iface
458 local ifaces = { }
459 local seen = { }
460 local nfs = { }
461 local baseof = { }
462
463 -- find normal interfaces
464 _uci_real:foreach("network", "interface",
465 function(s)
466 for iface in utl.imatch(s.ifname) do
467 if not _iface_ignore(iface) and not _wifi_iface(iface) then
468 seen[iface] = true
469 nfs[iface] = interface(iface)
470 end
471 end
472 end)
473
474 for iface in utl.kspairs(_interfaces) do
475 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
476 nfs[iface] = interface(iface)
477 end
478 end
479
480 -- find vlan interfaces
481 _uci_real:foreach("network", "switch_vlan",
482 function(s)
483 if not s.device then
484 return
485 end
486
487 local base = baseof[s.device]
488 if not base then
489 if not s.device:match("^eth%d") then
490 local l
491 for l in utl.execi("swconfig dev %q help 2>/dev/null" % s.device) do
492 if not base then
493 base = l:match("^%w+: (%w+)")
494 end
495 end
496 if not base or not base:match("^eth%d") then
497 base = "eth0"
498 end
499 else
500 base = s.device
501 end
502 baseof[s.device] = base
503 end
504
505 local vid = tonumber(s.vid or s.vlan)
506 if vid ~= nil and vid >= 0 and vid <= 4095 then
507 local iface = "%s.%d" %{ base, vid }
508 if not seen[iface] then
509 seen[iface] = true
510 nfs[iface] = interface(iface)
511 end
512 end
513 end)
514
515 for iface in utl.kspairs(nfs) do
516 ifaces[#ifaces+1] = nfs[iface]
517 end
518
519 -- find wifi interfaces
520 local num = { }
521 local wfs = { }
522 _uci_real:foreach("wireless", "wifi-iface",
523 function(s)
524 if s.device then
525 num[s.device] = num[s.device] and num[s.device] + 1 or 1
526 local i = "%s.network%d" %{ s.device, num[s.device] }
527 wfs[i] = interface(i)
528 end
529 end)
530
531 for iface in utl.kspairs(wfs) do
532 ifaces[#ifaces+1] = wfs[iface]
533 end
534
535 return ifaces
536 end
537
538 function ignore_interface(self, x)
539 return _iface_ignore(x)
540 end
541
542 function get_wifidev(self, dev)
543 if _uci_real:get("wireless", dev) == "wifi-device" then
544 return wifidev(dev)
545 end
546 end
547
548 function get_wifidevs(self)
549 local devs = { }
550 local wfd = { }
551
552 _uci_real:foreach("wireless", "wifi-device",
553 function(s) wfd[#wfd+1] = s['.name'] end)
554
555 local dev
556 for _, dev in utl.vspairs(wfd) do
557 devs[#devs+1] = wifidev(dev)
558 end
559
560 return devs
561 end
562
563 function get_wifinet(self, net)
564 local wnet = _wifi_lookup(net)
565 if wnet then
566 return wifinet(wnet)
567 end
568 end
569
570 function add_wifinet(self, net, options)
571 if type(options) == "table" and options.device and
572 _uci_real:get("wireless", options.device) == "wifi-device"
573 then
574 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
575 return wifinet(wnet)
576 end
577 end
578
579 function del_wifinet(self, net)
580 local wnet = _wifi_lookup(net)
581 if wnet then
582 _uci_real:delete("wireless", wnet)
583 return true
584 end
585 return false
586 end
587
588
589 function network(name, proto)
590 if name then
591 local p = proto or _uci_real:get("network", name, "proto")
592 local c = p and _protocols[p] or protocol
593 return c(name)
594 end
595 end
596
597 function protocol.__init__(self, name)
598 self.sid = name
599 end
600
601 function protocol._get(self, opt)
602 local v = _uci_real:get("network", self.sid, opt)
603 if type(v) == "table" then
604 return table.concat(v, " ")
605 end
606 return v or ""
607 end
608
609 function protocol._ubus(self, field)
610 if not _ubusnetcache[self.sid] then
611 _ubusnetcache[self.sid] = _ubus:call("network.interface.%s" % self.sid,
612 "status", { })
613 end
614 if _ubusnetcache[self.sid] and field then
615 return _ubusnetcache[self.sid][field]
616 end
617 return _ubusnetcache[self.sid]
618 end
619
620 function protocol.get(self, opt)
621 return _get("network", self.sid, opt)
622 end
623
624 function protocol.set(self, opt, val)
625 return _set("network", self.sid, opt, val)
626 end
627
628 function protocol.ifname(self)
629 local ifname
630 if self:is_floating() then
631 ifname = self:_ubus("l3_device")
632 else
633 ifname = self:_ubus("device")
634 end
635 if not ifname then
636 local num = { }
637 _uci_real:foreach("wireless", "wifi-iface",
638 function(s)
639 if s.device then
640 num[s.device] = num[s.device]
641 and num[s.device] + 1 or 1
642
643 if s.network == self.sid then
644 ifname = "%s.network%d" %{ s.device, num[s.device] }
645 return false
646 end
647 end
648 end)
649 end
650 return ifname
651 end
652
653 function protocol.proto(self)
654 return "none"
655 end
656
657 function protocol.get_i18n(self)
658 local p = self:proto()
659 if p == "none" then
660 return i18n.translate("Unmanaged")
661 elseif p == "static" then
662 return i18n.translate("Static address")
663 elseif p == "dhcp" then
664 return i18n.translate("DHCP client")
665 else
666 return i18n.translate("Unknown")
667 end
668 end
669
670 function protocol.type(self)
671 return self:_get("type")
672 end
673
674 function protocol.name(self)
675 return self.sid
676 end
677
678 function protocol.uptime(self)
679 return self:_ubus("uptime") or 0
680 end
681
682 function protocol.expires(self)
683 local a = tonumber(_uci_state:get("network", self.sid, "lease_acquired"))
684 local l = tonumber(_uci_state:get("network", self.sid, "lease_lifetime"))
685 if a and l then
686 l = l - (nxo.sysinfo().uptime - a)
687 return l > 0 and l or 0
688 end
689 return -1
690 end
691
692 function protocol.metric(self)
693 return tonumber(_uci_state:get("network", self.sid, "metric")) or 0
694 end
695
696 function protocol.ipaddr(self)
697 local addrs = self:_ubus("ipv4-address")
698 return addrs and #addrs > 0 and addrs[1].address
699 end
700
701 function protocol.netmask(self)
702 local addrs = self:_ubus("ipv4-address")
703 return addrs and #addrs > 0 and
704 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
705 end
706
707 function protocol.gwaddr(self)
708 local _, route
709 for _, route in ipairs(self:_ubus("route") or { }) do
710 if route.target == "0.0.0.0" and route.mask == 0 then
711 return route.nexthop
712 end
713 end
714 end
715
716 function protocol.dnsaddrs(self)
717 local dns = { }
718 local _, addr
719 for _, addr in ipairs(self:_ubus("dns-server") or { }) do
720 if not addr:match(":") then
721 dns[#dns+1] = addr
722 end
723 end
724 return dns
725 end
726
727 function protocol.ip6addr(self)
728 local addrs = self:_ubus("ipv6-address")
729 return addrs and #addrs > 0
730 and "%s/%d" %{ addrs[1].address, addrs[1].mask }
731 end
732
733 function protocol.gw6addr(self)
734 local _, route
735 for _, route in ipairs(self:_ubus("route") or { }) do
736 if route.target == "::" and route.mask == 0 then
737 return ipc.IPv6(route.nexthop):string()
738 end
739 end
740 end
741
742 function protocol.dns6addrs(self)
743 local dns = { }
744 local _, addr
745 for _, addr in ipairs(self:_ubus("dns-server") or { }) do
746 if addr:match(":") then
747 dns[#dns+1] = addr
748 end
749 end
750 return dns
751 end
752
753 function protocol.is_bridge(self)
754 return (not self:is_virtual() and self:type() == "bridge")
755 end
756
757 function protocol.opkg_package(self)
758 return nil
759 end
760
761 function protocol.is_installed(self)
762 return true
763 end
764
765 function protocol.is_virtual(self)
766 return false
767 end
768
769 function protocol.is_floating(self)
770 return false
771 end
772
773 function protocol.is_empty(self)
774 if self:is_floating() then
775 return false
776 else
777 local rv = true
778
779 if (self:_get("ifname") or ""):match("%S+") then
780 rv = false
781 end
782
783 _uci_real:foreach("wireless", "wifi-iface",
784 function(s)
785 if s.network == self.sid then
786 rv = false
787 return false
788 end
789 end)
790
791 return rv
792 end
793 end
794
795 function protocol.add_interface(self, ifname)
796 ifname = _M:ifnameof(ifname)
797 if ifname and not self:is_floating() then
798 -- remove the interface from all ifaces
799 _uci_real:foreach("network", "interface",
800 function(s)
801 _filter("network", s['.name'], "ifname", ifname)
802 end)
803
804 -- if its a wifi interface, change its network option
805 local wif = _wifi_lookup(ifname)
806 if wif then
807 _uci_real:set("wireless", wif, "network", self.sid)
808
809 -- add iface to our iface list
810 else
811 _append("network", self.sid, "ifname", ifname)
812 end
813 end
814 end
815
816 function protocol.del_interface(self, ifname)
817 ifname = _M:ifnameof(ifname)
818 if ifname and not self:is_floating() then
819 -- if its a wireless interface, clear its network option
820 local wif = _wifi_lookup(ifname)
821 if wif then _uci_real:delete("wireless", wif, "network") end
822
823 -- remove the interface
824 _filter("network", self.sid, "ifname", ifname)
825 end
826 end
827
828 function protocol.get_interface(self)
829 if self:is_virtual() then
830 _tunnel[self:proto() .. "-" .. self.sid] = true
831 return interface(self:proto() .. "-" .. self.sid, self)
832 elseif self:is_bridge() then
833 _bridge["br-" .. self.sid] = true
834 return interface("br-" .. self.sid, self)
835 else
836 local ifn = nil
837 local num = { }
838 for ifn in utl.imatch(_uci_real:get("network", self.sid, "ifname")) do
839 ifn = ifn:match("^[^:/]+")
840 return ifn and interface(ifn, self)
841 end
842 ifn = nil
843 _uci_real:foreach("wireless", "wifi-iface",
844 function(s)
845 if s.device then
846 num[s.device] = num[s.device] and num[s.device] + 1 or 1
847 if s.network == self.sid then
848 ifn = "%s.network%d" %{ s.device, num[s.device] }
849 return false
850 end
851 end
852 end)
853 return ifn and interface(ifn, self)
854 end
855 end
856
857 function protocol.get_interfaces(self)
858 if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
859 local ifaces = { }
860
861 local ifn
862 local nfs = { }
863 for ifn in utl.imatch(self:get("ifname")) do
864 ifn = ifn:match("^[^:/]+")
865 nfs[ifn] = interface(ifn, self)
866 end
867
868 for ifn in utl.kspairs(nfs) do
869 ifaces[#ifaces+1] = nfs[ifn]
870 end
871
872 local num = { }
873 local wfs = { }
874 _uci_real:foreach("wireless", "wifi-iface",
875 function(s)
876 if s.device then
877 num[s.device] = num[s.device] and num[s.device] + 1 or 1
878 if s.network == self.sid then
879 ifn = "%s.network%d" %{ s.device, num[s.device] }
880 wfs[ifn] = interface(ifn, self)
881 end
882 end
883 end)
884
885 for ifn in utl.kspairs(wfs) do
886 ifaces[#ifaces+1] = wfs[ifn]
887 end
888
889 return ifaces
890 end
891 end
892
893 function protocol.contains_interface(self, ifname)
894 ifname = _M:ifnameof(ifname)
895 if not ifname then
896 return false
897 elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
898 return true
899 elseif self:is_bridge() and "br-" .. self.sid == ifname then
900 return true
901 else
902 local ifn
903 for ifn in utl.imatch(self:get("ifname")) do
904 ifn = ifn:match("[^:]+")
905 if ifn == ifname then
906 return true
907 end
908 end
909
910 local wif = _wifi_lookup(ifname)
911 if wif then
912 return (_uci_real:get("wireless", wif, "network") == self.sid)
913 end
914 end
915
916 return false
917 end
918
919 function protocol.adminlink(self)
920 return dsp.build_url("admin", "network", "network", self.sid)
921 end
922
923
924 interface = utl.class()
925
926 function interface.__init__(self, ifname, network)
927 local wif = _wifi_lookup(ifname)
928 if wif then
929 self.wif = wifinet(wif)
930 self.ifname = _uci_state:get("wireless", wif, "ifname")
931 end
932
933 self.ifname = self.ifname or ifname
934 self.dev = _interfaces[self.ifname]
935 self.network = network
936 end
937
938 function interface._ubus(self, field)
939 if not _ubusdevcache[self.ifname] then
940 _ubusdevcache[self.ifname] = _ubus:call("network.device", "status",
941 { name = self.ifname })
942 end
943 if _ubusdevcache[self.ifname] and field then
944 return _ubusdevcache[self.ifname][field]
945 end
946 return _ubusdevcache[self.ifname]
947 end
948
949 function interface.name(self)
950 return self.wif and self.wif:ifname() or self.ifname
951 end
952
953 function interface.mac(self)
954 return (self:_ubus("macaddr") or "00:00:00:00:00:00"):upper()
955 end
956
957 function interface.ipaddrs(self)
958 return self.dev and self.dev.ipaddrs or { }
959 end
960
961 function interface.ip6addrs(self)
962 return self.dev and self.dev.ip6addrs or { }
963 end
964
965 function interface.type(self)
966 if self.wif or _wifi_iface(self.ifname) then
967 return "wifi"
968 elseif _bridge[self.ifname] then
969 return "bridge"
970 elseif _tunnel[self.ifname] then
971 return "tunnel"
972 elseif self.ifname:match("%.") then
973 return "vlan"
974 elseif _switch[self.ifname] then
975 return "switch"
976 else
977 return "ethernet"
978 end
979 end
980
981 function interface.shortname(self)
982 if self.wif then
983 return "%s %q" %{
984 self.wif:active_mode(),
985 self.wif:active_ssid() or self.wif:active_bssid()
986 }
987 else
988 return self.ifname
989 end
990 end
991
992 function interface.get_i18n(self)
993 if self.wif then
994 return "%s: %s %q" %{
995 i18n.translate("Wireless Network"),
996 self.wif:active_mode(),
997 self.wif:active_ssid() or self.wif:active_bssid()
998 }
999 else
1000 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1001 end
1002 end
1003
1004 function interface.get_type_i18n(self)
1005 local x = self:type()
1006 if x == "wifi" then
1007 return i18n.translate("Wireless Adapter")
1008 elseif x == "bridge" then
1009 return i18n.translate("Bridge")
1010 elseif x == "switch" then
1011 return i18n.translate("Ethernet Switch")
1012 elseif x == "vlan" then
1013 return i18n.translate("VLAN Interface")
1014 elseif x == "tunnel" then
1015 return i18n.translate("Tunnel Interface")
1016 else
1017 return i18n.translate("Ethernet Adapter")
1018 end
1019 end
1020
1021 function interface.adminlink(self)
1022 if self.wif then
1023 return self.wif:adminlink()
1024 end
1025 end
1026
1027 function interface.ports(self)
1028 local members = self:_ubus("bridge-members")
1029 if members then
1030 local _, iface
1031 local ifaces = { }
1032 for _, iface in ipairs(members) do
1033 ifaces[#ifaces+1] = interface(iface)
1034 end
1035 return ifaces
1036 end
1037 end
1038
1039 function interface.bridge_id(self)
1040 if self.br then
1041 return self.br.id
1042 else
1043 return nil
1044 end
1045 end
1046
1047 function interface.bridge_stp(self)
1048 if self.br then
1049 return self.br.stp
1050 else
1051 return false
1052 end
1053 end
1054
1055 function interface.is_up(self)
1056 if self.wif then
1057 return self.wif:is_up()
1058 else
1059 return self:_ubus("up") or false
1060 end
1061 end
1062
1063 function interface.is_bridge(self)
1064 return (self:type() == "bridge")
1065 end
1066
1067 function interface.is_bridgeport(self)
1068 return self.dev and self.dev.bridge and true or false
1069 end
1070
1071 local function uint(x)
1072 if x then
1073 return (x < 0) and ((2^32) + x) or x
1074 end
1075 return 0
1076 end
1077
1078 function interface.tx_bytes(self)
1079 local stat = self:_ubus("statistics")
1080 return stat and uint(stat.tx_bytes) or 0
1081 end
1082
1083 function interface.rx_bytes(self)
1084 local stat = self:_ubus("statistics")
1085 return stat and uint(stat.rx_bytes) or 0
1086 end
1087
1088 function interface.tx_packets(self)
1089 local stat = self:_ubus("statistics")
1090 return stat and uint(stat.tx_packets) or 0
1091 end
1092
1093 function interface.rx_packets(self)
1094 local stat = self:_ubus("statistics")
1095 return stat and uint(stat.rx_packets) or 0
1096 end
1097
1098 function interface.get_network(self)
1099 if not self.network then
1100 if self.dev and self.dev.network then
1101 self.network = _M:get_network(self.dev.network)
1102 end
1103 end
1104
1105 if not self.network then
1106 local net
1107 for _, net in ipairs(_M:get_networks()) do
1108 if net:contains_interface(self.ifname) or
1109 net:ifname() == self.ifname
1110 then
1111 self.network = net
1112 return net
1113 end
1114 end
1115 else
1116 return self.network
1117 end
1118 end
1119
1120 function interface.get_wifinet(self)
1121 return self.wif
1122 end
1123
1124
1125 wifidev = utl.class()
1126
1127 function wifidev.__init__(self, dev)
1128 self.sid = dev
1129 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1130 end
1131
1132 function wifidev.get(self, opt)
1133 return _get("wireless", self.sid, opt)
1134 end
1135
1136 function wifidev.set(self, opt, val)
1137 return _set("wireless", self.sid, opt, val)
1138 end
1139
1140 function wifidev.name(self)
1141 return self.sid
1142 end
1143
1144 function wifidev.hwmodes(self)
1145 local l = self.iwinfo.hwmodelist
1146 if l and next(l) then
1147 return l
1148 else
1149 return { b = true, g = true }
1150 end
1151 end
1152
1153 function wifidev.get_i18n(self)
1154 local t = "Generic"
1155 if self.iwinfo.type == "wl" then
1156 t = "Broadcom"
1157 elseif self.iwinfo.type == "madwifi" then
1158 t = "Atheros"
1159 end
1160
1161 local m = ""
1162 local l = self:hwmodes()
1163 if l.a then m = m .. "a" end
1164 if l.b then m = m .. "b" end
1165 if l.g then m = m .. "g" end
1166 if l.n then m = m .. "n" end
1167
1168 return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1169 end
1170
1171 function wifidev.is_up(self)
1172 local up = false
1173
1174 _uci_state:foreach("wireless", "wifi-iface",
1175 function(s)
1176 if s.device == self.sid then
1177 if s.up == "1" then
1178 up = true
1179 return false
1180 end
1181 end
1182 end)
1183
1184 return up
1185 end
1186
1187 function wifidev.get_wifinet(self, net)
1188 if _uci_real:get("wireless", net) == "wifi-iface" then
1189 return wifinet(net)
1190 else
1191 local wnet = _wifi_lookup(net)
1192 if wnet then
1193 return wifinet(wnet)
1194 end
1195 end
1196 end
1197
1198 function wifidev.get_wifinets(self)
1199 local nets = { }
1200
1201 _uci_real:foreach("wireless", "wifi-iface",
1202 function(s)
1203 if s.device == self.sid then
1204 nets[#nets+1] = wifinet(s['.name'])
1205 end
1206 end)
1207
1208 return nets
1209 end
1210
1211 function wifidev.add_wifinet(self, options)
1212 options = options or { }
1213 options.device = self.sid
1214
1215 local wnet = _uci_real:section("wireless", "wifi-iface", nil, options)
1216 if wnet then
1217 return wifinet(wnet, options)
1218 end
1219 end
1220
1221 function wifidev.del_wifinet(self, net)
1222 if utl.instanceof(net, wifinet) then
1223 net = net.sid
1224 elseif _uci_real:get("wireless", net) ~= "wifi-iface" then
1225 net = _wifi_lookup(net)
1226 end
1227
1228 if net and _uci_real:get("wireless", net, "device") == self.sid then
1229 _uci_real:delete("wireless", net)
1230 return true
1231 end
1232
1233 return false
1234 end
1235
1236
1237 wifinet = utl.class()
1238
1239 function wifinet.__init__(self, net, data)
1240 self.sid = net
1241
1242 local num = { }
1243 local netid
1244 _uci_real:foreach("wireless", "wifi-iface",
1245 function(s)
1246 if s.device then
1247 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1248 if s['.name'] == self.sid then
1249 netid = "%s.network%d" %{ s.device, num[s.device] }
1250 return false
1251 end
1252 end
1253 end)
1254
1255 local dev = _uci_state:get("wireless", self.sid, "ifname") or netid
1256
1257 self.netid = netid
1258 self.wdev = dev
1259 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1260 self.iwdata = data or _uci_state:get_all("wireless", self.sid) or
1261 _uci_real:get_all("wireless", self.sid) or { }
1262 end
1263
1264 function wifinet.get(self, opt)
1265 return _get("wireless", self.sid, opt)
1266 end
1267
1268 function wifinet.set(self, opt, val)
1269 return _set("wireless", self.sid, opt, val)
1270 end
1271
1272 function wifinet.mode(self)
1273 return _uci_state:get("wireless", self.sid, "mode") or "ap"
1274 end
1275
1276 function wifinet.ssid(self)
1277 return _uci_state:get("wireless", self.sid, "ssid")
1278 end
1279
1280 function wifinet.bssid(self)
1281 return _uci_state:get("wireless", self.sid, "bssid")
1282 end
1283
1284 function wifinet.network(self)
1285 return _uci_state:get("wifinet", self.sid, "network")
1286 end
1287
1288 function wifinet.id(self)
1289 return self.netid
1290 end
1291
1292 function wifinet.name(self)
1293 return self.sid
1294 end
1295
1296 function wifinet.ifname(self)
1297 local ifname = self.iwinfo.ifname
1298 if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1299 ifname = self.wdev
1300 end
1301 return ifname
1302 end
1303
1304 function wifinet.get_device(self)
1305 if self.iwdata.device then
1306 return wifidev(self.iwdata.device)
1307 end
1308 end
1309
1310 function wifinet.is_up(self)
1311 return (self.iwdata.up == "1")
1312 end
1313
1314 function wifinet.active_mode(self)
1315 local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1316
1317 if m == "ap" then m = "Master"
1318 elseif m == "sta" then m = "Client"
1319 elseif m == "adhoc" then m = "Ad-Hoc"
1320 elseif m == "mesh" then m = "Mesh"
1321 elseif m == "monitor" then m = "Monitor"
1322 end
1323
1324 return m
1325 end
1326
1327 function wifinet.active_mode_i18n(self)
1328 return i18n.translate(self:active_mode())
1329 end
1330
1331 function wifinet.active_ssid(self)
1332 return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1333 end
1334
1335 function wifinet.active_bssid(self)
1336 return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1337 end
1338
1339 function wifinet.active_encryption(self)
1340 local enc = self.iwinfo and self.iwinfo.encryption
1341 return enc and enc.description or "-"
1342 end
1343
1344 function wifinet.assoclist(self)
1345 return self.iwinfo.assoclist or { }
1346 end
1347
1348 function wifinet.frequency(self)
1349 local freq = self.iwinfo.frequency
1350 if freq and freq > 0 then
1351 return "%.03f" % (freq / 1000)
1352 end
1353 end
1354
1355 function wifinet.bitrate(self)
1356 local rate = self.iwinfo.bitrate
1357 if rate and rate > 0 then
1358 return (rate / 1000)
1359 end
1360 end
1361
1362 function wifinet.channel(self)
1363 return self.iwinfo.channel or
1364 tonumber(_uci_state:get("wireless", self.iwdata.device, "channel"))
1365 end
1366
1367 function wifinet.signal(self)
1368 return self.iwinfo.signal or 0
1369 end
1370
1371 function wifinet.noise(self)
1372 return self.iwinfo.noise or 0
1373 end
1374
1375 function wifinet.country(self)
1376 return self.iwinfo.country or "00"
1377 end
1378
1379 function wifinet.txpower(self)
1380 local pwr = (self.iwinfo.txpower or 0)
1381 return pwr + self:txpower_offset()
1382 end
1383
1384 function wifinet.txpower_offset(self)
1385 return self.iwinfo.txpower_offset or 0
1386 end
1387
1388 function wifinet.signal_level(self, s, n)
1389 if self:active_bssid() ~= "00:00:00:00:00:00" then
1390 local signal = s or self:signal()
1391 local noise = n or self:noise()
1392
1393 if signal < 0 and noise < 0 then
1394 local snr = -1 * (noise - signal)
1395 return math.floor(snr / 5)
1396 else
1397 return 0
1398 end
1399 else
1400 return -1
1401 end
1402 end
1403
1404 function wifinet.signal_percent(self)
1405 local qc = self.iwinfo.quality or 0
1406 local qm = self.iwinfo.quality_max or 0
1407
1408 if qc > 0 and qm > 0 then
1409 return math.floor((100 / qm) * qc)
1410 else
1411 return 0
1412 end
1413 end
1414
1415 function wifinet.shortname(self)
1416 return "%s %q" %{
1417 i18n.translate(self:active_mode()),
1418 self:active_ssid() or self:active_bssid()
1419 }
1420 end
1421
1422 function wifinet.get_i18n(self)
1423 return "%s: %s %q (%s)" %{
1424 i18n.translate("Wireless Network"),
1425 i18n.translate(self:active_mode()),
1426 self:active_ssid() or self:active_bssid(),
1427 self:ifname()
1428 }
1429 end
1430
1431 function wifinet.adminlink(self)
1432 return dsp.build_url("admin", "network", "wireless", self.netid)
1433 end
1434
1435 function wifinet.get_network(self)
1436 local net = tostring(self.iwdata.network)
1437 if net and _uci_real:get("network", net) == "interface" then
1438 return network(net)
1439 end
1440 end
1441
1442 function wifinet.get_interface(self)
1443 return interface(self:ifname())
1444 end
1445
1446
1447 -- setup base protocols
1448 _M:register_protocol("static")
1449 _M:register_protocol("dhcp")
1450 _M:register_protocol("none")
1451
1452 -- load protocol extensions
1453 local exts = nfs.dir(utl.libpath() .. "/model/network")
1454 if exts then
1455 local ext
1456 for ext in exts do
1457 if ext:match("%.lua$") then
1458 require("luci.model.network." .. ext:gsub("%.lua$", ""))
1459 end
1460 end
1461 end