diff --git a/command/cmd_wifiscan.uc b/command/cmd_wifiscan.uc index 8037c36..d291d75 100644 --- a/command/cmd_wifiscan.uc +++ b/command/cmd_wifiscan.uc @@ -1,13 +1,177 @@ {% -let verbose = args.verbose ? true : false; -let active = args.active ? true : false; -ctx.call("wifi", "scan", { active }); -system("sleep 5"); -let scan = ctx.call("wifi", "scan_dump", {verbose}); +let verbose = args?.verbose ? true : false; +let active = args?.active ? true : false; +let nl = require("nl80211"); +let def = nl.const; + +const SCAN_FLAG_AP = (1<<2); +const frequency_list_2g = [ 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 ]; +const frequency_list_5g = { '3': [ 5180, 5260, 5500, 5580, 5660, 5745 ], + '2': [ 5180, 5220, 5260, 5300, 5500, 5540, + 5580, 5620, 5660, 5745, 5785, 5825, + 5865, 5920, 5960 ], + '1': [ 5180, 5200, 5220, 5240, 5260, 5280, + 5300, 5320, 5500, 5520, 5540, 5560, + 5580, 5600, 5620, 5640, 5660, 5680, + 5700, 5720, 5745, 5765, 5785, 5805, + 5825, 5845, 5865, 5885 ], +}; +const frequency_offset = { '80': 30, '40': 10 }; +const frequency_width = { '80': 3, '40': 20, '20': 1 }; +const IFTYPE_STATION = 2; +const IFTYPE_AP = 3; +const IFTYPE_MESH = 7; + +function iface_get(wdev) { + let params = { dev: wdev }; + let res = nl.request(def.NL80211_CMD_GET_INTERFACE, wdev ? null : def.NLM_F_DUMP, wdev ? params : null); + + if (res === false) + warn("Unable to lookup interface: " + nl.error() + "\n"); + return res || []; +} + +function iface_find(wiphy, types, ifaces) { + if (!ifaces) + ifaces = iface_get(); + for (let iface in ifaces) { + if (iface.wiphy != wiphy) + continue; + if (iface.iftype in types) + return iface; + } + return; +} + +function scan_trigger(wdev, frequency, width) { + let params = { dev: wdev, scan_flags: SCAN_FLAG_AP }; + + if (frequency && type(frequency) == 'array') { + params.scan_frequencies = frequency; + } + else if (frequency && width) { + params.wiphy_freq = frequency; + params.center_freq1 = frequency + frequency_offset[width]; + params.channel_width = frequency_width[width]; + } + + if (active) + params.scan_ssids = [ '' ]; + + //printf("%.J\n", params); + let res = nl.request(def.NL80211_CMD_TRIGGER_SCAN, 0, params); + + if (res === false) + die("Unable to trigger scan: " + nl.error() + "\n"); + + res = nl.waitfor([ + def.NL80211_CMD_NEW_SCAN_RESULTS, + def.NL80211_CMD_SCAN_ABORTED + ], (frequency && width) ? 500 : 5000); + + if (!res) + warn("Netlink error while awaiting scan results: " + nl.error() + "\n"); + + else if (res.cmd == def.NL80211_CMD_SCAN_ABORTED) + warn("Scan aborted by kernel\n"); +} + +function trigger_scan_width(wdev, freqs, width) { + for (let freq in freqs) + scan_trigger(wdev, freq, width); +} + +function phy_get(wdev) { + let res = nl.request(def.NL80211_CMD_GET_WIPHY, def.NLM_F_DUMP, { split_wiphy_dump: true }); + + if (res === false) + warn("Unable to lookup phys: " + nl.error() + "\n"); + + return res; +} + +function phy_get_frequencies(phy) { + let freqs = []; + + for (let band in phy.wiphy_bands) { + for (let freq in band?.freqs || []) + if (!freq.disabled) + push(freqs, freq.freq); + } + return freqs; +} + +let phys = phy_get(); +let ifaces = iface_get(); + +function intersect(list, filter) { + let res = []; + + for (let item in list) + if (index(filter, item) >= 0) + push(res, item); + return res; +} + +function wifi_scan() { + let scan = []; + + for (let phy in phys) { + let iface = iface_find(phy.wiphy, [ IFTYPE_STATION, IFTYPE_AP ], ifaces); + if (!iface) { + warn('no valid interface found for phy' + phy.wiphy + '\n'); + break; + } + + let freqs = phy_get_frequencies(phy); + if (length(intersect(freqs, frequency_list_2g))) + scan_trigger(iface.dev, frequency_list_2g); + + let freqs_5g = intersect(freqs, frequency_list_5g[iface.channel_width]); + if (length(freqs_5g)) + trigger_scan_width(iface.dev, freqs_5g, iface.channel_width); + let res = nl.request(def.NL80211_CMD_GET_SCAN, def.NLM_F_DUMP, { dev: iface.dev }); + for (let bss in res) { + bss = bss.bss; + let res = { + bssid: bss.bssid, + tsf: +bss.tsf, + frequency: +bss.frequency, + signal: +bss.signal_mbm / 100, + last_seen: +bss.seen_ms_ago, + capability: +bss.capability, + }; + + for (let ie in bss.beacon_ies) { + switch (ie.type) { + case 0: + case 114: + res.ssid = ie.data; + break; + case 0x3d: + res.ht_oper = b64enc(ie.data); + break; + case 0xc0: + res.vht_oper = b64enc(ie.data); + break; + } + } + + push(scan, res); + } + // printf("%.J\n", nl.request(def.NL80211_CMD_GET_SCAN, def.NLM_F_DUMP, { dev: iface.dev })); + } + printf("%.J\n", scan); + return scan; +} + +let scan = wifi_scan(); + result_json({ - "error": 0, - "text": "Success", - "resultCode": 1, - scan: scan.scan + error: 0, + text: "Success", + resultCode: 1, + scan, }); %}