diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 513bd8719c..1922f9424f 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -131,7 +131,7 @@ struct _NMDeviceWifiPrivate { gint32 scheduled_scan_time; guint8 scan_interval; /* seconds */ guint pending_scan_id; - guint scanlist_cull_id; + guint ap_dump_id; gboolean requested_scan; NMSupplicantManager *sup_mgr; @@ -189,8 +189,6 @@ static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self); -static void schedule_scanlist_cull (NMDeviceWifi *self); - static gboolean request_wireless_scan (gpointer user_data); static void emit_ap_added_removed (NMDeviceWifi *self, @@ -305,10 +303,7 @@ supplicant_interface_release (NMDeviceWifi *self) _LOGD (LOGD_WIFI_SCAN, "reset scanning interval to %d seconds", priv->scan_interval); - if (priv->scanlist_cull_id) { - g_source_remove (priv->scanlist_cull_id); - priv->scanlist_cull_id = 0; - } + nm_clear_g_source (&priv->ap_dump_id); if (priv->sup_iface) { remove_supplicant_interface_error_handler (self); @@ -1420,11 +1415,6 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, priv->last_scan = nm_utils_get_monotonic_timestamp_s (); schedule_scan (self, success); - /* Ensure that old APs get removed, which otherwise only - * happens when there are new BSSes. - */ - schedule_scanlist_cull (self); - if (priv->requested_scan) { priv->requested_scan = FALSE; nm_device_remove_pending_action (NM_DEVICE (self), "scan", TRUE); @@ -1436,15 +1426,14 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, * */ -static void -ap_list_dump (NMDeviceWifi *self) +static gboolean +ap_list_dump (gpointer user_data) { + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); GSList *sorted, *iter; - if (!nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) - return; - + priv->ap_dump_id = 0; _LOGD (LOGD_WIFI_SCAN, "APs: [now:%u last:%u next:%u]", nm_utils_get_monotonic_timestamp_s (), priv->last_scan, @@ -1453,9 +1442,19 @@ ap_list_dump (NMDeviceWifi *self) for (iter = sorted; iter; iter = iter->next) nm_ap_dump (NM_AP (iter->data), " ", nm_device_get_iface (NM_DEVICE (self))); g_slist_free (sorted); + return G_SOURCE_REMOVE; } -#define WPAS_REMOVED_TAG "supplicant-removed" +static void +schedule_ap_list_dump (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + if (!nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) + return; + nm_clear_g_source (&priv->ap_dump_id); + priv->ap_dump_id = g_timeout_add_seconds (1, ap_list_dump, self); +} static void try_fill_ssid_for_hidden_ap (NMAccessPoint *ap) @@ -1489,137 +1488,6 @@ try_fill_ssid_for_hidden_ap (NMAccessPoint *ap) } } -static void -merge_scanned_ap (NMDeviceWifi *self, - NMAccessPoint *merge_ap, - const char *supplicant_path, - GVariant *properties) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMAccessPoint *found_ap = NULL; - const GByteArray *ssid; - const char *bssid; - - /* Let the manager try to fill in the SSID from seen-bssids lists */ - bssid = nm_ap_get_address (merge_ap); - ssid = nm_ap_get_ssid (merge_ap); - if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) { - /* Try to fill the SSID from the AP database */ - try_fill_ssid_for_hidden_ap (merge_ap); - - ssid = nm_ap_get_ssid (merge_ap); - if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) { - /* Yay, matched it, no longer treat as hidden */ - _LOGD (LOGD_WIFI_SCAN, "matched hidden AP %s => '%s'", - str_if_set (bssid, "(none)"), nm_utils_escape_ssid (ssid->data, ssid->len)); - } else { - /* Didn't have an entry for this AP in the database */ - _LOGD (LOGD_WIFI_SCAN, "failed to match hidden AP %s", - str_if_set (bssid, "(none)")); - } - } - - found_ap = get_ap_by_supplicant_path (self, supplicant_path); - if (!found_ap) - found_ap = nm_ap_match_in_hash (merge_ap, priv->aps); - if (found_ap) { - _LOGD (LOGD_WIFI_SCAN, "merging AP '%s' %s (%p) with existing (%p)", - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", - str_if_set (bssid, "(none)"), - merge_ap, - found_ap); - - nm_ap_update_from_properties (found_ap, supplicant_path, properties); - nm_ap_set_fake (found_ap, FALSE); - g_object_set_data (G_OBJECT (found_ap), WPAS_REMOVED_TAG, NULL); - } else { - /* New entry in the list */ - _LOGD (LOGD_WIFI_SCAN, "adding new AP '%s' %s (%p)", - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", - str_if_set (bssid, "(none)"), merge_ap); - - g_object_ref (merge_ap); - nm_ap_export_to_dbus (merge_ap); - g_hash_table_insert (priv->aps, (gpointer) nm_ap_get_dbus_path (merge_ap), merge_ap); - emit_ap_added_removed (self, ACCESS_POINT_ADDED, merge_ap, TRUE); - } -} - -static gboolean -cull_scan_list (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - gint32 now = nm_utils_get_monotonic_timestamp_s (); - guint32 removed = 0, total = 0; - GHashTableIter iter; - NMAccessPoint *ap; - - priv->scanlist_cull_id = 0; - - _LOGD (LOGD_WIFI_SCAN, "checking scan list for outdated APs"); - - /* Walk the access point list and remove any access points older than - * three times the inactive scan interval. - */ - g_hash_table_iter_init (&iter, priv->aps); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) { - const guint prune_interval_s = SCAN_INTERVAL_MAX * 3; - gint32 last_seen; - - /* Don't cull the associated AP or manually created APs */ - if (ap == priv->current_ap) - continue; - g_assert (!nm_ap_get_fake (ap)); /* only the current_ap can be fake */ - - /* Don't cull APs still known to the supplicant. Since the supplicant - * doesn't yet emit property updates for "last seen" we have to rely - * on changing signal strength for updating "last seen". But if the - * AP's strength doesn't change we won't get any updates for the AP, - * and we'll end up here even if the AP was still found by the - * supplicant in the last scan. - */ - if ( nm_ap_get_supplicant_path (ap) - && g_object_get_data (G_OBJECT (ap), WPAS_REMOVED_TAG) == NULL) - continue; - - last_seen = nm_ap_get_last_seen (ap); - if (!last_seen || last_seen + prune_interval_s < now) { - const GByteArray *ssid = nm_ap_get_ssid (ap); - - _LOGD (LOGD_WIFI_SCAN, - " removing %s (%s%s%s)", - str_if_set (nm_ap_get_address (ap), "(none)"), - ssid ? "'" : "", - ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)", - ssid ? "'" : ""); - emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE); - g_hash_table_iter_remove (&iter); - removed++; - } - } - - _LOGD (LOGD_WIFI_SCAN, "removed %d APs (of %d)", - removed, total); - - ap_list_dump (self); - - if(removed > 0) - nm_device_recheck_available_connections (NM_DEVICE (self)); - - return FALSE; -} - -static void -schedule_scanlist_cull (NMDeviceWifi *self) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - - /* Cull the scan list after the last request for it has come in */ - if (priv->scanlist_cull_id) - g_source_remove (priv->scanlist_cull_id); - priv->scanlist_cull_id = g_timeout_add_seconds (4, (GSourceFunc) cull_scan_list, self); -} - static void supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, const char *object_path, @@ -1629,6 +1497,9 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); NMDeviceState state; NMAccessPoint *ap; + NMAccessPoint *found_ap = NULL; + const GByteArray *ssid; + const char *bssid; g_return_if_fail (self != NULL); g_return_if_fail (properties != NULL); @@ -1642,23 +1513,52 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, return; ap = nm_ap_new_from_properties (object_path, properties); - if (ap) { - nm_ap_dump (ap, "New AP: ", nm_device_get_iface (NM_DEVICE (self))); - - /* Add the AP to the device's AP list */ - merge_scanned_ap (self, ap, object_path, properties); - g_object_unref (ap); - - /* Update the current AP if the supplicant notified a current BSS change - * before it sent the current BSS's scan result. - */ - if (g_strcmp0 (nm_supplicant_interface_get_current_bss (iface), object_path) == 0) - supplicant_iface_notify_current_bss (priv->sup_iface, NULL, self); - } else + if (!ap) { _LOGW (LOGD_WIFI_SCAN, "invalid AP properties received"); + return; + } - /* Remove outdated access points */ - schedule_scanlist_cull (self); + /* Let the manager try to fill in the SSID from seen-bssids lists */ + bssid = nm_ap_get_address (ap); + ssid = nm_ap_get_ssid (ap); + if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) { + /* Try to fill the SSID from the AP database */ + try_fill_ssid_for_hidden_ap (ap); + + ssid = nm_ap_get_ssid (ap); + if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) { + /* Yay, matched it, no longer treat as hidden */ + _LOGD (LOGD_WIFI_SCAN, "matched hidden AP %s => '%s'", + str_if_set (bssid, "(none)"), nm_utils_escape_ssid (ssid->data, ssid->len)); + } else { + /* Didn't have an entry for this AP in the database */ + _LOGD (LOGD_WIFI_SCAN, "failed to match hidden AP %s", + str_if_set (bssid, "(none)")); + } + } + + found_ap = get_ap_by_supplicant_path (self, object_path); + if (found_ap) { + nm_ap_dump (ap, "updated ", nm_device_get_iface (NM_DEVICE (self))); + nm_ap_update_from_properties (found_ap, object_path, properties); + } else { + nm_ap_dump (ap, "added ", nm_device_get_iface (NM_DEVICE (self))); + nm_ap_export_to_dbus (ap); + g_hash_table_insert (priv->aps, + (gpointer) nm_ap_get_dbus_path (ap), + g_object_ref (ap)); + emit_ap_added_removed (self, ACCESS_POINT_ADDED, ap, TRUE); + } + + g_object_unref (ap); + + /* Update the current AP if the supplicant notified a current BSS change + * before it sent the current BSS's scan result. + */ + if (g_strcmp0 (nm_supplicant_interface_get_current_bss (iface), object_path) == 0) + supplicant_iface_notify_current_bss (priv->sup_iface, NULL, self); + + schedule_ap_list_dump (self); } static void @@ -1679,13 +1579,12 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface, if (state <= NM_DEVICE_STATE_UNAVAILABLE) return; - /* Update the AP's last-seen property */ ap = get_ap_by_supplicant_path (self, object_path); - if (ap) + if (ap) { + nm_ap_dump (ap, "updated ", nm_device_get_iface (NM_DEVICE (self))); nm_ap_update_from_properties (ap, object_path, properties); - - /* Remove outdated access points */ - schedule_scanlist_cull (self); + schedule_ap_list_dump (self); + } } static void @@ -1693,27 +1592,19 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface, const char *object_path, NMDeviceWifi *self) { + NMDeviceWifiPrivate *priv; NMAccessPoint *ap; g_return_if_fail (self != NULL); g_return_if_fail (object_path != NULL); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); ap = get_ap_by_supplicant_path (self, object_path); - if (ap) { - gint32 now = nm_utils_get_monotonic_timestamp_s (); - gint32 last_seen = nm_ap_get_last_seen (ap); - - /* We don't know when the supplicant last saw the AP's beacons, - * it could be two minutes or it could be 2 seconds. Because the - * supplicant doesn't send property change notifications if the - * AP's other properties don't change, our last-seen time may be - * much older the supplicant's, and the AP would be immediately - * removed from the list on the next cleanup. So update the - * last-seen time to ensure the AP sticks around for at least - * one more periodic scan. - */ - nm_ap_set_last_seen (ap, MAX (last_seen, now - SCAN_INTERVAL_MAX)); - g_object_set_data (G_OBJECT (ap), WPAS_REMOVED_TAG, GUINT_TO_POINTER (TRUE)); + if (ap && ap != priv->current_ap) { + nm_ap_dump (ap, "removed ", nm_device_get_iface (NM_DEVICE (self))); + emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, TRUE); + g_hash_table_remove (priv->aps, nm_ap_get_dbus_path (ap)); + schedule_ap_list_dump (self); } } diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index 1867583a07..5068e66e96 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -1219,66 +1219,3 @@ nm_ap_complete_connection (NMAccessPoint *self, error); } -NMAccessPoint * -nm_ap_match_in_hash (NMAccessPoint *find_ap, GHashTable *hash) -{ - GHashTableIter iter; - NMAccessPoint *list_ap, *band_match = NULL; - - g_return_val_if_fail (find_ap != NULL, NULL); - - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, NULL, (gpointer) &list_ap)) { - const GByteArray * list_ssid = nm_ap_get_ssid (list_ap); - const char * list_addr = nm_ap_get_address (list_ap); - const guint32 list_freq = nm_ap_get_freq (list_ap); - - const GByteArray * find_ssid = nm_ap_get_ssid (find_ap); - const char * find_addr = nm_ap_get_address (find_ap); - const guint32 find_freq = nm_ap_get_freq (find_ap); - - /* SSID match; if both APs are hiding their SSIDs, - * let matching continue on BSSID and other properties - */ - if ( (!list_ssid && find_ssid) - || (list_ssid && !find_ssid)) - continue; - if ( list_ssid - && find_ssid - && !nm_utils_same_ssid (list_ssid->data, list_ssid->len, - find_ssid->data, find_ssid->len, - TRUE)) - continue; - - /* BSSID match */ - if ( nm_ethernet_address_is_valid (list_addr, -1) - && !nm_utils_hwaddr_matches (list_addr, -1, find_addr, -1)) - continue; - - /* mode match */ - if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap)) - continue; - - /* AP flags */ - if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap)) - continue; - - if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap)) - continue; - - if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap)) - continue; - - if (list_freq != find_freq) { - /* Must be last check to ensure all other properties match */ - if (freq_to_band (list_freq) == freq_to_band (find_freq)) - band_match = list_ap; - continue; - } - - return list_ap; - } - - return band_match; -} - diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h index cbea3e307f..f316a7ae1b 100644 --- a/src/devices/wifi/nm-wifi-ap.h +++ b/src/devices/wifi/nm-wifi-ap.h @@ -116,8 +116,6 @@ gboolean nm_ap_complete_connection (NMAccessPoint *self, gboolean lock_bssid, GError **error); -NMAccessPoint * nm_ap_match_in_hash (NMAccessPoint *find_ap, GHashTable *hash); - void nm_ap_dump (NMAccessPoint *self, const char *prefix, const char *ifname); diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c index 9c044308b1..4731ad7d76 100644 --- a/src/supplicant-manager/nm-supplicant-interface.c +++ b/src/supplicant-manager/nm-supplicant-interface.c @@ -645,6 +645,30 @@ on_iface_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_ _nm_dbus_signal_connect (priv->iface_proxy, "NetworkRequest", G_VARIANT_TYPE ("(oss)"), G_CALLBACK (wpas_iface_network_request), self); + /* Scan result aging parameters */ + g_dbus_proxy_call (priv->iface_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + WPAS_DBUS_IFACE_INTERFACE, + "BSSExpireAge", + g_variant_new_uint32 (250)), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + NULL, + NULL); + g_dbus_proxy_call (priv->iface_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + WPAS_DBUS_IFACE_INTERFACE, + "BSSExpireCount", + g_variant_new_uint32 (2)), + G_DBUS_CALL_FLAGS_NONE, + -1, + priv->init_cancellable, + NULL, + NULL); + /* Check whether NetworkReply and AP mode are supported */ priv->ready_count = 1; g_dbus_proxy_call (priv->iface_proxy,