wifi: export the Wi-Fi generation of access points

With this, clients can show the Wi-Fi generation to users. It can be
useful to choose which AP or network to connect to.
This commit is contained in:
Beniamino Galvani 2025-11-29 17:51:44 +01:00
parent d41fbc2fde
commit 5d3db07e40
10 changed files with 195 additions and 62 deletions

View file

@ -38,7 +38,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMWifiAP,
PROP_MAX_BITRATE,
PROP_BANDWIDTH,
PROP_STRENGTH,
PROP_LAST_SEEN, );
PROP_LAST_SEEN,
PROP_WIFI_GENERATION, );
struct _NMWifiAPPrivate {
/* Scanned or cached values */
@ -57,6 +58,7 @@ struct _NMWifiAPPrivate {
NM80211ApFlags flags; /* General flags */
NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */
NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */
NMWifiGeneration wifi_generation;
bool metered : 1;
@ -367,6 +369,19 @@ nm_wifi_ap_set_last_seen(NMWifiAP *ap, gint32 last_seen_msec)
return FALSE;
}
static gboolean
nm_wifi_ap_set_wifi_generation(NMWifiAP *ap, NMWifiGeneration wifi_generation)
{
NMWifiAPPrivate *priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->wifi_generation != wifi_generation) {
priv->wifi_generation = wifi_generation;
_notify(ap, PROP_WIFI_GENERATION);
return TRUE;
}
return FALSE;
}
gboolean
nm_wifi_ap_get_metered(const NMWifiAP *self)
{
@ -431,6 +446,8 @@ nm_wifi_ap_update_from_properties(NMWifiAP *ap, const NMSupplicantBssInfo *bss_i
changed |= nm_wifi_ap_set_wpa_flags(ap, bss_info->wpa_flags);
changed |= nm_wifi_ap_set_rsn_flags(ap, bss_info->rsn_flags);
changed |= nm_wifi_ap_set_wifi_generation(ap, bss_info->wifi_generation);
changed |= nm_wifi_ap_set_last_seen(ap, bss_info->last_seen_msec);
changed |= nm_wifi_ap_set_fake(ap, FALSE);
@ -718,6 +735,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
1)
: -1);
break;
case PROP_WIFI_GENERATION:
g_value_set_uint(value, priv->wifi_generation);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@ -737,11 +757,12 @@ nm_wifi_ap_init(NMWifiAP *self)
c_list_init(&self->aps_lst);
priv->mode = _NM_802_11_MODE_INFRA;
priv->flags = NM_802_11_AP_FLAGS_NONE;
priv->wpa_flags = NM_802_11_AP_SEC_NONE;
priv->rsn_flags = NM_802_11_AP_SEC_NONE;
priv->last_seen_msec = G_MININT64;
priv->mode = _NM_802_11_MODE_INFRA;
priv->flags = NM_802_11_AP_FLAGS_NONE;
priv->wpa_flags = NM_802_11_AP_SEC_NONE;
priv->rsn_flags = NM_802_11_AP_SEC_NONE;
priv->last_seen_msec = G_MININT64;
priv->wifi_generation = NM_WIFI_GENERATION_LEGACY;
}
NMWifiAP *
@ -898,9 +919,10 @@ static const NMDBusInterfaceInfoExtended interface_info_access_point = {
NM_WIFI_AP_MAX_BITRATE),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Bandwidth", "u", NM_WIFI_AP_BANDWIDTH),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Strength", "y", NM_WIFI_AP_STRENGTH),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("LastSeen",
"i",
NM_WIFI_AP_LAST_SEEN), ), ),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("LastSeen", "i", NM_WIFI_AP_LAST_SEEN),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WifiGeneration",
"u",
NM_WIFI_AP_WIFI_GENERATION), ), ),
};
static void
@ -1003,6 +1025,15 @@ nm_wifi_ap_class_init(NMWifiAPClass *ap_class)
-1,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_WIFI_GENERATION] =
g_param_spec_uint(NM_WIFI_AP_WIFI_GENERATION,
"",
"",
NM_WIFI_GENERATION_LEGACY,
G_MAXUINT,
NM_WIFI_GENERATION_LEGACY,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_BANDWIDTH] = g_param_spec_uint(NM_WIFI_AP_BANDWIDTH,
"",
"",

View file

@ -19,17 +19,18 @@
#define NM_IS_WIFI_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_WIFI_AP))
#define NM_WIFI_AP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_WIFI_AP, NMWifiAPClass))
#define NM_WIFI_AP_FLAGS "flags"
#define NM_WIFI_AP_WPA_FLAGS "wpa-flags"
#define NM_WIFI_AP_RSN_FLAGS "rsn-flags"
#define NM_WIFI_AP_SSID "ssid"
#define NM_WIFI_AP_FREQUENCY "frequency"
#define NM_WIFI_AP_HW_ADDRESS "hw-address"
#define NM_WIFI_AP_MODE "mode"
#define NM_WIFI_AP_MAX_BITRATE "max-bitrate"
#define NM_WIFI_AP_BANDWIDTH "bandwidth"
#define NM_WIFI_AP_STRENGTH "strength"
#define NM_WIFI_AP_LAST_SEEN "last-seen"
#define NM_WIFI_AP_FLAGS "flags"
#define NM_WIFI_AP_WPA_FLAGS "wpa-flags"
#define NM_WIFI_AP_RSN_FLAGS "rsn-flags"
#define NM_WIFI_AP_SSID "ssid"
#define NM_WIFI_AP_FREQUENCY "frequency"
#define NM_WIFI_AP_HW_ADDRESS "hw-address"
#define NM_WIFI_AP_MODE "mode"
#define NM_WIFI_AP_MAX_BITRATE "max-bitrate"
#define NM_WIFI_AP_BANDWIDTH "bandwidth"
#define NM_WIFI_AP_STRENGTH "strength"
#define NM_WIFI_AP_LAST_SEEN "last-seen"
#define NM_WIFI_AP_WIFI_GENERATION "wifi-generation"
typedef struct {
NMDBusObject parent;

View file

@ -4901,14 +4901,19 @@ get_bandwidth_vht(const guint8 *bytes, guint len, guint32 *out_bandwidth)
#define WLAN_EID_VHT_CAPABILITY 191
#define WLAN_EID_VHT_OPERATION 192
#define WLAN_EID_VENDOR_SPECIFIC 221
#define WLAN_EID_EXTENSION 255
#define WLAN_EID_EXT_HE_CAPABILITY 35
#define WLAN_EID_EXT_EHT_CAPABILITY 108
void
nm_wifi_utils_parse_ies(const guint8 *bytes,
gsize len,
guint32 *out_max_rate,
guint32 *out_bandwidth,
gboolean *out_metered,
gboolean *out_owe_transition_mode)
nm_wifi_utils_parse_ies(const guint8 *bytes,
gsize len,
guint32 *out_max_rate,
guint32 *out_bandwidth,
gboolean *out_metered,
gboolean *out_owe_transition_mode,
NMWifiGeneration *out_wifi_generation)
{
guint8 id, elem_len;
guint32 m;
@ -4917,6 +4922,7 @@ nm_wifi_utils_parse_ies(const guint8 *bytes,
NM_SET_OUT(out_bandwidth, 0);
NM_SET_OUT(out_metered, FALSE);
NM_SET_OUT(out_owe_transition_mode, FALSE);
NM_SET_OUT(out_wifi_generation, NM_WIFI_GENERATION_LEGACY);
while (len) {
if (len < 2)
@ -4931,6 +4937,9 @@ nm_wifi_utils_parse_ies(const guint8 *bytes,
switch (id) {
case WLAN_EID_HT_CAPABILITY:
if (out_wifi_generation && *out_wifi_generation < NM_WIFI_GENERATION_WIFI_4)
*out_wifi_generation = NM_WIFI_GENERATION_WIFI_4;
if (out_max_rate) {
if (get_max_rate_ht(bytes, elem_len, &m))
*out_max_rate = NM_MAX(*out_max_rate, m);
@ -4941,6 +4950,9 @@ nm_wifi_utils_parse_ies(const guint8 *bytes,
get_bandwidth_ht(bytes, elem_len, out_bandwidth);
break;
case WLAN_EID_VHT_CAPABILITY:
if (out_wifi_generation && *out_wifi_generation < NM_WIFI_GENERATION_WIFI_5)
*out_wifi_generation = NM_WIFI_GENERATION_WIFI_5;
if (out_max_rate) {
if (get_max_rate_vht(bytes, elem_len, &m))
*out_max_rate = NM_MAX(*out_max_rate, m);
@ -4950,6 +4962,19 @@ nm_wifi_utils_parse_ies(const guint8 *bytes,
if (out_bandwidth)
get_bandwidth_vht(bytes, elem_len, out_bandwidth);
break;
case WLAN_EID_EXTENSION:
if (out_wifi_generation && elem_len >= 1) {
guint8 ext_id = bytes[0];
if (ext_id == WLAN_EID_EXT_HE_CAPABILITY) {
if (*out_wifi_generation < NM_WIFI_GENERATION_WIFI_6)
*out_wifi_generation = NM_WIFI_GENERATION_WIFI_6;
} else if (ext_id == WLAN_EID_EXT_EHT_CAPABILITY) {
if (*out_wifi_generation < NM_WIFI_GENERATION_WIFI_7)
*out_wifi_generation = NM_WIFI_GENERATION_WIFI_7;
}
}
break;
case WLAN_EID_VENDOR_SPECIFIC:
if (len == 8 && bytes[0] == 0x00 /* OUI: Microsoft */
&& bytes[1] == 0x50 && bytes[2] == 0xf2

View file

@ -458,12 +458,13 @@ const char *nm_utils_parse_dns_domain(const char *domain, gboolean *is_routing);
/*****************************************************************************/
void nm_wifi_utils_parse_ies(const guint8 *bytes,
gsize len,
guint32 *out_max_rate,
guint32 *out_bandwidth,
gboolean *out_metered,
gboolean *out_owe_transition_mode);
void nm_wifi_utils_parse_ies(const guint8 *bytes,
gsize len,
guint32 *out_max_rate,
guint32 *out_bandwidth,
gboolean *out_metered,
gboolean *out_owe_transition_mode,
NMWifiGeneration *out_wifi_generation);
guint8 nm_wifi_utils_level_to_quality(int val);

View file

@ -11,6 +11,7 @@
#include <stdio.h>
#include <linux/if_ether.h>
#include "core/nm-core-utils.h"
#include "NetworkManagerUtils.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-glib-aux/nm-c-list.h"
@ -765,10 +766,11 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
v_v = nm_g_variant_lookup_value(properties, "IEs", G_VARIANT_TYPE_BYTESTRING);
if (v_v) {
gboolean p_owe_transition_mode;
gboolean p_metered;
guint32 rate;
guint32 bandwidth;
gboolean p_owe_transition_mode;
gboolean p_metered;
guint32 rate;
guint32 bandwidth;
NMWifiGeneration p_wifi_generation;
arr_data = g_variant_get_fixed_array(v_v, &arr_len, 1);
nm_wifi_utils_parse_ies(arr_data,
@ -776,7 +778,8 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
&rate,
&bandwidth,
&p_metered,
&p_owe_transition_mode);
&p_owe_transition_mode,
&p_wifi_generation);
p_max_rate = NM_MAX(p_max_rate, rate);
p_max_rate_has = TRUE;
g_variant_unref(v_v);
@ -786,8 +789,9 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
else
bss_info->rsn_flags &= ~NM_802_11_AP_SEC_KEY_MGMT_OWE_TM;
bss_info->metered = p_metered;
bss_info->bandwidth = bandwidth;
bss_info->metered = p_metered;
bss_info->bandwidth = bandwidth;
bss_info->wifi_generation = (guint8) p_wifi_generation;
}
if (p_max_rate_has)

View file

@ -195,6 +195,8 @@ typedef struct _NMSupplicantBssInfo {
bool _bss_dirty : 1;
guint8 wifi_generation; /* NMWifiGeneration */
} NMSupplicantBssInfo;
typedef struct _NMSupplicantPeerInfo {

View file

@ -2096,7 +2096,9 @@ global:
libnm_1_58_0 {
global:
nm_access_point_get_wifi_generation;
nm_utils_wifi_6ghz_freqs;
nm_utils_wifi_freq_to_band;
nm_wifi_band_get_type;
nm_wifi_generation_get_type;
} libnm_1_56_0;

View file

@ -34,20 +34,22 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMAccessPoint,
PROP_BANDWIDTH,
PROP_STRENGTH,
PROP_BSSID,
PROP_LAST_SEEN, );
PROP_LAST_SEEN,
PROP_WIFI_GENERATION, );
typedef struct {
GBytes *ssid;
char *bssid;
guint32 flags;
guint32 wpa_flags;
guint32 rsn_flags;
guint32 frequency;
guint32 mode;
guint32 max_bitrate;
guint32 bandwidth;
gint32 last_seen;
guint8 strength;
GBytes *ssid;
char *bssid;
guint32 flags;
guint32 wpa_flags;
guint32 rsn_flags;
guint32 frequency;
guint32 mode;
guint32 max_bitrate;
guint32 bandwidth;
gint32 last_seen;
guint8 strength;
NMWifiGeneration wifi_generation;
} NMAccessPointPrivate;
struct _NMAccessPoint {
@ -256,6 +258,24 @@ nm_access_point_get_last_seen(NMAccessPoint *ap)
}
NM_BACKPORT_SYMBOL(libnm_1_0_6, int, nm_access_point_get_last_seen, (NMAccessPoint * ap), (ap));
/**
* nm_access_point_get_wifi_generation:
* @ap: a #NMAccessPoint
*
* Gets the Wi-Fi Generation (Wi-Fi 4, Wi-Fi 5, etc.) of the access point.
*
* Returns: the Wi-Fi Generation
*
* Since: 1.58
**/
NMWifiGeneration
nm_access_point_get_wifi_generation(NMAccessPoint *ap)
{
g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), NM_WIFI_GENERATION_LEGACY);
return NM_ACCESS_POINT_GET_PRIVATE(ap)->wifi_generation;
}
/**
* nm_access_point_connection_valid:
* @ap: an #NMAccessPoint to validate @connection against
@ -496,6 +516,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_LAST_SEEN:
g_value_set_int(value, nm_access_point_get_last_seen(ap));
break;
case PROP_WIFI_GENERATION:
g_value_set_uint(value, nm_access_point_get_wifi_generation(ap));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@ -523,6 +546,10 @@ const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint = NML_DBUS_META_IFACE
NML_DBUS_META_PROPERTY_INIT_U("RsnFlags", PROP_RSN_FLAGS, NMAccessPoint, _priv.rsn_flags),
NML_DBUS_META_PROPERTY_INIT_AY("Ssid", PROP_SSID, NMAccessPoint, _priv.ssid),
NML_DBUS_META_PROPERTY_INIT_Y("Strength", PROP_STRENGTH, NMAccessPoint, _priv.strength),
NML_DBUS_META_PROPERTY_INIT_U("WifiGeneration",
PROP_WIFI_GENERATION,
NMAccessPoint,
_priv.wifi_generation),
NML_DBUS_META_PROPERTY_INIT_U("WpaFlags",
PROP_WPA_FLAGS,
NMAccessPoint,
@ -693,5 +720,21 @@ nm_access_point_class_init(NMAccessPointClass *ap_class)
-1,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMAccessPoint:wifi-generation:
*
* The Wi-Fi generation of the access point.
*
* Since: 1.58
**/
obj_properties[PROP_WIFI_GENERATION] =
g_param_spec_uint(NM_ACCESS_POINT_WIFI_GENERATION,
"",
"",
NM_WIFI_GENERATION_LEGACY,
NM_WIFI_GENERATION_WIFI_7,
NM_WIFI_GENERATION_LEGACY,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
_nml_dbus_meta_class_init_with_properties(object_class, &_nml_dbus_meta_iface_nm_accesspoint);
}

View file

@ -25,17 +25,18 @@ G_BEGIN_DECLS
#define NM_ACCESS_POINT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_ACCESS_POINT, NMAccessPointClass))
#define NM_ACCESS_POINT_FLAGS "flags"
#define NM_ACCESS_POINT_WPA_FLAGS "wpa-flags"
#define NM_ACCESS_POINT_RSN_FLAGS "rsn-flags"
#define NM_ACCESS_POINT_SSID "ssid"
#define NM_ACCESS_POINT_BSSID "bssid"
#define NM_ACCESS_POINT_FREQUENCY "frequency"
#define NM_ACCESS_POINT_MODE "mode"
#define NM_ACCESS_POINT_MAX_BITRATE "max-bitrate"
#define NM_ACCESS_POINT_STRENGTH "strength"
#define NM_ACCESS_POINT_LAST_SEEN "last-seen"
#define NM_ACCESS_POINT_BANDWIDTH "bandwidth"
#define NM_ACCESS_POINT_FLAGS "flags"
#define NM_ACCESS_POINT_WPA_FLAGS "wpa-flags"
#define NM_ACCESS_POINT_RSN_FLAGS "rsn-flags"
#define NM_ACCESS_POINT_SSID "ssid"
#define NM_ACCESS_POINT_BSSID "bssid"
#define NM_ACCESS_POINT_FREQUENCY "frequency"
#define NM_ACCESS_POINT_MODE "mode"
#define NM_ACCESS_POINT_MAX_BITRATE "max-bitrate"
#define NM_ACCESS_POINT_STRENGTH "strength"
#define NM_ACCESS_POINT_LAST_SEEN "last-seen"
#define NM_ACCESS_POINT_BANDWIDTH "bandwidth"
#define NM_ACCESS_POINT_WIFI_GENERATION "wifi-generation"
/* DEPRECATED */
#define NM_ACCESS_POINT_HW_ADDRESS "hw-address"
@ -63,6 +64,9 @@ int nm_access_point_get_last_seen(NMAccessPoint *ap);
NM_AVAILABLE_IN_1_46
guint32 nm_access_point_get_bandwidth(NMAccessPoint *ap);
NM_AVAILABLE_IN_1_58
NMWifiGeneration nm_access_point_get_wifi_generation(NMAccessPoint *ap);
GPtrArray *nm_access_point_filter_connections(NMAccessPoint *ap, const GPtrArray *connections);
gboolean nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection);

View file

@ -418,6 +418,26 @@ typedef enum /*< underscore_name=nm_802_11_ap_security_flags, flags >*/ {
NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192 = 0x00002000,
} NM80211ApSecurityFlags;
/**
* NMWifiGeneration:
* @NM_WIFI_GENERATION_LEGACY: pre-Wi-Fi 4 (802.11a/b/g)
* @NM_WIFI_GENERATION_WIFI_4: Wi-Fi 4 (802.11n)
* @NM_WIFI_GENERATION_WIFI_5: Wi-Fi 5 (802.11ac)
* @NM_WIFI_GENERATION_WIFI_6: Wi-Fi 6 (802.11ax)
* @NM_WIFI_GENERATION_WIFI_7: Wi-Fi 7 (802.11be)
*
* 802.11 Wi-Fi generation.
*
* Since: 1.58
*/
typedef enum {
NM_WIFI_GENERATION_LEGACY = 0,
NM_WIFI_GENERATION_WIFI_4 = 4,
NM_WIFI_GENERATION_WIFI_5 = 5,
NM_WIFI_GENERATION_WIFI_6 = 6,
NM_WIFI_GENERATION_WIFI_7 = 7,
} NMWifiGeneration;
/**
* NM80211Mode:
* @NM_802_11_MODE_UNKNOWN: the device or access point mode is unknown