libs/core: implement special treatment of wireless in network model
[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, loadfile, table, i18n
21 = type, pairs, ipairs, loadfile, table, luci.i18n
22
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"
30
31 module "luci.model.network"
32
33 -- load extensions
34 local ext
35 local handler = { }
36
37 for ext in nfs.glob(utl.libpath() .. "/model/network/*.lua") do
38 if nfs.access(ext) then
39 local m = loadfile(ext)
40 if m then
41 handler[#handler+1] = m()
42 end
43 end
44 end
45
46 function foreach_handler(code, ...)
47 local h
48 for _, h in ipairs(handler) do
49 if code(h, ...) then
50 return true
51 end
52 end
53 return false
54 end
55
56 local ub = uct.bind("network")
57 local ifs, brs, sws
58
59 function init(cursor)
60 if cursor then
61 cursor:unload("network")
62 cursor:load("network")
63 ub:init(cursor)
64
65 ifs = { }
66 brs = { }
67 sws = { }
68
69 -- init handler
70 foreach_handler(function(h)
71 h:init(cursor)
72 h:find_interfaces(ifs, brs)
73 end)
74
75 -- read interface information
76 local n, i
77 for n, i in ipairs(nxo.getifaddrs()) do
78 local name = i.name:match("[^:]+")
79 local prnt = name:match("^([^%.]+)%.")
80
81 if not _M:ignore_interface(name) then
82 ifs[name] = ifs[name] or {
83 idx = i.ifindex or n,
84 name = name,
85 rawname = i.name,
86 flags = { },
87 ipaddrs = { },
88 ip6addrs = { }
89 }
90
91 if prnt then
92 sws[name] = true
93 sws[prnt] = true
94 end
95
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)
104 end
105 end
106 end
107
108 -- read bridge informaton
109 local b, l
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)
113 if #r == 4 then
114 b = {
115 name = r[1],
116 id = r[2],
117 stp = r[3] == "yes",
118 ifnames = { ifs[r[4]] }
119 }
120 if b.ifnames[1] then
121 b.ifnames[1].bridge = b
122 end
123 brs[r[1]] = b
124 elseif b then
125 b.ifnames[#b.ifnames+1] = ifs[r[2]]
126 b.ifnames[#b.ifnames].bridge = b
127 end
128 end
129 end
130 end
131 end
132
133 function has_ipv6(self)
134 return nfs.access("/proc/net/ipv6_route")
135 end
136
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
140 return network(n)
141 end
142 end
143 end
144
145 function get_network(self, n)
146 if n and ub.uci:get("network", n) == "interface" then
147 return network(n)
148 end
149 end
150
151 function get_networks(self)
152 local nets = { }
153 ub.uci:foreach("network", "interface",
154 function(s)
155 nets[#nets+1] = network(s['.name'])
156 end)
157 return nets
158 end
159
160 function del_network(self, n)
161 local r = ub.uci:delete("network", n)
162 if r then
163 ub.uci:foreach("network", "alias",
164 function(s)
165 if s.interface == n then
166 ub.uci:delete("network", s['.name'])
167 end
168 end)
169 ub.uci:foreach("network", "route",
170 function(s)
171 if s.interface == n then
172 ub.uci:delete("network", s['.name'])
173 end
174 end)
175 ub.uci:foreach("network", "route6",
176 function(s)
177 if s.interface == n then
178 ub.uci:delete("network", s['.name'])
179 end
180 end)
181
182 foreach_handler(function(h) h:del_network(n) end)
183 end
184 return r
185 end
186
187 function rename_network(self, old, new)
188 local r
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))
192
193 if r then
194 ub.uci:foreach("network", "alias",
195 function(s)
196 if s.interface == old then
197 ub.uci:set("network", s['.name'], "interface", new)
198 end
199 end)
200 ub.uci:foreach("network", "route",
201 function(s)
202 if s.interface == old then
203 ub.uci:set("network", s['.name'], "interface", new)
204 end
205 end)
206 ub.uci:foreach("network", "route6",
207 function(s)
208 if s.interface == old then
209 ub.uci:set("network", s['.name'], "interface", new)
210 end
211 end)
212
213 foreach_handler(function(h) h:rename_network(old, new) end)
214 end
215 end
216 return r or false
217 end
218
219 function get_interface(self, i)
220 return ifs[i] and interface(i)
221 end
222
223 function get_interfaces(self)
224 local ifaces = { }
225 local iface
226 for iface, _ in pairs(ifs) do
227 ifaces[#ifaces+1] = interface(iface)
228 end
229 return ifaces
230 end
231
232 function ignore_interface(self, x)
233 if foreach_handler(function(h) return h:ignore_interface(x) end) then
234 return true
235 else
236 return (x:match("^wmaster%d") or x:match("^wifi%d")
237 or x:match("^hwsim%d") or x:match("^imq%d") or x == "lo")
238 end
239 end
240
241
242 network = ub:section("interface")
243 network:property("device")
244 network:property("ifname")
245 network:property("proto")
246 network:property("type")
247
248 function network.name(self)
249 return self.sid
250 end
251
252 function network.is_bridge(self)
253 return (self:type() == "bridge")
254 end
255
256 function network.add_interface(self, ifname)
257 local ifaces, iface
258
259 if type(ifname) ~= "string" then
260 ifaces = { ifname:name() }
261 else
262 ifaces = ub:list(ifname)
263 end
264
265 for _, iface in ipairs(ifaces) do
266 if ifs[iface] then
267 -- make sure the interface is removed from all networks
268 local i = interface(iface)
269 local n = i:get_network()
270 if n then n:del_interface(iface) end
271
272 if ifs[iface].handler then
273 ifs[iface].handler:add_interface(self, iface, ifs[iface])
274 else
275 self:ifname(ub:list((self:ifname() or ''), iface))
276 end
277 end
278 end
279 end
280
281 function network.del_interface(self, ifname)
282 if type(ifname) ~= "string" then
283 ifname = ifname:name()
284 end
285
286 if ifs[ifname] and ifs[ifname].handler then
287 ifs[ifname].handler:del_interface(self, ifname, ifs[ifname])
288 else
289 self:ifname(ub:list((self:ifname() or ''), nil, ifname))
290 end
291 end
292
293 function network.get_interfaces(self)
294 local ifaces = { }
295 local iface
296 for _, iface in ipairs(ub:list(self:ifname())) do
297 iface = iface:match("[^:]+")
298 if ifs[iface] then
299 ifaces[#ifaces+1] = interface(iface)
300 end
301 end
302 for iface, _ in pairs(ifs) do
303 if ifs[iface].network == self:name() then
304 ifaces[#ifaces+1] = interface(iface)
305 end
306 end
307 return ifaces
308 end
309
310 function network.contains_interface(self, iface)
311 local i
312 local ifaces = ub:list(self:ifname())
313
314 if type(iface) ~= "string" then
315 iface = iface:name()
316 end
317
318 for _, i in ipairs(ifaces) do
319 if i == iface then
320 return true
321 end
322 end
323
324 for i, _ in pairs(ifs) do
325 if ifs[i].dev and ifs[i].dev.network == self:name() then
326 return true
327 end
328 end
329
330 return false
331 end
332
333
334 interface = utl.class()
335 function interface.__init__(self, ifname)
336 if ifs[ifname] then
337 self.ifname = ifname
338 self.dev = ifs[ifname]
339 self.br = brs[ifname]
340 end
341 end
342
343 function interface.name(self)
344 return self.ifname
345 end
346
347 function interface.mac(self)
348 return self.dev.macaddr or "00:00:00:00:00:00"
349 end
350
351 function interface.ipaddrs(self)
352 return self.dev.ipaddrs or { }
353 end
354
355 function interface.ip6addrs(self)
356 return self.dev.ip6addrs or { }
357 end
358
359 function interface.type(self)
360 if self.dev and self.dev.type then
361 return self.dev.type
362 elseif brs[self.ifname] then
363 return "bridge"
364 elseif sws[self.ifname] or self.ifname:match("%.") then
365 return "switch"
366 else
367 return "ethernet"
368 end
369 end
370
371 function interface.shortname(self)
372 if self.dev and self.dev.handler then
373 return self.dev.handler:shortname(self)
374 else
375 return self.ifname
376 end
377 end
378
379 function interface.get_i18n(self)
380 if self.dev and self.dev.handler then
381 return self.dev.handler:get_i18n(self)
382 else
383 return "%s: %q" %{ self:get_type_i18n(), self:name() }
384 end
385 end
386
387 function interface.get_type_i18n(self)
388 local x = self:type()
389 if x == "wifi" then
390 return i18n.translate("a_s_if_wifidev", "Wireless Adapter")
391 elseif x == "bridge" then
392 return i18n.translate("a_s_if_bridge", "Bridge")
393 elseif x == "switch" then
394 return i18n.translate("a_s_if_ethswitch", "Ethernet Switch")
395 else
396 return i18n.translate("a_s_if_ethdev", "Ethernet Adapter")
397 end
398 end
399
400 function interface.ports(self)
401 if self.br then
402 local iface
403 local ifaces = { }
404 for _, iface in ipairs(self.br.ifnames) do
405 ifaces[#ifaces+1] = interface(iface.name)
406 end
407 return ifaces
408 end
409 end
410
411 function interface.bridge_id(self)
412 if self.br then
413 return self.br.id
414 else
415 return nil
416 end
417 end
418
419 function interface.bridge_stp(self)
420 if self.br then
421 return self.br.stp
422 else
423 return false
424 end
425 end
426
427 function interface.is_up(self)
428 return self.dev.flags and self.dev.flags.up
429 end
430
431 function interface.is_bridge(self)
432 return (self:type() == "bridge")
433 end
434
435 function interface.is_bridgeport(self)
436 return self.dev and self.dev.bridge and true or false
437 end
438
439 function interface.tx_bytes(self)
440 return self.dev and self.dev.stats
441 and self.dev.stats.tx_bytes or 0
442 end
443
444 function interface.rx_bytes(self)
445 return self.dev and self.dev.stats
446 and self.dev.stats.rx_bytes or 0
447 end
448
449 function interface.tx_packets(self)
450 return self.dev and self.dev.stats
451 and self.dev.stats.tx_packets or 0
452 end
453
454 function interface.rx_packets(self)
455 return self.dev and self.dev.stats
456 and self.dev.stats.rx_packets or 0
457 end
458
459 function interface.get_network(self)
460 if self.dev and self.dev.network then
461 self.network = _M:get_network(self.dev.network)
462 end
463
464 if not self.network then
465 local net
466 for _, net in ipairs(_M:get_networks()) do
467 if net:contains_interface(self.ifname) then
468 self.network = net
469 return net
470 end
471 end
472 else
473 return self.network
474 end
475 end
476