wifi: ensure user-created Ad-Hoc APs disappear when no longer used (bgo #569241)

Since NM doesn't scan while associated with an Ad-Hoc BSS (since
scanning in adhoc mode makes most driver quite angry and doesn't
work well anyway) the user-created BSS was never found in the scan
list, and the 'fake' flag wasn't cleared.  Thus the BSS stuck
around in NM's scan list forever.

Additionally, ensure that set_current_ap() maintains a reference
to the old AP until after setting the new AP, just in case the same
AP is being set again.

Third, handle IBSS coalescing by always updating the current AP's
address (if it's an Ad-Hoc AP) with the BSSID reported by the card.
The joined Ad-Hoc networks' BSSID will change if the card
coalesces with other stations in the same IBSS, which would make
NM fail to find the currently joined network in the scan list, and
lead to "roamed to (none)" messages and inability to find the
current AP.
This commit is contained in:
Dan Williams 2009-03-04 08:51:14 -05:00
parent 5b1fadb1ac
commit df92606d61

View file

@ -837,20 +837,35 @@ get_active_ap (NMDeviceWifi *self,
static void
set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
NMDeviceWifiPrivate *priv;
char *old_path = NULL;
NMAccessPoint *old_ap;
g_return_if_fail (NM_IS_DEVICE_WIFI (self));
if (priv->current_ap) {
old_path = g_strdup (nm_ap_get_dbus_path (priv->current_ap));
g_object_unref (priv->current_ap);
priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
old_ap = priv->current_ap;
if (old_ap) {
old_path = g_strdup (nm_ap_get_dbus_path (old_ap));
priv->current_ap = NULL;
}
if (new_ap)
if (new_ap) {
priv->current_ap = g_object_ref (new_ap);
/* Move the current AP to the front of the scan list. Since we
* do a lot of searches looking for the current AP, it saves
* time to have it in front.
*/
priv->ap_list = g_slist_remove (priv->ap_list, new_ap);
priv->ap_list = g_slist_prepend (priv->ap_list, new_ap);
}
/* Unref old AP here to ensure object lives if new_ap == old_ap */
if (old_ap)
g_object_unref (old_ap);
/* Only notify if it's really changed */
if ( (!old_path && new_ap)
|| (old_path && !new_ap)
@ -867,6 +882,24 @@ periodic_update (NMDeviceWifi *self)
NMAccessPoint *new_ap;
guint32 new_rate;
/* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where
* multiple IBSS stations using the same SSID will eventually switch to
* using the same BSSID to avoid network segmentation. When this happens,
* the card's reported BSSID will change, but the the new BSS may not
* be in the scan list, since scanning isn't done in ad-hoc mode for
* various reasons. So pull the BSSID from the card and update the
* current AP with it, if the current AP is adhoc.
*/
if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) {
struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
nm_device_wifi_get_bssid (self, &bssid);
/* 0x02 is the first byte of IBSS BSSIDs */
if ( (bssid.ether_addr_octet[0] == 0x02)
&& nm_ethernet_address_is_valid (&bssid))
nm_ap_set_address (priv->current_ap, &bssid);
}
new_ap = get_active_ap (self, NULL, FALSE);
if (new_ap)
nm_device_wifi_update_signal_strength (self, new_ap);
@ -1009,11 +1042,25 @@ real_take_down (NMDevice *dev)
static void
real_deactivate_quickly (NMDevice *dev)
{
NMDeviceWifi * self = NM_DEVICE_WIFI (dev);
NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
NMAccessPoint *orig_ap;
cleanup_association_attempt (self, TRUE);
/* If the AP is 'fake', i.e. it wasn't actually found from
* a scan but the user tried to connect to it manually (maybe it
* was non-broadcasting or something) clear the 'fake' flag here,
* becuase 'fake' APs should only live for as long as we're
* connected to them. Fixes a bug where user-created Ad-Hoc APs
* are never removed from the scan list, because scanning is
* disabled while in Ad-Hoc mode (for stability), and thus the
* AP culling never happens. (bgo #569241)
*/
orig_ap = nm_device_wifi_get_activation_ap (self);
if (orig_ap && nm_ap_get_fake (orig_ap))
nm_ap_set_fake (orig_ap, FALSE);
set_current_ap (self, NULL);
priv->rate = 0;
@ -1958,7 +2005,7 @@ merge_scanned_ap (NMDeviceWifi *self,
/* New entry in the list */
// FIXME: figure out if reference counts are correct here for AP objects
g_object_ref (merge_ap);
priv->ap_list = g_slist_append (priv->ap_list, merge_ap);
priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap);
nm_ap_export_to_dbus (merge_ap);
g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, merge_ap);
}
@ -2842,7 +2889,7 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
break;
}
priv->ap_list = g_slist_append (priv->ap_list, ap);
priv->ap_list = g_slist_prepend (priv->ap_list, ap);
nm_ap_export_to_dbus (ap);
g_signal_emit (self, signals[ACCESS_POINT_ADDED], 0, ap);
}
@ -3113,13 +3160,16 @@ activation_success_handler (NMDevice *dev)
ap = nm_device_wifi_get_activation_ap (self);
/* If the activate AP was fake, it probably won't have a BSSID at all.
* But if activation was successful, the card will know the BSSID. Grab
* the BSSID off the card and fill in the BSSID of the activation AP.
/* If the AP isn't fake, it was found in the scan list and all its
* details are known.
*/
if (!nm_ap_get_fake (ap))
goto done;
/* If the activate AP was fake, it probably won't have a BSSID at all.
* But if activation was successful, the card will know the BSSID. Grab
* the BSSID off the card and fill in the BSSID of the activation AP.
*/
nm_device_wifi_get_bssid (self, &bssid);
if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap)))
nm_ap_set_address (ap, &bssid);