wifi: parse access point announced bandwidth

Parse the access point announced bandwidth in MHz. This is considering
both HT and VHT. Please notice that for VHT 80+80 MHz we are representing it
as 160 MHz.
This commit is contained in:
Fernando Fernandez Mancera
2023-07-27 16:15:47 +02:00
parent d3620dd286
commit fe48a4b35c
10 changed files with 196 additions and 2 deletions

View File

@@ -79,6 +79,13 @@
-->
<property name="MaxBitrate" type="u" access="read"/>
<!--
Bandwidth:
The bandwidth announced by the access point in MHz.
-->
<property name="Bandwidth" type="u" access="read"/>
<!--
Strength:

View File

@@ -36,6 +36,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMWifiAP,
PROP_HW_ADDRESS,
PROP_MODE,
PROP_MAX_BITRATE,
PROP_BANDWIDTH,
PROP_STRENGTH,
PROP_LAST_SEEN, );
@@ -47,6 +48,7 @@ struct _NMWifiAPPrivate {
guint8 strength;
guint32 freq; /* Frequency in MHz; ie 2412 (== 2.412 GHz) */
guint32 max_bitrate; /* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
guint32 bandwidth; /* Bandwidth of the AP in MHz */
gint64
last_seen_msec; /* Timestamp when the AP was seen lastly (in nm_utils_get_monotonic_timestamp_*() scale).
@@ -277,6 +279,32 @@ nm_wifi_ap_get_max_bitrate(NMWifiAP *ap)
return NM_WIFI_AP_GET_PRIVATE(ap)->max_bitrate;
}
guint32
nm_wifi_ap_get_bandwidth(NMWifiAP *ap)
{
g_return_val_if_fail(NM_IS_WIFI_AP(ap), 0);
g_return_val_if_fail(nm_dbus_object_is_exported(NM_DBUS_OBJECT(ap)), 0);
return NM_WIFI_AP_GET_PRIVATE(ap)->bandwidth;
}
gboolean
nm_wifi_ap_set_bandwidth(NMWifiAP *ap, guint32 bandwidth)
{
NMWifiAPPrivate *priv;
g_return_val_if_fail(NM_IS_WIFI_AP(ap), FALSE);
priv = NM_WIFI_AP_GET_PRIVATE(ap);
if (priv->bandwidth != bandwidth) {
priv->bandwidth = bandwidth;
_notify(ap, PROP_BANDWIDTH);
return TRUE;
}
return FALSE;
}
gboolean
nm_wifi_ap_set_max_bitrate(NMWifiAP *ap, guint32 bitrate)
{
@@ -393,6 +421,7 @@ nm_wifi_ap_update_from_properties(NMWifiAP *ap, const NMSupplicantBssInfo *bss_i
}
changed |= nm_wifi_ap_set_max_bitrate(ap, bss_info->max_rate);
changed |= nm_wifi_ap_set_bandwidth(ap, bss_info->bandwidth);
if (priv->metered != bss_info->metered) {
priv->metered = bss_info->metered;
@@ -683,6 +712,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_MAX_BITRATE:
g_value_set_uint(value, priv->max_bitrate);
break;
case PROP_BANDWIDTH:
g_value_set_uint(value, priv->bandwidth);
break;
case PROP_STRENGTH:
g_value_set_uchar(value, priv->strength);
break;
@@ -873,6 +905,7 @@ static const NMDBusInterfaceInfoExtended interface_info_access_point = {
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("MaxBitrate",
"u",
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",
@@ -979,6 +1012,14 @@ nm_wifi_ap_class_init(NMWifiAPClass *ap_class)
-1,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_BANDWIDTH] = g_param_spec_uint(NM_WIFI_AP_BANDWIDTH,
"",
"",
0,
G_MAXUINT32,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}

View File

@@ -27,6 +27,7 @@
#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"
@@ -78,6 +79,8 @@ guint32 nm_wifi_ap_get_freq(NMWifiAP *ap);
gboolean nm_wifi_ap_set_freq(NMWifiAP *ap, guint32 freq);
guint32 nm_wifi_ap_get_max_bitrate(NMWifiAP *ap);
gboolean nm_wifi_ap_set_max_bitrate(NMWifiAP *ap, guint32 bitrate);
guint32 nm_wifi_ap_get_bandwidth(NMWifiAP *ap);
gboolean nm_wifi_ap_set_bandwidth(NMWifiAP *ap, guint32 bandwidth);
gboolean nm_wifi_ap_get_fake(const NMWifiAP *ap);
gboolean nm_wifi_ap_set_fake(NMWifiAP *ap, gboolean fake);
NM80211ApFlags nm_wifi_ap_get_flags(const NMWifiAP *self);

View File

@@ -4779,15 +4779,91 @@ get_max_rate_vht(const guint8 *bytes, guint len, guint32 *out_maxrate)
return TRUE;
}
static gboolean
get_bandwidth_ht(const guint8 *bytes, guint len, guint32 *out_bandwidth)
{
guint8 ht_op_flag_group;
/* http://standards.ieee.org/getieee802/download/802.11-2012.pdf
* https://mrncciew.com/2014/11/04/cwap-ht-operations-ie/
* IEEE std 802.11-2020 section 9.4.2.56
*/
if (len != 22)
return FALSE;
ht_op_flag_group = bytes[1];
/* Check bit for 20Mhz or 40Mhz */
if (ht_op_flag_group & (1 << 2))
*out_bandwidth = 40;
else
*out_bandwidth = 20;
return TRUE;
}
static gboolean
get_bandwidth_vht(const guint8 *bytes, guint len, guint32 *out_bandwidth)
{
guint8 sta_channel_width;
guint8 ccfs0;
guint8 ccfs1;
/* http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames
* https://community.arubanetworks.com/community-home/librarydocuments/viewdocument?DocumentKey=799aad1b-d9c4-421a-a492-a111e8680d34&CommunityKey=39a6bdf4-2376-46f9-853a-49420d2d0caa&tab=librarydocuments
* IEEE Std 802.11-2020 section 9.4.2.158
*/
if (len < 3)
return FALSE;
sta_channel_width = bytes[0];
ccfs0 = bytes[1];
ccfs1 = bytes[2];
switch (sta_channel_width) {
case 0:
/* we rely on HT Operation IE value*/
return FALSE;
case 1:
if (ccfs1 == 0)
*out_bandwidth = 80;
else if (abs(ccfs1 - ccfs0) == 8)
*out_bandwidth = 160;
else if (abs(ccfs1 - ccfs0) > 16)
/* we are considering 80+80 as 160 */
*out_bandwidth = 160;
else
/* falling back to 80 MHz */
*out_bandwidth = 80;
break;
case 2:
/* deprecated */
*out_bandwidth = 160;
break;
case 3:
/* deprecated */
*out_bandwidth = 160;
break;
default:
return FALSE;
}
return TRUE;
}
/* Management Frame Information Element IDs, ieee80211_eid */
#define WLAN_EID_HT_CAPABILITY 45
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_VHT_CAPABILITY 191
#define WLAN_EID_VHT_OPERATION 192
#define WLAN_EID_VENDOR_SPECIFIC 221
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)
{
@@ -4795,6 +4871,7 @@ nm_wifi_utils_parse_ies(const guint8 *bytes,
guint32 m;
NM_SET_OUT(out_max_rate, 0);
NM_SET_OUT(out_bandwidth, 0);
NM_SET_OUT(out_metered, FALSE);
NM_SET_OUT(out_owe_transition_mode, FALSE);
@@ -4816,12 +4893,20 @@ nm_wifi_utils_parse_ies(const guint8 *bytes,
*out_max_rate = NM_MAX(*out_max_rate, m);
}
break;
case WLAN_EID_HT_OPERATION:
if (out_bandwidth)
get_bandwidth_ht(bytes, elem_len, out_bandwidth);
break;
case WLAN_EID_VHT_CAPABILITY:
if (out_max_rate) {
if (get_max_rate_vht(bytes, elem_len, &m))
*out_max_rate = NM_MAX(*out_max_rate, m);
}
break;
case WLAN_EID_VHT_OPERATION:
if (out_bandwidth)
get_bandwidth_vht(bytes, elem_len, out_bandwidth);
break;
case WLAN_EID_VENDOR_SPECIFIC:
if (len == 8 && bytes[0] == 0x00 /* OUI: Microsoft */
&& bytes[1] == 0x50 && bytes[2] == 0xf2

View File

@@ -455,6 +455,7 @@ 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);

View File

@@ -765,9 +765,15 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
gboolean p_owe_transition_mode;
gboolean p_metered;
guint32 rate;
guint32 bandwidth;
arr_data = g_variant_get_fixed_array(v_v, &arr_len, 1);
nm_wifi_utils_parse_ies(arr_data, arr_len, &rate, &p_metered, &p_owe_transition_mode);
nm_wifi_utils_parse_ies(arr_data,
arr_len,
&rate,
&bandwidth,
&p_metered,
&p_owe_transition_mode);
p_max_rate = NM_MAX(p_max_rate, rate);
p_max_rate_has = TRUE;
g_variant_unref(v_v);
@@ -778,6 +784,7 @@ _bss_info_properties_changed(NMSupplicantInterface *self,
bss_info->rsn_flags &= ~NM_802_11_AP_SEC_KEY_MGMT_OWE_TM;
bss_info->metered = p_metered;
bss_info->bandwidth = bandwidth;
}
if (p_max_rate_has)

View File

@@ -179,6 +179,8 @@ typedef struct _NMSupplicantBssInfo {
guint32 max_rate;
guint32 bandwidth;
guint8 signal_percent;
NMEtherAddr bssid;

View File

@@ -1943,3 +1943,8 @@ global:
nm_setting_link_get_type;
nm_setting_link_new;
} libnm_1_42_0;
libnm_1_46_0 {
global:
nm_access_point_get_bandwidth;
} libnm_1_44_0;

View File

@@ -30,6 +30,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMAccessPoint,
PROP_HW_ADDRESS,
PROP_MODE,
PROP_MAX_BITRATE,
PROP_BANDWIDTH,
PROP_STRENGTH,
PROP_BSSID,
PROP_LAST_SEEN, );
@@ -43,6 +44,7 @@ typedef struct {
guint32 frequency;
guint32 mode;
guint32 max_bitrate;
guint32 bandwidth;
gint32 last_seen;
guint8 strength;
} NMAccessPointPrivate;
@@ -198,6 +200,24 @@ nm_access_point_get_max_bitrate(NMAccessPoint *ap)
return NM_ACCESS_POINT_GET_PRIVATE(ap)->max_bitrate;
}
/**
* nm_access_point_get_bandwidth:
* @ap: a #NMAccessPoint
*
* Gets the bandwidth advertised by the access point in MHz.
*
* Returns: the advertised bandwidth (MHz)
*
* Since: 1.46
**/
guint32
nm_access_point_get_bandwidth(NMAccessPoint *ap)
{
g_return_val_if_fail(NM_IS_ACCESS_POINT(ap), 0);
return NM_ACCESS_POINT_GET_PRIVATE(ap)->bandwidth;
}
/**
* nm_access_point_get_strength:
* @ap: a #NMAccessPoint
@@ -463,6 +483,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_MAX_BITRATE:
g_value_set_uint(value, nm_access_point_get_max_bitrate(ap));
break;
case PROP_BANDWIDTH:
g_value_set_uint(value, nm_access_point_get_bandwidth(ap));
break;
case PROP_STRENGTH:
g_value_set_uchar(value, nm_access_point_get_strength(ap));
break;
@@ -480,6 +503,7 @@ const NMLDBusMetaIface _nml_dbus_meta_iface_nm_accesspoint = NML_DBUS_META_IFACE
nm_access_point_get_type,
NML_DBUS_META_INTERFACE_PRIO_INSTANTIATE_30,
NML_DBUS_META_IFACE_DBUS_PROPERTIES(
NML_DBUS_META_PROPERTY_INIT_U("Bandwidth", PROP_BANDWIDTH, NMAccessPoint, _priv.bandwidth),
NML_DBUS_META_PROPERTY_INIT_U("Flags", PROP_FLAGS, NMAccessPoint, _priv.flags),
NML_DBUS_META_PROPERTY_INIT_U("Frequency", PROP_FREQUENCY, NMAccessPoint, _priv.frequency),
NML_DBUS_META_PROPERTY_INIT_FCN("HwAddress",
@@ -620,6 +644,21 @@ nm_access_point_class_init(NMAccessPointClass *ap_class)
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMAccessPoint:bandwidth:
*
* The channel bandwidth announced by the AP in MHz.
*
* Since: 1.46
**/
obj_properties[PROP_BANDWIDTH] = g_param_spec_uint(NM_ACCESS_POINT_BANDWIDTH,
"",
"",
0,
G_MAXUINT32,
0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMAccessPoint:strength:
*

View File

@@ -35,6 +35,7 @@ G_BEGIN_DECLS
#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"
/* DEPRECATED */
#define NM_ACCESS_POINT_HW_ADDRESS "hw-address"
@@ -59,6 +60,9 @@ guint8 nm_access_point_get_strength(NMAccessPoint *ap);
NM_AVAILABLE_IN_1_2
int nm_access_point_get_last_seen(NMAccessPoint *ap);
NM_AVAILABLE_IN_1_46
guint32 nm_access_point_get_bandwidth(NMAccessPoint *ap);
GPtrArray *nm_access_point_filter_connections(NMAccessPoint *ap, const GPtrArray *connections);
gboolean nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection);