Commit 92412357 authored by Dan Williams's avatar Dan Williams
Browse files

wifi: ensure APs remain in scan list when supplicant updates them

The port to the new supplicant D-Bus API for NM 0.9 had one unfinished
piece, which was to remove old APs from the scan list when the
supplicant returned no scan results or there was a scan error.  In
this case, the removal code would not be called.  This wasn't much
of a problem until 836f7d17 which
began removing APs from the scan list correctly in this case.

This uncovered a bug in NM's wpa_supplicant management code, which
was that NM only updates its internal AP object 'last seen' timestamp
when the AP is reported by the supplicant as a completely new BSS
(in merge_scanned_ap()).  But the new supplicant D-Bus interface
only reports the BSS as "new" when the supplicant doesn't know about
the BSS, either because it is a new BSS or because it's been removed
from the supplicant's scan list at some point in the past.

Thus for BSSes that are consistently kept in the supplicant's scan
list, because the wifi driver is actually doing its job and reporting
them consistently in scan results, NM would not be updating the
'last seen' value for the corresponding NM AP objects.  Due to
836f7d17 this would cause APs that
should be kept to be removed from the NM scan list.

To fix this, have the NMAccessPoint object track which supplicant
dbus object it came from, and have NMSupplicantInterface listen for
PropertyChanged signals for those APs the supplicant knows about.
When something changes (like signal strength as the result of updated
scan results) update the AP's 'last seen' timestamp since it clearly
still exists in the scan list.  This way we update the timestamp both
when the supplicant finds a new AP and when it updates the properties
of existing APs.
parent 2c6bade5
......@@ -170,9 +170,15 @@ static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
gpointer user_data);
static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
const char *object_path,
GHashTable *properties,
NMDeviceWifi * self);
static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
const char *object_path,
GHashTable *properties,
NMDeviceWifi *self);
static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface,
gboolean success,
NMDeviceWifi * self);
......@@ -358,6 +364,12 @@ supplicant_interface_acquire (NMDeviceWifi *self)
self);
priv->supplicant.sig_ids[i++] = id;
id = g_signal_connect (priv->supplicant.iface,
NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
G_CALLBACK (supplicant_iface_bss_updated_cb),
self);
priv->supplicant.sig_ids[i++] = id;
id = g_signal_connect (priv->supplicant.iface,
NM_SUPPLICANT_INTERFACE_SCAN_DONE,
G_CALLBACK (supplicant_iface_scan_done_cb),
......@@ -433,7 +445,6 @@ supplicant_interface_release (NMDeviceWifi *self)
}
}
static NMAccessPoint *
get_ap_by_path (NMDeviceWifi *self, const char *path)
{
......@@ -447,6 +458,19 @@ get_ap_by_path (NMDeviceWifi *self, const char *path)
return NULL;
}
static NMAccessPoint *
get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->ap_list; iter && path; iter = g_slist_next (iter)) {
if (strcmp (path, nm_ap_get_supplicant_path (NM_AP (iter->data))) == 0)
return NM_AP (iter->data);
}
return NULL;
}
static NMAccessPoint *
get_active_ap (NMDeviceWifi *self,
NMAccessPoint *ignore_ap,
......@@ -1599,9 +1623,7 @@ merge_scanned_ap (NMDeviceWifi *self,
gboolean strict_match = TRUE;
NMAccessPoint *current_ap = NULL;
/* Let the manager try to fill in the SSID from seen-bssids lists
* if it can
*/
/* 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)) {
......@@ -1634,7 +1656,9 @@ merge_scanned_ap (NMDeviceWifi *self,
if (current_ap && nm_ap_get_fake (current_ap))
strict_match = FALSE;
found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match);
found_ap = get_ap_by_supplicant_path (self, nm_ap_get_supplicant_path (merge_ap));
if (!found_ap)
found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match);
if (found_ap) {
nm_log_dbg (LOGD_WIFI_SCAN, "(%s): merging AP '%s' " MAC_FMT " (%p) with existing (%p)",
nm_device_get_iface (NM_DEVICE (self)),
......@@ -1643,6 +1667,7 @@ merge_scanned_ap (NMDeviceWifi *self,
merge_ap,
found_ap);
nm_ap_set_supplicant_path (found_ap, nm_ap_get_supplicant_path (merge_ap));
nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap));
nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap));
nm_ap_set_rsn_flags (found_ap, nm_ap_get_rsn_flags (merge_ap));
......@@ -1747,6 +1772,7 @@ schedule_scanlist_cull (NMDeviceWifi *self)
static void
supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
const char *object_path,
GHashTable *properties,
NMDeviceWifi *self)
{
......@@ -1762,7 +1788,7 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
if (state <= NM_DEVICE_STATE_UNAVAILABLE)
return;
ap = nm_ap_new_from_properties (properties);
ap = nm_ap_new_from_properties (object_path, properties);
if (ap) {
nm_ap_dump (ap, "New AP: ");
......@@ -1778,6 +1804,35 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
schedule_scanlist_cull (self);
}
static void
supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
const char *object_path,
GHashTable *properties,
NMDeviceWifi *self)
{
NMDeviceState state;
NMAccessPoint *ap;
GTimeVal now;
g_return_if_fail (self != NULL);
g_return_if_fail (properties != NULL);
/* Ignore new APs when unavailable or unamnaged */
state = nm_device_get_state (NM_DEVICE (self));
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) {
g_get_current_time (&now);
nm_ap_set_last_seen (ap, now.tv_sec);
}
/* Remove outdated access points */
schedule_scanlist_cull (self);
}
static void
cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
......
......@@ -42,6 +42,7 @@
typedef struct
{
char *dbus_path;
char *supplicant_path; /* D-Bus object path of this AP from wpa_supplicant */
/* Scanned or cached values */
GByteArray * ssid;
......@@ -106,6 +107,7 @@ finalize (GObject *object)
NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
g_free (priv->dbus_path);
g_free (priv->supplicant_path);
if (priv->ssid)
g_byte_array_free (priv->ssid, TRUE);
......@@ -345,15 +347,10 @@ nm_ap_export_to_dbus (NMAccessPoint *ap)
* Create a new, blank user access point info structure
*
*/
NMAccessPoint *nm_ap_new (void)
static NMAccessPoint *
nm_ap_new (void)
{
GObject *object;
object = g_object_new (NM_TYPE_AP, NULL);
if (!object)
return NULL;
return (NMAccessPoint *) object;
return (NMAccessPoint *) g_object_new (NM_TYPE_AP, NULL);
}
static NM80211ApSecurityFlags
......@@ -509,7 +506,7 @@ foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
}
NMAccessPoint *
nm_ap_new_from_properties (GHashTable *properties)
nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties)
{
NMAccessPoint *ap;
GTimeVal cur_time;
......@@ -524,6 +521,8 @@ nm_ap_new_from_properties (GHashTable *properties)
g_object_freeze_notify (G_OBJECT (ap));
g_hash_table_foreach (properties, foreach_property_cb, ap);
nm_ap_set_supplicant_path (ap, supplicant_path);
/* ignore APs with invalid BSSIDs */
addr = nm_ap_get_address (ap);
if ( !(memcmp (addr->ether_addr_octet, bad_bssid1, ETH_ALEN))
......@@ -783,6 +782,24 @@ nm_ap_get_dbus_path (NMAccessPoint *ap)
return NM_AP_GET_PRIVATE (ap)->dbus_path;
}
const char *
nm_ap_get_supplicant_path (NMAccessPoint *ap)
{
g_return_val_if_fail (NM_IS_AP (ap), NULL);
return NM_AP_GET_PRIVATE (ap)->supplicant_path;
}
void
nm_ap_set_supplicant_path (NMAccessPoint *ap, const char *path)
{
g_return_if_fail (NM_IS_AP (ap));
g_return_if_fail (path != NULL);
g_free (NM_AP_GET_PRIVATE (ap)->supplicant_path);
NM_AP_GET_PRIVATE (ap)->supplicant_path = g_strdup (path);
}
/*
* Get/set functions for ssid
*
......
......@@ -57,13 +57,17 @@ typedef struct {
GType nm_ap_get_type (void);
NMAccessPoint * nm_ap_new (void);
NMAccessPoint * nm_ap_new_from_properties (GHashTable *properties);
NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path,
GHashTable *properties);
NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection);
void nm_ap_export_to_dbus (NMAccessPoint *ap);
const char * nm_ap_get_dbus_path (NMAccessPoint *ap);
const char * nm_ap_get_supplicant_path (NMAccessPoint *ap);
void nm_ap_set_supplicant_path (NMAccessPoint *ap,
const char *path);
const GByteArray * nm_ap_get_ssid (const NMAccessPoint * ap);
void nm_ap_set_ssid (NMAccessPoint * ap, const GByteArray * ssid);
......
......@@ -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) 2006 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2012 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
......@@ -58,6 +58,7 @@ enum {
STATE, /* change in the interface's state */
REMOVED, /* interface was removed by the supplicant */
NEW_BSS, /* interface saw a new access point from a scan */
BSS_UPDATED, /* a BSS property changed */
SCAN_DONE, /* wifi scan is complete */
CONNECTION_ERROR, /* an error occurred during a connection request */
CREDENTIALS_REQUEST, /* 802.1x identity or password requested */
......@@ -95,6 +96,7 @@ typedef struct {
DBusGProxy * props_proxy;
char * net_path;
guint32 blobs_left;
GHashTable * bss_proxies;
guint32 last_scan;
......@@ -194,6 +196,14 @@ emit_error_helper (NMSupplicantInterface *self,
g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, err->message);
}
static void
signal_new_bss (NMSupplicantInterface *self,
const char *object_path,
GHashTable *props)
{
g_signal_emit (self, signals[NEW_BSS], 0, object_path, props);
}
static void
bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
......@@ -204,7 +214,7 @@ bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_
if (dbus_g_proxy_end_call (proxy, call_id, &error,
DBUS_TYPE_G_MAP_OF_VARIANT, &props,
G_TYPE_INVALID)) {
g_signal_emit (info->interface, signals[NEW_BSS], 0, props);
signal_new_bss (info->interface, dbus_g_proxy_get_path (proxy), props);
g_hash_table_destroy (props);
} else {
if (!strstr (error->message, "The BSSID requested was invalid")) {
......@@ -216,31 +226,67 @@ bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_
}
static void
request_bss_properties (NMSupplicantInterface *self,
GPtrArray *paths)
bss_properties_changed (DBusGProxy *proxy,
const char *interface,
GHashTable *props,
const char **unused,
gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
if (g_strcmp0 (interface, WPAS_DBUS_IFACE_BSS) != 0)
return;
g_signal_emit (self, signals[BSS_UPDATED], 0,
dbus_g_proxy_get_path (proxy),
props);
}
static void
handle_new_bss (NMSupplicantInterface *self,
const char *object_path,
GHashTable *props)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
int i;
/* Fire off a "properties" call for each returned BSSID */
for (i = 0; i < paths->len; i++) {
NMSupplicantInfo *info;
DBusGProxy *proxy;
DBusGProxyCall *call;
proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
WPAS_DBUS_SERVICE,
g_ptr_array_index (paths, i),
DBUS_INTERFACE_PROPERTIES);
info = nm_supplicant_info_new (self, proxy, priv->other_pcalls);
call = dbus_g_proxy_begin_call (proxy, "GetAll",
bssid_properties_cb,
info,
nm_supplicant_info_destroy,
G_TYPE_STRING, WPAS_DBUS_IFACE_BSS,
G_TYPE_INVALID);
DBusGProxy *bss_proxy;
NMSupplicantInfo *info;
DBusGProxyCall *call;
g_return_if_fail (object_path != NULL);
if (g_hash_table_lookup (priv->bss_proxies, object_path))
return;
bss_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
WPAS_DBUS_SERVICE,
object_path,
DBUS_INTERFACE_PROPERTIES);
g_hash_table_insert (priv->bss_proxies,
(gpointer) dbus_g_proxy_get_path (bss_proxy),
bss_proxy);
dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED_BOXED,
G_TYPE_NONE,
G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV,
G_TYPE_INVALID);
dbus_g_proxy_add_signal (bss_proxy, "PropertiesChanged",
G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_STRV,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (bss_proxy, "PropertiesChanged",
G_CALLBACK (bss_properties_changed),
self, NULL);
if (props) {
signal_new_bss (self, object_path, props);
} else {
info = nm_supplicant_info_new (self, bss_proxy, priv->other_pcalls);
call = dbus_g_proxy_begin_call (bss_proxy, "GetAll",
bssid_properties_cb,
info,
nm_supplicant_info_destroy,
G_TYPE_STRING, WPAS_DBUS_IFACE_BSS,
G_TYPE_INVALID);
nm_supplicant_info_set_call (info, call);
g_object_unref (proxy);
}
}
......@@ -250,7 +296,18 @@ wpas_iface_bss_added (DBusGProxy *proxy,
GHashTable *props,
gpointer user_data)
{
g_signal_emit (NM_SUPPLICANT_INTERFACE (user_data), signals[NEW_BSS], 0, props);
handle_new_bss (NM_SUPPLICANT_INTERFACE (user_data), object_path, props);
}
static void
wpas_iface_bss_removed (DBusGProxy *proxy,
const char *object_path,
gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
g_hash_table_remove (priv->bss_proxies, object_path);
}
static int
......@@ -321,6 +378,10 @@ set_state (NMSupplicantInterface *self, guint32 new_state)
"BSSAdded",
G_CALLBACK (wpas_iface_bss_added),
self);
dbus_g_proxy_disconnect_signal (priv->iface_proxy,
"BSSRemoved",
G_CALLBACK (wpas_iface_bss_removed),
self);
}
}
......@@ -406,8 +467,13 @@ wpas_iface_properties_changed (DBusGProxy *proxy,
set_state_from_string (self, g_value_get_string (value));
value = g_hash_table_lookup (props, "BSSs");
if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH))
request_bss_properties (self, g_value_get_boxed (value));
if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH)) {
GPtrArray *paths = g_value_get_boxed (value);
int i;
for (i = 0; paths && (i < paths->len); i++)
handle_new_bss (self, g_ptr_array_index (paths, i), NULL);
}
}
static void
......@@ -575,6 +641,18 @@ interface_add_done (NMSupplicantInterface *self, char *path)
self,
NULL);
dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE,
DBUS_TYPE_G_OBJECT_PATH,
G_TYPE_INVALID);
dbus_g_proxy_add_signal (priv->iface_proxy, "BSSRemoved",
DBUS_TYPE_G_OBJECT_PATH,
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSRemoved",
G_CALLBACK (wpas_iface_bss_removed),
self,
NULL);
dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING,
G_TYPE_NONE,
DBUS_TYPE_G_OBJECT_PATH, G_TYPE_STRING, G_TYPE_STRING,
......@@ -1177,6 +1255,8 @@ nm_supplicant_interface_init (NMSupplicantInterface * self)
WPAS_DBUS_SERVICE,
WPAS_DBUS_PATH,
WPAS_DBUS_INTERFACE);
priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
}
static void
......@@ -1237,6 +1317,8 @@ dispose (GObject *object)
if (priv->wpas_proxy)
g_object_unref (priv->wpas_proxy);
g_hash_table_destroy (priv->bss_proxies);
if (priv->smgr) {
if (priv->smgr_avail_id)
g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id);
......@@ -1301,8 +1383,17 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, new_bss),
NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
_nm_marshal_VOID__STRING_POINTER,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
signals[BSS_UPDATED] =
g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (NMSupplicantInterfaceClass, bss_updated),
NULL, NULL,
_nm_marshal_VOID__STRING_POINTER,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_POINTER);
signals[SCAN_DONE] =
g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE,
......
......@@ -57,6 +57,7 @@ enum {
#define NM_SUPPLICANT_INTERFACE_STATE "state"
#define NM_SUPPLICANT_INTERFACE_REMOVED "removed"
#define NM_SUPPLICANT_INTERFACE_NEW_BSS "new-bss"
#define NM_SUPPLICANT_INTERFACE_BSS_UPDATED "bss-updated"
#define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done"
#define NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR "connection-error"
#define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request"
......@@ -80,6 +81,12 @@ typedef struct {
/* interface saw a new BSS */
void (*new_bss) (NMSupplicantInterface *iface,
const char *object_path,
GHashTable *props);
/* a BSS property changed */
void (*bss_updated) (NMSupplicantInterface *iface,
const char *object_path,
GHashTable *props);
/* wireless scan is done */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment