53b3cf56b43559b06401ce039859217f30419029
[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, pairs, ipairs, loadfile, table, tonumber, i18n
21 = type, pairs, ipairs, loadfile, table, tonumber, luci.i18n
22
23 local nxo = require "nixio"
24 local ipc = require "luci.ip"
25 local sys = require "luci.sys"
26 local utl = require "luci.util"
27 local dsp = require "luci.dispatcher"
28 local uci = require "luci.model.uci"
29
30 module "luci.model.network"
31
32
33 local ifs, brs, sws, uci_r, uci_s
34
35 function list_remove(c, s, o, r)
36 local val = uci_r:get(c, s, o)
37 if val then
38 local l = { }
39 if type(val) == "string" then
40 for val in val:gmatch("%S+") do
41 if val ~= r then
42 l[#l+1] = val
43 end
44 end
45 if #l > 0 then
46 uci_r:set(c, s, o, table.concat(l, " "))
47 else
48 uci_r:delete(c, s, o)
49 end
50 elseif type(val) == "table" then
51 for _, val in ipairs(val) do
52 if val ~= r then
53 l[#l+1] = val
54 end
55 end
56 if #l > 0 then
57 uci_r:set(c, s, o, l)
58 else
59 uci_r:delete(c, s, o)
60 end
61 end
62 end
63 end
64
65 function list_add(c, s, o, a)
66 local val = uci_r:get(c, s, o) or ""
67 if type(val) == "string" then
68 local l = { }
69 for val in val:gmatch("%S+") do
70 if val ~= a then
71 l[#l+1] = val
72 end
73 end
74 l[#l+1] = a
75 uci_r:set(c, s, o, table.concat(l, " "))
76 elseif type(val) == "table" then
77 local l = { }
78 for _, val in ipairs(val) do
79 if val ~= a then
80 l[#l+1] = val
81 end
82 end
83 l[#l+1] = a
84 uci_r:set(c, s, o, l)
85 end
86 end
87
88 function wifi_iface(x)
89 return (
90 x:match("^wlan%d") or x:match("^wl%d") or x:match("^ath%d") or
91 x:match("^%w+%.network%d")
92 )
93 end
94
95 function wifi_lookup(ifn)
96 -- got a radio#.network# pseudo iface, locate the corresponding section
97 local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
98 if radio and ifnidx then
99 local sid = nil
100 local num = 0
101
102 ifnidx = tonumber(ifnidx)
103 uci_r:foreach("wireless", "wifi-iface",
104 function(s)
105 if s.device == radio then
106 num = num + 1
107 if num == ifnidx then
108 sid = s['.name']
109 return false
110 end
111 end
112 end)
113
114 return sid
115
116 -- looks like wifi, try to locate the section via state vars
117 elseif wifi_iface(ifn) then
118 local sid = nil
119
120 uci_s:foreach("wireless", "wifi-iface",
121 function(s)
122 if s.ifname == ifn then
123 sid = s['.name']
124 return false
125 end
126 end)
127
128 return sid
129 end
130 end
131
132 function iface_ignore(x)
133 return (
134 x:match("^wmaster%d") or x:match("^wifi%d") or x:match("^hwsim%d") or
135 x:match("^imq%d") or x:match("^mon.wlan%d") or x:match("^6in4-%w") or
136 x:match("^3g-%w") or x:match("^ppp-%w") or x:match("^pppoe-%w") or
137 x:match("^pppoa-%w") or x == "lo"
138 )
139 end
140
141
142 function init(cursor)
143 if cursor then
144 uci_r = cursor
145 uci_s = cursor:substate()
146
147 ifs = { }
148 brs = { }
149 sws = { }
150
151 -- read interface information
152 local n, i
153 for n, i in ipairs(nxo.getifaddrs()) do
154 local name = i.name:match("[^:]+")
155 local prnt = name:match("^([^%.]+)%.")
156
157 if not iface_ignore(name) then
158 ifs[name] = ifs[name] or {
159 idx = i.ifindex or n,
160 name = name,
161 rawname = i.name,
162 flags = { },
163 ipaddrs = { },
164 ip6addrs = { }
165 }
166
167 if prnt then
168 sws[name] = true
169 sws[prnt] = true
170 end
171
172 if i.family == "packet" then
173 ifs[name].flags = i.flags
174 ifs[name].stats = i.data
175 ifs[name].macaddr = i.addr
176 elseif i.family == "inet" then
177 ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
178 elseif i.family == "inet6" then
179 ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
180 end
181 end
182 end
183
184 -- read bridge informaton
185 local b, l
186 for l in utl.execi("brctl show") do
187 if not l:match("STP") then
188 local r = utl.split(l, "%s+", nil, true)
189 if #r == 4 then
190 b = {
191 name = r[1],
192 id = r[2],
193 stp = r[3] == "yes",
194 ifnames = { ifs[r[4]] }
195 }
196 if b.ifnames[1] then
197 b.ifnames[1].bridge = b
198 end
199 brs[r[1]] = b
200 elseif b then
201 b.ifnames[#b.ifnames+1] = ifs[r[2]]
202 b.ifnames[#b.ifnames].bridge = b
203 end
204 end
205 end
206 end
207 end
208
209 function has_ipv6(self)
210 return nfs.access("/proc/net/ipv6_route")
211 end
212
213 function add_network(self, n, options)
214 if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not self:get_network(n) then
215 if uci_r:section("network", "interface", n, options) then
216 return network(n)
217 end
218 end
219 end
220
221 function get_network(self, n)
222 if n and uci_r:get("network", n) == "interface" then
223 return network(n)
224 end
225 end
226
227 function get_networks(self)
228 local nets = { }
229 uci_r:foreach("network", "interface",
230 function(s)
231 nets[#nets+1] = network(s['.name'])
232 end)
233 return nets
234 end
235
236 function del_network(self, n)
237 local r = uci_r:delete("network", n)
238 if r then
239 uci_r:foreach("network", "alias",
240 function(s)
241 if s.interface == n then
242 uci_r:delete("network", s['.name'])
243 end
244 end)
245
246 uci_r:foreach("network", "route",
247 function(s)
248 if s.interface == n then
249 uci_r:delete("network", s['.name'])
250 end
251 end)
252
253 uci_r:foreach("network", "route6",
254 function(s)
255 if s.interface == n then
256 uci_r:delete("network", s['.name'])
257 end
258 end)
259
260 uci_r:foreach("wireless", "wifi-iface",
261 function(s)
262 if s.network == n then
263 uci_r:delete("wireless", s['.name'], "network")
264 end
265 end)
266
267 uci_r:delete("network", n)
268 end
269 return r
270 end
271
272 function rename_network(self, old, new)
273 local r
274 if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
275 r = uci_r:section("network", "interface", new, uci_r:get_all("network", old))
276
277 if r then
278 uci_r:foreach("network", "alias",
279 function(s)
280 if s.interface == old then
281 uci_r:set("network", s['.name'], "interface", new)
282 end
283 end)
284
285 uci_r:foreach("network", "route",
286 function(s)
287 if s.interface == old then
288 uci_r:set("network", s['.name'], "interface", new)
289 end
290 end)
291
292 uci_r:foreach("network", "route6",
293 function(s)
294 if s.interface == old then
295 uci_r:set("network", s['.name'], "interface", new)
296 end
297 end)
298
299 uci_r:foreach("wireless", "wifi-iface",
300 function(s)
301 if s.network == old then
302 uci_r:set("wireless", s['.name'], "network", new)
303 end
304 end)
305
306 uci_r:delete("network", old)
307 end
308 end
309 return r or false
310 end
311
312 function get_interface(self, i)
313 if ifs[i] or wifi_iface(i) then
314 return interface(i)
315 else
316 local ifc
317 local num = { }
318 uci_r:foreach("wireless", "wifi-iface",
319 function(s)
320 if s.device then
321 num[s.device] = num[s.device] and num[s.device] + 1 or 1
322 if s['.name'] == i then
323 ifc = interface(
324 "%s.network%d" %{s.device, num[s.device] })
325 return false
326 end
327 end
328 end)
329 return ifc
330 end
331 end
332
333 function get_interfaces(self)
334 local iface
335 local ifaces = { }
336
337 -- find normal interfaces
338 for iface in utl.kspairs(ifs) do
339 if not iface_ignore(iface) and not wifi_iface(iface) then
340 ifaces[#ifaces+1] = interface(iface)
341 end
342 end
343
344 -- find wifi interfaces
345 local num = { }
346 local wfs = { }
347 uci_r:foreach("wireless", "wifi-iface",
348 function(s)
349 if s.device then
350 num[s.device] = num[s.device] and num[s.device] + 1 or 1
351 local i = "%s.network%d" %{ s.device, num[s.device] }
352 wfs[i] = interface(i)
353 end
354 end)
355
356 for iface in utl.kspairs(wfs) do
357 ifaces[#ifaces+1] = wfs[iface]
358 end
359
360 return ifaces
361 end
362
363 function ignore_interface(self, x)
364 return iface_ignore(x)
365 end
366
367
368 network = utl.class()
369
370 function network.__init__(self, name)
371 self.sid = name
372 end
373
374 function network._get(self, opt)
375 local v = uci_r:get("network", self.sid, opt)
376 if type(v) == "table" then
377 return table.concat(v, " ")
378 end
379 return v or ""
380 end
381
382 function network.ifname(self)
383 local p = self:proto()
384 if self:is_bridge() then
385 return "br-" .. self.sid
386 elseif self:is_virtual() then
387 return p .. "-" .. self.sid
388 else
389 local dev = self:_get("ifname") or
390 uci_r:get("network", self.sid, "ifname")
391
392 dev = dev and dev:match("%S+")
393
394 if not dev then
395 uci_r:foreach("wireless", "wifi-iface",
396 function(s)
397 if s.device then
398 num[s.device] = num[s.device]
399 and num[s.device] + 1 or 1
400
401 if s.network == self.sid then
402 dev = "%s.network%d" %{ s.device, num[s.device] }
403 return false
404 end
405 end
406 end)
407 end
408
409 return dev
410 end
411 end
412
413 function network.device(self)
414 local dev = self:_get("device")
415 if not dev or dev:match("[^%w%-%.%s]") then
416 dev = uci_r:get("network", self.sid, "ifname")
417 end
418 return dev
419 end
420
421 function network.proto(self)
422 return self:_get("proto") or "none"
423 end
424
425 function network.type(self)
426 return self:_get("type")
427 end
428
429 function network.name(self)
430 return self.sid
431 end
432
433 function network.is_bridge(self)
434 return (self:type() == "bridge")
435 end
436
437 function network.is_virtual(self)
438 local p = self:proto()
439 return (
440 p == "3g" or p == "6in4" or p == "ppp" or
441 p == "pppoe" or p == "pppoa"
442 )
443 end
444
445 function network.is_empty(self)
446 if self:is_virtual() then
447 return false
448 else
449 local rv = true
450
451 if (self:_get("ifname") or ""):match("%S+") then
452 rv = false
453 end
454
455 uci_r:foreach("wireless", "wifi-iface",
456 function(s)
457 if s.network == self.sid then
458 rv = false
459 return false
460 end
461 end)
462
463 return rv
464 end
465 end
466
467 function network.add_interface(self, ifname)
468 if not self:is_virtual() then
469 if type(ifname) ~= "string" then
470 ifname = ifname:name()
471 else
472 ifname = ifname:match("[^%s:]+")
473 end
474
475 -- remove the interface from all ifaces
476 uci_r:foreach("network", "interface",
477 function(s)
478 list_remove("network", s['.name'], "ifname", ifname)
479 end)
480
481 -- if its a wifi interface, change its network option
482 local wif = wifi_lookup(ifname)
483 if wif then
484 uci_r:set("wireless", wif, "network", self.sid)
485
486 -- add iface to our iface list
487 else
488 list_add("network", self.sid, "ifname", ifname)
489 end
490 end
491 end
492
493 function network.del_interface(self, ifname)
494 if not self:is_virtual() then
495 if type(ifname) ~= "string" then
496 ifname = ifname:name()
497 else
498 ifname = ifname:match("[^%s:]+")
499 end
500
501 -- if its a wireless interface, clear its network option
502 local wif = wifi_lookup(ifname)
503 if wif then uci_r:delete("wireless", wif, "network") end
504
505 -- remove the interface
506 list_remove("network", self.sid, "ifname", ifname)
507 end
508 end
509
510 function network.get_interfaces(self)
511 local ifaces = { }
512
513 local ifn
514 if self:is_virtual() then
515 ifn = self:proto() .. "-" .. self.sid
516 ifaces = { interface(ifn) }
517 else
518 local nfs = { }
519 for ifn in self:_get("ifname"):gmatch("%S+") do
520 ifn = ifn:match("[^:]+")
521 nfs[ifn] = interface(ifn)
522 end
523
524 for ifn in utl.kspairs(nfs) do
525 ifaces[#ifaces+1] = nfs[ifn]
526 end
527
528 local num = { }
529 local wfs = { }
530 uci_r:foreach("wireless", "wifi-iface",
531 function(s)
532 if s.device then
533 num[s.device] = num[s.device] and num[s.device] + 1 or 1
534 if s.network == self.sid then
535 ifn = "%s.network%d" %{ s.device, num[s.device] }
536 wfs[ifn] = interface(ifn)
537 end
538 end
539 end)
540
541 for ifn in utl.kspairs(wfs) do
542 ifaces[#ifaces+1] = wfs[ifn]
543 end
544 end
545
546 return ifaces
547 end
548
549 function network.contains_interface(self, ifname)
550 if type(ifname) ~= "string" then
551 ifname = ifname:name()
552 else
553 ifname = ifname:match("[^%s:]+")
554 end
555
556 local ifn
557 if self:is_virtual() then
558 ifn = self:proto() .. "-" .. self.sid
559 return ifname == ifn
560 else
561 for ifn in self:_get("ifname"):gmatch("%S+") do
562 ifn = ifn:match("[^:]+")
563 if ifn == ifname then
564 return true
565 end
566 end
567
568 local wif = wifi_lookup(ifname)
569 if wif then
570 return (uci_r:get("wireless", wif, "network") == self.sid)
571 end
572 end
573
574 return false
575 end
576
577 function network.adminlink(self)
578 return dsp.build_url("admin", "network", "network", self.sid)
579 end
580
581
582 interface = utl.class()
583 function interface.__init__(self, ifname)
584 self.wif = wifi_lookup(ifname)
585
586 if self.wif then
587 self.ifname = uci_s:get("wireless", self.wif, "ifname")
588 self.iwinfo = self.ifname and sys.wifi.getiwinfo(self.ifname) or { }
589 self.iwdata = uci_s:get_all("wireless", self.wif) or { }
590 self.iwname = ifname
591 end
592
593 self.ifname = self.ifname or ifname
594 self.dev = ifs[self.ifname]
595 end
596
597 function interface.name(self)
598 return self.wif and uci_s:get("wireless", self.wif, "ifname") or self.ifname
599 end
600
601 function interface.mac(self)
602 return self.dev and self.dev or "00:00:00:00:00:00"
603 end
604
605 function interface.ipaddrs(self)
606 return self.dev and self.dev.ipaddrs or { }
607 end
608
609 function interface.ip6addrs(self)
610 return self.dev and self.dev.ip6addrs or { }
611 end
612
613 function interface.type(self)
614 if wifi_iface(self.ifname) then
615 return "wifi"
616 elseif brs[self.ifname] then
617 return "bridge"
618 elseif sws[self.ifname] or self.ifname:match("%.") then
619 return "switch"
620 else
621 return "ethernet"
622 end
623 end
624
625 function _choose(s1, s2)
626 if not s1 or #s1 == 0 then
627 return s2 and #s2 > 0 and s2
628 else
629 return s1
630 end
631 end
632
633 function interface.shortname(self)
634 if self.iwinfo or self.iwdata then
635 return "%s %q" %{
636 i18n.translate(self.iwinfo.mode),
637 _choose(self.iwinfo.ssid, self.iwdata.ssid ) or
638 _choose(self.iwinfo.bssid, self.iwdata.bssid) or
639 "%s (%s)" %{ i18n.translate("unknown"), self.ifname }
640 }
641 else
642 return self.ifname
643 end
644 end
645
646 function interface.get_i18n(self)
647 if self.iwinfo or self.iwdata then
648 return "%s: %s %q" %{
649 i18n.translate("Wireless Network"),
650 _choose(self.iwinfo.mode, self.iwdata.mode ),
651 _choose(self.iwinfo.ssid, self.iwdata.ssid ) or
652 _choose(self.iwinfo.bssid, self.iwdata.bssid) or
653 "%s (%s)" %{ i18n.translate("unknown"), self.ifname }
654 }
655 else
656 return "%s: %q" %{ self:get_type_i18n(), self:name() }
657 end
658 end
659
660 function interface.get_type_i18n(self)
661 local x = self:type()
662 if x == "wifi" then
663 return i18n.translate("Wireless Adapter")
664 elseif x == "bridge" then
665 return i18n.translate("Bridge")
666 elseif x == "switch" then
667 return i18n.translate("Ethernet Switch")
668 else
669 return i18n.translate("Ethernet Adapter")
670 end
671 end
672
673 function interface.adminlink(self)
674 if self:type() == "wifi" then
675 return dsp.build_url("admin", "network", "wireless",
676 self.iwdata.device, self.iwname)
677 end
678 end
679
680 function interface.ports(self)
681 if self.br then
682 local iface
683 local ifaces = { }
684 for _, iface in ipairs(self.br.ifnames) do
685 ifaces[#ifaces+1] = interface(iface.name)
686 end
687 return ifaces
688 end
689 end
690
691 function interface.bridge_id(self)
692 if self.br then
693 return self.br.id
694 else
695 return nil
696 end
697 end
698
699 function interface.bridge_stp(self)
700 if self.br then
701 return self.br.stp
702 else
703 return false
704 end
705 end
706
707 function interface.is_up(self)
708 return self.dev and self.dev.flags and self.dev.flags.up or false
709 end
710
711 function interface.is_bridge(self)
712 return (self:type() == "bridge")
713 end
714
715 function interface.is_bridgeport(self)
716 return self.dev and self.dev.bridge and true or false
717 end
718
719 function interface.tx_bytes(self)
720 return self.dev and self.dev.stats
721 and self.dev.stats.tx_bytes or 0
722 end
723
724 function interface.rx_bytes(self)
725 return self.dev and self.dev.stats
726 and self.dev.stats.rx_bytes or 0
727 end
728
729 function interface.tx_packets(self)
730 return self.dev and self.dev.stats
731 and self.dev.stats.tx_packets or 0
732 end
733
734 function interface.rx_packets(self)
735 return self.dev and self.dev.stats
736 and self.dev.stats.rx_packets or 0
737 end
738
739 function interface.get_network(self)
740 if self.dev and self.dev.network then
741 self.network = _M:get_network(self.dev.network)
742 end
743
744 if not self.network then
745 local net
746 for _, net in ipairs(_M:get_networks()) do
747 if net:contains_interface(self.ifname) then
748 self.network = net
749 return net
750 end
751 end
752 else
753 return self.network
754 end
755 end