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