4 Copyright 2009 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, i18n
21 = type, pairs, ipairs, loadfile, table, luci.i18n
23 local lmo = require "lmo"
24 local nxo = require "nixio"
25 local nfs = require "nixio.fs"
26 local iwi = require "iwinfo"
27 local ipc = require "luci.ip"
28 local utl = require "luci.util"
29 local uct = require "luci.model.uci.bind"
31 module "luci.model.network"
37 for ext in nfs.glob(utl.libpath() .. "/model/network/*.lua") do
38 if nfs.access(ext) then
39 local m = loadfile(ext)
41 handler[#handler+1] = m()
46 function foreach_handler(code, ...)
48 for _, h in ipairs(handler) do
56 local ub = uct.bind("network")
61 cursor:unload("network")
62 cursor:load("network")
70 foreach_handler(function(h)
72 h:find_interfaces(ifs, brs)
75 -- read interface information
77 for n, i in ipairs(nxo.getifaddrs()) do
78 local name = i.name:match("[^:]+")
79 local prnt = name:match("^([^%.]+)%.")
81 if not _M:ignore_interface(name) then
82 ifs[name] = ifs[name] or {
96 if i.family == "packet" then
97 ifs[name].flags = i.flags
98 ifs[name].stats = i.data
99 ifs[name].macaddr = i.addr
100 elseif i.family == "inet" then
101 ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
102 elseif i.family == "inet6" then
103 ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
108 -- read bridge informaton
110 for l in utl.execi("brctl show") do
111 if not l:match("STP") then
112 local r = utl.split(l, "%s+", nil, true)
118 ifnames = { ifs[r[4]] }
121 b.ifnames[1].bridge = b
125 b.ifnames[#b.ifnames+1] = ifs[r[2]]
126 b.ifnames[#b.ifnames].bridge = b
133 function has_ipv6(self)
134 return nfs.access("/proc/net/ipv6_route")
137 function add_network(self, n, options)
138 if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not self:get_network(n) then
139 if ub.uci:section("network", "interface", n, options) then
145 function get_network(self, n)
146 if n and ub.uci:get("network", n) == "interface" then
151 function get_networks(self)
153 ub.uci:foreach("network", "interface",
155 nets[#nets+1] = network(s['.name'])
160 function del_network(self, n)
161 local r = ub.uci:delete("network", n)
163 ub.uci:foreach("network", "alias",
165 if s.interface == n then
166 ub.uci:delete("network", s['.name'])
169 ub.uci:foreach("network", "route",
171 if s.interface == n then
172 ub.uci:delete("network", s['.name'])
175 ub.uci:foreach("network", "route6",
177 if s.interface == n then
178 ub.uci:delete("network", s['.name'])
182 foreach_handler(function(h) h:del_network(n) end)
187 function rename_network(self, old, new)
189 if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
190 r = ub.uci:section("network", "interface", new,
191 ub.uci:get_all("network", old))
194 ub.uci:foreach("network", "alias",
196 if s.interface == old then
197 ub.uci:set("network", s['.name'], "interface", new)
200 ub.uci:foreach("network", "route",
202 if s.interface == old then
203 ub.uci:set("network", s['.name'], "interface", new)
206 ub.uci:foreach("network", "route6",
208 if s.interface == old then
209 ub.uci:set("network", s['.name'], "interface", new)
213 foreach_handler(function(h) h:rename_network(old, new) end)
219 function get_interface(self, i)
224 for j, _ in pairs(ifs) do
225 if ifs[j].sid == i then
232 function get_interfaces(self)
235 for iface, _ in pairs(ifs) do
236 ifaces[#ifaces+1] = interface(iface)
241 function ignore_interface(self, x)
242 if foreach_handler(function(h) return h:ignore_interface(x) end) then
245 return (x:match("^wmaster%d") or x:match("^wifi%d")
246 or x:match("^hwsim%d") or x:match("^imq%d") or x == "lo")
251 network = ub:section("interface")
252 network:property("device")
253 network:property("ifname")
254 network:property("proto")
255 network:property("type")
257 function network.name(self)
261 function network.is_bridge(self)
262 return (self:type() == "bridge")
265 function network.add_interface(self, ifname)
268 if type(ifname) ~= "string" then
269 ifaces = { ifname:name() }
271 ifaces = ub:list(ifname)
274 for _, iface in ipairs(ifaces) do
276 -- make sure the interface is removed from all networks
277 local i = interface(iface)
278 local n = i:get_network()
279 if n then n:del_interface(iface) end
281 if ifs[iface].handler then
282 ifs[iface].handler:add_interface(self, iface, ifs[iface])
284 self:ifname(ub:list((self:ifname() or ''), iface))
290 function network.del_interface(self, ifname)
291 if type(ifname) ~= "string" then
292 ifname = ifname:name()
295 if ifs[ifname] and ifs[ifname].handler then
296 ifs[ifname].handler:del_interface(self, ifname, ifs[ifname])
298 self:ifname(ub:list((self:ifname() or ''), nil, ifname))
302 function network.get_interfaces(self)
305 for _, iface in ipairs(ub:list(self:ifname())) do
306 iface = iface:match("[^:]+")
308 ifaces[#ifaces+1] = interface(iface)
311 for iface, _ in pairs(ifs) do
312 if ifs[iface].network == self:name() then
313 ifaces[#ifaces+1] = interface(iface)
319 function network.contains_interface(self, iface)
321 local ifaces = ub:list(self:ifname())
323 if type(iface) ~= "string" then
327 for _, i in ipairs(ifaces) do
333 for i, _ in pairs(ifs) do
334 if ifs[i].dev and ifs[i].dev.network == self:name() then
343 interface = utl.class()
344 function interface.__init__(self, ifname)
347 self.dev = ifs[ifname]
348 self.br = brs[ifname]
352 function interface.name(self)
356 function interface.mac(self)
357 return self.dev.macaddr or "00:00:00:00:00:00"
360 function interface.ipaddrs(self)
361 return self.dev.ipaddrs or { }
364 function interface.ip6addrs(self)
365 return self.dev.ip6addrs or { }
368 function interface.type(self)
369 if self.dev and self.dev.type then
371 elseif brs[self.ifname] then
373 elseif sws[self.ifname] or self.ifname:match("%.") then
380 function interface.shortname(self)
381 if self.dev and self.dev.handler then
382 return self.dev.handler:shortname(self)
388 function interface.get_i18n(self)
389 if self.dev and self.dev.handler then
390 return self.dev.handler:get_i18n(self)
392 return "%s: %q" %{ self:get_type_i18n(), self:name() }
396 function interface.get_type_i18n(self)
397 local x = self:type()
399 return i18n.translate("Wireless Adapter")
400 elseif x == "bridge" then
401 return i18n.translate("Bridge")
402 elseif x == "switch" then
403 return i18n.translate("Ethernet Switch")
405 return i18n.translate("Ethernet Adapter")
409 function interface.ports(self)
413 for _, iface in ipairs(self.br.ifnames) do
414 ifaces[#ifaces+1] = interface(iface.name)
420 function interface.bridge_id(self)
428 function interface.bridge_stp(self)
436 function interface.is_up(self)
437 return self.dev.flags and self.dev.flags.up
440 function interface.is_bridge(self)
441 return (self:type() == "bridge")
444 function interface.is_bridgeport(self)
445 return self.dev and self.dev.bridge and true or false
448 function interface.tx_bytes(self)
449 return self.dev and self.dev.stats
450 and self.dev.stats.tx_bytes or 0
453 function interface.rx_bytes(self)
454 return self.dev and self.dev.stats
455 and self.dev.stats.rx_bytes or 0
458 function interface.tx_packets(self)
459 return self.dev and self.dev.stats
460 and self.dev.stats.tx_packets or 0
463 function interface.rx_packets(self)
464 return self.dev and self.dev.stats
465 and self.dev.stats.rx_packets or 0
468 function interface.get_network(self)
469 if self.dev and self.dev.network then
470 self.network = _M:get_network(self.dev.network)
473 if not self.network then
475 for _, net in ipairs(_M:get_networks()) do
476 if net:contains_interface(self.ifname) then
489 local uci = require "luci.model.uci".cursor_state()
490 local utl = require "luci.util"
491 local sys = require "luci.sys"
492 local lip = require "luci.ip"
493 local nxo = require "nixio"
494 local nfs = require "nixio.fs"
497 local x = getmetatable(uci)
500 local val = self:get(...)
503 if type(val) == "list" then
505 for _, v in ipairs(val) do
507 for i in v:gmatch("%S+") do
511 elseif type(val) == "string" then
513 for i in val:gmatch("%S+") do
524 system._switches = { }
527 function system:__init__()
530 uci:foreach("network2", "interface",
532 self._networks[#self._networks+1] = system.network(s, self)
536 function system:networks()
539 if index <= #self._networks then
541 return self._networks[index]
548 function system:find_network(name)
550 for _, v in ipairs(self._networks) do
551 if v:name() == name then
557 function system:find_interface(name)
559 for _, v in ipairs(self._networks) do
561 for i in v:interfaces() do
562 if i:is_bridge() then
564 for p in i:interfaces() do
565 if p:name() == name then
571 if i:name() == name then
578 function system:delete_network(name)
580 for i = 1, #self._networks do
581 if self._networks[i]:name() == name then
584 for x in self._networks[i]:aliases() do
585 uci:delete("network2", x:name())
588 for x in self._networks[i]:routes() do
589 uci:delete("network2", x:name())
592 uci:delete("network2", self._networks[i])
593 table.remove(self._networks, i)
601 function system:print()
603 for v in self:networks() do
610 function system.ignore_iface(ifn)
612 ifn:match("^wlan%d") or
613 ifn:match("^ath%d") or
614 ifn:match("^wl%d") or
615 ifn:match("^imq%d") or
616 ifn:match("^br%-") or
621 function system.find_wifi_networks(net)
625 uci:foreach("wireless", "wifi-iface",
627 if s.device and s.network == net then
628 lst[#lst+1] = { s.device, s['.name'], cnt }
636 function system.find_iface_names(net)
639 local val = uci:list("network2", net, "device")
640 if #val == 0 or val[1]:match("^/dev/") then
641 val = uci:list("network2", net, "ifname")
645 for _, ifn in ipairs(val) do
646 if not system.ignore_iface(ifn) then
654 function system.find_switch(name)
655 local swname, swdev, swvlan
658 uci:foreach("network2", "switch",
660 swname = s.name or s['.name']
662 -- special: rtl8366s is eth0 (wan is eth1)
663 if swname == "rtl8366s" then
666 -- special: rtl8366rb is eth0 (wan + lan)
667 elseif swname == "rtl8366rb" then
670 -- treat swname as swdev
680 uci:foreach("network2", "switch_vlan",
682 if s.device == swname then
683 local vlan = tonumber(s.vlan)
684 if vlan and (not swvlan or vlan < swvlan) then
692 local veth, vlan = name:match("^(%S+)%.(%d+)$")
694 -- have vlan id and matching switch
695 if vlan and veth == swdev then
696 return swname, swdev, vlan
698 -- have no vlan id but matching switch, assume first switch vlan
699 elseif not vlan and name == swdev then
700 return swname, swdev, swvlan
702 -- have vlan and no matching switch, assume software vlan
704 return nil, veth, vlan
709 system.network = utl.class()
711 function system.network:__init__(s, sys)
712 self._name = s['.name']
717 if s.type == "bridge" then
718 self._interfaces = { system.network.bridge(s['.name'], self) }
720 self._interfaces = { }
725 for _, ifn in ipairs(system.find_iface_names(self._name)) do
726 self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self)
729 -- find wifi networks
730 for _, ifn in ipairs(system.find_wifi_networks(self._name)) do
731 self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self)
736 uci:foreach("network2", "route",
738 if s.interface == self._name and s.target then
739 self._routes[#self._routes+1] = system.network.route(s, self)
744 uci:foreach("network2", "route6",
746 if s.interface == self._name and s.target then
747 self._routes[#self._routes+1] = system.network.route(s, self)
752 uci:foreach("network2", "alias",
754 if s.interface == self._name and s.proto then
755 self._aliases[#self._aliases+1] = system.network.alias(s, self)
760 function system.network:name()
764 function system.network:system()
768 function system.network:interfaces()
771 if index <= #self._interfaces then
773 return self._interfaces[index]
780 function system.network:interface()
781 return self._interfaces[1]
784 function system.network:num_routes()
788 function system.network:routes()
791 if index <= #self._routes then
793 return self._routes[index]
800 function system.network:num_aliases()
801 return #self._aliases
804 function system.network:aliases()
807 if index <= #self._aliases then
809 return self._aliases[index]
816 function system.network:delete_route(rt)
818 for i = 1, #self._routes do
819 if self._routes[i]:name() == rt:name() then
820 uci:delete("network2", rt:name())
821 table.remove(self._routes, i)
828 function system.network:delete_alias(al)
830 for i = 1, #self._aliases do
831 if self._aliases[i]:name() == al:name() then
832 uci:delete("network2", al:name())
833 table.remove(self._aliases, i)
840 function system.network:print()
841 self:interface():print()
845 system.network.iface = utl.class()
847 function system.network.iface:__init__(ifn, net, parent)
849 self._parent = parent
852 if type(ifn) == "table" then
853 local wifidev, network, index = unpack(ifn)
855 self._name = "%s.%d" %{ wifidev, index }
856 self._wifidev = wifidev
857 self._wifinet = index
858 self._ifname = uci:get("wireless", network, "ifname") or self._name
865 local switch, swdev, vlan = system.find_switch(self._ifname)
868 self._switch = system.switch(switch, swdev, self)
872 self._vlan = system.vlan(vlan, self._switch, self)
877 function system.network.iface:name()
881 function system.network.iface:parent()
885 function system.network.iface:network()
889 function system.network.iface:is_managed()
890 return (self._net ~= nil)
893 function system.network.iface:is_vlan()
894 return (self._vlan ~= nil)
897 function system.network.iface:is_software_vlan()
898 return (not self._switch and self._vlan ~= nil)
901 function system.network.iface:is_hardware_vlan()
902 return (self._switch ~= nil and self._vlan ~= nil)
905 function system.network.iface:_sysfs(path, default)
906 path = "/sys/class/net/%s/%s" %{ self._ifname, path }
908 local data = nfs.readfile(path)
910 if type(default) == "number" then
911 return tonumber(data) or default
912 elseif data and #data > 0 then
913 return data and data:gsub("%s+$", "") or default
919 function system.network.iface:rx_bytes()
920 return self:_sysfs("statistics/rx_bytes", 0)
923 function system.network.iface:tx_bytes()
924 return self:_sysfs("statistics/tx_bytes", 0)
927 function system.network.iface:rx_packets()
928 return self:_sysfs("statistics/rx_packets", 0)
931 function system.network.iface:tx_packets()
932 return self:_sysfs("statistics/tx_packets", 0)
935 function system.network.iface:macaddr()
936 return self:_sysfs("address")
939 function system.network.iface:mtu()
940 return self:_sysfs("mtu", 1500)
943 function system.network.iface:is_bridge()
944 return (self:_sysfs("bridge/max_age", 0) > 0)
947 function system.network.iface:is_bridge_port()
948 return (self:_sysfs("brport/port_no", 0) > 0)
951 function system.network.iface:delete()
952 if self._wifidev then
954 uci:foreach("wireless", "wifi-iface",
957 if s.device == self._wifidev and cnt == self._wifinet then
958 uci:delete("wireless", s['.name'])
965 function system.network.iface:print()
966 if self._wifidev then
967 print(" wifi: ", self._wifidev, "net: ", self._wifinet)
969 print(" iface: ", self._name)
972 print(" rx: ", self:rx_bytes(), self:rx_packets())
973 print(" tx: ", self:tx_bytes(), self:tx_packets())
974 print(" mtu: ", self:mtu())
975 print(" mac: ", self:macaddr())
976 print(" bridge? ", self:is_bridge())
977 print(" port? ", self:is_bridge_port())
978 print(" swvlan? ", self:is_software_vlan())
979 print(" hwvlan? ", self:is_hardware_vlan())
991 system.network.bridge = utl.class(system.network.iface)
993 function system.network.bridge:__init__(brn, net)
995 self._name = "br-" .. brn
996 self._ifname = self._name
997 self._interfaces = { }
1001 -- find wired ifaces
1002 for _, ifn in ipairs(system.find_iface_names(brn)) do
1003 self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self)
1006 -- find wifi networks
1007 for _, ifn in ipairs(system.find_wifi_networks(brn)) do
1008 self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self)
1012 function system.network.bridge:interfaces()
1015 if index <= #self._interfaces then
1017 return self._interfaces[index]
1024 function system.network.bridge:print()
1026 for v in self:interfaces() do
1030 print(" rx: ", self:rx_bytes(), self:rx_packets())
1031 print(" tx: ", self:tx_bytes(), self:tx_packets())
1032 print(" mtu: ", self:mtu())
1033 print(" mac: ", self:macaddr())
1034 print(" bridge? ", self:is_bridge())
1035 print(" port? ", self:is_bridge_port())
1039 system.network.route = utl.class()
1041 function system.network.route:__init__(rt, net)
1043 self._name = rt['.name']
1044 self._ipv6 = (rt['.type'] == "route6")
1045 self._mtu = tonumber(rt.mtu) or (net and net:interface():mtu() or 1500)
1046 self._metric = tonumber(rt.metric) or 0
1049 self._gateway = lip.IPv6(rt.gateway or "::")
1050 self._target = lip.IPv6(rt.target or "::")
1052 self._gateway = lip.IPv4(rt.gateway or "0.0.0.0")
1053 self._target = lip.IPv4(rt.target or "0.0.0.0", rt.netmask or "0.0.0.0")
1057 function system.network.route:name()
1061 function system.network.route:network()
1065 function system.network.route:mtu()
1069 function system.network.route:metric()
1073 function system.network.route:is_ipv4()
1074 return not self._ipv6
1077 function system.network.route:is_ipv6()
1081 function system.network.route:target()
1085 function system.network.route:gateway()
1086 return self._gateway
1090 system.network.alias = utl.class()
1092 function system.network.alias:__init__(a, net)
1094 self._name = a['.name']
1098 system.switch = utl.class()
1100 function system.switch:__init__(switch, swdev, net)
1102 self._ifname = swdev
1105 if not system._switches[switch] then
1106 local x = io.popen("swconfig dev %q help 2>/dev/null" % switch)
1108 local desc = x:read("*l")
1111 local name, num_ports, num_cpu, num_vlans =
1112 desc:match("Switch %d: %S+%((.-)%), ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+)")
1115 self._ports = tonumber(num_ports)
1116 self._cpuport = tonumber(num_cpu)
1117 self._vlans = tonumber(num_vlans)
1122 elseif nfs.access("/proc/switch/%s" % switch) then
1123 self._model = self:_proc("driver", switch)
1124 self._ports = self:_proc_count("port", 6)
1125 self._vlans = self:_proc_count("vlan", 16)
1129 self._model = self._model or switch
1130 self._ports = self._ports or 6
1131 self._vlans = self._vlans or 16
1132 self._cpuport = self._cpuport or 5
1134 system._switches[switch] = self
1136 self._model = system._switches[switch]._model
1137 self._ports = system._switches[switch]._ports
1138 self._vlans = system._switches[switch]._vlans
1139 self._cpuport = system._switches[switch]._cpuport
1143 function system.switch:_proc(path, default)
1144 local data = nfs.readfile("/proc/switch/%s/%s" %{ self._name, path })
1146 return data:gsub("%s+$", "")
1151 function system.switch:_proc_count(path, default)
1153 for _ in nfs.dir("/proc/switch/%s/%s" %{ self._name, path }) do
1156 return cnt > 0 and cnt or default
1159 function system.switch:name()
1163 function system.switch:model()
1167 function system.switch:num_possible_vlans()
1171 function system.switch:num_active_vlans()
1173 uci:foreach("network2", "switch_vlan",
1175 if s.device == self._name then cnt = cnt + 1 end
1180 function system.switch:vlans()
1184 uci:foreach("network2", "switch_vlan",
1186 if s.device == self._name and tonumber(s.vlan) then
1187 vlans[#vlans+1] = tonumber(s.vlan)
1192 if index <= #vlans then
1194 return system.vlan(vlans[index], self)
1201 function system.switch:num_ports()
1205 function system.switch:delete_vlan(vlan)
1208 uci:foreach("network2", "switch_vlan",
1210 if s.device == self._name and tonumber(s.vlan) == vlan then
1212 uci:delete("network2", s['.name'])
1214 if system._vlans[s.device] and system._vlans[s.device][vlan] then
1215 table.remove(system._vlans[s.device], vlan)
1225 function system.switch:print()
1226 print("Switch:", self._model)
1227 print(" Ports:", self._ports, "Cpu:", self._cpuport)
1228 print(" Vlans:", self._vlans)
1232 system.vlan = utl.class()
1234 function system.vlan:__init__(vlan, switch, iface)
1236 self._switch = switch
1239 local swid = (switch and switch:name()) or (iface and iface:name()) or ""
1241 if not system._vlans[swid] or not system._vlans[swid][vlan] then
1245 uci:foreach("network2", "switch_vlan",
1247 if s.device == switch:name() and tonumber(s.vlan) == vlan then
1249 for _, p in ipairs(uci:list("network2", s['.name'], "ports")) do
1250 self._ports[#self._ports+1] = system.vlan.port(p, self)
1252 self._name = s['.name']
1256 self._ports[#self._ports+1] = system.vlan.port("0t", self)
1259 system._vlans[swid] = system._vlans[swid] or { }
1260 system._vlans[swid][vlan] = self
1262 self._ports = system._vlans[swid][vlan]._ports
1266 function system.vlan:name()
1270 function system.vlan:number()
1274 function system.vlan:switch()
1278 function system.vlan:interface()
1282 function system.vlan:is_software()
1283 return (self._switch == nil)
1286 function system.vlan:is_hardware()
1287 return not self:is_software()
1290 function system.vlan:num_ports()
1294 function system.vlan:ports()
1297 if index <= #self._ports then
1299 return self._ports[index]
1306 function system.vlan:_update()
1310 for i = 1, #self._ports do
1311 ports[#ports+1] = self._ports[i]:string()
1314 uci:set("network2", self._name, "ports", table.concat(ports, " "))
1317 function system.vlan:delete_port(port)
1318 if self._switch then
1320 for i = 1, #self._ports do
1321 if self._ports[i]:number() == port then
1322 table.remove(self._ports, i)
1331 function system.vlan:print()
1332 print(" Vlan:", self._vlan, "Software?", self:is_software())
1334 for p in self:ports() do
1340 system.vlan.port = utl.class()
1342 function system.vlan.port:__init__(port, vlan)
1343 local num, tag = port:match("^(%d+)([tu]?)")
1346 self._port = tonumber(num)
1347 self._tagged = (tag == "t")
1350 function system.vlan.port:number()
1354 function system.vlan.port:vlan()
1358 function system.vlan.port:string()
1359 return "%i%s" %{ self._port, self._tagged ? "t" : "" }
1362 function system.vlan.port:is_tagged()
1366 function system.vlan.port:print()
1367 print(" Port:", self._port, "Tagged:", self._tagged)
1371 -- ------------------------------
1377 s:find_network("wan"):print()
1378 s:find_interface("eth0"):parent():print()