summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2019-09-10 10:32:55 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2019-09-10 15:52:01 +0200
commit203c1600dd169c320fad8329cbed89ac0707c6e8 (patch)
treed5db9f4349be7cc2808091be0733476b96086e0b
parenta9777d9178993ba5dc100d1ee91df5f70fee0efa (diff)
downloadNetworkManager-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.c5
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/devices/wifi/nm-device-wifi.c10
-rw-r--r--src/devices/wifi/nm-wifi-ap.c61
-rw-r--r--src/devices/wifi/nm-wifi-ap.h1
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,