diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2019-09-10 10:32:55 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2019-09-10 15:52:01 +0200 |
commit | 203c1600dd169c320fad8329cbed89ac0707c6e8 (patch) | |
tree | d5db9f4349be7cc2808091be0733476b96086e0b | |
parent | a9777d9178993ba5dc100d1ee91df5f70fee0efa (diff) | |
download | NetworkManager-bg/wifi-network-cost-ie.tar.gz |
wifi: guess metered flag based on Network Cost information elementbg/wifi-network-cost-ie
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
-rw-r--r-- | src/devices/nm-device.c | 5 | ||||
-rw-r--r-- | src/devices/nm-device.h | 2 | ||||
-rw-r--r-- | src/devices/wifi/nm-device-wifi.c | 10 | ||||
-rw-r--r-- | src/devices/wifi/nm-wifi-ap.c | 61 | ||||
-rw-r--r-- | src/devices/wifi/nm-wifi-ap.h | 1 |
5 files changed, 58 insertions, 21 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 21450b4aa0..e55b9f1a6d 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -14097,6 +14097,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 */ if (value == NM_METERED_INVALID) { if ( priv->ip_config_4 diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index bde106235b..3b0a6e105c 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -466,6 +466,8 @@ typedef struct _NMDeviceClass { guint32 (* get_dhcp_timeout) (NMDevice *self, int addr_family); + gboolean (* get_guessed_metered) (NMDevice *self); + /* Controls, whether to call act_stage2_config() callback also for assuming * a device or for external activations. In this case, act_stage2_config() must * take care not to touch the device's configuration. */ diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 28e8bbefb6..713b2b0d20 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -3180,6 +3180,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 can_reapply_change (NMDevice *device, const char *setting_name, NMSetting *s_old, @@ -3388,6 +3397,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) device_class->check_connection_available = check_connection_available; device_class->complete_connection = complete_connection; device_class->get_enabled = get_enabled; + device_class->get_guessed_metered = get_guessed_metered; device_class->set_enabled = set_enabled; device_class->act_stage1_prepare = act_stage1_prepare; diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index fe615be816..4facbfa7df 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -67,10 +67,12 @@ struct _NMWifiAPPrivate { NM80211ApSecurityFlags wpa_flags; /* WPA-related flags */ NM80211ApSecurityFlags rsn_flags; /* RSN (WPA2) -related flags */ + bool metered:1; + /* Non-scanned attributes */ - 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 */ - gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ + 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 */ + gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ }; typedef struct _NMWifiAPPrivate NMWifiAPPrivate; @@ -406,6 +408,12 @@ nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen) return FALSE; } +gboolean +nm_wifi_ap_get_metered (const NMWifiAP *self) +{ + return NM_WIFI_AP_GET_PRIVATE (self)->metered; +} + /*****************************************************************************/ static NM80211ApSecurityFlags @@ -731,44 +739,50 @@ get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate) /* Management Frame Information Element IDs, ieee80211_eid */ #define WLAN_EID_HT_CAPABILITY 45 #define WLAN_EID_VHT_CAPABILITY 191 +#define WLAN_EID_VENDOR_SPECIFIC 221 -static guint32 -get_max_rate (const guint8 *bytes, gsize len) +static void +parse_ies (const guint8 *bytes, gsize len, guint32 *out_max_rate, gboolean *out_metered) { guint8 id, elem_len; - guint32 max_rate = 0; + guint32 m; - while (len) { - guint32 m; + *out_max_rate = 0; + *out_metered = FALSE; + while (len) { if (len < 2) - return 0; + break; id = *bytes++; elem_len = *bytes++; len -= 2; if (elem_len > len) - return 0; + break; switch (id) { case WLAN_EID_HT_CAPABILITY: - if (!get_max_rate_ht (bytes, elem_len, &m)) - return 0; - max_rate = NM_MAX (max_rate, m); + if (get_max_rate_ht (bytes, elem_len, &m)) + *out_max_rate = NM_MAX (*out_max_rate, m); break; case WLAN_EID_VHT_CAPABILITY: - if (!get_max_rate_vht (bytes, elem_len, &m)) - return 0; - max_rate = NM_MAX (max_rate, m); + if (get_max_rate_vht (bytes, elem_len, &m)) + *out_max_rate = NM_MAX (*out_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; } len -= elem_len; bytes += elem_len; } - - return max_rate; } /*****************************************************************************/ @@ -788,7 +802,8 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, gint16 i16; guint16 u16; 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 (properties, FALSE); @@ -869,9 +884,12 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); if (v) { 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); + priv->metered = metered; } + if (max_rate) changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000); @@ -1002,7 +1020,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self, export_path = "/"; 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)", (ssid_to_free = _nm_utils_ssid_to_string (priv->ssid)), (priv->mode == NM_802_11_MODE_ADHOC @@ -1017,6 +1035,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self, chan, priv->strength, priv->flags & NM_802_11_AP_FLAGS_PRIVACY ? 'P' : '_', + priv->metered ? 'M' : '_', priv->wpa_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, diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index 755e722c3e..3d457e8eb2 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -95,6 +95,7 @@ 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); +gboolean nm_wifi_ap_get_metered (const NMWifiAP *self); const char *nm_wifi_ap_to_string (const NMWifiAP *self, char *str_buf, |