From df92606d61cd1f1627213b97135b356b8252c8af Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 4 Mar 2009 08:51:14 -0500 Subject: [PATCH] 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. --- src/nm-device-wifi.c | 72 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index 743abacb74..9fe51a3a0c 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -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);