diff options
| author | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2017-11-07 00:14:39 +0100 |
| commit | 90e8691111889a7b5f3c812f5a41f15a8a058913 (patch) | |
| tree | f101a879eca27c34a9bfa5f3da52266b22539a36 /src/devices/wifi | |
| parent | bdb6eeb0670658255c2a4c3c501c0a27fa8cfe55 (diff) | |
New upstream version 1.9.90 upstream/1.9.90
Diffstat (limited to 'src/devices/wifi')
| -rw-r--r-- | src/devices/wifi/nm-device-olpc-mesh.c | 23 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-wifi.c | 516 | ||||
| -rw-r--r-- | src/devices/wifi/nm-device-wifi.h | 2 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-ap.c | 368 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-ap.h | 3 | ||||
| -rw-r--r-- | src/devices/wifi/nm-wifi-utils.c | 4 | ||||
| -rw-r--r-- | src/devices/wifi/tests/test-general.c | 32 |
7 files changed, 749 insertions, 199 deletions
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 24811931..ac78757d 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -312,13 +312,13 @@ companion_state_changed_cb (NMDeviceWifi *companion, } static gboolean -companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data) +companion_scan_prohibited_cb (NMDeviceWifi *companion, gpointer user_data) { NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data); NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); /* Don't allow the companion to scan while configuring the mesh interface */ - return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG); + return (state >= NM_DEVICE_STATE_PREPARE) && (state <= NM_DEVICE_STATE_IP_CONFIG); } static gboolean @@ -358,8 +358,8 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other) g_signal_connect (G_OBJECT (other), "notify::" NM_DEVICE_WIFI_SCANNING, G_CALLBACK (companion_notify_cb), self); - g_signal_connect (G_OBJECT (other), NM_DEVICE_WIFI_SCANNING_ALLOWED, - G_CALLBACK (companion_scan_allowed_cb), self); + g_signal_connect (G_OBJECT (other), NM_DEVICE_WIFI_SCANNING_PROHIBITED, + G_CALLBACK (companion_scan_prohibited_cb), self); g_signal_connect (G_OBJECT (other), NM_DEVICE_AUTOCONNECT_ALLOWED, G_CALLBACK (companion_autoconnect_allowed_cb), self); @@ -425,6 +425,13 @@ state_changed (NMDevice *device, find_companion (NM_DEVICE_OLPC_MESH (device)); } +static guint32 +get_dhcp_timeout (NMDevice *device, int addr_family) +{ + /* shorter timeout for mesh connectivity */ + return 20; +} + /*****************************************************************************/ static void @@ -465,11 +472,8 @@ constructed (GObject *object) priv->manager = g_object_ref (nm_manager_get ()); - g_signal_connect (priv->manager, "device-added", G_CALLBACK (device_added_cb), self); - g_signal_connect (priv->manager, "device-removed", G_CALLBACK (device_removed_cb), self); - - /* shorter timeout for mesh connectivity */ - nm_device_set_dhcp_timeout (NM_DEVICE (self), 20); + g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added_cb), self); + g_signal_connect (priv->manager, NM_MANAGER_DEVICE_REMOVED, G_CALLBACK (device_removed_cb), self); } NMDevice * @@ -519,6 +523,7 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass) parent_class->act_stage1_prepare = act_stage1_prepare; parent_class->act_stage2_config = act_stage2_config; parent_class->state_changed = state_changed; + parent_class->get_dhcp_timeout = get_dhcp_timeout; obj_properties[PROP_COMPANION] = g_param_spec_string (NM_DEVICE_OLPC_MESH_COMPANION, "", "", diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 20692ed9..8bfddbd9 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2012 Red Hat, Inc. + * Copyright (C) 2005 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -63,8 +63,6 @@ _LOG_DECLARE_SELF(NMDeviceWifi); #define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5 -static NM_CACHED_QUARK_FCN ("wireless-secrets-tries", wireless_secrets_tries_quark) - /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, @@ -79,7 +77,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, enum { ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED, - SCANNING_ALLOWED, + SCANNING_PROHIBITED, LAST_SIGNAL }; @@ -119,6 +117,8 @@ typedef struct { NMDeviceWifiCapabilities capabilities; gint32 hw_addr_scan_expire; + + guint wps_timeout_id; } NMDeviceWifiPrivate; struct _NMDeviceWifi @@ -132,7 +132,7 @@ struct _NMDeviceWifiClass NMDeviceClass parent; /* Signals */ - gboolean (*scanning_allowed) (NMDeviceWifi *device); + gboolean (*scanning_prohibited) (NMDeviceWifi *device, gboolean periodic); }; /*****************************************************************************/ @@ -143,7 +143,7 @@ G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE) /*****************************************************************************/ -static gboolean check_scanning_allowed (NMDeviceWifi *self); +static gboolean check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic); static void schedule_scan (NMDeviceWifi *self, gboolean backoff); @@ -169,6 +169,10 @@ static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface, gboolean success, NMDeviceWifi * self); +static void supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, + GVariant *credentials, + NMDeviceWifi *self); + static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, GParamSpec * pspec, NMDeviceWifi * self); @@ -177,7 +181,10 @@ static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self); -static void request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant *scan_options); +static void request_wireless_scan (NMDeviceWifi *self, + gboolean periodic, + gboolean force_if_scanning, + const GPtrArray *ssids); static void ap_add_remove (NMDeviceWifi *self, guint signum, @@ -268,6 +275,10 @@ supplicant_interface_acquire (NMDeviceWifi *self) G_CALLBACK (supplicant_iface_scan_done_cb), self); g_signal_connect (priv->sup_iface, + NM_SUPPLICANT_INTERFACE_WPS_CREDENTIALS, + G_CALLBACK (supplicant_iface_wps_credentials_cb), + self); + g_signal_connect (priv->sup_iface, "notify::"NM_SUPPLICANT_INTERFACE_SCANNING, G_CALLBACK (supplicant_iface_notify_scanning_cb), self); @@ -559,7 +570,7 @@ deactivate (NMDevice *device) /* Ensure we trigger a scan after deactivating a Hotspot */ if (old_mode == NM_802_11_MODE_AP) - request_wireless_scan (self, FALSE, NULL); + request_wireless_scan (self, FALSE, FALSE, NULL); } static void @@ -781,15 +792,18 @@ complete_connection (NMDevice *device, NMSettingWireless *s_wifi; const char *setting_mac; char *str_ssid = NULL; - NMWifiAP *ap = NULL; + NMWifiAP *ap; const GByteArray *ssid = NULL; GByteArray *tmp_ssid = NULL; GBytes *setting_ssid = NULL; gboolean hidden = FALSE; const char *perm_hw_addr; + const char *mode; s_wifi = nm_connection_get_setting_wireless (connection); + mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL; + if (!specific_object) { /* If not given a specific object, we need at minimum an SSID */ if (!s_wifi) { @@ -809,19 +823,29 @@ complete_connection (NMDevice *device, return FALSE; } - /* Find a compatible AP in the scan list */ - ap = find_first_compatible_ap (self, connection, FALSE); + if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { + /* Find a compatible AP in the scan list */ + ap = find_first_compatible_ap (self, connection, FALSE); - /* If we still don't have an AP, then the WiFI settings needs to be - * fully specified by the client. Might not be able to find an AP - * if the network isn't broadcasting the SSID for example. - */ - if (!ap) { + /* If we still don't have an AP, then the WiFI settings needs to be + * fully specified by the client. Might not be able to find an AP + * if the network isn't broadcasting the SSID for example. + */ + if (!ap) { + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; + + hidden = TRUE; + } + } else { if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) return FALSE; - - hidden = TRUE; + ap = NULL; } + } else if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; + ap = NULL; } else { ap = get_ap_by_path (self, specific_object); if (!ap) { @@ -1169,23 +1193,65 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset) } } +static GPtrArray * +ssids_options_to_ptrarray (GVariant *value, GError **error) +{ + GPtrArray *ssids = NULL; + GByteArray *ssid_array; + GVariant *v; + const guint8 *bytes; + gsize len; + int num_ssids, i; + + num_ssids = g_variant_n_children (value); + if (num_ssids > 32) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "too many SSIDs requested to scan"); + return NULL; + } + + if (num_ssids) { + ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_byte_array_unref); + for (i = 0; i < num_ssids; i++) { + v = g_variant_get_child_value (value, i); + bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8)); + if (len > 32) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "SSID at index %d more than 32 bytes", i); + g_ptr_array_unref (ssids); + return NULL; + } + + ssid_array = g_byte_array_new (); + g_byte_array_append (ssid_array, bytes, len); + g_ptr_array_add (ssids, ssid_array); + } + } + return ssids; +} + static void -request_scan_cb (NMDevice *device, - GDBusMethodInvocation *context, - NMAuthSubject *subject, - GError *error, - gpointer user_data) +dbus_request_scan_cb (NMDevice *device, + GDBusMethodInvocation *context, + NMAuthSubject *subject, + GError *error, + gpointer user_data) { NMDeviceWifi *self = NM_DEVICE_WIFI (device); NMDeviceWifiPrivate *priv; - gs_unref_variant GVariant *new_scan_options = user_data; + gs_unref_variant GVariant *scan_options = user_data; + gs_unref_ptrarray GPtrArray *ssids = NULL; if (error) { g_dbus_method_invocation_return_gerror (context, error); return; } - if (!check_scanning_allowed (self)) { + if (check_scanning_prohibited (self, FALSE)) { g_dbus_method_invocation_return_error_literal (context, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -1195,7 +1261,29 @@ request_scan_cb (NMDevice *device, priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - request_wireless_scan (self, FALSE, new_scan_options); + if (scan_options) { + gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL); + + if (val) { + gs_free_error GError *ssid_error = NULL; + + if (!g_variant_is_of_type (val, G_VARIANT_TYPE ("aay"))) { + g_dbus_method_invocation_return_error_literal (context, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "Invalid 'ssid' scan option"); + return; + } + + ssids = ssids_options_to_ptrarray (val, &ssid_error); + if (ssid_error) { + g_dbus_method_invocation_return_gerror (context, ssid_error); + return; + } + } + } + + request_wireless_scan (self, FALSE, FALSE, ssids); g_dbus_method_invocation_return_value (context, NULL); } @@ -1243,22 +1331,23 @@ impl_device_wifi_request_scan (NMDeviceWifi *self, NULL, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE, - request_scan_cb, + dbus_request_scan_cb, options ? g_variant_ref (options) : NULL); } static gboolean -scanning_allowed (NMDeviceWifi *self) +scanning_prohibited (NMDeviceWifi *self, gboolean periodic) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMSupplicantInterfaceState supplicant_state; - NMConnection *connection; - g_return_val_if_fail (priv->sup_iface != NULL, FALSE); + g_return_val_if_fail (priv->sup_iface != NULL, TRUE); - /* Scanning not done in AP mode */ - if (priv->mode == NM_802_11_MODE_AP) - return FALSE; + /* Don't scan when a an AP or Ad-Hoc connection is active as it will + * disrupt connected clients or peers. + */ + if (priv->mode == NM_802_11_MODE_ADHOC || priv->mode == NM_802_11_MODE_AP) + return TRUE; switch (nm_device_get_state (NM_DEVICE (self))) { case NM_DEVICE_STATE_UNKNOWN: @@ -1271,78 +1360,42 @@ scanning_allowed (NMDeviceWifi *self) case NM_DEVICE_STATE_IP_CHECK: case NM_DEVICE_STATE_SECONDARIES: case NM_DEVICE_STATE_DEACTIVATING: - /* Don't scan when unusable or activating */ - return FALSE; + /* Prohibit scans when unusable or activating */ + return TRUE; case NM_DEVICE_STATE_DISCONNECTED: case NM_DEVICE_STATE_FAILED: /* Can always scan when disconnected */ - return TRUE; + return FALSE; case NM_DEVICE_STATE_ACTIVATED: - /* Need to do further checks when activated */ + /* Prohibit periodic scans when connected; we ask the supplicant to + * background scan for us, unless the connection is locked to a specifc + * BSSID. + */ + if (periodic) + return TRUE; break; } - /* Don't scan if the supplicant is busy */ + /* Prohibit scans if the supplicant is busy */ supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface); if ( supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE || nm_supplicant_interface_get_scanning (priv->sup_iface)) - return FALSE; - - connection = nm_device_get_applied_connection (NM_DEVICE (self)); - if (connection) { - NMSettingWireless *s_wifi; - const char *ip4_method = NULL; - - /* Don't scan when a shared connection is active; it makes drivers mad */ - ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - - if (!strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) - return FALSE; - - /* Don't scan when the connection is locked to a specifc AP, since - * intra-ESS roaming (which requires periodic scanning) isn't being - * used due to the specific AP lock. (bgo #513820) - */ - s_wifi = nm_connection_get_setting_wireless (connection); - g_assert (s_wifi); - if (nm_setting_wireless_get_bssid (s_wifi)) - return FALSE; - } - - return TRUE; -} + return TRUE; -static gboolean -scanning_allowed_accumulator (GSignalInvocationHint *ihint, - GValue *return_accu, - const GValue *handler_return, - gpointer data) -{ - if (!g_value_get_boolean (handler_return)) - g_value_set_boolean (return_accu, FALSE); - return TRUE; + /* Allow the scan */ + return FALSE; } static gboolean -check_scanning_allowed (NMDeviceWifi *self) +check_scanning_prohibited (NMDeviceWifi *self, gboolean periodic) { - GValue instance = G_VALUE_INIT; - GValue retval = G_VALUE_INIT; - - g_value_init (&instance, G_TYPE_OBJECT); - g_value_take_object (&instance, self); - - g_value_init (&retval, G_TYPE_BOOLEAN); - g_value_set_boolean (&retval, TRUE); + gboolean prohibited = FALSE; - /* Use g_signal_emitv() rather than g_signal_emit() to avoid the return - * value being changed if no handlers are connected */ - g_signal_emitv (&instance, signals[SCANNING_ALLOWED], 0, &retval); - - return g_value_get_boolean (&retval); + g_signal_emit (self, signals[SCANNING_PROHIBITED], 0, periodic, &prohibited); + return prohibited; } static gboolean @@ -1354,8 +1407,12 @@ hidden_filter_func (NMSettings *settings, if (!nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_WIRELESS_SETTING_NAME)) return FALSE; - s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (NM_CONNECTION (connection)); - return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE; + s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection)); + if (!s_wifi) + return FALSE; + if (nm_streq0 (nm_setting_wireless_get_mode (s_wifi), NM_SETTING_WIRELESS_MODE_AP)) + return FALSE; + return nm_setting_wireless_get_hidden (s_wifi); } static GPtrArray * @@ -1410,32 +1467,11 @@ build_hidden_probe_list (NMDeviceWifi *self) return ssids; } -static GPtrArray * -ssids_options_to_ptrarray (GVariant *value) -{ - GPtrArray *ssids = NULL; - GByteArray *ssid_array; - GVariant *v; - const guint8 *bytes; - gsize len; - int num_ssids, i; - - num_ssids = g_variant_n_children (value); - if (num_ssids) { - ssids = g_ptr_array_new_full (num_ssids, (GDestroyNotify) g_byte_array_unref); - for (i = 0; i < num_ssids; i++) { - v = g_variant_get_child_value (value, i); - bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8)); - ssid_array = g_byte_array_new (); - g_byte_array_append (ssid_array, bytes, len); - g_ptr_array_add (ssids, ssid_array); - } - } - return ssids; -} - static void -request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant *scan_options) +request_wireless_scan (NMDeviceWifi *self, + gboolean periodic, + gboolean force_if_scanning, + const GPtrArray *ssids) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); gboolean request_started = FALSE; @@ -1447,24 +1483,14 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant return; } - if (check_scanning_allowed (self)) { - gs_unref_ptrarray GPtrArray *ssids = NULL; + if (!check_scanning_prohibited (self, periodic)) { + gs_unref_ptrarray GPtrArray *hidden_ssids = NULL; _LOGD (LOGD_WIFI, "wifi-scan: scanning requested"); - if (scan_options) { - GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL); - - if (val) { - if (g_variant_is_of_type (val, G_VARIANT_TYPE ("aay"))) - ssids = ssids_options_to_ptrarray (val); - else - _LOGD (LOGD_WIFI, "wifi-scan: ignoring invalid 'ssids' scan option"); - g_variant_unref (val); - } + if (!ssids) { + ssids = hidden_ssids = build_hidden_probe_list (self); } - if (!ssids) - ssids = build_hidden_probe_list (self); if (_LOGD_ENABLED (LOGD_WIFI)) { if (ssids) { @@ -1478,7 +1504,7 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant ? nm_utils_ssid_to_utf8 (ssid->data, ssid->len) : NULL; _LOGD (LOGD_WIFI, "wifi-scan: (%u) probe scanning SSID %s%s%s", - i, NM_PRINT_FMT_QUOTED (foo, "\"", foo, "\"", "<hidden>")); + i, NM_PRINT_FMT_QUOTED (foo, "\"", foo, "\"", "*any*")); g_free (foo); } } else @@ -1504,7 +1530,7 @@ request_wireless_scan_periodic (gpointer user_data) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); priv->pending_scan_id = 0; - request_wireless_scan (self, FALSE, NULL); + request_wireless_scan (self, TRUE, FALSE, NULL); return G_SOURCE_REMOVE; } @@ -1747,6 +1773,7 @@ cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); if (disconnect && priv->sup_iface) nm_supplicant_interface_disconnect (priv->sup_iface); } @@ -1789,9 +1816,19 @@ wifi_secrets_cb (NMActRequest *req, if (error) { _LOGW (LOGD_WIFI, "%s", error->message); - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); + + if (g_error_matches (error, NM_AGENT_MANAGER_ERROR, + NM_AGENT_MANAGER_ERROR_USER_CANCELED)) { + /* Don't wait for WPS timeout on an explicit cancel. */ + nm_clear_g_source (&priv->wps_timeout_id); + } + + if (!priv->wps_timeout_id) { + /* Fail the device only if the WPS period is over too. */ + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } } else nm_device_activate_schedule_stage1_device_prepare (device); } @@ -1807,6 +1844,78 @@ wifi_secrets_cancel (NMDeviceWifi *self) } static void +supplicant_iface_wps_credentials_cb (NMSupplicantInterface *iface, + GVariant *credentials, + NMDeviceWifi *self) +{ + NMActRequest *req; + GVariant *val, *secrets = NULL; + const char *array; + gsize psk_len = 0; + GError *error = NULL; + + if (nm_device_get_state (NM_DEVICE (self)) != NM_DEVICE_STATE_NEED_AUTH) { + _LOGI (LOGD_DEVICE | LOGD_WIFI, "WPS: The connection can't be updated with credentials"); + return; + } + + _LOGI (LOGD_DEVICE | LOGD_WIFI, "WPS: Updating the connection with credentials"); + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + val = g_variant_lookup_value (credentials, "Key", G_VARIANT_TYPE_BYTESTRING); + if (val) { + char psk[64]; + + array = g_variant_get_fixed_array (val, &psk_len, 1); + if (psk_len >= 8 && psk_len <= 63) { + memcpy (psk, array, psk_len); + psk[psk_len] = '\0'; + if (g_utf8_validate (psk, psk_len, NULL)) { + secrets = g_variant_new_parsed ("[{%s, [{%s, <%s>}]}]", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, psk); + g_variant_ref_sink (secrets); + } + } + if (!secrets) + _LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: ignore invalid PSK"); + g_variant_unref (val); + } + if (secrets) { + if (nm_settings_connection_new_secrets (nm_act_request_get_settings_connection (req), + nm_act_request_get_applied_connection (req), + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + secrets, &error)) { + wifi_secrets_cancel (self); + nm_device_activate_schedule_stage1_device_prepare (NM_DEVICE (self)); + } else { + _LOGW (LOGD_DEVICE | LOGD_WIFI, "WPS: Could not update the connection with credentials: %s", error->message); + g_error_free (error); + } + g_variant_unref (secrets); + } +} + +static gboolean +wps_timeout_cb (gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + priv->wps_timeout_id = 0; + if (!priv->wifi_secrets_id) { + /* Fail only if the secrets are not being requested. */ + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } + + return G_SOURCE_REMOVE; +} + +static void wifi_secrets_get_secrets (NMDeviceWifi *self, const char *setting_name, NMSecretAgentGetSecretsFlags flags) @@ -2055,6 +2164,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); /* If this is the initial association during device activation, * schedule the next activation stage. @@ -2131,7 +2241,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, /* we would clear _requested_scan_set() and trigger a new scan. * However, we don't want to cancel the current pending action, so force * a new scan request. */ - request_wireless_scan (self, TRUE, NULL); + request_wireless_scan (self, FALSE, TRUE, NULL); break; default: break; @@ -2222,9 +2332,15 @@ handle_auth_or_fail (NMDeviceWifi *self, NMActRequest *req, gboolean new_secrets) { + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); const char *setting_name; - guint32 tries; NMConnection *applied_connection; + NMSettingWirelessSecurity *s_wsec; + const char *bssid = NULL; + NM80211ApFlags ap_flags; + NMSettingWirelessSecurityWpsMethod wps_method; + const char *type; + NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION; g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE); @@ -2233,14 +2349,50 @@ handle_auth_or_fail (NMDeviceWifi *self, g_return_val_if_fail (req, FALSE); } - applied_connection = nm_act_request_get_applied_connection (req); - - tries = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark ())); - if (tries > 3) + if (!nm_device_auth_retries_try_next (NM_DEVICE (self))) return FALSE; nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + applied_connection = nm_act_request_get_applied_connection (req); + s_wsec = nm_connection_get_setting_wireless_security (applied_connection); + wps_method = nm_setting_wireless_security_get_wps_method (s_wsec); + + /* Negotiate the WPS method */ + if (wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT) + wps_method = NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO; + + if ( wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO + && priv->current_ap) { + /* Determine the method to use from AP capabilities. */ + ap_flags = nm_wifi_ap_get_flags (priv->current_ap); + if (ap_flags & NM_802_11_AP_FLAGS_WPS_PBC) + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC; + if (ap_flags & NM_802_11_AP_FLAGS_WPS_PIN) + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN; + if ( ap_flags & NM_802_11_AP_FLAGS_WPS + && wps_method == NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO) { + /* The AP doesn't specify which methods are supported. Allow all. */ + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC; + wps_method |= NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN; + } + } + + if (wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PBC) { + get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE; + type = "pbc"; + } else if (wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN) { + type = "pin"; + } else + type = NULL; + + if (type) { + priv->wps_timeout_id = g_timeout_add_seconds (30, wps_timeout_cb, self); + if (priv->current_ap) + bssid = nm_wifi_ap_get_address (priv->current_ap); + nm_supplicant_interface_enroll_wps (priv->sup_iface, type, bssid, NULL); + } + nm_act_request_clear_secrets (req); setting_name = nm_connection_need_secrets (applied_connection, NULL); if (!setting_name) { @@ -2248,10 +2400,9 @@ handle_auth_or_fail (NMDeviceWifi *self, return FALSE; } - wifi_secrets_get_secrets (self, setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0)); - g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), GUINT_TO_POINTER (++tries)); + if (new_secrets) + get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; + wifi_secrets_get_secrets (self, setting_name, get_secret_flags); return TRUE; } @@ -2348,6 +2499,8 @@ build_supplicant_config (NMDeviceWifi *self, NMSupplicantConfig *config = NULL; NMSettingWireless *s_wireless; NMSettingWirelessSecurity *s_wireless_sec; + NMSettingWirelessSecurityPmf pmf; + gs_free char *value = NULL; g_return_val_if_fail (priv->sup_iface, NULL); @@ -2370,6 +2523,11 @@ build_supplicant_config (NMDeviceWifi *self, goto error; } + if (!nm_supplicant_config_add_bgscan (config, connection, error)) { + g_prefix_error (error, "bgscan: "); + goto error; + } + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); if (s_wireless_sec) { NMSetting8021x *s_8021x; @@ -2378,12 +2536,46 @@ build_supplicant_config (NMDeviceWifi *self, nm_device_get_ifindex (NM_DEVICE (self))); g_assert (con_uuid); + + /* Configure PMF (802.11w) */ + pmf = nm_setting_wireless_security_get_pmf (s_wireless_sec); + if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_DEFAULT) { + value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "wifi-sec.pmf", + NM_DEVICE (self)); + pmf = _nm_utils_ascii_str_to_int64 (value, 10, + NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE, + NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED, + NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL); + } + + /* Don't try to enable PMF on non-WPA networks */ + if (!NM_IN_STRSET (nm_setting_wireless_security_get_key_mgmt (s_wireless_sec), + "wpa-eap", + "wpa-psk")) + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + + /* Check if we actually support PMF */ + if (nm_supplicant_interface_get_pmf_support (priv->sup_iface) != NM_SUPPLICANT_FEATURE_YES) { + if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED) { + g_set_error_literal (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG, + "Supplicant does not support PMF"); + goto error; + } else if (pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL) { + /* To be on the safe side, assume no support if we can't determine + * capabilities. + */ + pmf = NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE; + } + } + s_8021x = nm_connection_get_setting_802_1x (connection); if (!nm_supplicant_config_add_setting_wireless_security (config, s_wireless_sec, s_8021x, con_uuid, mtu, + pmf, error)) { g_prefix_error (error, "802-11-wireless-security: "); goto error; @@ -2430,6 +2622,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); + nm_supplicant_interface_cancel_wps (priv->sup_iface); + mode = nm_setting_wireless_get_mode (s_wireless); if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0) priv->mode = NM_802_11_MODE_INFRA; @@ -2578,6 +2772,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_clear_g_source (&priv->sup_timeout_id); nm_clear_g_source (&priv->link_timeout_id); + nm_clear_g_source (&priv->wps_timeout_id); req = nm_device_get_act_request (device); g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); @@ -2873,9 +3068,6 @@ activation_success_handler (NMDevice *device) /* Clear any critical protocol notification in the wifi stack */ nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE); - /* Clear wireless secrets tries on success */ - g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL); - /* There should always be a current AP, either a fake one because we haven't * seen a scan result for the activated AP yet, or a real one from the * supplicant's scan list. @@ -2922,21 +3114,6 @@ activation_success_handler (NMDevice *device) } static void -activation_failure_handler (NMDevice *device) -{ - NMConnection *applied_connection; - - applied_connection = nm_device_get_applied_connection (device); - g_assert (applied_connection); - - /* Clear wireless secrets tries on failure */ - g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL); - - /* Clear any critical protocol notification in the wifi stack */ - nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), FALSE); -} - -static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -2990,12 +3167,13 @@ device_state_changed (NMDevice *device, activation_success_handler (device); break; case NM_DEVICE_STATE_FAILED: - activation_failure_handler (device); + /* Clear any critical protocol notification in the wifi stack */ + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), FALSE); break; case NM_DEVICE_STATE_DISCONNECTED: /* Kick off a scan to get latest results */ priv->scan_interval = SCAN_INTERVAL_MIN; - request_wireless_scan (self, FALSE, NULL); + request_wireless_scan (self, FALSE, FALSE, NULL); break; default: break; @@ -3159,7 +3337,7 @@ nm_device_wifi_init (NMDeviceWifi *self) NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); priv->mode = NM_802_11_MODE_INFRA; - priv->aps = g_hash_table_new (g_str_hash, g_str_equal); + priv->aps = g_hash_table_new (nm_str_hash, g_str_equal); } static void @@ -3261,7 +3439,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->state_changed = device_state_changed; - klass->scanning_allowed = scanning_allowed; + klass->scanning_prohibited = scanning_prohibited; obj_properties[PROP_MODE] = g_param_spec_uint (NM_DEVICE_WIFI_MODE, "", "", @@ -3322,13 +3500,13 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) G_TYPE_NONE, 1, NM_TYPE_WIFI_AP); - signals[SCANNING_ALLOWED] = - g_signal_new (NM_DEVICE_WIFI_SCANNING_ALLOWED, + signals[SCANNING_PROHIBITED] = + g_signal_new (NM_DEVICE_WIFI_SCANNING_PROHIBITED, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed), - scanning_allowed_accumulator, NULL, NULL, - G_TYPE_BOOLEAN, 0); + G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited), + NULL, NULL, NULL, + G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), NMDBUS_TYPE_DEVICE_WIFI_SKELETON, diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index 024fe0ef..09707d4f 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -44,7 +44,7 @@ #define NM_DEVICE_WIFI_ACCESS_POINT_REMOVED "access-point-removed" /* internal signals */ -#define NM_DEVICE_WIFI_SCANNING_ALLOWED "scanning-allowed" +#define NM_DEVICE_WIFI_SCANNING_PROHIBITED "scanning-prohibited" typedef struct _NMDeviceWifi NMDeviceWifi; typedef struct _NMDeviceWifiClass NMDeviceWifiClass; diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index 7de0838f..bc823af0 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2011 Red Hat, Inc. + * Copyright (C) 2004 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -71,7 +71,7 @@ typedef struct { /* 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()) */ + gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ } NMWifiAPPrivate; struct _NMWifiAP { @@ -79,7 +79,7 @@ struct _NMWifiAP { NMWifiAPPrivate _priv; }; -struct _NMWifiAPClass{ +struct _NMWifiAPClass { NMExportedObjectClass parent; }; @@ -376,6 +376,14 @@ nm_wifi_ap_set_fake (NMWifiAP *ap, gboolean fake) return FALSE; } +NM80211ApFlags +nm_wifi_ap_get_flags (const NMWifiAP *ap) +{ + g_return_val_if_fail (NM_IS_WIFI_AP (ap), NM_802_11_AP_FLAGS_NONE); + + return NM_WIFI_AP_GET_PRIVATE (ap)->flags; +} + static gboolean nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen) { @@ -435,6 +443,326 @@ security_from_vardict (GVariant *security) return flags; } +/*****************************************************************************/ + +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 mcs, 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 */ + mcs = -1; + 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 + +static guint32 +get_max_rate (const guint8 *bytes, gsize len) +{ + guint8 id, elem_len; + guint32 max_rate = 0; + + while (len) { + guint32 m; + + if (len < 2) + return 0; + + id = *bytes++; + elem_len = *bytes++; + len -= 2; + + if (elem_len > len) + return 0; + + 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); + break; + case WLAN_EID_VHT_CAPABILITY: + if (!get_max_rate_vht (bytes, elem_len, &m)) + return 0; + max_rate = NM_MAX (max_rate, m); + break; + } + + len -= elem_len; + bytes += elem_len; + } + + return max_rate; +} + +/*****************************************************************************/ + gboolean nm_wifi_ap_update_from_properties (NMWifiAP *ap, const char *supplicant_path, @@ -444,11 +772,13 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, const guint8 *bytes; GVariant *v; gsize len; + gsize i; gboolean b = FALSE; const char *s; gint16 i16; guint16 u16; gboolean changed = FALSE; + guint32 max_rate; g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE); g_return_val_if_fail (properties, FALSE); @@ -460,6 +790,18 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, if (g_variant_lookup (properties, "Privacy", "b", &b) && b) changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY); + v = g_variant_lookup_value (properties, "WPS", G_VARIANT_TYPE_VARDICT); + if (v) { + if (g_variant_lookup (v, "Type", "&s", &s)) { + changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS); + if (strcmp (s, "pbc") == 0) + changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PBC); + else if (strcmp (s, "pin") == 0) + changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_WPS_PIN); + } + g_variant_unref (v); + } + if (g_variant_lookup (properties, "Mode", "&s", &s)) { if (!g_strcmp0 (s, "infrastructure")) changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA); @@ -497,21 +839,23 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap, g_variant_unref (v); } + max_rate = 0; v = g_variant_lookup_value (properties, "Rates", G_VARIANT_TYPE ("au")); if (v) { const guint32 *rates = g_variant_get_fixed_array (v, &len, sizeof (guint32)); - guint32 maxrate = 0; - int i; - /* Find the max AP rate */ - for (i = 0; i < len; i++) { - if (rates[i] > maxrate) - maxrate = rates[i]; - } - if (maxrate) - changed |= nm_wifi_ap_set_max_bitrate (ap, maxrate / 1000); + for (i = 0; i < len; i++) + max_rate = NM_MAX (max_rate, rates[i]); + g_variant_unref (v); + } + 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)); g_variant_unref (v); } + if (max_rate) + changed |= nm_wifi_ap_set_max_bitrate (ap, max_rate / 1000); v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT); if (v) { diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index 5e64087c..dd5a4ad1 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2011 Red Hat, Inc. + * Copyright (C) 2004 - 2017 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -88,6 +88,7 @@ gboolean nm_wifi_ap_set_max_bitrate (NMWifiAP *ap, 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); const char *nm_wifi_ap_to_string (const NMWifiAP *self, char *str_buf, diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c index 06da92ce..3ff82004 100644 --- a/src/devices/wifi/nm-wifi-utils.c +++ b/src/devices/wifi/nm-wifi-utils.c @@ -777,10 +777,8 @@ nm_wifi_utils_level_to_quality (gint val) val = 100 - (int) ((100.0 * (double) val) / 60.0); } else { /* Assume signal is a "quality" percentage */ - val = CLAMP (val, 0, 100); } - g_assert (val >= 0); - return (guint32) val; + return CLAMP (val, 0, 100); } diff --git a/src/devices/wifi/tests/test-general.c b/src/devices/wifi/tests/test-general.c index 3e61c5f0..89eebb22 100644 --- a/src/devices/wifi/tests/test-general.c +++ b/src/devices/wifi/tests/test-general.c @@ -44,16 +44,16 @@ \ success = nm_connection_compare (src, expected, NM_SETTING_COMPARE_FLAG_EXACT); \ if (success == FALSE && DEBUG) { \ - g_message ("\n- COMPLETED ---------------------------------\n"); \ + g_print ("\n- COMPLETED ---------------------------------\n"); \ nm_connection_dump (src); \ - g_message ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \ + g_print ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \ nm_connection_dump (expected); \ - g_message ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \ + g_print ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \ } \ g_assert (success == TRUE); \ } else { \ if (success) { \ - g_message ("\n- COMPLETED ---------------------------------\n"); \ + g_print ("\n- COMPLETED ---------------------------------\n"); \ nm_connection_dump (src); \ } \ g_assert (success == FALSE); \ @@ -1334,6 +1334,28 @@ test_strength_wext (void) g_assert_cmpint (nm_wifi_utils_level_to_quality (215), ==, 99); } +#define _assert_strength_in_range(x) \ + ({ \ + guint32 _x = (x); \ + g_assert_cmpint (_x, >=, 0); \ + g_assert_cmpint (_x, <=, 100); \ + }) + +static void +test_strength_all (void) +{ + int val; + + for (val = -200; val < 300; val++) + _assert_strength_in_range (nm_wifi_utils_level_to_quality (val)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MININT)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MAXINT)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MININT32)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MAXINT32)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MININT16)); + _assert_strength_in_range (nm_wifi_utils_level_to_quality (G_MAXINT16)); +} + /*****************************************************************************/ NMTST_DEFINE (); @@ -1497,6 +1519,8 @@ main (int argc, char **argv) test_strength_percent); g_test_add_func ("/wifi/strength/wext", test_strength_wext); + g_test_add_func ("/wifi/strength/all", + test_strength_all); return g_test_run (); } |