wifi: guess metered flag based on Network Cost information element
Network Cost [1] is a vendor-specific information element defined by Microsoft and used to advertise the cost of Wi-Fi networks to clients. We can use it together with the ANDROID_METERED mechanism to automatically set the metered flag on the device. [1] https://docs.microsoft.com/en-us/windows-hardware/drivers/mobilebroadband/network-cost-information-element https://gitlab.freedesktop.org/NetworkManager/NetworkManager/issues/200
This commit is contained in:
@@ -14091,6 +14091,11 @@ nm_device_update_metered (NMDevice *self)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( value == NM_METERED_INVALID
|
||||||
|
&& NM_DEVICE_GET_CLASS (self)->get_guessed_metered
|
||||||
|
&& NM_DEVICE_GET_CLASS (self)->get_guessed_metered (self))
|
||||||
|
value = NM_METERED_GUESS_YES;
|
||||||
|
|
||||||
/* Try to guess a value using the metered flag in IP configuration */
|
/* Try to guess a value using the metered flag in IP configuration */
|
||||||
if (value == NM_METERED_INVALID) {
|
if (value == NM_METERED_INVALID) {
|
||||||
if ( priv->ip_config_4
|
if ( priv->ip_config_4
|
||||||
|
@@ -435,6 +435,8 @@ typedef struct _NMDeviceClass {
|
|||||||
guint32 (* get_dhcp_timeout) (NMDevice *self,
|
guint32 (* get_dhcp_timeout) (NMDevice *self,
|
||||||
int addr_family);
|
int addr_family);
|
||||||
|
|
||||||
|
gboolean (* get_guessed_metered) (NMDevice *self);
|
||||||
|
|
||||||
/* Controls, whether to call act_stage2_config() callback also for assuming
|
/* Controls, whether to call act_stage2_config() callback also for assuming
|
||||||
* a device or for external activations. In this case, act_stage2_config() must
|
* a device or for external activations. In this case, act_stage2_config() must
|
||||||
* take care not to touch the device's configuration. */
|
* take care not to touch the device's configuration. */
|
||||||
|
@@ -3165,6 +3165,15 @@ set_enabled (NMDevice *device, gboolean enabled)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
get_guessed_metered (NMDevice *device)
|
||||||
|
{
|
||||||
|
NMDeviceWifi *self = NM_DEVICE_WIFI (device);
|
||||||
|
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
return priv->current_ap && nm_wifi_ap_get_metered (priv->current_ap);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
can_reapply_change (NMDevice *device,
|
can_reapply_change (NMDevice *device,
|
||||||
const char *setting_name,
|
const char *setting_name,
|
||||||
@@ -3375,6 +3384,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
|
|||||||
device_class->check_connection_available = check_connection_available;
|
device_class->check_connection_available = check_connection_available;
|
||||||
device_class->complete_connection = complete_connection;
|
device_class->complete_connection = complete_connection;
|
||||||
device_class->get_enabled = get_enabled;
|
device_class->get_enabled = get_enabled;
|
||||||
|
device_class->get_guessed_metered = get_guessed_metered;
|
||||||
device_class->set_enabled = set_enabled;
|
device_class->set_enabled = set_enabled;
|
||||||
|
|
||||||
device_class->act_stage1_prepare = act_stage1_prepare;
|
device_class->act_stage1_prepare = act_stage1_prepare;
|
||||||
|
@@ -53,10 +53,12 @@ struct _NMWifiAPPrivate {
|
|||||||
NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */
|
NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */
|
||||||
NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */
|
NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */
|
||||||
|
|
||||||
|
bool metered:1;
|
||||||
|
|
||||||
/* Non-scanned attributes */
|
/* Non-scanned attributes */
|
||||||
bool fake:1; /* Whether or not the AP is from a scan */
|
bool fake:1; /* Whether or not the AP is from a scan */
|
||||||
bool hotspot:1; /* Whether the AP is a local device's hotspot network */
|
bool hotspot:1; /* Whether the AP is a local device's hotspot network */
|
||||||
gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
|
gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _NMWifiAPPrivate NMWifiAPPrivate;
|
typedef struct _NMWifiAPPrivate NMWifiAPPrivate;
|
||||||
@@ -392,6 +394,12 @@ nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_wifi_ap_get_metered (const NMWifiAP *self)
|
||||||
|
{
|
||||||
|
return NM_WIFI_AP_GET_PRIVATE (self)->metered;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static NM80211ApSecurityFlags
|
static NM80211ApSecurityFlags
|
||||||
@@ -717,44 +725,50 @@ get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate)
|
|||||||
/* Management Frame Information Element IDs, ieee80211_eid */
|
/* Management Frame Information Element IDs, ieee80211_eid */
|
||||||
#define WLAN_EID_HT_CAPABILITY 45
|
#define WLAN_EID_HT_CAPABILITY 45
|
||||||
#define WLAN_EID_VHT_CAPABILITY 191
|
#define WLAN_EID_VHT_CAPABILITY 191
|
||||||
|
#define WLAN_EID_VENDOR_SPECIFIC 221
|
||||||
|
|
||||||
static guint32
|
static void
|
||||||
get_max_rate (const guint8 *bytes, gsize len)
|
parse_ies (const guint8 *bytes, gsize len, guint32 *out_max_rate, gboolean *out_metered)
|
||||||
{
|
{
|
||||||
guint8 id, elem_len;
|
guint8 id, elem_len;
|
||||||
guint32 max_rate = 0;
|
guint32 m;
|
||||||
|
|
||||||
|
*out_max_rate = 0;
|
||||||
|
*out_metered = FALSE;
|
||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
guint32 m;
|
|
||||||
|
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
return 0;
|
break;
|
||||||
|
|
||||||
id = *bytes++;
|
id = *bytes++;
|
||||||
elem_len = *bytes++;
|
elem_len = *bytes++;
|
||||||
len -= 2;
|
len -= 2;
|
||||||
|
|
||||||
if (elem_len > len)
|
if (elem_len > len)
|
||||||
return 0;
|
break;
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case WLAN_EID_HT_CAPABILITY:
|
case WLAN_EID_HT_CAPABILITY:
|
||||||
if (!get_max_rate_ht (bytes, elem_len, &m))
|
if (get_max_rate_ht (bytes, elem_len, &m))
|
||||||
return 0;
|
*out_max_rate = NM_MAX (*out_max_rate, m);
|
||||||
max_rate = NM_MAX (max_rate, m);
|
|
||||||
break;
|
break;
|
||||||
case WLAN_EID_VHT_CAPABILITY:
|
case WLAN_EID_VHT_CAPABILITY:
|
||||||
if (!get_max_rate_vht (bytes, elem_len, &m))
|
if (get_max_rate_vht (bytes, elem_len, &m))
|
||||||
return 0;
|
*out_max_rate = NM_MAX (*out_max_rate, m);
|
||||||
max_rate = NM_MAX (max_rate, m);
|
break;
|
||||||
|
case WLAN_EID_VENDOR_SPECIFIC:
|
||||||
|
if ( len == 8
|
||||||
|
&& bytes[0] == 0x00 /* OUI: Microsoft */
|
||||||
|
&& bytes[1] == 0x50
|
||||||
|
&& bytes[2] == 0xf2
|
||||||
|
&& bytes[3] == 0x11) /* OUI type: Network cost */
|
||||||
|
*out_metered = (bytes[7] > 1); /* Cost level > 1 */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
len -= elem_len;
|
len -= elem_len;
|
||||||
bytes += elem_len;
|
bytes += elem_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
return max_rate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@@ -774,7 +788,8 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
|
|||||||
gint16 i16;
|
gint16 i16;
|
||||||
guint16 u16;
|
guint16 u16;
|
||||||
gboolean changed = FALSE;
|
gboolean changed = FALSE;
|
||||||
guint32 max_rate;
|
gboolean metered;
|
||||||
|
guint32 max_rate, rate;
|
||||||
|
|
||||||
g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
|
g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
|
||||||
g_return_val_if_fail (properties, FALSE);
|
g_return_val_if_fail (properties, FALSE);
|
||||||
@@ -855,9 +870,12 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
|
|||||||
v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING);
|
v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING);
|
||||||
if (v) {
|
if (v) {
|
||||||
bytes = g_variant_get_fixed_array (v, &len, 1);
|
bytes = g_variant_get_fixed_array (v, &len, 1);
|
||||||
max_rate = NM_MAX (max_rate, get_max_rate (bytes, len));
|
parse_ies (bytes, len, &rate, &metered);
|
||||||
|
max_rate = NM_MAX (max_rate, rate);
|
||||||
g_variant_unref (v);
|
g_variant_unref (v);
|
||||||
|
priv->metered = metered;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max_rate)
|
if (max_rate)
|
||||||
changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000);
|
changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000);
|
||||||
|
|
||||||
@@ -988,7 +1006,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
|
|||||||
export_path = "/";
|
export_path = "/";
|
||||||
|
|
||||||
g_snprintf (str_buf, buf_len,
|
g_snprintf (str_buf, buf_len,
|
||||||
"%17s %-35s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
|
"%17s %-35s [ %c %3u %3u%% %c%c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
|
||||||
priv->address ?: "(none)",
|
priv->address ?: "(none)",
|
||||||
(ssid_to_free = _nm_utils_ssid_to_string (priv->ssid)),
|
(ssid_to_free = _nm_utils_ssid_to_string (priv->ssid)),
|
||||||
(priv->mode == NM_802_11_MODE_ADHOC
|
(priv->mode == NM_802_11_MODE_ADHOC
|
||||||
@@ -1003,6 +1021,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
|
|||||||
chan,
|
chan,
|
||||||
priv->strength,
|
priv->strength,
|
||||||
priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_',
|
priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_',
|
||||||
|
priv->metered ? 'M' : '_',
|
||||||
priv->wpa_flags & 0xFFFF,
|
priv->wpa_flags & 0xFFFF,
|
||||||
priv->rsn_flags & 0xFFFF,
|
priv->rsn_flags & 0xFFFF,
|
||||||
priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1,
|
priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1,
|
||||||
|
@@ -81,6 +81,7 @@ gboolean nm_wifi_ap_get_fake (const NMWifiAP *ap);
|
|||||||
gboolean nm_wifi_ap_set_fake (NMWifiAP *ap,
|
gboolean nm_wifi_ap_set_fake (NMWifiAP *ap,
|
||||||
gboolean fake);
|
gboolean fake);
|
||||||
NM80211ApFlags nm_wifi_ap_get_flags (const NMWifiAP *self);
|
NM80211ApFlags nm_wifi_ap_get_flags (const NMWifiAP *self);
|
||||||
|
gboolean nm_wifi_ap_get_metered (const NMWifiAP *self);
|
||||||
|
|
||||||
const char *nm_wifi_ap_to_string (const NMWifiAP *self,
|
const char *nm_wifi_ap_to_string (const NMWifiAP *self,
|
||||||
char *str_buf,
|
char *str_buf,
|
||||||
|
Reference in New Issue
Block a user