summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2015-04-10 15:59:05 -0500
committerDan Williams <dcbw@redhat.com>2015-04-10 15:59:05 -0500
commit62f40bade074ac8e555730957149d0888a2b7715 (patch)
treed3c30dd37796e0714e2818e8a35fa8fb71b12421
parent1eaddced105a7e04b571f772cc67b0d16178d092 (diff)
parentfbf60536f925b22719499e7cd9b057812bf63f36 (diff)
downloadNetworkManager-62f40bade074ac8e555730957149d0888a2b7715.tar.gz
wifi: use the supplicant's CurrentBSS as the current AP (bgo #747424)
Instead of trying to do a bunch of fragile frequency/SSID/security matching, just use what the supplicant thinks the current AP is.
-rw-r--r--src/devices/wifi/nm-device-wifi.c671
-rw-r--r--src/devices/wifi/nm-wifi-ap.c172
-rw-r--r--src/devices/wifi/nm-wifi-ap.h13
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c54
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.h6
5 files changed, 368 insertions, 548 deletions
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 9c417f45b4..1361bd89dd 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -42,6 +42,7 @@
#include "nm-device-private.h"
#include "nm-utils.h"
#include "nm-logging.h"
+#include "gsystem-local-alloc.h"
#include "NetworkManagerUtils.h"
#include "nm-activation-request.h"
#include "nm-supplicant-manager.h"
@@ -116,14 +117,12 @@ enum {
static guint signals[LAST_SIGNAL] = { 0 };
struct _NMDeviceWifiPrivate {
- gboolean disposed;
-
char * perm_hw_addr; /* Permanent MAC address */
char * initial_hw_addr; /* Initial MAC address (as seen when NM starts) */
gint8 invalid_strength_counter;
- GSList * ap_list;
+ GHashTable * aps;
NMAccessPoint * current_ap;
guint32 rate;
gboolean enabled; /* rfkilled or not */
@@ -185,11 +184,18 @@ static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
GParamSpec * pspec,
NMDeviceWifi * self);
+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 remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap);
+static void emit_ap_added_removed (NMDeviceWifi *self,
+ guint signum,
+ NMAccessPoint *ap,
+ gboolean recheck_available_connections);
static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
@@ -273,6 +279,10 @@ supplicant_interface_acquire (NMDeviceWifi *self)
"notify::scanning",
G_CALLBACK (supplicant_iface_notify_scanning_cb),
self);
+ g_signal_connect (priv->sup_iface,
+ "notify::" NM_SUPPLICANT_INTERFACE_CURRENT_BSS,
+ G_CALLBACK (supplicant_iface_notify_current_bss),
+ self);
return TRUE;
}
@@ -315,172 +325,27 @@ supplicant_interface_release (NMDeviceWifi *self)
static NMAccessPoint *
get_ap_by_path (NMDeviceWifi *self, const char *path)
{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- GSList *iter;
-
- if (!path)
- return NULL;
+ g_return_val_if_fail (path != NULL, NULL);
+ return g_hash_table_lookup (NM_DEVICE_WIFI_GET_PRIVATE (self)->aps, path);
- for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
- if (g_strcmp0 (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0)
- return NM_AP (iter->data);
- }
- return NULL;
}
static NMAccessPoint *
get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path)
{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- GSList *iter;
+ GHashTableIter iter;
+ NMAccessPoint *ap;
- if (!path)
- return NULL;
+ g_return_val_if_fail (path != NULL, NULL);
- for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
- if (g_strcmp0 (path, nm_ap_get_supplicant_path (NM_AP (iter->data))) == 0)
- return NM_AP (iter->data);
+ g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+ if (g_strcmp0 (path, nm_ap_get_supplicant_path (ap)) == 0)
+ return ap;
}
return NULL;
}
-static NMAccessPoint *
-find_active_ap (NMDeviceWifi *self,
- NMAccessPoint *ignore_ap,
- gboolean match_hidden)
-{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
- guint8 bssid[ETH_ALEN];
- GByteArray *ssid;
- GSList *iter;
- int i = 0;
- NMAccessPoint *match_nofreq = NULL, *active_ap = NULL;
- gboolean found_a_band = FALSE;
- gboolean found_bg_band = FALSE;
- NM80211Mode devmode;
- guint32 devfreq;
-
- nm_platform_wifi_get_bssid (ifindex, bssid);
- _LOGT (LOGD_WIFI, "active BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
- bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
-
- if (!nm_ethernet_address_is_valid (bssid, ETH_ALEN))
- return NULL;
-
- ssid = nm_platform_wifi_get_ssid (ifindex);
- _LOGT (LOGD_WIFI, "active SSID: %s%s%s",
- ssid ? "'" : "",
- ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
- ssid ? "'" : "");
-
- devmode = nm_platform_wifi_get_mode (ifindex);
- devfreq = nm_platform_wifi_get_frequency (ifindex);
-
- /* When matching hidden APs, do a second pass that ignores the SSID check,
- * because NM might not yet know the SSID of the hidden AP in the scan list
- * and therefore it won't get matched the first time around.
- */
- while (i++ < (match_hidden ? 2 : 1)) {
- _LOGT (LOGD_WIFI, " Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : "");
-
- /* Find this SSID + BSSID in the device's AP list */
- for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
- NMAccessPoint *ap = NM_AP (iter->data);
- const char *ap_bssid = nm_ap_get_address (ap);
- const GByteArray *ap_ssid = nm_ap_get_ssid (ap);
- NM80211Mode apmode;
- guint32 apfreq;
-
- _LOGT (LOGD_WIFI, " AP: %s%s%s %s",
- ap_ssid ? "'" : "",
- ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
- ap_ssid ? "'" : "",
- str_if_set (ap_bssid, "(none)"));
-
- if (ap == ignore_ap) {
- _LOGT (LOGD_WIFI, " ignored");
- continue;
- }
-
- if (!nm_utils_hwaddr_matches (bssid, ETH_ALEN, ap_bssid, -1)) {
- _LOGT (LOGD_WIFI, " BSSID mismatch");
- continue;
- }
-
- if (i == 0) {
- if ( (ssid && !ap_ssid)
- || (ap_ssid && !ssid)
- || (ssid && ap_ssid && !nm_utils_same_ssid (ssid->data, ssid->len,
- ap_ssid->data, ap_ssid->len,
- TRUE))) {
- _LOGT (LOGD_WIFI, " SSID mismatch");
- continue;
- }
- }
-
- apmode = nm_ap_get_mode (ap);
- if (devmode != apmode) {
- _LOGT (LOGD_WIFI, " mode mismatch (device %d, ap %d)",
- devmode, apmode);
- continue;
- }
-
- apfreq = nm_ap_get_freq (ap);
- if (devfreq != apfreq) {
- _LOGT (LOGD_WIFI, " frequency mismatch (device %u, ap %u)",
- devfreq, apfreq);
-
- if (match_nofreq == NULL)
- match_nofreq = ap;
-
- if (apfreq > 4000)
- found_a_band = TRUE;
- else if (apfreq > 2000)
- found_bg_band = TRUE;
- continue;
- }
-
- // FIXME: handle security settings here too
- _LOGT (LOGD_WIFI, " matched");
- active_ap = ap;
- goto done;
- }
- }
-
- /* Some proprietary drivers (wl.o) report tuned frequency (like when
- * scanning) instead of the associated AP's frequency. This is a great
- * example of how WEXT is underspecified. We use frequency to find the
- * active AP in the scan list because some configurations use the same
- * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to
- * make sure we get the right AP in the right band. This configuration
- * is uncommon though, and the frequency check penalizes closed drivers we
- * can't fix. Because we're not total dicks, ignore the frequency condition
- * if the associated BSSID/SSID exists only in one band since that's most
- * likely the AP we want. Sometimes wl.o returns a frequency of 0, so if
- * we can't match the AP based on frequency at all, just give up.
- */
- if (match_nofreq && ((found_a_band != found_bg_band) || (devfreq == 0))) {
- const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq);
-
- _LOGT (LOGD_WIFI, " matched %s%s%s %s",
- ap_ssid ? "'" : "",
- ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
- ap_ssid ? "'" : "",
- str_if_set (nm_ap_get_address (match_nofreq), "(none)"));
-
- active_ap = match_nofreq;
- goto done;
- }
-
- _LOGT (LOGD_WIFI, " No matching AP found.");
-
-done:
- if (ssid)
- g_byte_array_free (ssid, TRUE);
- return active_ap;
-}
-
static void
update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
{
@@ -521,13 +386,6 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_avai
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);
-
/* Update seen BSSIDs cache */
update_seen_bssids_cache (self, priv->current_ap);
} else
@@ -537,7 +395,8 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_avai
NM80211Mode mode = nm_ap_get_mode (old_ap);
if (force_remove_old_ap || mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_ap_get_fake (old_ap)) {
- remove_access_point (self, old_ap);
+ emit_ap_added_removed (self, ACCESS_POINT_REMOVED, old_ap, FALSE);
+ g_hash_table_remove (priv->aps, nm_ap_get_dbus_path (old_ap));
if (recheck_available_connections)
nm_device_recheck_available_connections (NM_DEVICE (self));
}
@@ -548,11 +407,10 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_avai
}
static void
-periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
+periodic_update (NMDeviceWifi *self)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
- NMAccessPoint *new_ap;
guint32 new_rate;
int percent;
NMDeviceState state;
@@ -579,65 +437,15 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
if (priv->mode == NM_802_11_MODE_AP)
return;
- /* 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 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)) {
- guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
-
- nm_platform_wifi_get_bssid (ifindex, bssid);
- /* 0x02 means "locally administered" and should be OR-ed into
- * the first byte of IBSS BSSIDs.
- */
- if ((bssid[0] & 0x02) && nm_ethernet_address_is_valid (bssid, ETH_ALEN)) {
- char *bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
- nm_ap_set_address (priv->current_ap, bssid_str);
- g_free (bssid_str);
- }
- }
-
- new_ap = find_active_ap (self, ignore_ap, FALSE);
- if (new_ap) {
- /* Try to smooth out the strength. Atmel cards, for example, will give no strength
- * one second and normal strength the next.
- */
+ if (priv->current_ap) {
+ /* Smooth out the strength to work around crappy drivers */
percent = nm_platform_wifi_get_quality (ifindex);
if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
- nm_ap_set_strength (new_ap, (gint8) percent);
+ nm_ap_set_strength (priv->current_ap, (gint8) percent);
priv->invalid_strength_counter = 0;
}
}
- if (new_ap != priv->current_ap) {
- const char *new_bssid = NULL;
- const GByteArray *new_ssid = NULL;
- const char *old_bssid = NULL;
- const GByteArray *old_ssid = NULL;
-
- if (new_ap) {
- new_bssid = nm_ap_get_address (new_ap);
- new_ssid = nm_ap_get_ssid (new_ap);
- }
-
- if (priv->current_ap) {
- old_bssid = nm_ap_get_address (priv->current_ap);
- old_ssid = nm_ap_get_ssid (priv->current_ap);
- }
-
- _LOGI (LOGD_WIFI, "roamed from BSSID %s (%s) to %s (%s)",
- old_bssid ? old_bssid : "(none)",
- old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)",
- new_bssid ? new_bssid : "(none)",
- new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)");
-
- set_current_ap (self, new_ap, TRUE, FALSE);
- }
-
new_rate = nm_platform_wifi_get_rate (ifindex);
if (new_rate != priv->rate) {
priv->rate = new_rate;
@@ -648,7 +456,7 @@ periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
static gboolean
periodic_update_cb (gpointer user_data)
{
- periodic_update (NM_DEVICE_WIFI (user_data), NULL);
+ periodic_update (NM_DEVICE_WIFI (user_data));
return TRUE;
}
@@ -675,32 +483,20 @@ emit_ap_added_removed (NMDeviceWifi *self,
}
static void
-remove_access_point (NMDeviceWifi *device,
- NMAccessPoint *ap)
-{
- NMDeviceWifi *self = NM_DEVICE_WIFI (device);
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
- g_return_if_fail (ap);
- g_return_if_fail (ap != priv->current_ap);
- g_return_if_fail (g_slist_find (priv->ap_list, ap));
-
- priv->ap_list = g_slist_remove (priv->ap_list, ap);
- emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE);
- g_object_unref (ap);
-}
-
-static void
remove_all_aps (NMDeviceWifi *self)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ GHashTableIter iter;
+ NMAccessPoint *ap;
- if (priv->ap_list) {
+ if (g_hash_table_size (priv->aps)) {
set_current_ap (self, NULL, FALSE, FALSE);
- while (priv->ap_list)
- remove_access_point (self, NM_AP (priv->ap_list->data));
-
+ g_hash_table_iter_init (&iter, priv->aps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+ emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE);
+ g_hash_table_iter_remove (&iter);
+ }
nm_device_recheck_available_connections (NM_DEVICE (self));
}
}
@@ -862,6 +658,28 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
return TRUE;
}
+static NMAccessPoint *
+find_first_compatible_ap (NMDeviceWifi *self,
+ NMConnection *connection,
+ gboolean allow_unstable_order)
+{
+ GHashTableIter iter;
+ NMAccessPoint *ap;
+ NMAccessPoint *cand_ap = NULL;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+
+ g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+ if (!nm_ap_check_compatible (ap, connection))
+ continue;
+ if (allow_unstable_order)
+ return ap;
+ if (!cand_ap || (nm_ap_get_id (cand_ap) < nm_ap_get_id (ap)))
+ cand_ap = ap;
+ }
+ return cand_ap;
+}
static gboolean
check_connection_available (NMDevice *device,
@@ -869,10 +687,8 @@ check_connection_available (NMDevice *device,
NMDeviceCheckConAvailableFlags flags,
const char *specific_object)
{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
NMSettingWireless *s_wifi;
const char *mode;
- GSList *ap_iter = NULL;
s_wifi = nm_connection_get_setting_wireless (connection);
g_return_val_if_fail (s_wifi, FALSE);
@@ -906,13 +722,8 @@ check_connection_available (NMDevice *device,
if (nm_setting_wireless_get_hidden (s_wifi) || NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
return TRUE;
- /* check if its visible */
- for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
- if (nm_ap_check_compatible (NM_AP (ap_iter->data), connection))
- return TRUE;
- }
-
- return FALSE;
+ /* check at least one AP is compatible with this connection */
+ return !!find_first_compatible_ap (NM_DEVICE_WIFI (device), connection, TRUE);
}
/*
@@ -969,7 +780,6 @@ complete_connection (NMDevice *device,
const GByteArray *ssid = NULL;
GByteArray *tmp_ssid = NULL;
GBytes *setting_ssid = NULL;
- GSList *iter;
gboolean hidden = FALSE;
s_wifi = nm_connection_get_setting_wireless (connection);
@@ -996,12 +806,7 @@ complete_connection (NMDevice *device,
}
/* Find a compatible AP in the scan list */
- for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
- if (nm_ap_check_compatible (NM_AP (iter->data), connection)) {
- ap = NM_AP (iter->data);
- break;
- }
- }
+ 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
@@ -1162,8 +967,7 @@ can_auto_connect (NMDevice *device,
char **specific_object)
{
NMDeviceWifi *self = NM_DEVICE_WIFI (device);
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- GSList *ap_iter;
+ NMAccessPoint *ap;
const char *method = NULL;
guint64 timestamp = 0;
@@ -1184,33 +988,53 @@ can_auto_connect (NMDevice *device,
if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
return TRUE;
- for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
- NMAccessPoint *ap = NM_AP (ap_iter->data);
-
- if (nm_ap_check_compatible (ap, connection)) {
- /* All good; connection is usable */
- *specific_object = (char *) nm_ap_get_dbus_path (ap);
- return TRUE;
- }
+ ap = find_first_compatible_ap (self, connection, FALSE);
+ if (ap) {
+ /* All good; connection is usable */
+ *specific_object = (char *) nm_ap_get_dbus_path (ap);
+ return TRUE;
}
return FALSE;
}
+static gint
+ap_id_compare (NMAccessPoint *a, NMAccessPoint *b)
+{
+ guint32 a_id = nm_ap_get_id (a);
+ guint32 b_id = nm_ap_get_id (b);
+
+ return a_id < b_id ? -1 : (a_id == b_id ? 0 : 1);
+}
+
+static GSList *
+get_sorted_ap_list (NMDeviceWifi *self)
+{
+ GSList *sorted = NULL;
+ GHashTableIter iter;
+ NMAccessPoint *ap;
+
+ g_hash_table_iter_init (&iter, NM_DEVICE_WIFI_GET_PRIVATE (self)->aps);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap))
+ sorted = g_slist_prepend (sorted, ap);
+ return g_slist_sort (sorted, (GCompareFunc) ap_id_compare);
+}
+
static void
ap_list_dump (NMDeviceWifi *self)
{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- GSList * elt;
- int i = 0;
+ GSList *sorted, *iter;
g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+ if (!nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN))
+ return;
+
_LOGD (LOGD_WIFI_SCAN, "Current AP list:");
- for (elt = priv->ap_list; elt; elt = g_slist_next (elt), i++) {
- NMAccessPoint * ap = NM_AP (elt->data);
- nm_ap_dump (ap, "List AP: ");
- }
+ sorted = get_sorted_ap_list (self);
+ for (iter = sorted; iter; iter = iter->next)
+ nm_ap_dump (NM_AP (iter->data), "List AP: ");
+ g_slist_free (sorted);
_LOGD (LOGD_WIFI_SCAN, "Current AP list: done");
}
@@ -1219,16 +1043,17 @@ impl_device_get_access_points (NMDeviceWifi *self,
GPtrArray **aps,
GError **err)
{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- GSList *elt;
+ GSList *sorted, *iter;
*aps = g_ptr_array_new ();
- for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) {
- NMAccessPoint *ap = NM_AP (elt->data);
+ sorted = get_sorted_ap_list (self);
+ for (iter = sorted; iter; iter = iter->next) {
+ NMAccessPoint *ap = NM_AP (iter->data);
if (nm_ap_get_ssid (ap))
g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (ap)));
}
+ g_slist_free (sorted);
return TRUE;
}
@@ -1237,12 +1062,13 @@ impl_device_get_all_access_points (NMDeviceWifi *self,
GPtrArray **aps,
GError **err)
{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- GSList *elt;
+ GSList *sorted, *iter;
*aps = g_ptr_array_new ();
- for (elt = priv->ap_list; elt; elt = g_slist_next (elt))
- g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (NM_AP (elt->data))));
+ sorted = get_sorted_ap_list (self);
+ for (iter = sorted; iter; iter = iter->next)
+ g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (NM_AP (iter->data))));
+ g_slist_free (sorted);
return TRUE;
}
@@ -1657,27 +1483,16 @@ try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
}
}
-/*
- * merge_scanned_ap
- *
- * If there is already an entry that matches the BSSID and ESSID of the
- * AP to merge, replace that entry with the scanned AP. Otherwise, add
- * the scanned AP to the list.
- *
- * TODO: possibly need to differentiate entries based on security too; i.e. if
- * there are two scan results with the same BSSID and SSID but different
- * security options?
- *
- */
static void
merge_scanned_ap (NMDeviceWifi *self,
- NMAccessPoint *merge_ap)
+ 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;
- gboolean strict_match = TRUE;
/* Let the manager try to fill in the SSID from seen-bssids lists */
bssid = nm_ap_get_address (merge_ap);
@@ -1691,7 +1506,6 @@ merge_scanned_ap (NMDeviceWifi *self,
/* 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));
- nm_ap_set_broadcast (merge_ap, FALSE);
} else {
/* Didn't have an entry for this AP in the database */
_LOGD (LOGD_WIFI_SCAN, "failed to match hidden AP %s",
@@ -1699,18 +1513,9 @@ merge_scanned_ap (NMDeviceWifi *self,
}
}
- /* If the incoming scan result matches the hidden AP that NM is currently
- * connected to but hasn't been seen in the scan list yet, don't use
- * strict matching. Because the capabilities of the fake AP have to be
- * constructed from the NMConnection of the activation request, they won't
- * always be the same as the capabilities of the real AP from the scan.
- */
- if (priv->current_ap && nm_ap_get_fake (priv->current_ap))
- strict_match = FALSE;
-
- found_ap = get_ap_by_supplicant_path (self, nm_ap_get_supplicant_path (merge_ap));
+ found_ap = get_ap_by_supplicant_path (self, supplicant_path);
if (!found_ap)
- found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match);
+ 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)",
@@ -1718,19 +1523,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));
- nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap));
- nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap));
- nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap));
- nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap));
- nm_ap_set_max_bitrate (found_ap, nm_ap_get_max_bitrate (merge_ap));
-
- /* If the AP is noticed in a scan, it's automatically no longer
- * fake, since it clearly exists somewhere.
- */
+ 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 {
@@ -1740,8 +1533,8 @@ merge_scanned_ap (NMDeviceWifi *self,
str_if_set (bssid, "(none)"), merge_ap);
g_object_ref (merge_ap);
- priv->ap_list = g_slist_prepend (priv->ap_list, 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);
}
}
@@ -1751,9 +1544,9 @@ cull_scan_list (NMDeviceWifi *self)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
gint32 now = nm_utils_get_monotonic_timestamp_s ();
- GSList *outdated_list = NULL;
- GSList *elt;
guint32 removed = 0, total = 0;
+ GHashTableIter iter;
+ NMAccessPoint *ap;
priv->scanlist_cull_id = 0;
@@ -1762,8 +1555,8 @@ cull_scan_list (NMDeviceWifi *self)
/* Walk the access point list and remove any access points older than
* three times the inactive scan interval.
*/
- for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) {
- NMAccessPoint *ap = elt->data;
+ 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;
@@ -1784,27 +1577,20 @@ cull_scan_list (NMDeviceWifi *self)
continue;
last_seen = nm_ap_get_last_seen (ap);
- if (!last_seen || last_seen + prune_interval_s < now)
- outdated_list = g_slist_prepend (outdated_list, ap);
- }
-
- /* Remove outdated APs */
- for (elt = outdated_list; elt; elt = g_slist_next (elt)) {
- NMAccessPoint *outdated_ap = NM_AP (elt->data);
- const GByteArray *ssid;
-
- ssid = nm_ap_get_ssid (outdated_ap);
- _LOGD (LOGD_WIFI_SCAN,
- " removing %s (%s%s%s)",
- str_if_set (nm_ap_get_address (outdated_ap), "(none)"),
- ssid ? "'" : "",
- ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
- ssid ? "'" : "");
-
- remove_access_point (self, outdated_ap);
- removed++;
+ 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++;
+ }
}
- g_slist_free (outdated_list);
_LOGD (LOGD_WIFI_SCAN, "removed %d APs (of %d)",
removed, total);
@@ -1834,6 +1620,7 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
GVariant *properties,
NMDeviceWifi *self)
{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
NMDeviceState state;
NMAccessPoint *ap;
@@ -1853,8 +1640,14 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
nm_ap_dump (ap, "New AP: ");
/* Add the AP to the device's AP list */
- merge_scanned_ap (self, ap);
+ 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
_LOGW (LOGD_WIFI_SCAN, "invalid AP properties received");
@@ -1883,7 +1676,7 @@ supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
/* Update the AP's last-seen property */
ap = get_ap_by_supplicant_path (self, object_path);
if (ap)
- nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
+ nm_ap_update_from_properties (ap, object_path, properties);
/* Remove outdated access points */
schedule_scanlist_cull (self);
@@ -1915,7 +1708,7 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
*/
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));
-}
+ }
}
static void
@@ -2215,7 +2008,7 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
g_bytes_get_size (ssid)) : "(none)");
nm_device_activate_schedule_stage3_ip_config_start (device);
} else if (devstate == NM_DEVICE_STATE_ACTIVATED)
- periodic_update (self, NULL);
+ periodic_update (self);
break;
case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
@@ -2315,7 +2108,53 @@ supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
/* Run a quick update of current AP when coming out of a scan */
state = nm_device_get_state (NM_DEVICE (self));
if (!scanning && state == NM_DEVICE_STATE_ACTIVATED)
- periodic_update (self, NULL);
+ periodic_update (self);
+}
+
+static void
+supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
+ GParamSpec *pspec,
+ NMDeviceWifi *self)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ const char *current_bss;
+ NMAccessPoint *new_ap = NULL;
+
+ current_bss = nm_supplicant_interface_get_current_bss (iface);
+ if (current_bss)
+ new_ap = get_ap_by_supplicant_path (self, current_bss);
+
+ if (new_ap != priv->current_ap) {
+ const char *new_bssid = NULL;
+ const GByteArray *new_ssid = NULL;
+ const char *old_bssid = NULL;
+ const GByteArray *old_ssid = NULL;
+
+ /* Don't ever replace a "fake" current AP if we don't know about the
+ * supplicant's current BSS yet. It'll get replaced when we receive
+ * the current BSS's scan result.
+ */
+ if (new_ap == NULL && nm_ap_get_fake (priv->current_ap))
+ return;
+
+ if (new_ap) {
+ new_bssid = nm_ap_get_address (new_ap);
+ new_ssid = nm_ap_get_ssid (new_ap);
+ }
+
+ if (priv->current_ap) {
+ old_bssid = nm_ap_get_address (priv->current_ap);
+ old_ssid = nm_ap_get_ssid (priv->current_ap);
+ }
+
+ _LOGD (LOGD_WIFI, "roamed from BSSID %s (%s) to %s (%s)",
+ old_bssid ? old_bssid : "(none)",
+ old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)",
+ new_bssid ? new_bssid : "(none)",
+ new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)");
+
+ set_current_ap (self, new_ap, TRUE, FALSE);
+ }
}
static NMActStageReturn
@@ -2573,7 +2412,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
NMConnection *connection;
NMSettingWireless *s_wireless;
const char *cloned_mac;
- GSList *iter;
const char *mode;
const char *ap_path;
@@ -2620,21 +2458,12 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
/* AP mode never uses a specific object or existing scanned AP */
if (priv->mode != NM_802_11_MODE_AP) {
-
ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
ap = ap_path ? get_ap_by_path (self, ap_path) : NULL;
if (ap)
goto done;
- /* Find a compatible AP in the scan list */
- for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
- NMAccessPoint *candidate = NM_AP (iter->data);
-
- if (nm_ap_check_compatible (candidate, connection)) {
- ap = candidate;
- break;
- }
- }
+ ap = find_first_compatible_ap (self, connection, FALSE);
}
if (ap) {
@@ -2651,13 +2480,11 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
ap = nm_ap_new_fake_from_connection (connection);
g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
- if (nm_ap_get_mode (ap) == NM_802_11_MODE_INFRA)
- nm_ap_set_broadcast (ap, FALSE);
- else if (nm_ap_is_hotspot (ap))
+ if (nm_ap_is_hotspot (ap))
nm_ap_set_address (ap, nm_device_get_hw_address (device));
- priv->ap_list = g_slist_prepend (priv->ap_list, ap);
nm_ap_export_to_dbus (ap);
+ g_hash_table_insert (priv->aps, (gpointer) nm_ap_get_dbus_path (ap), ap);
g_object_freeze_notify (G_OBJECT (self));
set_current_ap (self, ap, FALSE, FALSE);
emit_ap_added_removed (self, ACCESS_POINT_ADDED, ap, TRUE);
@@ -2989,9 +2816,6 @@ activation_success_handler (NMDevice *device)
NMDeviceWifi *self = NM_DEVICE_WIFI (device);
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
int ifindex = nm_device_get_ifindex (device);
- NMAccessPoint *ap;
- guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
- NMAccessPoint *tmp_ap = NULL;
NMActRequest *req;
NMConnection *connection;
@@ -3007,62 +2831,38 @@ activation_success_handler (NMDevice *device)
/* Clear wireless secrets tries on success */
g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
- ap = priv->current_ap;
-
- /* If the AP isn't fake, it was found in the scan list and all its
- * details are known.
- */
- if (!ap || !nm_ap_get_fake (ap)){
- ap = NULL;
- 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.
+ /* 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.
*/
- nm_platform_wifi_get_bssid (ifindex, bssid);
- if (!nm_ap_get_address (ap)) {
- char *bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
- nm_ap_set_address (ap, bssid_str);
- g_free (bssid_str);
- }
- if (!nm_ap_get_freq (ap))
- nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (ifindex));
- if (!nm_ap_get_max_bitrate (ap))
- nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (ifindex));
-
- tmp_ap = find_active_ap (self, ap, TRUE);
- if (tmp_ap) {
- const GByteArray *ssid = nm_ap_get_ssid (tmp_ap);
-
- /* Found a better match in the scan list than the fake AP. Use it
- * instead.
- */
-
- /* If the better match was a hidden AP, update its SSID */
- if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
- ssid = nm_ap_get_ssid (ap);
- nm_ap_set_ssid (tmp_ap, ssid->data, ssid->len);
+ g_warn_if_fail (priv->current_ap);
+ if (priv->current_ap) {
+ if (nm_ap_get_fake (priv->current_ap)) {
+ /* If the activation AP hasn't been seen by the supplicant in a scan
+ * yet, it will be "fake". This usually happens for Ad-Hoc and
+ * AP-mode connections. Fill in the details from the device itself
+ * until the supplicant sends the scan result.
+ */
+ if (!nm_ap_get_address (priv->current_ap)) {
+ guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+ gs_free char *bssid_str = NULL;
+
+ if ( nm_platform_wifi_get_bssid (ifindex, bssid)
+ && nm_ethernet_address_is_valid (bssid, ETH_ALEN)) {
+ bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
+ nm_ap_set_address (priv->current_ap, bssid_str);
+ }
+ }
+ if (!nm_ap_get_freq (priv->current_ap))
+ nm_ap_set_freq (priv->current_ap, nm_platform_wifi_get_frequency (ifindex));
+ if (!nm_ap_get_max_bitrate (priv->current_ap))
+ nm_ap_set_max_bitrate (priv->current_ap, nm_platform_wifi_get_rate (ifindex));
}
- nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
- nm_ap_get_dbus_path (tmp_ap));
+ nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (priv->current_ap));
}
-done:
- periodic_update (self, ap);
-
- /* ap might be already unrefed, because it was a fake_ap. But we don't touch it... */
- if (tmp_ap && ap == priv->current_ap) {
- /* Strange, we would expect periodic_update() to find a better AP
- * then the fake one and reset it. Reset the fake current_ap to NULL
- * now, which will remove the fake ap.
- **/
- set_current_ap (self, NULL, TRUE, FALSE);
- }
-
- /* No need to update seen BSSIDs cache, that is done by set_current_ap() already */
+ periodic_update (self);
/* Reset scan interval to something reasonable */
priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2);
@@ -3223,7 +3023,10 @@ nm_device_wifi_new (NMPlatformLink *platform_device)
static void
nm_device_wifi_init (NMDeviceWifi *self)
{
- NM_DEVICE_WIFI_GET_PRIVATE (self)->mode = NM_802_11_MODE_INFRA;
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ priv->mode = NM_802_11_MODE_INFRA;
+ priv->aps = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
}
static void
@@ -3232,13 +3035,6 @@ dispose (GObject *object)
NMDeviceWifi *self = NM_DEVICE_WIFI (object);
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- if (priv->disposed) {
- G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object);
- return;
- }
-
- priv->disposed = TRUE;
-
if (priv->periodic_source_id) {
g_source_remove (priv->periodic_source_id);
priv->periodic_source_id = 0;
@@ -3262,6 +3058,7 @@ finalize (GObject *object)
g_free (priv->perm_hw_addr);
g_free (priv->initial_hw_addr);
+ g_clear_pointer (&priv->aps, g_hash_table_unref);
G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
}
@@ -3272,8 +3069,9 @@ get_property (GObject *object, guint prop_id,
{
NMDeviceWifi *device = NM_DEVICE_WIFI (object);
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+ GHashTableIter iter;
+ const char *dbus_path;
GPtrArray *array;
- GSList *iter;
switch (prop_id) {
case PROP_PERM_HW_ADDRESS:
@@ -3289,9 +3087,10 @@ get_property (GObject *object, guint prop_id,
g_value_set_uint (value, priv->capabilities);
break;
case PROP_ACCESS_POINTS:
- array = g_ptr_array_sized_new (4);
- for (iter = priv->ap_list; iter; iter = g_slist_next (iter))
- g_ptr_array_add (array, g_strdup (nm_ap_get_dbus_path (NM_AP (iter->data))));
+ array = g_ptr_array_sized_new (g_hash_table_size (priv->aps));
+ g_hash_table_iter_init (&iter, priv->aps);
+ while (g_hash_table_iter_next (&iter, (gpointer) &dbus_path, NULL))
+ g_ptr_array_add (array, g_strdup (dbus_path));
g_value_take_boxed (value, array);
break;
case PROP_ACTIVE_ACCESS_POINT:
@@ -3313,11 +3112,7 @@ static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
- switch (prop_id) {
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index c0f8b11dff..0519784c12 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -34,6 +34,7 @@
#include "nm-setting-wireless.h"
#include "nm-glib-compat.h"
+#include "gsystem-local-alloc.h"
#include "nm-access-point-glue.h"
@@ -44,6 +45,7 @@ typedef struct
{
char *dbus_path;
char *supplicant_path; /* D-Bus object path of this AP from wpa_supplicant */
+ guint32 id; /* ID for stable sorting of APs */
/* Scanned or cached values */
GByteArray * ssid;
@@ -60,7 +62,6 @@ typedef struct
/* Non-scanned attributes */
gboolean fake; /* Whether or not the AP is from a scan */
gboolean hotspot; /* Whether the AP is a local device's hotspot network */
- gboolean broadcast; /* Whether or not the AP is broadcasting (hidden) */
gint32 last_seen; /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
} NMAccessPointPrivate;
@@ -92,7 +93,6 @@ nm_ap_init (NMAccessPoint *ap)
priv->flags = NM_802_11_AP_FLAGS_NONE;
priv->wpa_flags = NM_802_11_AP_SEC_NONE;
priv->rsn_flags = NM_802_11_AP_SEC_NONE;
- priv->broadcast = TRUE;
}
static void
@@ -314,7 +314,8 @@ nm_ap_export_to_dbus (NMAccessPoint *ap)
return;
}
- priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", counter++);
+ priv->id = counter++;
+ priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", priv->id);
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->dbus_path, ap);
}
@@ -368,14 +369,13 @@ security_from_vardict (GVariant *security)
return flags;
}
-NMAccessPoint *
-nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
+void
+nm_ap_update_from_properties (NMAccessPoint *ap,
+ const char *supplicant_path,
+ GVariant *properties)
{
- const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
const char *addr;
const guint8 *bytes;
- NMAccessPoint *ap;
GVariant *v;
gsize len;
gboolean b = FALSE;
@@ -383,9 +383,8 @@ nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
gint16 i16;
guint16 u16;
- g_return_val_if_fail (properties != NULL, NULL);
-
- ap = nm_ap_new ();
+ g_return_if_fail (ap != NULL);
+ g_return_if_fail (properties != NULL);
g_object_freeze_notify (G_OBJECT (ap));
@@ -457,7 +456,28 @@ nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
g_variant_unref (v);
}
- nm_ap_set_supplicant_path (ap, supplicant_path);
+ if (!nm_ap_get_supplicant_path (ap))
+ nm_ap_set_supplicant_path (ap, supplicant_path);
+
+ nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
+
+ g_object_thaw_notify (G_OBJECT (ap));
+}
+
+NMAccessPoint *
+nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
+{
+ const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ NMAccessPoint *ap;
+ const char *addr;
+
+ g_return_val_if_fail (supplicant_path != NULL, NULL);
+ g_return_val_if_fail (properties != NULL, NULL);
+
+ ap = nm_ap_new ();
+
+ nm_ap_update_from_properties (ap, supplicant_path, properties);
/* ignore APs with invalid BSSIDs */
addr = nm_ap_get_address (ap);
@@ -467,13 +487,6 @@ nm_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
return NULL;
}
- nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
-
- if (!nm_ap_get_ssid (ap))
- nm_ap_set_broadcast (ap, FALSE);
-
- g_object_thaw_notify (G_OBJECT (ap));
-
return ap;
}
@@ -723,6 +736,14 @@ nm_ap_get_supplicant_path (NMAccessPoint *ap)
return NM_AP_GET_PRIVATE (ap)->supplicant_path;
}
+guint32
+nm_ap_get_id (NMAccessPoint *ap)
+{
+ g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+ return NM_AP_GET_PRIVATE (ap)->id;
+}
+
void
nm_ap_set_supplicant_path (NMAccessPoint *ap, const char *path)
{
@@ -1025,26 +1046,6 @@ void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake)
NM_AP_GET_PRIVATE (ap)->fake = fake;
}
-
-/*
- * Get/Set functions to indicate whether an AP broadcasts its SSID.
- */
-gboolean nm_ap_get_broadcast (NMAccessPoint *ap)
-{
- g_return_val_if_fail (NM_IS_AP (ap), TRUE);
-
- return NM_AP_GET_PRIVATE (ap)->broadcast;
-}
-
-
-void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast)
-{
- g_return_if_fail (NM_IS_AP (ap));
-
- NM_AP_GET_PRIVATE (ap)->broadcast = broadcast;
-}
-
-
/*
* Get/Set functions for how long ago the AP was last seen in a scan.
* APs older than a certain date are dropped from the list.
@@ -1066,6 +1067,16 @@ nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen)
NM_AP_GET_PRIVATE (ap)->last_seen = last_seen;
}
+static guint
+freq_to_band (guint32 freq)
+{
+ if (freq >= 4915 && freq <= 5825)
+ return 5;
+ else if (freq >= 2412 && freq <= 2484)
+ return 2;
+ return 0;
+}
+
gboolean
nm_ap_check_compatible (NMAccessPoint *self,
NMConnection *connection)
@@ -1116,13 +1127,12 @@ nm_ap_check_compatible (NMAccessPoint *self,
band = nm_setting_wireless_get_band (s_wireless);
if (band) {
- if (!strcmp (band, "a")) {
- if (priv->freq < 4915 || priv->freq > 5825)
- return FALSE;
- } else if (!strcmp (band, "bg")) {
- if (priv->freq < 2412 || priv->freq > 2484)
- return FALSE;
- }
+ guint ap_band = freq_to_band (priv->freq);
+
+ if (!strcmp (band, "a") && ap_band != 5)
+ return FALSE;
+ else if (!strcmp (band, "bg") && ap_band != 2)
+ return FALSE;
}
channel = nm_setting_wireless_get_channel (s_wireless);
@@ -1164,43 +1174,23 @@ nm_ap_complete_connection (NMAccessPoint *self,
error);
}
-static gboolean
-capabilities_compatible (NM80211ApSecurityFlags a_flags, NM80211ApSecurityFlags b_flags)
-{
- if (a_flags == b_flags)
- return TRUE;
-
- /* Make sure there's a common key management method */
- if (!((a_flags & 0x300) & (b_flags & 0x300)))
- return FALSE;
-
- /* Ensure common pairwise ciphers */
- if (!((a_flags & 0xF) & (b_flags & 0xF)))
- return FALSE;
-
- /* Ensure common group ciphers */
- if (!((a_flags & 0xF0) & (b_flags & 0xF0)))
- return FALSE;
-
- return TRUE;
-}
-
NMAccessPoint *
-nm_ap_match_in_list (NMAccessPoint *find_ap,
- GSList *ap_list,
- gboolean strict_match)
+nm_ap_match_in_hash (NMAccessPoint *find_ap, GHashTable *hash)
{
- GSList *iter;
+ GHashTableIter iter;
+ NMAccessPoint *list_ap, *band_match = NULL;
g_return_val_if_fail (find_ap != NULL, NULL);
- for (iter = ap_list; iter; iter = g_slist_next (iter)) {
- NMAccessPoint * list_ap = NM_AP (iter->data);
+ 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
@@ -1216,8 +1206,7 @@ nm_ap_match_in_list (NMAccessPoint *find_ap,
continue;
/* BSSID match */
- if ( (strict_match || nm_ethernet_address_is_valid (find_addr, -1))
- && nm_ethernet_address_is_valid (list_addr, -1)
+ if ( nm_ethernet_address_is_valid (list_addr, -1)
&& !nm_utils_hwaddr_matches (list_addr, -1, find_addr, -1))
continue;
@@ -1225,35 +1214,26 @@ nm_ap_match_in_list (NMAccessPoint *find_ap,
if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap))
continue;
- /* Frequency match */
- if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap))
- continue;
-
/* AP flags */
if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap))
continue;
- if (strict_match) {
- 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;
- } else {
- NM80211ApSecurityFlags list_wpa_flags = nm_ap_get_wpa_flags (list_ap);
- NM80211ApSecurityFlags find_wpa_flags = nm_ap_get_wpa_flags (find_ap);
- NM80211ApSecurityFlags list_rsn_flags = nm_ap_get_rsn_flags (list_ap);
- NM80211ApSecurityFlags find_rsn_flags = nm_ap_get_rsn_flags (find_ap);
-
- /* Just ensure that there is overlap in the capabilities */
- if ( !capabilities_compatible (list_wpa_flags, find_wpa_flags)
- && !capabilities_compatible (list_rsn_flags, find_rsn_flags))
- 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 NULL;
+ return band_match;
}
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index 8ad9acb71b..bcbf7eea0e 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -60,12 +60,18 @@ NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path,
NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection);
void nm_ap_export_to_dbus (NMAccessPoint *ap);
+void nm_ap_update_from_properties (NMAccessPoint *ap,
+ const char *supplicant_path,
+ GVariant *properties);
+
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);
+guint32 nm_ap_get_id (NMAccessPoint *ap);
+
const GByteArray *nm_ap_get_ssid (const NMAccessPoint * ap);
void nm_ap_set_ssid (NMAccessPoint * ap, const guint8 * ssid, gsize len);
@@ -98,9 +104,6 @@ void nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate);
gboolean nm_ap_get_fake (const NMAccessPoint *ap);
void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake);
-gboolean nm_ap_get_broadcast (NMAccessPoint *ap);
-void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast);
-
gint32 nm_ap_get_last_seen (const NMAccessPoint *ap);
void nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen);
@@ -112,9 +115,7 @@ gboolean nm_ap_complete_connection (NMAccessPoint *self,
gboolean lock_bssid,
GError **error);
-NMAccessPoint * nm_ap_match_in_list (NMAccessPoint *find_ap,
- GSList *ap_list,
- gboolean strict_match);
+NMAccessPoint * nm_ap_match_in_hash (NMAccessPoint *find_ap, GHashTable *hash);
void nm_ap_dump (NMAccessPoint *ap, const char *prefix);
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index 7f79964410..9c044308b1 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -64,6 +64,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0 = 0,
PROP_SCANNING,
+ PROP_CURRENT_BSS,
LAST_PROP
};
@@ -91,6 +92,7 @@ typedef struct {
char * net_path;
guint32 blobs_left;
GHashTable * bss_proxies;
+ char * current_bss;
gint32 last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */
@@ -320,6 +322,17 @@ nm_supplicant_interface_get_scanning (NMSupplicantInterface *self)
return FALSE;
}
+const char *
+nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self)
+{
+ NMSupplicantInterfacePrivate *priv;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ return priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY ? priv->current_bss : NULL;
+}
+
gint32
nm_supplicant_interface_get_last_scan_time (NMSupplicantInterface *self)
{
@@ -550,6 +563,8 @@ props_changed_cb (GDBusProxy *proxy,
gint32 i32;
GVariant *v;
+ g_object_freeze_notify (G_OBJECT (self));
+
if (g_variant_lookup (changed_properties, "Scanning", "b", &b))
set_scanning (self, b);
@@ -570,6 +585,16 @@ props_changed_cb (GDBusProxy *proxy,
g_free (array);
}
+ if (g_variant_lookup (changed_properties, "CurrentBSS", "&o", &s)) {
+ if (strcmp (s, "/") == 0)
+ s = NULL;
+ if (g_strcmp0 (s, priv->current_bss) != 0) {
+ g_free (priv->current_bss);
+ priv->current_bss = g_strdup (s);
+ g_object_notify (G_OBJECT (self), NM_SUPPLICANT_INTERFACE_CURRENT_BSS);
+ }
+ }
+
v = g_variant_lookup_value (changed_properties, "Capabilities", G_VARIANT_TYPE_VARDICT);
if (v) {
parse_capabilities (self, v);
@@ -589,6 +614,8 @@ props_changed_cb (GDBusProxy *proxy,
priv->disconnect_reason);
}
}
+
+ g_object_thaw_notify (G_OBJECT (self));
}
static void
@@ -861,8 +888,10 @@ log_result_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
gs_free_error GError *error = NULL;
reply = g_dbus_proxy_call_finish (proxy, result, &error);
- if (!reply && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
- nm_log_warn (LOGD_SUPPLICANT, "Failed to %s: %s.", error->message, (char *) user_data);
+ if ( !reply
+ && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
+ && !strstr (error->message, "fi.w1.wpa_supplicant1.NotConnected"))
+ nm_log_warn (LOGD_SUPPLICANT, "Failed to %s: %s.", (char *) user_data, error->message);
}
void
@@ -1268,11 +1297,7 @@ set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
- switch (prop_id) {
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
static void
@@ -1281,9 +1306,14 @@ get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
+
switch (prop_id) {
case PROP_SCANNING:
- g_value_set_boolean (value, NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object)->scanning);
+ g_value_set_boolean (value, priv->scanning);
+ break;
+ case PROP_CURRENT_BSS:
+ g_value_set_string (value, priv->current_bss);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1314,6 +1344,7 @@ dispose (GObject *object)
g_clear_pointer (&priv->net_path, g_free);
g_clear_pointer (&priv->dev, g_free);
g_clear_pointer (&priv->object_path, g_free);
+ g_clear_pointer (&priv->current_bss, g_free);
g_clear_object (&priv->cfg);
@@ -1340,6 +1371,13 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_CURRENT_BSS,
+ g_param_spec_string (NM_SUPPLICANT_INTERFACE_CURRENT_BSS, "", "",
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
/* Signals */
signals[STATE] =
g_signal_new (NM_SUPPLICANT_INTERFACE_STATE,
diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h
index da3ad57048..6be89f4222 100644
--- a/src/supplicant-manager/nm-supplicant-interface.h
+++ b/src/supplicant-manager/nm-supplicant-interface.h
@@ -54,6 +54,10 @@ enum {
#define NM_IS_SUPPLICANT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_INTERFACE))
#define NM_SUPPLICANT_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass))
+/* Properties */
+#define NM_SUPPLICANT_INTERFACE_CURRENT_BSS "current-bss"
+
+/* Signals */
#define NM_SUPPLICANT_INTERFACE_STATE "state"
#define NM_SUPPLICANT_INTERFACE_REMOVED "removed"
#define NM_SUPPLICANT_INTERFACE_NEW_BSS "new-bss"
@@ -145,6 +149,8 @@ const char *nm_supplicant_interface_state_to_string (guint32 state);
gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self);
+const char *nm_supplicant_interface_get_current_bss (NMSupplicantInterface *self);
+
gint32 nm_supplicant_interface_get_last_scan_time (NMSupplicantInterface *self);
const char *nm_supplicant_interface_get_ifname (NMSupplicantInterface *self);