luci-mod-network: switch to client side interface configuration pages
[oweals/luci.git] / modules / luci-mod-network / luasrc / controller / admin / network.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2011-2018 Jo-Philipp Wich <jo@mein.io>
3 -- Licensed to the public under the Apache License 2.0.
4
5 module("luci.controller.admin.network", package.seeall)
6
7 function index()
8         local uci = require("luci.model.uci").cursor()
9         local page
10
11 --      if page.inreq then
12                 local has_switch = false
13
14                 uci:foreach("network", "switch",
15                         function(s)
16                                 has_switch = true
17                                 return false
18                         end)
19
20                 if has_switch then
21                         page  = node("admin", "network", "vlan")
22                         page.target = cbi("admin_network/vlan")
23                         page.title  = _("Switch")
24                         page.order  = 20
25
26                         page = entry({"admin", "network", "switch_status"}, call("switch_status"), nil)
27                         page.leaf = true
28                 end
29
30
31                 local has_wifi = false
32
33                 uci:foreach("wireless", "wifi-device",
34                         function(s)
35                                 has_wifi = true
36                                 return false
37                         end)
38
39                 if has_wifi then
40                         page = entry({"admin", "network", "wireless_join"}, post("wifi_join"), nil)
41                         page.leaf = true
42
43                         page = entry({"admin", "network", "wireless_add"}, post("wifi_add"), nil)
44                         page.leaf = true
45
46                         page = entry({"admin", "network", "wireless_status"}, call("wifi_status"), nil)
47                         page.leaf = true
48
49                         page = entry({"admin", "network", "wireless_reconnect"}, post("wifi_reconnect"), nil)
50                         page.leaf = true
51
52                         page = entry({"admin", "network", "wireless_scan_trigger"}, post("wifi_scan_trigger"), nil)
53                         page.leaf = true
54
55                         page = entry({"admin", "network", "wireless_scan_results"}, call("wifi_scan_results"), nil)
56                         page.leaf = true
57
58                         page = entry({"admin", "network", "wireless"}, arcombine(cbi("admin_network/wifi_overview"), cbi("admin_network/wifi")), _("Wireless"), 15)
59                         page.leaf = true
60                         page.subindex = true
61
62                         if page.inreq then
63                                 local wdev
64                                 local net = require "luci.model.network".init(uci)
65                                 for _, wdev in ipairs(net:get_wifidevs()) do
66                                         local wnet
67                                         for _, wnet in ipairs(wdev:get_wifinets()) do
68                                                 entry(
69                                                         {"admin", "network", "wireless", wnet:id()},
70                                                         alias("admin", "network", "wireless"),
71                                                         wdev:name() .. ": " .. wnet:shortname()
72                                                 )
73                                         end
74                                 end
75                         end
76                 end
77
78
79                 page = entry({"admin", "network", "iface_status"}, call("iface_status"), nil)
80                 page.leaf = true
81
82                 page = entry({"admin", "network", "iface_reconnect"}, post("iface_reconnect"), nil)
83                 page.leaf = true
84
85                 page = entry({"admin", "network", "iface_down"}, post("iface_down"), nil)
86                 page.leaf = true
87
88                 page = entry({"admin", "network", "network"}, view("network/interfaces"), _("Interfaces"), 10)
89                 page.leaf   = true
90                 page.subindex = true
91
92
93                 if nixio.fs.access("/etc/config/dhcp") then
94                         page = node("admin", "network", "dhcp")
95                         page.target = view("network/dhcp")
96                         page.title  = _("DHCP and DNS")
97                         page.order  = 30
98
99                         page = node("admin", "network", "hosts")
100                         page.target = view("network/hosts")
101                         page.title  = _("Hostnames")
102                         page.order  = 40
103                 end
104
105                 page  = node("admin", "network", "routes")
106                 page.target = view("network/routes")
107                 page.title  = _("Static Routes")
108                 page.order  = 50
109
110                 page = node("admin", "network", "diagnostics")
111                 page.target = template("admin_network/diagnostics")
112                 page.title  = _("Diagnostics")
113                 page.order  = 60
114
115                 page = entry({"admin", "network", "diag_ping"}, post("diag_ping"), nil)
116                 page.leaf = true
117
118                 page = entry({"admin", "network", "diag_nslookup"}, post("diag_nslookup"), nil)
119                 page.leaf = true
120
121                 page = entry({"admin", "network", "diag_traceroute"}, post("diag_traceroute"), nil)
122                 page.leaf = true
123
124                 page = entry({"admin", "network", "diag_ping6"}, post("diag_ping6"), nil)
125                 page.leaf = true
126
127                 page = entry({"admin", "network", "diag_traceroute6"}, post("diag_traceroute6"), nil)
128                 page.leaf = true
129 --      end
130 end
131
132 function wifi_join()
133         local tpl  = require "luci.template"
134         local http = require "luci.http"
135         local dev  = http.formvalue("device")
136         local ssid = http.formvalue("join")
137
138         if dev and ssid then
139                 local cancel = (http.formvalue("cancel") or http.formvalue("cbi.cancel"))
140                 if not cancel then
141                         local cbi = require "luci.cbi"
142                         local map = luci.cbi.load("admin_network/wifi_add")[1]
143
144                         if map:parse() ~= cbi.FORM_DONE then
145                                 tpl.render("header")
146                                 map:render()
147                                 tpl.render("footer")
148                         end
149
150                         return
151                 end
152         end
153
154         tpl.render("admin_network/wifi_join")
155 end
156
157 function wifi_add()
158         local dev = luci.http.formvalue("device")
159         local ntm = require "luci.model.network".init()
160
161         dev = dev and ntm:get_wifidev(dev)
162
163         if dev then
164                 local net = dev:add_wifinet({
165                         mode       = "ap",
166                         ssid       = "OpenWrt",
167                         encryption = "none",
168                         disabled   = 1
169                 })
170
171                 ntm:save("wireless")
172                 luci.http.redirect(net:adminlink())
173         end
174 end
175
176 function iface_status(ifaces)
177         local netm = require "luci.model.network".init()
178         local rv   = { }
179
180         local iface
181         for iface in ifaces:gmatch("[%w%.%-_]+") do
182                 local net = netm:get_network(iface)
183                 local device = net and net:get_interface()
184                 if device then
185                         local data = {
186                                 id         = iface,
187                                 desc       = net:get_i18n(),
188                                 proto      = net:proto(),
189                                 uptime     = net:uptime(),
190                                 gwaddr     = net:gwaddr(),
191                                 ipaddrs    = net:ipaddrs(),
192                                 ip6addrs   = net:ip6addrs(),
193                                 dnsaddrs   = net:dnsaddrs(),
194                                 ip6prefix  = net:ip6prefix(),
195                                 errors     = net:errors(),
196                                 name       = device:shortname(),
197                                 type       = device:type(),
198                                 typename   = device:get_type_i18n(),
199                                 ifname     = device:name(),
200                                 macaddr    = device:mac(),
201                                 is_up      = net:is_up() and device:is_up(),
202                                 is_alias   = net:is_alias(),
203                                 is_dynamic = net:is_dynamic(),
204                                 is_auto    = net:is_auto(),
205                                 rx_bytes   = device:rx_bytes(),
206                                 tx_bytes   = device:tx_bytes(),
207                                 rx_packets = device:rx_packets(),
208                                 tx_packets = device:tx_packets(),
209
210                                 subdevices = { }
211                         }
212
213                         for _, device in ipairs(net:get_interfaces() or {}) do
214                                 data.subdevices[#data.subdevices+1] = {
215                                         name       = device:shortname(),
216                                         type       = device:type(),
217                                         typename   = device:get_type_i18n(),
218                                         ifname     = device:name(),
219                                         macaddr    = device:mac(),
220                                         is_up      = device:is_up(),
221                                         rx_bytes   = device:rx_bytes(),
222                                         tx_bytes   = device:tx_bytes(),
223                                         rx_packets = device:rx_packets(),
224                                         tx_packets = device:tx_packets(),
225                                 }
226                         end
227
228                         rv[#rv+1] = data
229                 else
230                         rv[#rv+1] = {
231                                 id   = iface,
232                                 name = iface,
233                                 type = "ethernet"
234                         }
235                 end
236         end
237
238         if #rv > 0 then
239                 luci.http.prepare_content("application/json")
240                 luci.http.write_json(rv)
241                 return
242         end
243
244         luci.http.status(404, "No such device")
245 end
246
247 function iface_reconnect(iface)
248         local netmd = require "luci.model.network".init()
249         local net = netmd:get_network(iface)
250         if net then
251                 luci.sys.call("env -i /sbin/ifup %s >/dev/null 2>/dev/null"
252                         % luci.util.shellquote(iface))
253                 luci.http.status(200, "Reconnected")
254                 return
255         end
256
257         luci.http.status(404, "No such interface")
258 end
259
260 local function addr2dev(addr, src)
261         local ip = require "luci.ip"
262         local route = ip.route(addr, src)
263         if not src and route and route.src then
264                 route = ip.route(addr, route.src:string())
265         end
266         return route and route.dev
267 end
268
269 function iface_down(iface, force)
270         local netmd = require "luci.model.network".init()
271         local peer = luci.http.getenv("REMOTE_ADDR")
272         local serv = luci.http.getenv("SERVER_ADDR")
273
274         if force ~= "force" and serv and peer then
275                 local dev = addr2dev(peer, serv)
276                 if dev then
277                         local nets = netmd:get_networks()
278                         local outnet = nil
279                         local _, net, ai
280
281                         for _, net in ipairs(nets) do
282                                 if net:contains_interface(dev) then
283                                         outnet = net
284                                         break
285                                 end
286                         end
287
288                         if outnet:name() == iface then
289                                 luci.http.status(409, "Is inbound interface")
290                                 return
291                         end
292
293                         local peeraddr = outnet:get("peeraddr")
294                         for _, ai in ipairs(peeraddr and nixio.getaddrinfo(peeraddr) or {}) do
295                                 local peerdev = addr2dev(ai.address)
296                                 for _, net in ipairs(peerdev and nets or {}) do
297                                         if net:contains_interface(peerdev) and net:name() == iface then
298                                                 luci.http.status(409, "Is inbound interface")
299                                                 return
300                                         end
301                                 end
302                         end
303                 end
304         end
305
306         if netmd:get_network(iface) then
307                 luci.sys.call("env -i /sbin/ifdown %s >/dev/null 2>/dev/null"
308                         % luci.util.shellquote(iface))
309                 luci.http.status(200, "Shut down")
310                 return
311         end
312
313         luci.http.status(404, "No such interface")
314 end
315
316 function wifi_status(devs)
317         local s    = require "luci.tools.status"
318         local rv   = { }
319
320         if type(devs) == "string" then
321                 local dev
322                 for dev in devs:gmatch("[%w%.%-]+") do
323                         rv[#rv+1] = s.wifi_network(dev)
324                 end
325         end
326
327         if #rv > 0 then
328                 luci.http.prepare_content("application/json")
329                 luci.http.write_json(rv)
330                 return
331         end
332
333         luci.http.status(404, "No such device")
334 end
335
336 function wifi_reconnect(radio)
337         local rc = luci.sys.call("env -i /sbin/wifi up %s" % luci.util.shellquote(radio))
338
339         if rc == 0 then
340                 luci.http.status(200, "Reconnected")
341         else
342                 luci.http.status(500, "Error")
343         end
344 end
345
346 local function _wifi_get_scan_results(cache_key)
347         local results = luci.util.ubus("session", "get", {
348                 ubus_rpc_session = luci.model.uci:get_session_id(),
349                 keys = { cache_key }
350         })
351
352         if type(results) == "table" and
353            type(results.values) == "table" and
354            type(results.values[cache_key]) == "table"
355         then
356                 return results.values[cache_key]
357         end
358
359         return nil
360 end
361
362 function wifi_scan_trigger(radio, update)
363         local iw = radio and luci.sys.wifi.getiwinfo(radio)
364
365         if not iw then
366                 luci.http.status(404, "No such radio device")
367                 return
368         end
369
370         luci.http.status(204, "Scan scheduled")
371
372         if nixio.fork() == 0 then
373                 io.stderr:close()
374                 io.stdout:close()
375
376                 local _, bss
377                 local data, bssids = { }, { }
378                 local cache_key = "scan_%s" % radio
379
380                 luci.util.ubus("session", "set", {
381                         ubus_rpc_session = luci.model.uci:get_session_id(),
382                         values = { [cache_key] = nil }
383                 })
384
385                 for _, bss in ipairs(iw.scanlist or { }) do
386                         data[_] = bss
387                         bssids[bss.bssid] = bss
388                 end
389
390                 if update then
391                         local cached = _wifi_get_scan_results(cache_key)
392                         if cached then
393                                 for _, bss in ipairs(cached) do
394                                         if not bssids[bss.bssid] then
395                                                 bss.stale = true
396                                                 data[#data + 1] = bss
397                                         end
398                                 end
399                         end
400                 end
401
402                 luci.util.ubus("session", "set", {
403                         ubus_rpc_session = luci.model.uci:get_session_id(),
404                         values = { [cache_key] = data }
405                 })
406         end
407 end
408
409 function wifi_scan_results(radio)
410         local results = radio and _wifi_get_scan_results("scan_%s" % radio)
411
412         if results then
413                 luci.http.prepare_content("application/json")
414                 luci.http.write_json(results)
415         else
416                 luci.http.status(404, "No wireless scan results")
417         end
418 end
419
420 function switch_status(switches)
421         local s = require "luci.tools.status"
422
423         luci.http.prepare_content("application/json")
424         luci.http.write_json(s.switch_status(switches))
425 end
426
427 function diag_command(cmd, addr)
428         if addr and addr:match("^[a-zA-Z0-9%-%.:_]+$") then
429                 luci.http.prepare_content("text/plain")
430
431                 local util = io.popen(cmd % luci.util.shellquote(addr))
432                 if util then
433                         while true do
434                                 local ln = util:read("*l")
435                                 if not ln then break end
436                                 luci.http.write(ln)
437                                 luci.http.write("\n")
438                         end
439
440                         util:close()
441                 end
442
443                 return
444         end
445
446         luci.http.status(500, "Bad address")
447 end
448
449 function diag_ping(addr)
450         diag_command("ping -c 5 -W 1 %s 2>&1", addr)
451 end
452
453 function diag_traceroute(addr)
454         diag_command("traceroute -q 1 -w 1 -n %s 2>&1", addr)
455 end
456
457 function diag_nslookup(addr)
458         diag_command("nslookup %s 2>&1", addr)
459 end
460
461 function diag_ping6(addr)
462         diag_command("ping6 -c 5 %s 2>&1", addr)
463 end
464
465 function diag_traceroute6(addr)
466         diag_command("traceroute6 -q 1 -w 2 -n %s 2>&1", addr)
467 end