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