diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index e07fde543a..18f1a5a910 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -452,329 +452,6 @@ security_from_vardict (GVariant *security) /*****************************************************************************/ -static guint32 -get_max_rate_ht_20 (int mcs) -{ - switch (mcs) { - case 0: return 6500000; - case 1: - case 8: return 13000000; - case 2: - case 16: return 19500000; - case 3: - case 9: - case 24: return 26000000; - case 4: - case 10: - case 17: return 39000000; - case 5: - case 11: - case 25: return 52000000; - case 6: - case 18: return 58500000; - case 7: return 65000000; - case 12: - case 19: - case 26: return 78000000; - case 13: - case 27: return 104000000; - case 14: - case 20: return 117000000; - case 15: return 130000000; - case 21: - case 28: return 156000000; - case 22: return 175500000; - case 23: return 195000000; - case 29: return 208000000; - case 30: return 234000000; - case 31: return 260000000; - } - return 0; -} - -static guint32 -get_max_rate_ht_40 (int mcs) -{ - switch (mcs) { - case 0: return 13500000; - case 1: - case 8: return 27000000; - case 2: return 40500000; - case 3: - case 9: - case 24: return 54000000; - case 4: - case 10: - case 17: return 81000000; - case 5: - case 11: - case 25: return 108000000; - case 6: - case 18: return 121500000; - case 7: return 135000000; - case 12: - case 19: - case 26: return 162000000; - case 13: - case 27: return 216000000; - case 14: - case 20: return 243000000; - case 15: return 270000000; - case 16: return 40500000; - case 21: - case 28: return 324000000; - case 22: return 364500000; - case 23: return 405000000; - case 29: return 432000000; - case 30: return 486000000; - case 31: return 540000000; - } - return 0; -} - -static guint32 -get_max_rate_vht_80_ss1 (int mcs) -{ - switch (mcs) { - case 0: return 29300000; - case 1: return 58500000; - case 2: return 87800000; - case 3: return 117000000; - case 4: return 175500000; - case 5: return 234000000; - case 6: return 263300000; - case 7: return 292500000; - case 8: return 351000000; - case 9: return 390000000; - } - return 0; -} - -static guint32 -get_max_rate_vht_80_ss2 (int mcs) -{ - switch (mcs) { - case 0: return 58500000; - case 1: return 117000000; - case 2: return 175500000; - case 3: return 234000000; - case 4: return 351000000; - case 5: return 468000000; - case 6: return 526500000; - case 7: return 585000000; - case 8: return 702000000; - case 9: return 780000000; - } - return 0; -} - -static guint32 -get_max_rate_vht_80_ss3 (int mcs) -{ - switch (mcs) { - case 0: return 87800000; - case 1: return 175500000; - case 2: return 263300000; - case 3: return 351000000; - case 4: return 526500000; - case 5: return 702000000; - case 6: return 0; - case 7: return 877500000; - case 8: return 105300000; - case 9: return 117000000; - } - return 0; -} - -static guint32 -get_max_rate_vht_160_ss1 (int mcs) -{ - switch (mcs) { - case 0: return 58500000; - case 1: return 117000000; - case 2: return 175500000; - case 3: return 234000000; - case 4: return 351000000; - case 5: return 468000000; - case 6: return 526500000; - case 7: return 585000000; - case 8: return 702000000; - case 9: return 780000000; - } - return 0; -} - -static guint32 -get_max_rate_vht_160_ss2 (int mcs) -{ - switch (mcs) { - case 0: return 117000000; - case 1: return 234000000; - case 2: return 351000000; - case 3: return 468000000; - case 4: return 702000000; - case 5: return 936000000; - case 6: return 1053000000; - case 7: return 1170000000; - case 8: return 1404000000; - case 9: return 1560000000; - } - return 0; -} - -static guint32 -get_max_rate_vht_160_ss3 (int mcs) -{ - switch (mcs) { - case 0: return 175500000; - case 1: return 351000000; - case 2: return 526500000; - case 3: return 702000000; - case 4: return 1053000000; - case 5: return 1404000000; - case 6: return 1579500000; - case 7: return 1755000000; - case 8: return 2106000000; - case 9: return 0; - } - return 0; -} - -static gboolean -get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate) -{ - guint32 i; - guint8 ht_cap_info; - const guint8 *supported_mcs_set; - guint32 rate; - - /* http://standards.ieee.org/getieee802/download/802.11-2012.pdf - * https://mrncciew.com/2014/10/19/cwap-ht-capabilities-ie/ - */ - - if (len != 26) - return FALSE; - - ht_cap_info = bytes[0]; - supported_mcs_set = &bytes[3]; - *out_maxrate = 0; - - /* Find the maximum supported mcs rate */ - for (i = 0; i <= 76; i++) { - unsigned int mcs_octet = i / 8; - unsigned int MCS_RATE_BIT = 1 << i % 8; - - if (supported_mcs_set[mcs_octet] & MCS_RATE_BIT) { - /* Check for 40Mhz wide channel support */ - if (ht_cap_info & (1 << 1)) - rate = get_max_rate_ht_40 (i); - else - rate = get_max_rate_ht_20 (i); - - if (rate > *out_maxrate) - *out_maxrate = rate; - } - } - - return TRUE; -} - -static gboolean -get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate) -{ - guint32 mcs, m; - guint8 vht_cap, tx_map; - - /* https://tda802dot11.blogspot.it/2014/10/vht-capabilities-element-vht.html - * http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames */ - - if (len != 12) - return FALSE; - - vht_cap = bytes[0]; - tx_map = bytes[8]; - - /* Check for mcs rates 8 and 9 support */ - if (tx_map & 0x2a) - mcs = 9; - else if (tx_map & 0x15) - mcs = 8; - else - mcs = 7; - - /* Check for 160Mhz wide channel support and - * spatial stream support */ - if (vht_cap & (1 << 2)) { - if (tx_map & 0x30) - m = get_max_rate_vht_160_ss3 (mcs); - else if (tx_map & 0x0C) - m = get_max_rate_vht_160_ss2 (mcs); - else - m = get_max_rate_vht_160_ss1 (mcs); - } else { - if (tx_map & 0x30) - m = get_max_rate_vht_80_ss3 (mcs); - else if (tx_map & 0x0C) - m = get_max_rate_vht_80_ss2 (mcs); - else - m = get_max_rate_vht_80_ss1 (mcs); - } - - *out_maxrate = m; - return TRUE; -} - -/* 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 void -parse_ies (const guint8 *bytes, gsize len, guint32 *out_max_rate, gboolean *out_metered) -{ - guint8 id, elem_len; - guint32 m; - - *out_max_rate = 0; - *out_metered = FALSE; - - while (len) { - if (len < 2) - break; - - id = *bytes++; - elem_len = *bytes++; - len -= 2; - - if (elem_len > len) - break; - - switch (id) { - case WLAN_EID_HT_CAPABILITY: - 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)) - *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; - } -} - -/*****************************************************************************/ - gboolean nm_wifi_ap_update_from_properties (NMWifiAP *ap, const char *supplicant_path, @@ -872,7 +549,7 @@ 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); - parse_ies (bytes, len, &rate, &metered); + nm_wifi_utils_parse_ies (bytes, len, &rate, &metered); max_rate = NM_MAX (max_rate, rate); g_variant_unref (v); priv->metered = metered; diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index eccb5f7b15..9529c3e01a 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -4073,6 +4073,338 @@ nm_utils_strdict_to_variant (GHashTable *options) /*****************************************************************************/ +static guint32 +get_max_rate_ht_20 (int mcs) +{ + switch (mcs) { + case 0: return 6500000; + case 1: + case 8: return 13000000; + case 2: + case 16: return 19500000; + case 3: + case 9: + case 24: return 26000000; + case 4: + case 10: + case 17: return 39000000; + case 5: + case 11: + case 25: return 52000000; + case 6: + case 18: return 58500000; + case 7: return 65000000; + case 12: + case 19: + case 26: return 78000000; + case 13: + case 27: return 104000000; + case 14: + case 20: return 117000000; + case 15: return 130000000; + case 21: + case 28: return 156000000; + case 22: return 175500000; + case 23: return 195000000; + case 29: return 208000000; + case 30: return 234000000; + case 31: return 260000000; + } + return 0; +} + +static guint32 +get_max_rate_ht_40 (int mcs) +{ + switch (mcs) { + case 0: return 13500000; + case 1: + case 8: return 27000000; + case 2: return 40500000; + case 3: + case 9: + case 24: return 54000000; + case 4: + case 10: + case 17: return 81000000; + case 5: + case 11: + case 25: return 108000000; + case 6: + case 18: return 121500000; + case 7: return 135000000; + case 12: + case 19: + case 26: return 162000000; + case 13: + case 27: return 216000000; + case 14: + case 20: return 243000000; + case 15: return 270000000; + case 16: return 40500000; + case 21: + case 28: return 324000000; + case 22: return 364500000; + case 23: return 405000000; + case 29: return 432000000; + case 30: return 486000000; + case 31: return 540000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_80_ss1 (int mcs) +{ + switch (mcs) { + case 0: return 29300000; + case 1: return 58500000; + case 2: return 87800000; + case 3: return 117000000; + case 4: return 175500000; + case 5: return 234000000; + case 6: return 263300000; + case 7: return 292500000; + case 8: return 351000000; + case 9: return 390000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_80_ss2 (int mcs) +{ + switch (mcs) { + case 0: return 58500000; + case 1: return 117000000; + case 2: return 175500000; + case 3: return 234000000; + case 4: return 351000000; + case 5: return 468000000; + case 6: return 526500000; + case 7: return 585000000; + case 8: return 702000000; + case 9: return 780000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_80_ss3 (int mcs) +{ + switch (mcs) { + case 0: return 87800000; + case 1: return 175500000; + case 2: return 263300000; + case 3: return 351000000; + case 4: return 526500000; + case 5: return 702000000; + case 6: return 0; + case 7: return 877500000; + case 8: return 105300000; + case 9: return 117000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_160_ss1 (int mcs) +{ + switch (mcs) { + case 0: return 58500000; + case 1: return 117000000; + case 2: return 175500000; + case 3: return 234000000; + case 4: return 351000000; + case 5: return 468000000; + case 6: return 526500000; + case 7: return 585000000; + case 8: return 702000000; + case 9: return 780000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_160_ss2 (int mcs) +{ + switch (mcs) { + case 0: return 117000000; + case 1: return 234000000; + case 2: return 351000000; + case 3: return 468000000; + case 4: return 702000000; + case 5: return 936000000; + case 6: return 1053000000; + case 7: return 1170000000; + case 8: return 1404000000; + case 9: return 1560000000; + } + return 0; +} + +static guint32 +get_max_rate_vht_160_ss3 (int mcs) +{ + switch (mcs) { + case 0: return 175500000; + case 1: return 351000000; + case 2: return 526500000; + case 3: return 702000000; + case 4: return 1053000000; + case 5: return 1404000000; + case 6: return 1579500000; + case 7: return 1755000000; + case 8: return 2106000000; + case 9: return 0; + } + return 0; +} + +static gboolean +get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate) +{ + guint32 i; + guint8 ht_cap_info; + const guint8 *supported_mcs_set; + guint32 rate; + + /* http://standards.ieee.org/getieee802/download/802.11-2012.pdf + * https://mrncciew.com/2014/10/19/cwap-ht-capabilities-ie/ + */ + + if (len != 26) + return FALSE; + + ht_cap_info = bytes[0]; + supported_mcs_set = &bytes[3]; + *out_maxrate = 0; + + /* Find the maximum supported mcs rate */ + for (i = 0; i <= 76; i++) { + const unsigned mcs_octet = i / 8; + const unsigned MCS_RATE_BIT = 1 << i % 8; + + if (supported_mcs_set[mcs_octet] & MCS_RATE_BIT) { + /* Check for 40Mhz wide channel support */ + if (ht_cap_info & (1 << 1)) + rate = get_max_rate_ht_40 (i); + else + rate = get_max_rate_ht_20 (i); + + if (rate > *out_maxrate) + *out_maxrate = rate; + } + } + + return TRUE; +} + +static gboolean +get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate) +{ + guint32 mcs, m; + guint8 vht_cap, tx_map; + + /* https://tda802dot11.blogspot.it/2014/10/vht-capabilities-element-vht.html + * http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames */ + + if (len != 12) + return FALSE; + + vht_cap = bytes[0]; + tx_map = bytes[8]; + + /* Check for mcs rates 8 and 9 support */ + if (tx_map & 0x2a) + mcs = 9; + else if (tx_map & 0x15) + mcs = 8; + else + mcs = 7; + + /* Check for 160Mhz wide channel support and + * spatial stream support */ + if (vht_cap & (1 << 2)) { + if (tx_map & 0x30) + m = get_max_rate_vht_160_ss3 (mcs); + else if (tx_map & 0x0C) + m = get_max_rate_vht_160_ss2 (mcs); + else + m = get_max_rate_vht_160_ss1 (mcs); + } else { + if (tx_map & 0x30) + m = get_max_rate_vht_80_ss3 (mcs); + else if (tx_map & 0x0C) + m = get_max_rate_vht_80_ss2 (mcs); + else + m = get_max_rate_vht_80_ss1 (mcs); + } + + *out_maxrate = m; + return TRUE; +} + +/* 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 + +void +nm_wifi_utils_parse_ies (const guint8 *bytes, + gsize len, + guint32 *out_max_rate, + gboolean *out_metered) +{ + guint8 id, elem_len; + guint32 m; + + NM_SET_OUT (out_max_rate, 0); + NM_SET_OUT (out_metered, FALSE); + + while (len) { + if (len < 2) + break; + + id = *bytes++; + elem_len = *bytes++; + len -= 2; + + if (elem_len > len) + break; + + switch (id) { + case WLAN_EID_HT_CAPABILITY: + if (out_max_rate) { + 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 (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_VENDOR_SPECIFIC: + if (out_metered) { + 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; + } +} + +/*****************************************************************************/ + guint8 nm_wifi_utils_level_to_quality (int val) { diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index ca3e7dbf3b..14cdae3cdb 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -473,6 +473,11 @@ 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, + gboolean *out_metered); + guint8 nm_wifi_utils_level_to_quality (int val); /*****************************************************************************/