Merge pull request #1371 from kernelkit/misc

Misc fixes
This commit is contained in:
Joachim Wiberg
2026-01-27 14:33:14 +01:00
committed by GitHub
9 changed files with 80 additions and 54 deletions
+12 -5
View File
@@ -13,6 +13,13 @@ import sys
import json
import subprocess
import re
def decode_iw_ssid(ssid):
"""Decode iw escaped SSID (\\xHH) to UTF-8, stripping non-printable chars."""
try:
ssid = ssid.encode().decode('unicode_escape').encode('latin-1').decode('utf-8')
except (UnicodeDecodeError, UnicodeEncodeError):
return ssid
return ''.join(c for c in ssid if c.isprintable())
def run_iw(*args):
@@ -260,7 +267,7 @@ def parse_interface_info(ifname):
# SSID
elif stripped.startswith('ssid '):
result['ssid'] = ' '.join(stripped.split()[1:])
result['ssid'] = decode_iw_ssid(' '.join(stripped.split()[1:]))
# Channel/frequency
elif stripped.startswith('channel '):
@@ -322,7 +329,7 @@ def parse_stations(ifname):
try:
if key == 'signal':
# Format: "-42 dBm" or "-42 [-44, -45] dBm"
current['rssi'] = int(value.split()[0])
current['signal-strength'] = int(value.split()[0])
elif key == 'connected time':
# Format: "123 seconds"
current['connected-time'] = int(value.split()[0])
@@ -488,7 +495,7 @@ def parse_link(ifname):
# SSID: NetworkName
elif stripped.startswith('SSID: '):
result['ssid'] = stripped[6:]
result['ssid'] = decode_iw_ssid(stripped[6:])
# freq: 5180
elif stripped.startswith('freq: '):
@@ -500,7 +507,7 @@ def parse_link(ifname):
# signal: -42 dBm
elif stripped.startswith('signal: '):
try:
result['rssi'] = int(stripped.split()[1])
result['signal-strength'] = int(stripped.split()[1])
except (ValueError, IndexError):
pass
@@ -582,7 +589,7 @@ def main():
else:
data = {'error': f'Unknown command: {command}'}
print(json.dumps(data, indent=2))
print(json.dumps(data, indent=2, ensure_ascii=False))
except Exception as e:
print(json.dumps({'error': str(e)}))
+2 -2
View File
@@ -238,8 +238,8 @@ CoffeeShop 00:1a:2b:3c:4d:5e Open bad 1
</code></pre>
In the CLI, signal strength is reported as: excellent, good, fair or bad.
For precise RSSI values in dBm, use NETCONF or RESTCONF to access the
operational datastore directly.
For precise signal strength values in dBm, use NETCONF or RESTCONF to access
the `signal-strength` leaf in the operational datastore.
## Station Mode (Client)
+9 -12
View File
@@ -14,18 +14,15 @@
#define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf"
/*
* Determine WiFi mode from YANG configuration
*/
typedef enum wifi_mode_t {
wifi_station,
wifi_ap,
wifi_unknown
} wifi_mode_t;
static wifi_mode_t wifi_get_mode(struct lyd_node *wifi)
wifi_mode_t wifi_get_mode(struct lyd_node *iface)
{
struct lyd_node *ap;
struct lyd_node *ap, *wifi;
wifi = lydx_get_child(iface, "wifi");
if (!wifi)
return wifi_unknown;
ap = lydx_get_child(wifi, "access-point");
if (ap) {
@@ -60,7 +57,7 @@ int wifi_mode_changed(struct lyd_node *wifi)
/*
* Generate wpa_supplicant config for station mode
*/
static int wifi_gen_station(struct lyd_node *cif)
int wifi_gen_station(struct lyd_node *cif)
{
const char *ifname, *ssid, *secret_name, *secret, *security_mode, *radio;
struct lyd_node *security, *secret_node, *radio_node, *station, *wifi;
@@ -213,7 +210,7 @@ int wifi_add_iface(struct lyd_node *cif, struct dagger *net)
return SR_ERR_INTERNAL;
}
mode = wifi_get_mode(wifi);
mode = wifi_get_mode(cif);
probe_timeout = wifi_get_probe_timeout(net->session, radio);
fprintf(iw, "# Generated by Infix confd - WiFi Interface Creation\n");
+2 -3
View File
@@ -452,9 +452,8 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net,
case IFT_ETH:
return netdag_gen_ethtool(net, cif, dif);
case IFT_WIFI:
/* WiFi daemon config (hostapd/wpa_supplicant) is handled by
* hardware.c when the radio (phy) is configured. Interface
* creation/deletion is handled in netdag_gen_afspec_add(). */
if (wifi_get_mode(cif) == wifi_station)
return wifi_gen_station(cif);
return 0;
case IFT_DUMMY:
case IFT_GRE:
+8
View File
@@ -124,9 +124,17 @@ int bridge_mcd_gen(struct lyd_node *cifs);
int bridge_port_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
/* if-wifi.c */
typedef enum wifi_mode_t {
wifi_station,
wifi_ap,
wifi_unknown
} wifi_mode_t;
int wifi_add_iface(struct lyd_node *cif, struct dagger *net);
int wifi_del_iface(struct lyd_node *dif, struct dagger *net);
int wifi_mode_changed(struct lyd_node *wifi);
int wifi_gen_station(struct lyd_node *cif);
wifi_mode_t wifi_get_mode(struct lyd_node *wifi);
/* if-gre.c */
int gre_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
+13 -4
View File
@@ -100,6 +100,9 @@ submodule infix-if-wifi {
must "count(/if:interfaces/if:interface[wifi/radio = current()][not(wifi/access-point)]) <= 1" {
error-message "Only one station or scan interface is allowed per radio";
}
must "count(/if:interfaces/if:interface[wifi/radio = current()][not(infix-if:custom-phys-address/*)]) <= 1" {
error-message "Only one interface per radio can use the default MAC address. Configure custom-phys-address on additional interfaces.";
}
description
"Reference to parent WiFi radio (PHY).
@@ -148,6 +151,9 @@ submodule infix-if-wifi {
leaf ssid {
type string {
length "1..32";
pattern '[^\x00-\x1f\x22\x5c\x7f]*' {
error-message "SSID must not contain control characters, double quotes, or backslashes.";
}
}
mandatory true;
description
@@ -201,12 +207,12 @@ submodule infix-if-wifi {
/* Operational state */
leaf rssi {
leaf signal-strength {
config false;
type int16;
units "dBm";
description
"Current received signal strength indication (RSSI) in dBm.
"Current signal strength in dBm.
More negative values indicate weaker signal.
@@ -266,7 +272,7 @@ submodule infix-if-wifi {
description "BSSID (MAC address) of the AP.";
}
leaf rssi {
leaf signal-strength {
type int16;
units "dBm";
description "Signal strength of the network.";
@@ -319,6 +325,9 @@ submodule infix-if-wifi {
leaf ssid {
type string {
length "1..32";
pattern '[^\x00-\x1f\x22\x5c\x7f]*' {
error-message "SSID must not contain control characters, double quotes, or backslashes.";
}
}
mandatory true;
description
@@ -415,7 +424,7 @@ submodule infix-if-wifi {
description "Client MAC address.";
}
leaf rssi {
leaf signal-strength {
type int16;
units "dBm";
description "Client signal strength in dBm.";
+18 -18
View File
@@ -565,12 +565,12 @@ class Decore():
print(txt)
def rssi_to_status(rssi):
if rssi <= -75:
def signal_to_status(signal):
if signal >= -50:
status = Decore.bright_green("excellent")
elif rssi <= -65:
elif signal >= -60:
status = Decore.green("good")
elif rssi <= -50:
elif signal >= -70:
status = Decore.yellow("poor")
else:
status = Decore.red("bad")
@@ -1249,7 +1249,7 @@ class Iface:
ssid = result.get('ssid', 'Hidden')
bssid = result.get("bssid", "unknown")
encstr = ", ".join(result.get("encryption", ["Unknown"]))
status = rssi_to_status(result.get("rssi", -100))
status = signal_to_status(result.get("signal-strength", -100))
channel = result.get("channel", "?")
ssid_table.row(ssid, bssid, encstr, status, channel)
@@ -1283,8 +1283,8 @@ class Iface:
for station in stations:
mac = station.get("mac-address", "unknown")
rssi = station.get("rssi")
signal_str = rssi_to_status(rssi) if rssi is not None else "------"
signal = station.get("signal-strength")
signal_str = signal_to_status(signal) if signal is not None else "------"
conn_time = station.get("connected-time", 0)
time_str = f"{conn_time}s"
@@ -1310,11 +1310,11 @@ class Iface:
row = self._pr_proto_common("ethernet", True, pipe);
print(row)
ssid = None
rssi = None
signal = None
mode = None
if self.wifi:
# Detect mode: AP has "stations", Station has "rssi" or "scan-results"
# Detect mode: AP has "stations", Station has "signal-strength" or "scan-results"
ap=self.wifi.get("access-point", {})
if ap:
ssid = ap.get("ssid", "------")
@@ -1326,11 +1326,11 @@ class Iface:
else:
station=self.wifi.get("station", {})
ssid = station.get("ssid", "------")
rssi = station.get("rssi")
signal = station.get("signal-strength")
mode = "Station"
if rssi is not None:
signal = rssi_to_status(rssi)
data_str = f"{mode}, ssid: {ssid}, signal: {signal}"
if signal is not None:
signal_str = signal_to_status(signal)
data_str = f"{mode}, ssid: {ssid}, signal: {signal_str}"
else:
data_str = f"{mode}, ssid: {ssid}"
else:
@@ -1618,7 +1618,7 @@ class Iface:
print(f"{'out-octets':<{20}}: {self.out_octets}")
if self.wifi:
# Detect mode: AP has "stations", Station has "rssi" or "scan-results"
# Detect mode: AP has "stations", Station has "signal-strength" or "scan-results"
ap = self.wifi.get('access-point')
if ap:
mode = "access-point"
@@ -1632,13 +1632,13 @@ class Iface:
else:
mode = "station"
station = self.wifi.get('station', {})
rssi = station.get('rssi')
signal = station.get('signal-strength')
ssid = station.get('ssid', "----")
print(f"{'mode':<{20}}: {mode}")
print(f"{'ssid':<{20}}: {ssid}")
if rssi is not None:
signal_status = rssi_to_status(rssi)
print(f"{'signal':<{20}}: {rssi} dBm ({signal_status})")
if signal is not None:
signal_status = signal_to_status(signal)
print(f"{'signal':<{20}}: {signal} dBm ({signal_status})")
rx_speed = station.get('rx-speed')
tx_speed = station.get('tx-speed')
if rx_speed is not None:
+1 -1
View File
@@ -97,7 +97,7 @@ def main():
common.LOG.warning("Unsupported model %s", args.model)
sys.exit(1)
print(json.dumps(yang_data, indent=2))
print(json.dumps(yang_data, indent=2, ensure_ascii=False))
if __name__ == "__main__":
@@ -62,14 +62,14 @@ def wifi_station(ifname):
"""Get operational data for Station mode using iw + wpa_cli for scanning"""
station_data = {}
# Get link info (includes SSID and RSSI when connected)
# Get link info (includes SSID and signal strength when connected)
link = get_iw_link(ifname)
if link.get('connected'):
if link.get('ssid'):
station_data['ssid'] = link['ssid']
if link.get('rssi') is not None:
station_data['rssi'] = link['rssi']
if link.get('signal-strength') is not None:
station_data['signal-strength'] = link['signal-strength']
if link.get('rx-speed') is not None:
station_data['rx-speed'] = link['rx-speed']
if link.get('tx-speed') is not None:
@@ -122,29 +122,35 @@ def parse_wpa_scan_result(scan_output):
flags = parts[3].strip()
ssid = parts[4].strip() if len(parts) > 4 else ""
try:
ssid = ssid.encode().decode('unicode_escape').encode('latin-1').decode('utf-8')
except (UnicodeDecodeError, UnicodeEncodeError):
pass
# Strip control chars (terminal injection risk from rogue APs)
ssid = ''.join(c for c in ssid if c.isprintable())
# Skip hidden SSIDs (empty or null-filled)
if not ssid or ssid.isspace() or '\\x00' in ssid:
if not ssid or ssid.isspace():
continue
encryption = extract_encryption(flags)
channel = frequency_to_channel(frequency)
# Keep best RSSI per SSID
if ssid not in networks or rssi > networks[ssid]['rssi']:
# Keep best signal per SSID
if ssid not in networks or rssi > networks[ssid]['signal-strength']:
networks[ssid] = {
'bssid': bssid,
'ssid': ssid,
'rssi': rssi,
'signal-strength': rssi,
'encryption': encryption,
'channel': channel
}
except Exception:
continue
# Sort by RSSI (best first)
# Sort by signal strength (best first)
result = list(networks.values())
result.sort(key=lambda x: x['rssi'], reverse=True)
result.sort(key=lambda x: x['signal-strength'], reverse=True)
return result