diff options
author | Jiří Klimeš <jklimes@redhat.com> | 2011-05-27 17:32:40 +0200 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2011-06-08 14:51:27 -0500 |
commit | 9549c70d943e3709694c4b0eb2595af11962c0eb (patch) | |
tree | aca26071704f82552d572590e7608413d3069247 | |
parent | 060e865ecd38c4ed109e88dabc8c2528861a44b8 (diff) | |
download | NetworkManager-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.c | 30 | ||||
-rw-r--r-- | src/nm-manager.c | 44 | ||||
-rw-r--r-- | src/settings/nm-settings-connection.c | 216 | ||||
-rw-r--r-- | src/settings/nm-settings-connection.h | 9 | ||||
-rw-r--r-- | src/settings/nm-settings.c | 3 |
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); |