libs/core: fixes for luci.model.firewall, luci.model.network and luci.model.network...
[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         if ifs[i] then
221                 return interface(i)
222         else
223                 local j
224                 for j, _ in pairs(ifs) do
225                         if ifs[j].sid == i then
226                                 return interface(j)
227                         end
228                 end
229         end
230 end
231
232 function get_interfaces(self)
233         local ifaces = { }
234         local iface
235         for iface, _ in pairs(ifs) do
236                 ifaces[#ifaces+1] = interface(iface)
237         end
238         return ifaces
239 end
240
241 function ignore_interface(self, x)
242         if foreach_handler(function(h) return h:ignore_interface(x) end) then
243                 return true
244         else
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")
247         end
248 end
249
250
251 network = ub:section("interface")
252 network:property("device")
253 network:property("ifname")
254 network:property("proto")
255 network:property("type")
256
257 function network.name(self)
258         return self.sid
259 end
260
261 function network.is_bridge(self)
262         return (self:type() == "bridge")
263 end
264
265 function network.add_interface(self, ifname)
266         local ifaces, iface
267
268         if type(ifname) ~= "string" then
269                 ifaces = { ifname:name() }
270         else
271                 ifaces = ub:list(ifname)
272         end
273
274         for _, iface in ipairs(ifaces) do
275                 if ifs[iface] then
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
280
281                         if ifs[iface].handler then
282                                 ifs[iface].handler:add_interface(self, iface, ifs[iface])
283                         else
284                                 self:ifname(ub:list((self:ifname() or ''), iface))
285                         end
286                 end
287         end
288 end
289
290 function network.del_interface(self, ifname)
291         if type(ifname) ~= "string" then
292                 ifname = ifname:name()
293         end
294
295         if ifs[ifname] and ifs[ifname].handler then
296                 ifs[ifname].handler:del_interface(self, ifname, ifs[ifname])
297         else
298                 self:ifname(ub:list((self:ifname() or ''), nil, ifname))
299         end
300 end
301
302 function network.get_interfaces(self)
303         local ifaces = { }
304         local iface
305         for _, iface in ipairs(ub:list(self:ifname())) do
306                 iface = iface:match("[^:]+")
307                 if ifs[iface] then
308                         ifaces[#ifaces+1] = interface(iface)
309                 end
310         end
311         for iface, _ in pairs(ifs) do
312                 if ifs[iface].network == self:name() then
313                         ifaces[#ifaces+1] = interface(iface)
314                 end
315         end
316         return ifaces
317 end
318
319 function network.contains_interface(self, iface)
320         local i
321         local ifaces = ub:list(self:ifname())
322
323         if type(iface) ~= "string" then
324                 iface = iface:name()
325         end
326
327         for _, i in ipairs(ifaces) do
328                 if i == iface then
329                         return true
330                 end
331         end
332
333         for i, _ in pairs(ifs) do
334                 if ifs[i].dev and ifs[i].dev.network == self:name() then
335                         return true
336                 end
337         end
338
339         return false
340 end
341
342
343 interface = utl.class()
344 function interface.__init__(self, ifname)
345         if ifs[ifname] then
346                 self.ifname = ifname
347                 self.dev    = ifs[ifname]
348                 self.br     = brs[ifname]
349         end
350 end
351
352 function interface.name(self)
353         return self.ifname
354 end
355
356 function interface.mac(self)
357         return self.dev.macaddr or "00:00:00:00:00:00"
358 end
359
360 function interface.ipaddrs(self)
361         return self.dev.ipaddrs or { }
362 end
363
364 function interface.ip6addrs(self)
365         return self.dev.ip6addrs or { }
366 end
367
368 function interface.type(self)
369         if self.dev and self.dev.type then
370                 return self.dev.type
371         elseif brs[self.ifname] then
372                 return "bridge"
373         elseif sws[self.ifname] or self.ifname:match("%.") then
374                 return "switch"
375         else
376                 return "ethernet"
377         end
378 end
379
380 function interface.shortname(self)
381         if self.dev and self.dev.handler then
382                 return self.dev.handler:shortname(self)
383         else
384                 return self.ifname
385         end
386 end
387
388 function interface.get_i18n(self)
389         if self.dev and self.dev.handler then
390                 return self.dev.handler:get_i18n(self)
391         else
392                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
393         end
394 end
395
396 function interface.get_type_i18n(self)
397         local x = self:type()
398         if x == "wifi" then
399                 return i18n.translate("a_s_if_wifidev", "Wireless Adapter")
400         elseif x == "bridge" then
401                 return i18n.translate("a_s_if_bridge", "Bridge")
402         elseif x == "switch" then
403                 return i18n.translate("a_s_if_ethswitch", "Ethernet Switch")
404         else
405                 return i18n.translate("a_s_if_ethdev", "Ethernet Adapter")
406         end
407 end
408
409 function interface.ports(self)
410         if self.br then
411                 local iface
412                 local ifaces = { }
413                 for _, iface in ipairs(self.br.ifnames) do
414                         ifaces[#ifaces+1] = interface(iface.name)
415                 end
416                 return ifaces
417         end
418 end
419
420 function interface.bridge_id(self)
421         if self.br then
422                 return self.br.id
423         else
424                 return nil
425         end
426 end
427
428 function interface.bridge_stp(self)
429         if self.br then
430                 return self.br.stp
431         else
432                 return false
433         end
434 end
435
436 function interface.is_up(self)
437         return self.dev.flags and self.dev.flags.up
438 end
439
440 function interface.is_bridge(self)
441         return (self:type() == "bridge")
442 end
443
444 function interface.is_bridgeport(self)
445         return self.dev and self.dev.bridge and true or false
446 end
447
448 function interface.tx_bytes(self)
449         return self.dev and self.dev.stats
450                 and self.dev.stats.tx_bytes or 0
451 end
452
453 function interface.rx_bytes(self)
454         return self.dev and self.dev.stats
455                 and self.dev.stats.rx_bytes or 0
456 end
457
458 function interface.tx_packets(self)
459         return self.dev and self.dev.stats
460                 and self.dev.stats.tx_packets or 0
461 end
462
463 function interface.rx_packets(self)
464         return self.dev and self.dev.stats
465                 and self.dev.stats.rx_packets or 0
466 end
467
468 function interface.get_network(self)
469         if self.dev and self.dev.network then
470                 self.network = _M:get_network(self.dev.network)
471         end
472
473         if not self.network then
474                 local net
475                 for _, net in ipairs(_M:get_networks()) do
476                         if net:contains_interface(self.ifname) then
477                                 self.network = net
478                                 return net
479                         end
480                 end
481         else
482                 return self.network
483         end
484 end
485