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