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