Commit 9549c70d authored by Jiří Klimeš's avatar Jiří Klimeš Committed by Dan Williams

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: default avatarJiří Klimeš <jklimes@redhat.com>
parent 060e865e
......@@ -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,
......@@ -977,6 +978,28 @@ get_active_ap (NMDeviceWifi *self,
return NULL;
}
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)
{
......@@ -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)
{
......
......@@ -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);
......
......@@ -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)
......
......@@ -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 */
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment