diff --git a/files/www/cgi-bin/scan b/files/www/cgi-bin/scan index 547ae50d..197385e0 100755 --- a/files/www/cgi-bin/scan +++ b/files/www/cgi-bin/scan @@ -36,6 +36,7 @@ --]] require("nixio") +require("luci.jsonc") require("aredn.http") require("aredn.hardware") require("aredn.utils") @@ -55,125 +56,208 @@ if board_type:match("^ubnt,") and board_type:match("ac") then ubnt_ac = true end -local channels = aredn.hardware.get_rfchannels(wifiiface) -local scan_list = "" -for _, channel in ipairs(channels) -do - scan_list = scan_list .. " " .. channel.frequency -end +local rescan = string.find((nixio.getenv("QUERY_STRING") or ""):lower(),"rescan=1") --- 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 - 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 +local scanlist = {} +-- Show the last scan if we have one, it's not too old, and we're not rescanning +if not rescan then + local lscan = io.open("/tmp/last-scan.json") + if lscan then + scanlist = luci.jsonc.parse(lscan:read("*a") or "") + lscan:close() end - f:close() end --- Ubiquiti AC device workaround -if ubnt_ac then - os.execute("iw dev " .. wifiiface .. " ibss leave > /dev/null 2>&1") - os.execute("wifi up > /dev/null 2>&1") - local attempt = 10 - while attempt > 0 +if rescan then + local channels = aredn.hardware.get_rfchannels(wifiiface) + local scan_list = "" + for _, channel in ipairs(channels) do - attempt = attempt - 1 - for line in io.popen("iw dev " .. wifiiface .. " scan"):lines() + scan_list = scan_list .. " " .. channel.frequency + 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 - if line:match("^BSS ") then - attempt = 0 + 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[tostring(myfreq)] = true + end + m = line:match("signal avg:%s+([%d%-]+)") + if m then + scan.signal = tonumber(m) end - break end - nixio.nanosleep(2, 0) + f:close() end -end -local f = io.popen("iw dev " .. wifiiface .. " scan freq" .. scan_list .. " passive") -if f then - local scan = {} - for line in f:lines() + -- Ubiquiti AC device workaround + if ubnt_ac then + os.execute("iw dev " .. wifiiface .. " ibss leave > /dev/null 2>&1") + 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 - 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" + if v.signal ~= 9999 or v.joined then + scanlist[#scanlist + 1] = v 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 --- scan end - -- generate page http_header() html.header(node .. " WiFi scan", false) @@ -181,21 +265,30 @@ local autoscan = string.find((nixio.getenv("QUERY_STRING") or ""):lower(),"autos if autoscan then html.print("") end +if rescan then + html.print([[ + +]]) +end html.print([[ -
+
]]) @@ -205,9 +298,11 @@ html.print("

" .. node .. " WiFi scan


") if autoscan then html.print("") else - html.print("") - html.print("   ") - html.print([[]]) + html.print([[]]) + if not ubnt_ac then + html.print("   ") + html.print([[]]) + end end html.print("   ") @@ -217,77 +312,34 @@ html.print("