libs/core: luci.model.network: implement contains_inteface(), fix bugs
[project/luci.git] / libs / core / luasrc / model / network.lua
1 --[[
2 LuCI - Network model
3
4 Copyright 2009 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, table, i18n
21         = type, pairs, ipairs, table, luci.i18n
22
23 local lmo = require "lmo"
24 local nxo = require "nixio"
25 local iwi = require "iwinfo"
26 local ipc = require "luci.ip"
27 local utl = require "luci.util"
28 local uct = require "luci.model.uci.bind"
29
30 module "luci.model.network"
31
32
33 local ub = uct.bind("network")
34 local ifs, brs
35
36 function init(cursor)
37         if cursor then
38                 cursor:unload("network")
39                 cursor:load("network")
40                 ub:init(cursor)
41
42                 ifs = { }
43                 brs = { }
44
45                 -- read interface information
46                 local n, i
47                 for n, i in ipairs(nxo.getifaddrs()) do
48                         local name = i.name:match("[^:]+")
49
50                         if not _M:ignore_interface(name) then
51                                 ifs[name] = ifs[name] or {
52                                         idx      = i.ifindex or n,
53                                         name     = name,
54                                         rawname  = i.name,
55                                         flags    = { },
56                                         ipaddrs  = { },
57                                         ip6addrs = { }
58                                 }
59
60                                 if i.family == "packet" then
61                                         ifs[name].flags   = i.flags
62                                         ifs[name].stats   = i.data
63                                         ifs[name].macaddr = i.addr
64                                 elseif i.family == "inet" then
65                                         ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
66                                 elseif i.family == "inet6" then
67                                         ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
68                                 end
69                         end
70                 end             
71
72                 -- read bridge informaton
73                 local b, l
74                 for l in utl.execi("brctl show") do
75                         if not l:match("STP") then
76                                 local r = utl.split(l, "%s+", nil, true)
77                                 if #r == 4 then
78                                         b = {
79                                                 name    = r[1],
80                                                 id      = r[2],
81                                                 stp     = r[3] == "yes",
82                                                 ifnames = { ifs[r[4]] }
83                                         }
84                                         if b.ifnames[1] then
85                                                 b.ifnames[1].bridge = b
86                                         end
87                                         brs[r[1]] = b
88                                 elseif b then
89                                         b.ifnames[#b.ifnames+1] = ifs[r[2]]
90                                         b.ifnames[#b.ifnames].bridge = b
91                                 end
92                         end
93                 end
94         end
95 end
96
97 function add_network(self, n, options)
98         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not self:get_network(n) then
99                 if ub.uci:section("network", "interface", n, options) then
100                         return network(n)
101                 end
102         end
103 end
104
105 function get_network(self, n)
106         if n and ub.uci:get("network", n) == "interface" then
107                 return network(n)
108         end
109 end
110
111 function get_networks(self)
112         local nets = { }
113         ub.uci:foreach("network", "interface",
114                 function(s)
115                         nets[#nets+1] = network(s['.name'])
116                 end)
117         return nets
118 end
119
120 function del_network(self, n)
121         local r = ub.uci:delete("network", n)
122         if r then
123                 ub.uci:foreach("network", "alias",
124                         function(s)
125                                 if s.interface == n then
126                                         ub.uci:delete("network", s['.name'])
127                                 end
128                         end)
129                 ub.uci:foreach("network", "route",
130                         function(s)
131                                 if s.interface == n then
132                                         ub.uci:delete("network", s['.name'])
133                                 end
134                         end)
135                 ub.uci:foreach("network", "route6",
136                         function(s)
137                                 if s.interface == n then
138                                         ub.uci:delete("network", s['.name'])
139                                 end
140                         end)
141         end
142         return r
143 end
144
145 function rename_network(self, old, new)
146         local r
147         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
148                 r = ub.uci:section("network", "interface", new,
149                         ub.uci:get_all("network", old))
150
151                 if r then
152                         ub.uci:foreach("network", "alias",
153                                 function(s)
154                                         if s.interface == old then
155                                                 ub.uci:set("network", s['.name'], "interface", new)
156                                         end
157                                 end)
158                         ub.uci:foreach("network", "route",
159                                 function(s)
160                                         if s.interface == old then
161                                                 ub.uci:set("network", s['.name'], "interface", new)
162                                         end
163                                 end)
164                         ub.uci:foreach("network", "route6",
165                                 function(s)
166                                         if s.interface == old then
167                                                 ub.uci:set("network", s['.name'], "interface", new)
168                                         end
169                                 end)
170                 end
171         end
172         return r or false
173 end
174
175 function get_interface(self, i)
176         return ifs[i] and interface(i)
177 end
178
179 function get_interfaces(self)
180         local ifaces = { }
181         local iface
182         for iface, _ in pairs(ifs) do
183                 ifaces[#ifaces+1] = interface(iface)
184         end
185         return ifaces
186 end
187
188 function ignore_interface(self, x)
189         return (x:match("^wmaster%d") or x:match("^wifi%d")
190                 or x:match("^hwsim%d") or x:match("^imq%d") or x == "lo")
191 end
192
193
194 network = ub:section("interface")
195 network:property("device")
196 network:property("ifname")
197 network:property("proto")
198 network:property("type")
199
200 function network.name(self)
201         return self.sid
202 end
203
204 function network.is_bridge(self)
205         return (self:type() == "bridge")
206 end
207
208 function network.add_interface(self, ifname)
209         if type(ifname) ~= "string" then
210                 ifname = ifname:name()
211         end
212         if ifs[ifname] then
213                 self:ifname(ub:list((self:ifname() or ''), ifname))
214         end
215 end
216
217 function network.del_interface(self, ifname)
218         if type(ifname) ~= "string" then
219                 ifname = ifname:name()
220         end
221         self:ifname(ub:list((self:ifname() or ''), nil, ifname))
222 end
223
224 function network.get_interfaces(self)
225         local ifaces = { }
226         local iface
227         for _, iface in ub:list(
228                 (self:ifname() or '') .. ' ' .. (self:device() or '')
229         ) do
230                 iface = iface:match("[^:]+")
231                 if ifs[iface] then
232                         ifaces[#ifaces+1] = interface(iface)
233                 end
234         end
235         return ifaces
236 end
237
238 function network.contains_interface(self, iface)
239         local i
240         local ifaces = ub:list(
241                 (self:ifname() or '') .. ' ' .. (self:device() or '')
242         )
243
244         if type(iface) ~= "string" then
245                 iface = iface:name()
246         end
247
248         for _, i in ipairs(ifaces) do
249                 if i == iface then
250                         return true
251                 end
252         end
253
254         return false
255 end
256
257
258 interface = utl.class()
259 function interface.__init__(self, ifname)
260         if ifs[ifname] then
261                 self.ifname = ifname
262                 self.dev    = ifs[ifname]
263                 self.br     = brs[ifname]
264         end
265 end
266
267 function interface.name(self)
268         return self.ifname
269 end
270
271 function interface.type(self)
272         if iwi.type(self.ifname) and iwi.type(self.ifname) ~= "dummy" then
273                 return "wifi"
274         elseif brs[self.ifname] then
275                 return "bridge"
276         elseif self.ifname:match("%.") then
277                 return "switch"
278         else
279                 return "ethernet"
280         end
281 end
282
283 function interface.get_type_i18n(self)
284         local x = self:type()
285         if x == "wifi" then
286                 return i18n.translate("a_s_if_wifidev", "Wireless Adapter")
287         elseif x == "bridge" then
288                 return i18n.translate("a_s_if_bridge", "Bridge")
289         elseif x == "switch" then
290                 return i18n.translate("a_s_if_ethswitch", "Ethernet Switch")
291         else
292                 return i18n.translate("a_s_if_ethdev", "Ethernet Adapter")
293         end
294 end
295
296 function interface.ports(self)
297         if self.br then
298                 local iface
299                 local ifaces = { }
300                 for _, iface in ipairs(self.br.ifnames) do
301                         ifaces[#ifaces+1] = interface(iface)
302                 end
303                 return ifaces
304         end
305 end
306
307 function interface.is_up(self)
308         return self.dev.flags and self.dev.flags.up
309 end
310
311 function interface.is_bridge(self)
312         return (self:type() == "bridge")
313 end
314
315 function interface.get_network(self)
316         local net
317         for _, net in ipairs(_M:get_networks()) do
318                 if net:contains_interface(self.ifname) then
319                         return net
320                 end
321         end
322 end
323