4 Copyright 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
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
10 http://www.apache.org/licenses/LICENSE-2.0
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.
20 local type, pairs, ipairs, loadfile, table, tonumber, i18n
21 = type, pairs, ipairs, loadfile, table, tonumber, luci.i18n
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"
30 module "luci.model.network"
33 local ifs, brs, sws, uci_r, uci_s
35 function list_remove(c, s, o, r)
36 local val = uci_r:get(c, s, o)
39 if type(val) == "string" then
40 for val in val:gmatch("%S+") do
46 uci_r:set(c, s, o, table.concat(l, " "))
50 elseif type(val) == "table" then
51 for _, val in ipairs(val) do
65 function list_add(c, s, o, a)
66 local val = uci_r:get(c, s, o) or ""
67 if type(val) == "string" then
69 for val in val:gmatch("%S+") do
75 uci_r:set(c, s, o, table.concat(l, " "))
76 elseif type(val) == "table" then
78 for _, val in ipairs(val) do
88 function wifi_iface(x)
90 x:match("^wlan%d") or x:match("^wl%d") or x:match("^ath%d") or
91 x:match("^%w+%.network%d")
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
102 ifnidx = tonumber(ifnidx)
103 uci_r:foreach("wireless", "wifi-iface",
105 if s.device == radio then
107 if num == ifnidx then
116 -- looks like wifi, try to locate the section via state vars
117 elseif wifi_iface(ifn) then
120 uci_s:foreach("wireless", "wifi-iface",
122 if s.ifname == ifn then
132 function iface_ignore(x)
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"
142 function init(cursor)
145 uci_s = cursor:substate()
151 -- read interface information
153 for n, i in ipairs(nxo.getifaddrs()) do
154 local name = i.name:match("[^:]+")
155 local prnt = name:match("^([^%.]+)%.")
157 if not iface_ignore(name) then
158 ifs[name] = ifs[name] or {
159 idx = i.ifindex or n,
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)
184 -- read bridge informaton
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)
194 ifnames = { ifs[r[4]] }
197 b.ifnames[1].bridge = b
201 b.ifnames[#b.ifnames+1] = ifs[r[2]]
202 b.ifnames[#b.ifnames].bridge = b
209 function has_ipv6(self)
210 return nfs.access("/proc/net/ipv6_route")
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
221 function get_network(self, n)
222 if n and uci_r:get("network", n) == "interface" then
227 function get_networks(self)
229 uci_r:foreach("network", "interface",
231 nets[#nets+1] = network(s['.name'])
236 function del_network(self, n)
237 local r = uci_r:delete("network", n)
239 uci_r:foreach("network", "alias",
241 if s.interface == n then
242 uci_r:delete("network", s['.name'])
246 uci_r:foreach("network", "route",
248 if s.interface == n then
249 uci_r:delete("network", s['.name'])
253 uci_r:foreach("network", "route6",
255 if s.interface == n then
256 uci_r:delete("network", s['.name'])
260 uci_r:foreach("wireless", "wifi-iface",
262 if s.network == n then
263 uci_r:delete("wireless", s['.name'], "network")
267 uci_r:delete("network", n)
272 function rename_network(self, old, new)
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))
278 uci_r:foreach("network", "alias",
280 if s.interface == old then
281 uci_r:set("network", s['.name'], "interface", new)
285 uci_r:foreach("network", "route",
287 if s.interface == old then
288 uci_r:set("network", s['.name'], "interface", new)
292 uci_r:foreach("network", "route6",
294 if s.interface == old then
295 uci_r:set("network", s['.name'], "interface", new)
299 uci_r:foreach("wireless", "wifi-iface",
301 if s.network == old then
302 uci_r:set("wireless", s['.name'], "network", new)
306 uci_r:delete("network", old)
312 function get_interface(self, i)
313 if ifs[i] or wifi_iface(i) then
318 uci_r:foreach("wireless", "wifi-iface",
321 num[s.device] = num[s.device] and num[s.device] + 1 or 1
322 if s['.name'] == i then
324 "%s.network%d" %{s.device, num[s.device] })
333 function get_interfaces(self)
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)
344 -- find wifi interfaces
347 uci_r:foreach("wireless", "wifi-iface",
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)
356 for iface in utl.kspairs(wfs) do
357 ifaces[#ifaces+1] = wfs[iface]
363 function ignore_interface(self, x)
364 return iface_ignore(x)
368 network = utl.class()
370 function network.__init__(self, name)
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, " ")
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
389 local dev = self:_get("ifname") or
390 uci_r:get("network", self.sid, "ifname")
392 dev = dev and dev:match("%S+")
395 uci_r:foreach("wireless", "wifi-iface",
398 num[s.device] = num[s.device]
399 and num[s.device] + 1 or 1
401 if s.network == self.sid then
402 dev = "%s.network%d" %{ s.device, num[s.device] }
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")
421 function network.proto(self)
422 return self:_get("proto") or "none"
425 function network.type(self)
426 return self:_get("type")
429 function network.name(self)
433 function network.is_bridge(self)
434 return (self:type() == "bridge")
437 function network.is_virtual(self)
438 local p = self:proto()
440 p == "3g" or p == "6in4" or p == "ppp" or
441 p == "pppoe" or p == "pppoa"
445 function network.is_empty(self)
446 if self:is_virtual() then
451 if (self:_get("ifname") or ""):match("%S+") then
455 uci_r:foreach("wireless", "wifi-iface",
457 if s.network == self.sid then
467 function network.add_interface(self, ifname)
468 if not self:is_virtual() then
469 if type(ifname) ~= "string" then
470 ifname = ifname:name()
472 ifname = ifname:match("[^%s:]+")
475 -- remove the interface from all ifaces
476 uci_r:foreach("network", "interface",
478 list_remove("network", s['.name'], "ifname", ifname)
481 -- if its a wifi interface, change its network option
482 local wif = wifi_lookup(ifname)
484 uci_r:set("wireless", wif, "network", self.sid)
486 -- add iface to our iface list
488 list_add("network", self.sid, "ifname", ifname)
493 function network.del_interface(self, ifname)
494 if not self:is_virtual() then
495 if type(ifname) ~= "string" then
496 ifname = ifname:name()
498 ifname = ifname:match("[^%s:]+")
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
505 -- remove the interface
506 list_remove("network", self.sid, "ifname", ifname)
510 function network.get_interfaces(self)
514 if self:is_virtual() then
515 ifn = self:proto() .. "-" .. self.sid
516 ifaces = { interface(ifn) }
519 for ifn in self:_get("ifname"):gmatch("%S+") do
520 ifn = ifn:match("[^:]+")
521 nfs[ifn] = interface(ifn)
524 for ifn in utl.kspairs(nfs) do
525 ifaces[#ifaces+1] = nfs[ifn]
530 uci_r:foreach("wireless", "wifi-iface",
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)
541 for ifn in utl.kspairs(wfs) do
542 ifaces[#ifaces+1] = wfs[ifn]
549 function network.contains_interface(self, ifname)
550 if type(ifname) ~= "string" then
551 ifname = ifname:name()
553 ifname = ifname:match("[^%s:]+")
557 if self:is_virtual() then
558 ifn = self:proto() .. "-" .. self.sid
561 for ifn in self:_get("ifname"):gmatch("%S+") do
562 ifn = ifn:match("[^:]+")
563 if ifn == ifname then
568 local wif = wifi_lookup(ifname)
570 return (uci_r:get("wireless", wif, "network") == self.sid)
577 function network.adminlink(self)
578 return dsp.build_url("admin", "network", "network", self.sid)
582 interface = utl.class()
583 function interface.__init__(self, ifname)
584 self.wif = wifi_lookup(ifname)
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 { }
593 self.ifname = self.ifname or ifname
594 self.dev = ifs[self.ifname]
597 function interface.name(self)
598 return self.wif and uci_s:get("wireless", self.wif, "ifname") or self.ifname
601 function interface.mac(self)
602 return self.dev and self.dev or "00:00:00:00:00:00"
605 function interface.ipaddrs(self)
606 return self.dev and self.dev.ipaddrs or { }
609 function interface.ip6addrs(self)
610 return self.dev and self.dev.ip6addrs or { }
613 function interface.type(self)
614 if wifi_iface(self.ifname) then
616 elseif brs[self.ifname] then
618 elseif sws[self.ifname] or self.ifname:match("%.") then
625 function _choose(s1, s2)
626 if not s1 or #s1 == 0 then
627 return s2 and #s2 > 0 and s2
633 function interface.shortname(self)
634 if self.iwinfo or self.iwdata then
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 }
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 }
656 return "%s: %q" %{ self:get_type_i18n(), self:name() }
660 function interface.get_type_i18n(self)
661 local x = self:type()
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")
669 return i18n.translate("Ethernet Adapter")
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)
680 function interface.ports(self)
684 for _, iface in ipairs(self.br.ifnames) do
685 ifaces[#ifaces+1] = interface(iface.name)
691 function interface.bridge_id(self)
699 function interface.bridge_stp(self)
707 function interface.is_up(self)
708 return self.dev and self.dev.flags and self.dev.flags.up or false
711 function interface.is_bridge(self)
712 return (self:type() == "bridge")
715 function interface.is_bridgeport(self)
716 return self.dev and self.dev.bridge and true or false
719 function interface.tx_bytes(self)
720 return self.dev and self.dev.stats
721 and self.dev.stats.tx_bytes or 0
724 function interface.rx_bytes(self)
725 return self.dev and self.dev.stats
726 and self.dev.stats.rx_bytes or 0
729 function interface.tx_packets(self)
730 return self.dev and self.dev.stats
731 and self.dev.stats.tx_packets or 0
734 function interface.rx_packets(self)
735 return self.dev and self.dev.stats
736 and self.dev.stats.rx_packets or 0
739 function interface.get_network(self)
740 if self.dev and self.dev.network then
741 self.network = _M:get_network(self.dev.network)
744 if not self.network then
746 for _, net in ipairs(_M:get_networks()) do
747 if net:contains_interface(self.ifname) then