mirror of https://github.com/aredn/aredn.git
Scan cache (#1160)
* Cache the last wifi scan and update it when a re-scan is requested. This change was suggested as a way of handling Ubiquiti AC devices which disconnect while scanning and making retrieving the results problematic if that was your connection. Now we scan and store the results so they can be retrieved later. In fact we no longer scan when navigating to this page but require an explicity scan button push. This make the page generally more responsive when initially navigated to.
This commit is contained in:
parent
c25be28b97
commit
86271040a0
|
@ -36,6 +36,7 @@
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
require("nixio")
|
require("nixio")
|
||||||
|
require("luci.jsonc")
|
||||||
require("aredn.http")
|
require("aredn.http")
|
||||||
require("aredn.hardware")
|
require("aredn.hardware")
|
||||||
require("aredn.utils")
|
require("aredn.utils")
|
||||||
|
@ -55,125 +56,208 @@ if board_type:match("^ubnt,") and board_type:match("ac") then
|
||||||
ubnt_ac = true
|
ubnt_ac = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local channels = aredn.hardware.get_rfchannels(wifiiface)
|
local rescan = string.find((nixio.getenv("QUERY_STRING") or ""):lower(),"rescan=1")
|
||||||
local scan_list = ""
|
|
||||||
for _, channel in ipairs(channels)
|
|
||||||
do
|
|
||||||
scan_list = scan_list .. " " .. channel.frequency
|
|
||||||
end
|
|
||||||
|
|
||||||
-- scan start
|
local scanlist = {}
|
||||||
|
-- Show the last scan if we have one, it's not too old, and we're not rescanning
|
||||||
local scanned = {}
|
if not rescan then
|
||||||
|
local lscan = io.open("/tmp/last-scan.json")
|
||||||
local f = io.popen("iw dev " .. wifiiface .. " station dump")
|
if lscan then
|
||||||
if f then
|
scanlist = luci.jsonc.parse(lscan:read("*a") or "")
|
||||||
local scan = {}
|
lscan:close()
|
||||||
local myssid = aredn.info.getSSID()
|
|
||||||
for line in f:lines()
|
|
||||||
do
|
|
||||||
local m = line:match("^Station ([%da-fA-F:]+) %(on " .. wifiiface .. "%)")
|
|
||||||
if m then
|
|
||||||
scan = scanned[m]
|
|
||||||
if not scan then
|
|
||||||
scan = {
|
|
||||||
mac = m,
|
|
||||||
signal = 9999,
|
|
||||||
freq = {},
|
|
||||||
key = "",
|
|
||||||
joined = false
|
|
||||||
}
|
|
||||||
scanned[m] = scan
|
|
||||||
end
|
|
||||||
scan.mode = "Connected Ad-Hoc Station"
|
|
||||||
scan.ssid = myssid
|
|
||||||
scan.freq[myfreq] = true
|
|
||||||
end
|
|
||||||
m = line:match("signal avg:%s+([%d%-]+)")
|
|
||||||
if m then
|
|
||||||
scan.signal = tonumber(m)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
f:close()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Ubiquiti AC device workaround
|
if rescan then
|
||||||
if ubnt_ac then
|
local channels = aredn.hardware.get_rfchannels(wifiiface)
|
||||||
os.execute("iw dev " .. wifiiface .. " ibss leave > /dev/null 2>&1")
|
local scan_list = ""
|
||||||
os.execute("wifi up > /dev/null 2>&1")
|
for _, channel in ipairs(channels)
|
||||||
local attempt = 10
|
|
||||||
while attempt > 0
|
|
||||||
do
|
do
|
||||||
attempt = attempt - 1
|
scan_list = scan_list .. " " .. channel.frequency
|
||||||
for line in io.popen("iw dev " .. wifiiface .. " scan"):lines()
|
end
|
||||||
|
|
||||||
|
-- scan start
|
||||||
|
|
||||||
|
local scanned = {}
|
||||||
|
|
||||||
|
local f = io.popen("iw dev " .. wifiiface .. " station dump")
|
||||||
|
if f then
|
||||||
|
local scan = {}
|
||||||
|
local myssid = aredn.info.getSSID()
|
||||||
|
for line in f:lines()
|
||||||
do
|
do
|
||||||
if line:match("^BSS ") then
|
local m = line:match("^Station ([%da-fA-F:]+) %(on " .. wifiiface .. "%)")
|
||||||
attempt = 0
|
if m then
|
||||||
|
scan = scanned[m]
|
||||||
|
if not scan then
|
||||||
|
scan = {
|
||||||
|
mac = m,
|
||||||
|
signal = 9999,
|
||||||
|
freq = {},
|
||||||
|
key = "",
|
||||||
|
joined = false
|
||||||
|
}
|
||||||
|
scanned[m] = scan
|
||||||
|
end
|
||||||
|
scan.mode = "Connected Ad-Hoc Station"
|
||||||
|
scan.ssid = myssid
|
||||||
|
scan.freq[tostring(myfreq)] = true
|
||||||
|
end
|
||||||
|
m = line:match("signal avg:%s+([%d%-]+)")
|
||||||
|
if m then
|
||||||
|
scan.signal = tonumber(m)
|
||||||
end
|
end
|
||||||
break
|
|
||||||
end
|
end
|
||||||
nixio.nanosleep(2, 0)
|
f:close()
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
local f = io.popen("iw dev " .. wifiiface .. " scan freq" .. scan_list .. " passive")
|
-- Ubiquiti AC device workaround
|
||||||
if f then
|
if ubnt_ac then
|
||||||
local scan = {}
|
os.execute("iw dev " .. wifiiface .. " ibss leave > /dev/null 2>&1")
|
||||||
for line in f:lines()
|
os.execute("wifi up > /dev/null 2>&1")
|
||||||
|
local attempt = 10
|
||||||
|
while attempt > 0
|
||||||
|
do
|
||||||
|
attempt = attempt - 1
|
||||||
|
for line in io.popen("iw dev " .. wifiiface .. " scan"):lines()
|
||||||
|
do
|
||||||
|
if line:match("^BSS ") then
|
||||||
|
attempt = 0
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
nixio.nanosleep(2, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = io.popen("iw dev " .. wifiiface .. " scan freq" .. scan_list .. " passive")
|
||||||
|
if f then
|
||||||
|
local scan = {}
|
||||||
|
for line in f:lines()
|
||||||
|
do
|
||||||
|
local m = line:match("^BSS ([%da-fA-F:]+)")
|
||||||
|
if m then
|
||||||
|
scan = scanned[m]
|
||||||
|
if not scan then
|
||||||
|
scan = {
|
||||||
|
mac = m,
|
||||||
|
mode = "AP",
|
||||||
|
ssid = "",
|
||||||
|
signal = 9999,
|
||||||
|
freq = {},
|
||||||
|
key = "",
|
||||||
|
joined = false
|
||||||
|
}
|
||||||
|
scanned[m] = scan
|
||||||
|
elseif scan.joined then
|
||||||
|
scan = {
|
||||||
|
freq = {}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
if line:match("joined") then
|
||||||
|
scan.mode = "My Ad-Hoc Network"
|
||||||
|
scan.joined = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m = line:match("freq: (%d+)")
|
||||||
|
if m then
|
||||||
|
scan.freq[m] = true
|
||||||
|
if tonumber(m) == myfreq and scan.mode == "AP" then
|
||||||
|
scan.mode = "My Ad-Hoc Network"
|
||||||
|
scan.joined = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m = line:match("SSID: (.+)")
|
||||||
|
if m then
|
||||||
|
scan.ssid = m
|
||||||
|
end
|
||||||
|
m = line:match("signal: ([%d%-]+)")
|
||||||
|
if m then
|
||||||
|
scan.signal = tonumber(m)
|
||||||
|
end
|
||||||
|
m = line:match("Group cipher: (.+)")
|
||||||
|
if m then
|
||||||
|
scan.key = m
|
||||||
|
end
|
||||||
|
if line:match("capability: IBSS") and scan.mode == "AP" then
|
||||||
|
scan.mode = "Foreign Ad-Hoc Network"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- scan end
|
||||||
|
|
||||||
|
-- load arp cache
|
||||||
|
local arpcache = {}
|
||||||
|
arptable(function(a)
|
||||||
|
arpcache[a["HW address"]] = a["IP address"]
|
||||||
|
end)
|
||||||
|
|
||||||
|
scanlist = {}
|
||||||
|
for _, v in pairs(scanned)
|
||||||
do
|
do
|
||||||
local m = line:match("^BSS ([%da-fA-F:]+)")
|
if v.signal ~= 9999 or v.joined then
|
||||||
if m then
|
scanlist[#scanlist + 1] = v
|
||||||
scan = scanned[m]
|
|
||||||
if not scan then
|
|
||||||
scan = {
|
|
||||||
mac = m,
|
|
||||||
mode = "AP",
|
|
||||||
ssid = "",
|
|
||||||
signal = 9999,
|
|
||||||
freq = {},
|
|
||||||
key = "",
|
|
||||||
joined = false
|
|
||||||
}
|
|
||||||
scanned[m] = scan
|
|
||||||
elseif scan.joined then
|
|
||||||
scan = {
|
|
||||||
freq = {}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
if line:match("joined") then
|
|
||||||
scan.mode = "My Ad-Hoc Network"
|
|
||||||
scan.joined = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
m = line:match("freq: (%d+)")
|
|
||||||
if m then
|
|
||||||
scan.freq[m] = true
|
|
||||||
if tonumber(m) == myfreq and scan.mode == "AP" then
|
|
||||||
scan.mode = "My Ad-Hoc Network"
|
|
||||||
scan.joined = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
m = line:match("SSID: (.+)")
|
|
||||||
if m then
|
|
||||||
scan.ssid = m
|
|
||||||
end
|
|
||||||
m = line:match("signal: ([%d%-]+)")
|
|
||||||
if m then
|
|
||||||
scan.signal = tonumber(m)
|
|
||||||
end
|
|
||||||
m = line:match("Group cipher: (.+)")
|
|
||||||
if m then
|
|
||||||
scan.key = m
|
|
||||||
end
|
|
||||||
if line:match("capability: IBSS") and scan.mode == "AP" then
|
|
||||||
scan.mode = "Foreign Ad-Hoc Network"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
f:close()
|
table.sort(scanlist, function(a, b) return a.signal > b.signal end)
|
||||||
|
for _, scan in ipairs(scanlist)
|
||||||
|
do
|
||||||
|
-- freq to chan
|
||||||
|
local chan = {}
|
||||||
|
for f, _ in pairs(scan.freq)
|
||||||
|
do
|
||||||
|
f = tonumber(f)
|
||||||
|
if f < 256 then
|
||||||
|
elseif f == 2484 then
|
||||||
|
chan[#chan + 1] = 14
|
||||||
|
elseif f == 2407 then
|
||||||
|
chan[#chan + 1] = 0
|
||||||
|
elseif f < 2484 then
|
||||||
|
chan[#chan + 1] = (f - 2407) / 5
|
||||||
|
elseif f < 5000 then
|
||||||
|
elseif f < 5380 then
|
||||||
|
chan[#chan + 1] = (f - 5000) / 5
|
||||||
|
elseif f < 5500 then
|
||||||
|
chan[#chan + 1] = f - 2000
|
||||||
|
elseif f < 6000 then
|
||||||
|
chan[#chan + 1] = (f - 5000) / 5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(chan)
|
||||||
|
scan.chan = table.concat(chan, " ")
|
||||||
|
if scan.joined then
|
||||||
|
scan.hostname = node
|
||||||
|
else
|
||||||
|
-- ip lookup then host lookup
|
||||||
|
local ip = arpcache[scan.mac]
|
||||||
|
if ip then
|
||||||
|
scan.hostname = ip
|
||||||
|
local f = io.popen("nslookup " .. ip)
|
||||||
|
if f then
|
||||||
|
for line in f:lines()
|
||||||
|
do
|
||||||
|
local m = line:match("name = (.*)%.local%.mesh")
|
||||||
|
if m then
|
||||||
|
scan.hostname = m:gsub("^mid[0-9]*%.",""):gsub("^dtdlink%.",""):gsub("%.local%.mesh$","")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
scan.hostname = "-"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
lscan = io.open("/tmp/last-scan.json", "w")
|
||||||
|
if lscan then
|
||||||
|
lscan:write(luci.jsonc.stringify(scanlist, true))
|
||||||
|
lscan:close()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- scan end
|
|
||||||
|
|
||||||
-- generate page
|
-- generate page
|
||||||
http_header()
|
http_header()
|
||||||
html.header(node .. " WiFi scan", false)
|
html.header(node .. " WiFi scan", false)
|
||||||
|
@ -181,21 +265,30 @@ local autoscan = string.find((nixio.getenv("QUERY_STRING") or ""):lower(),"autos
|
||||||
if autoscan then
|
if autoscan then
|
||||||
html.print("<script>setTimeout(function(){ window.location.reload(); }, 10000);</script>")
|
html.print("<script>setTimeout(function(){ window.location.reload(); }, 10000);</script>")
|
||||||
end
|
end
|
||||||
|
if rescan then
|
||||||
|
html.print([[
|
||||||
|
<script>
|
||||||
|
if (history.replaceState) {
|
||||||
|
history.replaceState(null, "", window.location.origin + window.location.pathname)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
]])
|
||||||
|
end
|
||||||
html.print([[
|
html.print([[
|
||||||
<script src="/js/sorttable-min.js"></script>
|
<script src="/js/sorttable-min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
table {
|
table {
|
||||||
border-collapse:collapse;
|
border-collapse:collapse;
|
||||||
}
|
}
|
||||||
table.sortable thead {
|
table.sortable thead {
|
||||||
background-color:#eee;
|
background-color:#eee;
|
||||||
color:#666666;
|
color:#666666;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body><form method=post action=/cgi-bin/scan enctype='multipart/form-data'>
|
<body>
|
||||||
<center>
|
<center>
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
@ -205,9 +298,11 @@ html.print("<h1>" .. node .. " WiFi scan</h1><hr>")
|
||||||
if autoscan then
|
if autoscan then
|
||||||
html.print("<input type=button name=stop value=Stop title='Abort continuous scan' onclick='window.location = window.location.origin + window.location.pathname'>")
|
html.print("<input type=button name=stop value=Stop title='Abort continuous scan' onclick='window.location = window.location.origin + window.location.pathname'>")
|
||||||
else
|
else
|
||||||
html.print("<input type=button name=refresh value=Refresh title='Refresh this page' onclick='window.location.reload();'>")
|
html.print([[<input type=button name=refresh value=Rescan title='Run a new scan' onclick='window.location = window.location.origin + window.location.pathname + "?rescan=1"'>]])
|
||||||
html.print(" ")
|
if not ubnt_ac then
|
||||||
html.print([[<input type=button name=auto value=Auto title='Begin continuous scan' onclick='window.location = window.location.origin + window.location.pathname + "?autoscan=1"'>]])
|
html.print(" ")
|
||||||
|
html.print([[<input type=button name=auto value=Auto title='Begin continuous scan' onclick='window.location = window.location.origin + window.location.pathname + "?autoscan=1"'>]])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
html.print(" ")
|
html.print(" ")
|
||||||
|
@ -217,77 +312,34 @@ html.print("<button type=button onClick='window.location=\"status\"' title='Retu
|
||||||
html.print("<table class=sortable border=1 cellpadding=5>")
|
html.print("<table class=sortable border=1 cellpadding=5>")
|
||||||
html.print("<tr><th>SNR</th><th>Signal</th><th>Chan</th><th>Enc</th><th>SSID</th><th>Hostname</th><th>MAC/BSSID</th><th>802.11 Mode</th></tr>")
|
html.print("<tr><th>SNR</th><th>Signal</th><th>Chan</th><th>Enc</th><th>SSID</th><th>Hostname</th><th>MAC/BSSID</th><th>802.11 Mode</th></tr>")
|
||||||
|
|
||||||
-- load arp cache
|
|
||||||
local arpcache = {}
|
|
||||||
arptable(function(a)
|
|
||||||
arpcache[a["HW address"]] = a["IP address"]
|
|
||||||
end)
|
|
||||||
|
|
||||||
local scanlist = {}
|
|
||||||
for _, v in pairs(scanned)
|
|
||||||
do
|
|
||||||
if v.signal ~= 9999 or v.joined then
|
|
||||||
scanlist[#scanlist + 1] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(scanlist, function(a, b) return a.signal > b.signal end)
|
|
||||||
for _, scan in ipairs(scanlist)
|
for _, scan in ipairs(scanlist)
|
||||||
do
|
do
|
||||||
-- freq to chan
|
|
||||||
local chan = {}
|
|
||||||
for f, _ in pairs(scan.freq)
|
|
||||||
do
|
|
||||||
f = tonumber(f)
|
|
||||||
if f < 256 then
|
|
||||||
elseif f == 2484 then
|
|
||||||
chan[#chan + 1] = 14
|
|
||||||
elseif f == 2407 then
|
|
||||||
chan[#chan + 1] = 0
|
|
||||||
elseif f < 2484 then
|
|
||||||
chan[#chan + 1] = (f - 2407) / 5
|
|
||||||
elseif f < 5000 then
|
|
||||||
elseif f < 5380 then
|
|
||||||
chan[#chan + 1] = (f - 5000) / 5
|
|
||||||
elseif f < 5500 then
|
|
||||||
chan[#chan + 1] = f - 2000
|
|
||||||
elseif f < 6000 then
|
|
||||||
chan[#chan + 1] = (f - 5000) / 5
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(chan)
|
|
||||||
chan = table.concat(chan, " ")
|
|
||||||
if scan.joined then
|
|
||||||
hostname = node
|
|
||||||
else
|
|
||||||
-- ip lookup then host lookup
|
|
||||||
local ip = arpcache[scan.mac]
|
|
||||||
if ip then
|
|
||||||
hostname = ip
|
|
||||||
local f = io.popen("nslookup " .. ip)
|
|
||||||
if f then
|
|
||||||
for line in f:lines()
|
|
||||||
do
|
|
||||||
local m = line:match("name = (.*)%.local%.mesh")
|
|
||||||
if m then
|
|
||||||
hostname = m:gsub("^mid[0-9]*%.",""):gsub("^dtdlink%.",""):gsub("%.local%.mesh$","")
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
hostname = "-"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if scan.ssid:match("^AREDN-") then
|
if scan.ssid:match("^AREDN-") then
|
||||||
html.print("<tr class=\"wscan-row-node\">")
|
html.print("<tr class=\"wscan-row-node\">")
|
||||||
else
|
else
|
||||||
html.print("<tr>")
|
html.print("<tr>")
|
||||||
end
|
end
|
||||||
html.print("<td>" .. (scan.signal - nf) .. "</td><td>" .. scan.signal .. "</td><td>" .. chan .. "</td><td>" .. scan.key .. "</td><td>" .. scan.ssid .. "</td><td align=center>" .. hostname .. "</td><td>" .. scan.mac:upper() .. "</td><td>" .. scan.mode .. "</td>")
|
html.print("<td>" .. (scan.signal - nf) .. "</td><td>" .. scan.signal .. "</td><td>" .. scan.chan .. "</td><td>" .. scan.key .. "</td><td>" .. scan.ssid .. "</td><td align=center>" .. scan.hostname .. "</td><td>" .. scan.mac:upper() .. "</td><td>" .. scan.mode .. "</td>")
|
||||||
html.print("</tr>")
|
html.print("</tr>")
|
||||||
end
|
end
|
||||||
|
|
||||||
html.print("</table><br></center>")
|
html.print("</table><br>")
|
||||||
|
local lastscan = nixio.fs.stat("/tmp/last-scan.json", "mtime")
|
||||||
|
if lastscan then
|
||||||
|
lastscan = os.time() - lastscan
|
||||||
|
if lastscan == 1 then
|
||||||
|
html.print("<div>Last scan: 1 second ago")
|
||||||
|
elseif lastscan < 60 then
|
||||||
|
html.print("<div>Last scan: " .. lastscan .. " seconds ago")
|
||||||
|
elseif lastscan < 120 then
|
||||||
|
html.print("<div>Last scan: 1 minute ago")
|
||||||
|
elseif lastscan < 3600 then
|
||||||
|
html.print("<div>Last scan: " .. math.floor(lastscan / 60) .. " minutes ago")
|
||||||
|
else
|
||||||
|
html.print("<div>Last scan: a long time ago")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
html.print("<div>Last scan: none")
|
||||||
|
end
|
||||||
html.footer()
|
html.footer()
|
||||||
html.print("</body></html>")
|
html.print("</center></body></html>")
|
||||||
|
|
Loading…
Reference in New Issue