summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiří Klimeš <jklimes@redhat.com>2011-05-27 17:32:40 +0200
committerDan Williams <dcbw@redhat.com>2011-06-08 14:51:27 -0500
commit9549c70d943e3709694c4b0eb2595af11962c0eb (patch)
treeaca26071704f82552d572590e7608413d3069247
parent060e865ecd38c4ed109e88dabc8c2528861a44b8 (diff)
downloadNetworkManager-9549c70d943e3709694c4b0eb2595af11962c0eb.tar.gz
core: fix auto-connect to hidden SSIDs (rh #707406)
Previously (in NM 0.8.x) most WiFi connection were from user settings service. And the service updated 'seen-bssids' property when got connected. But the settings service in 0.9 don't do that. That inhibits auto-connecting to hidden networks. This commit takes care of updating 'seen-bssids'. However, we don't want to write out the conection each time it's activated (touching /etc). So, seen BSSIDs are kept separately from the connection in a look-aside file. Signed-off-by: Jiří Klimeš <jklimes@redhat.com>
-rw-r--r--src/nm-device-wifi.c30
-rw-r--r--src/nm-manager.c44
-rw-r--r--src/settings/nm-settings-connection.c216
-rw-r--r--src/settings/nm-settings-connection.h9
-rw-r--r--src/settings/nm-settings.c3
5 files changed, 256 insertions, 46 deletions
diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c
index 7a6e7528b8..7f9b5d22ec 100644
--- a/src/nm-device-wifi.c
+++ b/src/nm-device-wifi.c
@@ -55,6 +55,7 @@
#include "nm-setting-ip4-config.h"
#include "nm-setting-ip6-config.h"
#include "nm-system.h"
+#include "nm-settings-connection.h"
static gboolean impl_device_get_access_points (NMDeviceWifi *device,
GPtrArray **aps,
@@ -978,6 +979,28 @@ get_active_ap (NMDeviceWifi *self,
}
static void
+update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
+{
+ NMActRequest *req;
+ NMConnection *connection;
+
+ g_return_if_fail (ap != NULL);
+
+ /* Don't cache the BSSID for Ad-Hoc APs */
+ if (nm_ap_get_mode (ap) != NM_802_11_MODE_INFRA)
+ return;
+
+ if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) {
+ req = nm_device_get_act_request (NM_DEVICE (self));
+ if (req) {
+ connection = nm_act_request_get_connection (req);
+ nm_settings_connection_add_seen_bssid (NM_SETTINGS_CONNECTION (connection),
+ nm_ap_get_address (ap));
+ }
+ }
+}
+
+static void
set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap)
{
NMDeviceWifiPrivate *priv;
@@ -1003,6 +1026,9 @@ set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap)
*/
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);
}
/* Unref old AP here to ensure object lives if new_ap == old_ap */
@@ -3436,11 +3462,13 @@ activation_success_handler (NMDevice *dev)
done:
periodic_update (self);
+ /* Update seen BSSIDs cache with the connected AP */
+ update_seen_bssids_cache (self, priv->current_ap);
+
/* Reset scan interval to something reasonable */
priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2);
}
-
static void
activation_failure_handler (NMDevice *dev)
{
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 5a3f7f7773..a15e4b7916 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -63,6 +63,7 @@
#include "nm-settings-connection.h"
#include "nm-manager-auth.h"
#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
@@ -1040,52 +1041,27 @@ manager_hidden_ap_found (NMDeviceInterface *device,
{
NMManager *manager = NM_MANAGER (user_data);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
- const struct ether_addr *ap_addr;
- const GByteArray *ap_ssid;
+ const struct ether_addr *bssid;
GSList *iter;
GSList *connections;
gboolean done = FALSE;
- ap_ssid = nm_ap_get_ssid (ap);
- if (ap_ssid && ap_ssid->len)
- return;
+ g_return_if_fail (nm_ap_get_ssid (ap) == NULL);
- ap_addr = nm_ap_get_address (ap);
- g_assert (ap_addr);
+ bssid = nm_ap_get_address (ap);
+ g_assert (bssid);
/* Look for this AP's BSSID in the seen-bssids list of a connection,
* and if a match is found, copy over the SSID */
connections = nm_settings_get_connections (priv->settings);
-
for (iter = connections; iter && !done; iter = g_slist_next (iter)) {
NMConnection *connection = NM_CONNECTION (iter->data);
- NMSettingWireless *s_wireless;
- const GByteArray *ssid;
- guint32 num_bssids;
- guint32 i;
-
- s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
- if (!s_wireless)
- continue;
-
- num_bssids = nm_setting_wireless_get_num_seen_bssids (s_wireless);
- if (num_bssids < 1)
- continue;
-
- ssid = nm_setting_wireless_get_ssid (s_wireless);
- g_assert (ssid);
-
- for (i = 0; i < num_bssids && !done; i++) {
- const char *seen_bssid = nm_setting_wireless_get_seen_bssid (s_wireless, i);
- struct ether_addr seen_addr;
+ NMSettingWireless *s_wifi;
- if (ether_aton_r (seen_bssid, &seen_addr)) {
- if (memcmp (ap_addr, &seen_addr, sizeof (struct ether_addr)) == 0) {
- /* Copy the SSID from the connection to the AP */
- nm_ap_set_ssid (ap, ssid);
- done = TRUE;
- }
- }
+ s_wifi = nm_connection_get_setting_wireless (connection);
+ if (s_wifi) {
+ if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid))
+ nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
}
}
g_slist_free (connections);
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 828a036f76..c20ba8284a 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -22,6 +22,7 @@
#include "config.h"
#include <string.h>
+#include <netinet/ether.h>
#include <NetworkManager.h>
#include <dbus/dbus-glib-lowlevel.h>
@@ -38,8 +39,10 @@
#include "nm-manager-auth.h"
#include "nm-marshal.h"
#include "nm-agent-manager.h"
+#include "NetworkManagerUtils.h"
#define SETTINGS_TIMESTAMPS_FILE LOCALSTATEDIR"/lib/NetworkManager/timestamps"
+#define SETTINGS_SEEN_BSSIDS_FILE LOCALSTATEDIR"/lib/NetworkManager/seen-bssids"
static void impl_settings_connection_get_settings (NMSettingsConnection *connection,
DBusGMethodInvocation *context);
@@ -91,7 +94,8 @@ typedef struct {
NMSessionMonitor *session_monitor;
guint session_changed_id;
- guint64 timestamp; /* Up-to-date timestamp of connection use */
+ guint64 timestamp; /* Up-to-date timestamp of connection use */
+ GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
} NMSettingsConnectionPrivate;
/**************************************************************/
@@ -455,12 +459,20 @@ commit_changes (NMSettingsConnection *connection,
}
static void
-remove_timestamp_from_db (NMSettingsConnection *connection)
+remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
{
- GKeyFile *timestamps_file;
+ GKeyFile *key_file;
+ const char *db_file;
- timestamps_file = g_key_file_new ();
- if (g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
+ if (strcmp (db_name, "timestamps") == 0)
+ db_file = SETTINGS_TIMESTAMPS_FILE;
+ else if (strcmp (db_name, "seen-bssids") == 0)
+ db_file = SETTINGS_SEEN_BSSIDS_FILE;
+ else
+ return;
+
+ key_file = g_key_file_new ();
+ if (g_key_file_load_from_file (key_file, db_file, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
const char *connection_uuid;
char *data;
gsize len;
@@ -468,18 +480,18 @@ remove_timestamp_from_db (NMSettingsConnection *connection)
connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
- g_key_file_remove_key (timestamps_file, "timestamps", connection_uuid, NULL);
- data = g_key_file_to_data (timestamps_file, &len, &error);
+ g_key_file_remove_key (key_file, db_name, connection_uuid, NULL);
+ data = g_key_file_to_data (key_file, &len, &error);
if (data) {
- g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error);
+ g_file_set_contents (db_file, data, len, &error);
g_free (data);
}
if (error) {
- nm_log_warn (LOGD_SETTINGS, "error writing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message);
+ nm_log_warn (LOGD_SETTINGS, "error writing %s file '%s': %s", db_name, db_file, error->message);
g_error_free (error);
}
}
- g_key_file_free (timestamps_file);
+ g_key_file_free (key_file);
}
static void
@@ -499,7 +511,10 @@ do_delete (NMSettingsConnection *connection,
nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents, FALSE, 0);
/* Remove timestamp from timestamps database file */
- remove_timestamp_from_db (connection);
+ remove_entry_from_db (connection, "timestamps");
+
+ /* Remove connection from seen-bssids database file */
+ remove_entry_from_db (connection, "seen-bssids");
/* Signal the connection is removed and deleted */
g_signal_emit (connection, signals[REMOVED], 0);
@@ -1440,6 +1455,181 @@ nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection
g_key_file_free (timestamps_file);
}
+static guint
+mac_hash (gconstpointer v)
+{
+ const guint8 *p = v;
+ guint32 i, h = 5381;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ h = (h << 5) + h + p[i];
+ return h;
+}
+
+static gboolean
+mac_equal (gconstpointer a, gconstpointer b)
+{
+ return memcmp (a, b, ETH_ALEN) == 0;
+}
+
+static guint8 *
+mac_dup (const struct ether_addr *old)
+{
+ guint8 *new;
+
+ g_return_val_if_fail (old != NULL, NULL);
+
+ new = g_malloc0 (ETH_ALEN);
+ memcpy (new, old, ETH_ALEN);
+ return new;
+}
+
+/**
+ * nm_settings_connection_has_seen_bssid:
+ * @connection: the #NMSettingsConnection
+ * @bssid: the BSSID to check the seen BSSID list for
+ *
+ * Returns: TRUE if the given @bssid is in the seen BSSIDs list
+ **/
+gboolean
+nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
+ const struct ether_addr *bssid)
+{
+ g_return_val_if_fail (connection != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (connection), FALSE);
+ g_return_val_if_fail (bssid != NULL, FALSE);
+
+ return !!g_hash_table_lookup (NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->seen_bssids, bssid);
+}
+
+/**
+ * nm_settings_connection_add_seen_bssid:
+ * @connection: the #NMSettingsConnection
+ * @seen_bssid: BSSID to set into the connection and to store into
+ * the seen-bssids database
+ *
+ * Updates the connection and seen-bssids database with the provided BSSID.
+ **/
+void
+nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
+ const struct ether_addr *seen_bssid)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+ const char *connection_uuid;
+ GKeyFile *seen_bssids_file;
+ char *data, *bssid_str;
+ const char **list;
+ gsize len;
+ GError *error = NULL;
+ GHashTableIter iter;
+ guint n;
+
+ g_return_if_fail (seen_bssid != NULL);
+
+ if (g_hash_table_lookup (priv->seen_bssids, seen_bssid))
+ return; /* Already in the list */
+
+ /* Add the new BSSID; let the hash take ownership of the allocated BSSID string */
+ bssid_str = nm_ether_ntop (seen_bssid);
+ g_return_if_fail (bssid_str != NULL);
+ g_hash_table_insert (priv->seen_bssids, mac_dup (seen_bssid), bssid_str);
+
+ /* Build up a list of all the BSSIDs in string form */
+ n = 0;
+ list = g_malloc0 (g_hash_table_size (priv->seen_bssids) * sizeof (char *));
+ g_hash_table_iter_init (&iter, priv->seen_bssids);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &bssid_str))
+ list[n++] = bssid_str;
+
+ /* Save BSSID to seen-bssids file */
+ seen_bssids_file = g_key_file_new ();
+ g_key_file_set_list_separator (seen_bssids_file, ',');
+ if (!g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) {
+ if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
+ nm_log_warn (LOGD_SETTINGS, "error parsing seen-bssids file '%s': %s",
+ SETTINGS_SEEN_BSSIDS_FILE, error->message);
+ }
+ g_clear_error (&error);
+ }
+
+ connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+ g_key_file_set_string_list (seen_bssids_file, "seen-bssids", connection_uuid, list, n);
+ g_free (list);
+
+ data = g_key_file_to_data (seen_bssids_file, &len, &error);
+ if (data) {
+ g_file_set_contents (SETTINGS_SEEN_BSSIDS_FILE, data, len, &error);
+ g_free (data);
+ }
+ g_key_file_free (seen_bssids_file);
+
+ if (error) {
+ nm_log_warn (LOGD_SETTINGS, "error saving seen-bssids to file '%s': %s",
+ SETTINGS_SEEN_BSSIDS_FILE, error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+add_seen_bssid_string (NMSettingsConnection *self, const char *bssid)
+{
+ struct ether_addr mac;
+
+ g_return_if_fail (bssid != NULL);
+ if (ether_aton_r (bssid, &mac)) {
+ g_hash_table_insert (NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->seen_bssids,
+ mac_dup (&mac),
+ g_strdup (bssid));
+ }
+}
+
+/**
+ * nm_settings_connection_read_and_fill_seen_bssids:
+ * @connection: the #NMSettingsConnection
+ *
+ * Retrieves seen BSSIDs of the connection from database file and stores then into the
+ * connection private data.
+ **/
+void
+nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection)
+{
+ NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection);
+ const char *connection_uuid;
+ GKeyFile *seen_bssids_file;
+ char **tmp_strv = NULL;
+ gsize i, len = 0;
+ NMSettingWireless *s_wifi;
+
+ /* Get seen BSSIDs from database file */
+ seen_bssids_file = g_key_file_new ();
+ g_key_file_set_list_separator (seen_bssids_file, ',');
+ if (g_key_file_load_from_file (seen_bssids_file, SETTINGS_SEEN_BSSIDS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
+ connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+ tmp_strv = g_key_file_get_string_list (seen_bssids_file, "seen-bssids", connection_uuid, &len, NULL);
+ }
+ g_key_file_free (seen_bssids_file);
+
+ /* Update connection's seen-bssids */
+ if (tmp_strv) {
+ g_hash_table_remove_all (priv->seen_bssids);
+ for (i = 0; i < len; i++)
+ add_seen_bssid_string (connection, tmp_strv[i]);
+ g_strfreev (tmp_strv);
+ } else {
+ /* If this connection didn't have an entry in the seen-bssids database,
+ * maybe this is the first time we've read it in, so populate the
+ * seen-bssids list from the deprecated seen-bssids property of the
+ * wifi setting.
+ */
+ s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (connection));
+ if (s_wifi) {
+ len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
+ for (i = 0; i < len; i++)
+ add_seen_bssid_string (connection, nm_setting_wireless_get_seen_bssid (s_wifi, i));
+ }
+ }
+}
+
/**************************************************************/
static void
@@ -1463,6 +1653,8 @@ nm_settings_connection_init (NMSettingsConnection *self)
self);
priv->agent_mgr = nm_agent_manager_get ();
+
+ priv->seen_bssids = g_hash_table_new_full (mac_hash, mac_equal, g_free, g_free);
}
static void
@@ -1490,6 +1682,8 @@ dispose (GObject *object)
nm_agent_manager_cancel_secrets (priv->agent_mgr, GPOINTER_TO_UINT (iter->data));
g_slist_free (priv->reqs);
+ g_hash_table_destroy (priv->seen_bssids);
+
set_visible (self, FALSE);
if (priv->session_changed_id)
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index 116bfdcc94..af90c5144d 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -24,6 +24,7 @@
#include <nm-connection.h>
#include "nm-settings-flags.h"
+#include <net/ethernet.h>
G_BEGIN_DECLS
@@ -124,6 +125,14 @@ void nm_settings_connection_update_timestamp (NMSettingsConnection *connection,
void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection);
+gboolean nm_settings_connection_has_seen_bssid (NMSettingsConnection *connection,
+ const struct ether_addr *bssid);
+
+void nm_settings_connection_add_seen_bssid (NMSettingsConnection *connection,
+ const struct ether_addr *seen_bssid);
+
+void nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *connection);
+
G_END_DECLS
#endif /* NM_SETTINGS_CONNECTION_H */
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index e23e8d1336..83297809d0 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -773,6 +773,9 @@ claim_connection (NMSettings *self,
/* Read timestamp from look-aside file and put it into the connection's data */
nm_settings_connection_read_and_fill_timestamp (connection);
+ /* Read seen-bssids from look-aside file and put it into the connection's data */
+ nm_settings_connection_read_and_fill_seen_bssids (connection);
+
/* Ensure it's initial visibility is up-to-date */
nm_settings_connection_recheck_visibility (connection);