modules/admin-full: when toggling a wifi-iface, first bring the radio down, then...
[project/luci.git] / modules / admin-full / luasrc / controller / admin / network.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11         http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14 ]]--
15
16 module("luci.controller.admin.network", package.seeall)
17
18 function index()
19         local uci = require("luci.model.uci").cursor()
20         local net = require "luci.model.network".init(uci)
21         local has_wifi = nixio.fs.stat("/etc/config/wireless")
22         local has_switch = false
23
24         uci:foreach("network", "switch",
25                 function(s)
26                         has_switch = true
27                         return false
28                 end
29         )
30
31         local page
32
33         page = node("admin", "network")
34         page.target = firstchild()
35         page.title  = _("Network")
36         page.order  = 50
37         page.index  = true
38
39         if has_switch then
40                 page  = node("admin", "network", "vlan")
41                 page.target = cbi("admin_network/vlan")
42                 page.title  = _("Switch")
43                 page.order  = 20
44         end
45
46         if has_wifi and has_wifi.size > 0 then
47                 page = entry({"admin", "network", "wireless"}, arcombine(template("admin_network/wifi_overview"), cbi("admin_network/wifi")), _("Wifi"), 15)
48                 page.leaf = true
49                 page.subindex = true
50
51                 page = entry({"admin", "network", "wireless_join"}, call("wifi_join"), nil)
52                 page.leaf = true
53
54                 page = entry({"admin", "network", "wireless_add"}, call("wifi_add"), nil)
55                 page.leaf = true
56
57                 page = entry({"admin", "network", "wireless_delete"}, call("wifi_delete"), nil)
58                 page.leaf = true
59
60                 page = entry({"admin", "network", "wireless_status"}, call("wifi_status"), nil)
61                 page.leaf = true
62
63                 page = entry({"admin", "network", "wireless_reconnect"}, call("wifi_reconnect"), nil)
64                 page.leaf = true
65
66                 page = entry({"admin", "network", "wireless_shutdown"}, call("wifi_reconnect"), nil)
67                 page.leaf = true
68
69                 local wdev
70                 for _, wdev in ipairs(net:get_wifidevs()) do
71                         local wnet
72                         for _, wnet in ipairs(wdev:get_wifinets()) do
73                                 entry(
74                                         {"admin", "network", "wireless", wnet:id()},
75                                         alias("admin", "network", "wireless"),
76                                         wdev:name() .. ": " .. wnet:shortname()
77                                 )
78                         end
79                 end
80         end
81
82         page = entry({"admin", "network", "network"}, arcombine(cbi("admin_network/network"), cbi("admin_network/ifaces")), _("Interfaces"), 10)
83         page.leaf   = true
84         page.subindex = true
85
86         page = entry({"admin", "network", "iface_add"}, cbi("admin_network/iface_add"), nil)
87         page.leaf = true
88
89         page = entry({"admin", "network", "iface_delete"}, call("iface_delete"), nil)
90         page.leaf = true
91
92         page = entry({"admin", "network", "iface_status"}, call("iface_status"), nil)
93         page.leaf = true
94
95         page = entry({"admin", "network", "iface_reconnect"}, call("iface_reconnect"), nil)
96         page.leaf = true
97
98         page = entry({"admin", "network", "iface_shutdown"}, call("iface_shutdown"), nil)
99         page.leaf = true
100
101         uci:foreach("network", "interface",
102                 function (section)
103                         local ifc = section[".name"]
104                         if ifc ~= "loopback" then
105                                 entry({"admin", "network", "network", ifc},
106                                  true,
107                                  ifc:upper())
108                         end
109                 end
110         )
111
112         if nixio.fs.access("/etc/config/dhcp") then
113                 page = node("admin", "network", "dhcp")
114                 page.target = cbi("admin_network/dhcp")
115                 page.title  = _("DHCP and DNS")
116                 page.order  = 30
117
118                 page = entry({"admin", "network", "dhcplease_status"}, call("lease_status"), nil)
119                 page.leaf = true
120
121                 page = node("admin", "network", "hosts")
122                 page.target = cbi("admin_network/hosts")
123                 page.title  = _("Hostnames")
124                 page.order  = 40
125         end
126
127         page  = node("admin", "network", "routes")
128         page.target = cbi("admin_network/routes")
129         page.title  = _("Static Routes")
130         page.order  = 50
131
132         page = node("admin", "network", "diagnostics")
133         page.target = template("admin_network/diagnostics")
134         page.title  = _("Diagnostics")
135         page.order  = 60
136
137         page = entry({"admin", "network", "diag_ping"}, call("diag_ping"), nil)
138         page.leaf = true
139
140         page = entry({"admin", "network", "diag_nslookup"}, call("diag_nslookup"), nil)
141         page.leaf = true
142
143         page = entry({"admin", "network", "diag_traceroute"}, call("diag_traceroute"), nil)
144         page.leaf = true
145 end
146
147 function wifi_join()
148         local function param(x)
149                 return luci.http.formvalue(x)
150         end
151
152         local function ptable(x)
153                 x = param(x)
154                 return x and (type(x) ~= "table" and { x } or x) or {}
155         end
156
157         local dev  = param("device")
158         local ssid = param("join")
159
160         if dev and ssid then
161                 local cancel  = (param("cancel") or param("cbi.cancel")) and true or false
162
163                 if cancel then
164                         luci.http.redirect(luci.dispatcher.build_url("admin/network/wireless_join?device=" .. dev))
165                 else
166                         local cbi = require "luci.cbi"
167                         local tpl = require "luci.template"
168                         local map = luci.cbi.load("admin_network/wifi_add")[1]
169
170                         if map:parse() ~= cbi.FORM_DONE then
171                                 tpl.render("header")
172                                 map:render()
173                                 tpl.render("footer")
174                         end
175                 end
176         else
177                 luci.template.render("admin_network/wifi_join")
178         end
179 end
180
181 function wifi_add()
182         local dev = luci.http.formvalue("device")
183         local ntm = require "luci.model.network".init()
184
185         dev = dev and ntm:get_wifidev(dev)
186
187         if dev then
188                 local net = dev:add_wifinet({
189                         mode       = "ap",
190                         ssid       = "OpenWrt",
191                         encryption = "none"
192                 })
193
194                 ntm:save("wireless")
195                 luci.http.redirect(net:adminlink())
196         end
197 end
198
199 function wifi_delete(network)
200         local ntm = require "luci.model.network".init()
201         local wnet = ntm:get_wifinet(network)
202         if wnet then
203                 local dev = wnet:get_device()
204                 local net = wnet:get_network()
205                 if dev then
206                         luci.sys.call("env -i /sbin/wifi down %q" % dev:name())
207                         ntm:del_wifinet(network)
208                         ntm:commit("wireless")
209                         if net:is_empty() then
210                                 ntm:del_network(net:name())
211                                 ntm:commit("network")
212                         end
213                         luci.sys.call("env -i /sbin/wifi up %q" % dev:name())
214                 end
215         end
216
217         luci.http.redirect(luci.dispatcher.build_url("admin/network/wireless"))
218 end
219
220 function iface_status()
221         local path = luci.dispatcher.context.requestpath
222         local netm = require "luci.model.network".init()
223         local rv   = { }
224
225         local iface
226         for iface in path[#path]:gmatch("[%w%.%-_]+") do
227                 local net = netm:get_network(iface)
228                 local device = net and net:get_interface()
229                 if device then
230                         local data = {
231                                 id         = iface,
232                                 proto      = net:proto(),
233                                 uptime     = net:uptime(),
234                                 gwaddr     = net:gwaddr(),
235                                 dnsaddrs   = net:dnsaddrs(),
236                                 name       = device:shortname(),
237                                 type       = device:type(),
238                                 ifname     = device:name(),
239                                 macaddr    = device:mac(),
240                                 is_up      = device:is_up(),
241                                 rx_bytes   = device:rx_bytes(),
242                                 tx_bytes   = device:tx_bytes(),
243                                 rx_packets = device:rx_packets(),
244                                 tx_packets = device:tx_packets(),
245
246                                 ipaddrs    = { },
247                                 ip6addrs   = { },
248                                 subdevices = { }
249                         }
250
251                         local _, a
252                         for _, a in ipairs(device:ipaddrs()) do
253                                 data.ipaddrs[#data.ipaddrs+1] = {
254                                         addr      = a:host():string(),
255                                         netmask   = a:mask():string(),
256                                         prefix    = a:prefix()
257                                 }
258                         end
259                         for _, a in ipairs(device:ip6addrs()) do
260                                 data.ip6addrs[#data.ip6addrs+1] = {
261                                         addr      = a:host():string(),
262                                         netmask   = a:mask():string(),
263                                         prefix    = a:prefix()
264                                 }
265                         end
266
267                         for _, device in ipairs(net:get_interfaces() or {}) do
268                                 data.subdevices[#data.subdevices+1] = {
269                                         name       = device:shortname(),
270                                         type       = device:type(),
271                                         ifname     = device:name(),
272                                         macaddr    = device:mac(),
273                                         macaddr    = device:mac(),
274                                         is_up      = device:is_up(),
275                                         rx_bytes   = device:rx_bytes(),
276                                         tx_bytes   = device:tx_bytes(),
277                                         rx_packets = device:rx_packets(),
278                                         tx_packets = device:tx_packets(),
279                                 }
280                         end
281
282                         rv[#rv+1] = data
283                 else
284                         rv[#rv+1] = {
285                                 id   = iface,
286                                 name = iface,
287                                 type = "ethernet"
288                         }
289                 end
290         end
291
292         if #rv > 0 then
293                 luci.http.prepare_content("application/json")
294                 luci.http.write_json(rv)
295                 return
296         end
297
298         luci.http.status(404, "No such device")
299 end
300
301 function iface_reconnect()
302         local path  = luci.dispatcher.context.requestpath
303         local iface = path[#path]
304         local netmd = require "luci.model.network".init()
305
306         local net = netmd:get_network(iface)
307         if net then
308                 luci.sys.call("env -i /sbin/ifup %q >/dev/null 2>/dev/null" % iface)
309                 luci.http.status(200, "Reconnected")
310                 return
311         end
312
313         luci.http.status(404, "No such interface")
314 end
315
316 function iface_shutdown()
317         local path  = luci.dispatcher.context.requestpath
318         local iface = path[#path]
319         local netmd = require "luci.model.network".init()
320
321         local net = netmd:get_network(iface)
322         if net then
323                 luci.sys.call("env -i /sbin/ifdown %q >/dev/null 2>/dev/null" % iface)
324                 luci.http.status(200, "Shutdown")
325                 return
326         end
327
328         luci.http.status(404, "No such interface")
329 end
330
331 function iface_delete()
332         local path  = luci.dispatcher.context.requestpath
333         local iface = path[#path]
334         local netmd = require "luci.model.network".init()
335
336         local net = netmd:del_network(iface)
337         if net then
338                 luci.sys.call("env -i /sbin/ifdown %q >/dev/null 2>/dev/null" % iface)
339                 luci.http.redirect(luci.dispatcher.build_url("admin/network/network"))
340                 netmd:commit("network")
341                 netmd:commit("wireless")
342                 return
343         end
344
345         luci.http.status(404, "No such interface")
346 end
347
348 function wifi_status()
349         local path = luci.dispatcher.context.requestpath
350         local s    = require "luci.tools.status"
351         local rv   = { }
352
353         local dev
354         for dev in path[#path]:gmatch("[%w%.%-]+") do
355                 rv[#rv+1] = s.wifi_network(dev)
356         end
357
358         if #rv > 0 then
359                 luci.http.prepare_content("application/json")
360                 luci.http.write_json(rv)
361                 return
362         end
363
364         luci.http.status(404, "No such device")
365 end
366
367 function wifi_reconnect()
368         local path  = luci.dispatcher.context.requestpath
369         local mode  = path[#path-1]
370         local wnet  = path[#path]
371         local netmd = require "luci.model.network".init()
372
373         local net = netmd:get_wifinet(wnet)
374         local dev = net:get_device()
375         if dev and net then
376                 luci.sys.call("env -i /sbin/wifi down >/dev/null 2>/dev/null")
377
378                 dev:set("disabled", nil)
379                 net:set("disabled", (mode == "wireless_shutdown") and 1 or nil)
380                 netmd:commit("wireless")
381
382                 luci.sys.call("env -i /sbin/wifi up >/dev/null 2>/dev/null")
383                 luci.http.status(200, (mode == "wireless_shutdown") and "Shutdown" or "Reconnected")
384
385                 return
386         end
387
388         luci.http.status(404, "No such radio")
389 end
390
391 function lease_status()
392         local s = require "luci.tools.status"
393
394         luci.http.prepare_content("application/json")
395         luci.http.write_json(s.dhcp_leases())
396 end
397
398 function diag_command(cmd)
399         local path = luci.dispatcher.context.requestpath
400         local addr = path[#path]
401
402         if addr and addr:match("^[a-zA-Z0-9%-%.:_]+$") then
403                 luci.http.prepare_content("text/plain")
404
405                 local util = io.popen(cmd % addr)
406                 if util then
407                         while true do
408                                 local ln = util:read("*l")
409                                 if not ln then break end
410                                 luci.http.write(ln)
411                                 luci.http.write("\n")
412                         end
413
414                         util:close()
415                 end
416
417                 return
418         end
419
420         luci.http.status(500, "Bad address")
421 end
422
423 function diag_ping()
424         diag_command("ping -c 5 -W 1 %q 2>&1")
425 end
426
427 function diag_traceroute()
428         diag_command("traceroute -q 1 -w 1 -n %q 2>&1")
429 end
430
431 function diag_nslookup()
432         diag_command("nslookup %q 2>&1")
433 end