diff --git a/applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js b/applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js
index 1ccf24e65101..198ed1470645 100644
--- a/applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js
+++ b/applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js
@@ -2,6 +2,7 @@
'require view';
'require rpc';
'require poll';
+'require fs';
'require dom';
'require ui';
'require form';
@@ -12,6 +13,7 @@
let Hosts, Remotehosts, Remoteinfo, Localinfo, Clients, WifiNetworks;
const dns_cache = [];
+const hostapdClientData = [];
function SplitWlan(wlan) {
let wlansplit = [];
@@ -115,7 +117,56 @@ function collectWlanAPInfoEntries(connectioninfo_table_entries, wlanAPInfos) {
}
};
-function tootltip(mac, IP, hostname) {
+
+const RSN_CIPHER_MAP = {
+ "00-0f-ac-0": _("Use group cipher"),
+ "00-0f-ac-1": "WEP-40",
+ "00-0f-ac-2": "TKIP",
+ "00-0f-ac-3": _("Reserved"),
+ "00-0f-ac-4": "AES-CCMP-128",
+ "00-0f-ac-5": "WEP-104",
+ "00-0f-ac-6": "BIP-CMAC-128",
+ "00-0f-ac-7": _("Group addressed traffic not allowed"),
+ "00-0f-ac-8": "AES-GCMP-128",
+ "00-0f-ac-9": "AES-GCMP-256",
+ "00-0f-ac-10": "AES-CCMP-256",
+ "00-0f-ac-11": "BIP-GMAC-128",
+ "00-0f-ac-12": "BIP-GMAC-256",
+ "00-0f-ac-13": "BIP-CMAC-256",
+};
+
+const RSN_AKM_MAP = {
+ "00-0f-ac-1": "802.1X",
+ "00-0f-ac-2": "PSK",
+ "00-0f-ac-3": "FT 802.1X",
+ "00-0f-ac-4": "FT PSK",
+ "00-0f-ac-5": "WPA2 Enterprise SHA-256",
+ "00-0f-ac-6": "WPA2 PSK SHA-256",
+ "00-0f-ac-7": "TDLS",
+ "00-0f-ac-8": "SAE",
+ "00-0f-ac-9": "FT SAE",
+ "00-0f-ac-10": _("AP PeerKey"),
+ "00-0f-ac-11": "Suite B 192-bit",
+ "00-0f-ac-12": "Suite B 192-bit FT",
+ "00-0f-ac-13": "FILS SHA-256",
+ "00-0f-ac-14": "FILS SHA-384",
+ "00-0f-ac-15": "FILS FT SHA-256",
+ "00-0f-ac-16": "FILS FT SHA-384",
+ "00-0f-ac-17": "OWE",
+ "00-0f-ac-18": "FT OWE",
+};
+
+function translateCipher(value) {
+ if (!value) return "";
+ return RSN_CIPHER_MAP[value] ?? _("Unrecognized cipher code")+": "+value;
+}
+
+function translateAkm(value) {
+ if (!value) return _("Install hostapd_cli for AKM and cipher info");
+ return RSN_AKM_MAP[value] ?? _("Unknown AKM")+": "+value;
+}
+
+function tooltip(mac, IP, hostname, wlan) {
const body= E([]);
body.appendChild(E('div', '%h'.format(mac)));
if (typeof IP !== 'undefined') {
@@ -125,12 +176,19 @@ function tootltip(mac, IP, hostname) {
if (hostname !== '') {
body.appendChild(E('div', '%h'.format(hostname)));
}
+ if (wlan==_('This AP')) {
+ body.appendChild(E('div',
+ '%h '.format(translateAkm(hostapdClientData[mac.toUpperCase()]?.AKMSuiteSelector))+
+ '%h'.format(translateCipher(hostapdClientData[mac.toUpperCase()]?.dot11RSNAStatsSelectedPairwiseCipher))
+ ));
+ }
return body;
}
function collectWlanAPInfos(compactconnectioninfo_table_entries, wlanAPInfos) {
for (let wlan in wlanAPInfos) {
const hostl = E([]);
+ const wlansplit=SplitWlan(wlan);
for (let mac in Clients) {
if (typeof Clients[mac] !== 'undefined')
if (typeof Clients[mac][wlan] !== 'undefined')
@@ -152,12 +210,11 @@ function collectWlanAPInfos(compactconnectioninfo_table_entries, wlanAPInfos) {
hostl.appendChild(
E('span', { 'class': 'cbi-tooltip-container' }, [
'%h\u2003'.format(foundname),
- E('div', { 'class': 'cbi-tooltip' }, tootltip(mac, Hosts[macUp], hostname))
+ E('div', { 'class': 'cbi-tooltip' }, tooltip(mac, Hosts[macUp], hostname, wlansplit[0]))
])
);
}
}
- const wlansplit=SplitWlan(wlan);
compactconnectioninfo_table_entries.push([
'' + '%h'.format(wlansplit[0]) + '',
'' + '%h'.format(wlansplit[1]) + '',
@@ -291,6 +348,35 @@ const Settingsfooter = form.DummyValue.extend({
}
});
+function parseAllSta(text) {
+ const lines = text.split('\n');
+ let currentMac = null;
+
+ for (const raw of lines) {
+ const line = raw.trim();
+ // Detect MAC address line
+ if (/^[0-9a-f]{2}(:[0-9a-f]{2}){5}$/i.test(line)) {
+ currentMac = line.toUpperCase();
+ hostapdClientData[currentMac] = {};
+ continue;
+ }
+ if (currentMac && line.includes('=')) {
+ const [key, value] = line.split('=');
+ hostapdClientData[currentMac][key] = value;
+ }
+ }
+}
+
+function getCipherAKM() {
+ for (const wlan in Localinfo) {
+ fs.stat('/usr/sbin/hostapd_cli').then(stat => {
+ if (!stat || stat.type !== 'file') { return; }
+ fs.exec_direct('/usr/sbin/hostapd_cli', ['-i', wlan.split('.')[1], 'all_sta'])
+ .then(res => { parseAllSta(res); })
+ .catch(err => {});
+ }).catch (function (){return null;});
+ }
+}
return view.extend({
callHostHints: rpc.declare({
@@ -337,7 +423,9 @@ return view.extend({
Remoteinfo = data[3];
Localinfo = data[4];
Clients = data[5];
-
+
+ getCipherAKM();
+
const remotehosttableentries = [];
collectRemoteHosts(remotehosttableentries,Remotehosts);
cbi_update_table(nodes.querySelector('#remotehost_table'), remotehosttableentries, E('em', _('No data')));
@@ -383,6 +471,8 @@ return view.extend({
Clients = data[5];
WifiNetworks = data[6];
+ getCipherAKM();
+
s = m.section(form.TypedSection);
s.anonymous = true;
s.tab('status', _('Status'));
diff --git a/applications/luci-app-usteer/root/usr/share/rpcd/acl.d/luci-app-usteer.json b/applications/luci-app-usteer/root/usr/share/rpcd/acl.d/luci-app-usteer.json
index e0d24e880b0f..5194d2fb6810 100644
--- a/applications/luci-app-usteer/root/usr/share/rpcd/acl.d/luci-app-usteer.json
+++ b/applications/luci-app-usteer/root/usr/share/rpcd/acl.d/luci-app-usteer.json
@@ -16,7 +16,11 @@
"ubus": {
"uci": [ "set", "commit" ]
},
- "uci": [ "usteer" ]
+ "uci": [ "usteer" ],
+ "file": {
+ "/usr/sbin/hostapd_cli -i * all_sta": [ "exec" ]
+ }
}
}
}
+