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