From 8278719840fe926bf103cd47547ecc38fc882477 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 25 Jun 2021 16:03:29 +0200 Subject: [PATCH] settings: limit number of seen-bssids and preserve order Previously, there was no limit how many seen-bssids are tracked. That seems problematic, also because there is no API how to get rid of an excessive list of entries. We should limit the number of entries. Add an (arbitrary) limit of 30. But this means that we drop the surplus of entries, and for that it seems important to keep the newest, most recently seen entries. Previously, entries were merely sorted ASCIIbetically. Now, honor their order (with most recently seen first). Also, normalize the BSSIDs. From internal code, we should only get normalize strings, but when we load them from disk, they might be bogus. As we might cut of the list, we don't want that invalid entries cut of valid ones. And of course, invalid entries make no sense at all. --- src/core/settings/nm-settings-connection.c | 209 +++++++++++++++------ src/core/settings/nm-settings-connection.h | 2 - 2 files changed, 155 insertions(+), 56 deletions(-) diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c index de5b7c296e..36ef6acb2f 100644 --- a/src/core/settings/nm-settings-connection.c +++ b/src/core/settings/nm-settings-connection.c @@ -11,6 +11,7 @@ #include "c-list/src/c-list.h" #include "libnm-glib-aux/nm-keyfile-aux.h" +#include "libnm-glib-aux/nm-c-list.h" #include "libnm-core-aux-intern/nm-common-macros.h" #include "nm-config.h" #include "nm-config-data.h" @@ -30,6 +31,8 @@ #define AUTOCONNECT_RETRIES_FOREVER -1 #define AUTOCONNECT_RESET_RETRIES_TIMER 300 +#define SEEN_BSSIDS_MAX 30 + #define _NM_SETTINGS_UPDATE2_FLAG_ALL_PERSIST_MODES \ ((NMSettingsUpdate2Flags) (NM_SETTINGS_UPDATE2_FLAG_TO_DISK \ | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY \ @@ -59,6 +62,56 @@ nm_settings_connections_array_to_connections(NMSettingsConnection *const *connec /*****************************************************************************/ +typedef struct { + char bssid[sizeof(NMEtherAddr) * 3]; + CList seen_bssids_lst; +} SeenBssidEntry; + +static inline SeenBssidEntry * +_seen_bssid_entry_init_stale(SeenBssidEntry *entry, const NMEtherAddr *bssid_bin) +{ + _nm_utils_hwaddr_ntoa(bssid_bin, sizeof(NMEtherAddr), TRUE, entry->bssid, sizeof(entry->bssid)); + return entry; +} + +static inline SeenBssidEntry * +_seen_bssid_entry_new_stale_bin(const NMEtherAddr *bssid_bin) +{ + return _seen_bssid_entry_init_stale(g_slice_new(SeenBssidEntry), bssid_bin); +} + +static inline SeenBssidEntry * +_seen_bssid_entry_new_stale_copy(const SeenBssidEntry *src) +{ + SeenBssidEntry *entry; + + entry = g_slice_new(SeenBssidEntry); + memcpy(entry->bssid, src->bssid, sizeof(entry->bssid)); + return entry; +} + +static void +_seen_bssid_entry_free(gpointer data) +{ + SeenBssidEntry *entry = data; + + c_list_unlink_stale(&entry->seen_bssids_lst); + nm_g_slice_free(entry); +} + +/*****************************************************************************/ + +static GHashTable * +_seen_bssids_hash_new(void) +{ + return g_hash_table_new_full(nm_str_hash, + g_str_equal, + (GDestroyNotify) _seen_bssid_entry_free, + NULL); +} + +/*****************************************************************************/ + NM_GOBJECT_PROPERTIES_DEFINE(NMSettingsConnection, PROP_UNSAVED, PROP_FLAGS, PROP_FILENAME, ); enum { UPDATED_INTERNAL, FLAGS_CHANGED, LAST_SIGNAL }; @@ -99,7 +152,8 @@ typedef struct _NMSettingsConnectionPrivate { */ GVariant *agent_secrets; - GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */ + CList seen_bssids_lst_head; + GHashTable *seen_bssids_hash; guint64 timestamp; /* Up-to-date timestamp of connection use */ @@ -167,7 +221,9 @@ static const GDBusSignalInfo signal_info_updated; static const GDBusSignalInfo signal_info_removed; static const NMDBusInterfaceInfoExtended interface_info_settings_connection; -static void update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new); +static void update_agent_secrets_cache(NMSettingsConnection *self, NMConnection *new); +static guint _get_seen_bssids(NMSettingsConnection *self, + const char * strv_buf[static(SEEN_BSSIDS_MAX + 1)]); /*****************************************************************************/ @@ -246,14 +302,6 @@ nm_settings_connection_still_valid(NMSettingsConnection *self) /*****************************************************************************/ -static GHashTable * -_seen_bssids_hash_new(void) -{ - return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, NULL); -} - -/*****************************************************************************/ - static void _getsettings_cached_clear(NMSettingsConnectionPrivate *priv) { @@ -1300,8 +1348,8 @@ get_settings_auth_cb(NMSettingsConnection * self, GError * error, gpointer data) { - gs_free const char ** seen_bssids = NULL; - NMConnectionSerializationOptions options = {}; + const char * seen_bssids_strv[SEEN_BSSIDS_MAX + 1]; + NMConnectionSerializationOptions options = {}; if (error) { g_dbus_method_invocation_return_gerror(context, error); @@ -1321,8 +1369,8 @@ get_settings_auth_cb(NMSettingsConnection * self, * from the same reason as timestamp. Thus we put it here to GetSettings() * return settings too. */ - seen_bssids = nm_settings_connection_get_seen_bssids(self); - options.seen_bssids = seen_bssids; + _get_seen_bssids(self, seen_bssids_strv); + options.seen_bssids = seen_bssids_strv; /* Secrets should *never* be returned by the GetSettings method, they * get returned by the GetSecrets method which can be better @@ -2304,44 +2352,66 @@ _nm_settings_connection_register_kf_dbs(NMSettingsConnection *self, if (priv->kf_db_seen_bssids != kf_db_seen_bssids) { gs_strfreev char **tmp_strv = NULL; - gsize i, len; + gsize len; + gsize i; + guint result_len; nm_key_file_db_unref(priv->kf_db_seen_bssids); priv->kf_db_seen_bssids = nm_key_file_db_ref(kf_db_seen_bssids); tmp_strv = nm_key_file_db_get_string_list(priv->kf_db_seen_bssids, connection_uuid, &len); - nm_clear_pointer(&priv->seen_bssids, g_hash_table_unref); + if (priv->seen_bssids_hash) + g_hash_table_remove_all(priv->seen_bssids_hash); - if (len > 0) { - _LOGT("read %zu seen-bssids from keyfile database \"%s\"", - len, - nm_key_file_db_get_filename(priv->kf_db_seen_bssids)); - priv->seen_bssids = _seen_bssids_hash_new(); - for (i = len; i > 0;) - g_hash_table_add(priv->seen_bssids, g_steal_pointer(&tmp_strv[--i])); - nm_clear_g_free(&tmp_strv); + for (result_len = 0, i = 0; i < len; i++) { + NMEtherAddr addr_bin; + SeenBssidEntry *entry; + + nm_assert(result_len == nm_g_hash_table_size(priv->seen_bssids_hash)); + if (result_len >= SEEN_BSSIDS_MAX) + break; + + if (!_nm_utils_hwaddr_aton_exact(tmp_strv[i], &addr_bin, sizeof(addr_bin))) + continue; + + if (!priv->seen_bssids_hash) + priv->seen_bssids_hash = _seen_bssids_hash_new(); + + entry = _seen_bssid_entry_new_stale_bin(&addr_bin); + if (!g_hash_table_insert(priv->seen_bssids_hash, entry, entry)) { + /* duplicate detected! The @entry key was freed by g_hash_table_insert(). */ + continue; + } + c_list_link_tail(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst); + result_len++; } + if (result_len > 0) { + _LOGT("read %u seen-bssids from keyfile database \"%s\"", + result_len, + nm_key_file_db_get_filename(priv->kf_db_seen_bssids)); + } else + nm_clear_pointer(&priv->seen_bssids_hash, g_hash_table_destroy); + + nm_assert(nm_g_hash_table_size(priv->seen_bssids_hash) == result_len); + nm_assert(result_len <= SEEN_BSSIDS_MAX); } } -/** - * nm_settings_connection_get_seen_bssids: - * @self: the #NMSettingsConnection - * - * Returns current list of seen BSSIDs for the connection. - * - * Returns: (transfer container) list of seen BSSIDs (in the standard hex-digits-and-colons notation). - * The caller is responsible for freeing the list, but not the content. - **/ -const char ** -nm_settings_connection_get_seen_bssids(NMSettingsConnection *self) +static guint +_get_seen_bssids(NMSettingsConnection *self, const char *strv_buf[static(SEEN_BSSIDS_MAX + 1)]) { - g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), NULL); + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self); + SeenBssidEntry * entry; + guint i; - return nm_utils_strdict_get_keys(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids, - TRUE, - NULL); + i = 0; + c_list_for_each_entry (entry, &priv->seen_bssids_lst_head, seen_bssids_lst) { + nm_assert(i <= SEEN_BSSIDS_MAX); + strv_buf[i++] = entry->bssid; + } + strv_buf[i] = NULL; + return i; } /** @@ -2355,14 +2425,17 @@ gboolean nm_settings_connection_has_seen_bssid(NMSettingsConnection *self, const char *bssid) { NMSettingsConnectionPrivate *priv; + NMEtherAddr addr_bin; g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(self), FALSE); g_return_val_if_fail(bssid, FALSE); + nm_assert(_nm_utils_hwaddr_aton_exact(bssid, &addr_bin, sizeof(addr_bin))); priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self); - return priv->seen_bssids - && g_hash_table_contains(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids, bssid); + return priv->seen_bssids_hash + && g_hash_table_contains(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->seen_bssids_hash, + bssid); } /** @@ -2377,15 +2450,47 @@ void nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *seen_bssid) { NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE(self); - gs_free const char ** strv = NULL; + const char * seen_bssids_strv[SEEN_BSSIDS_MAX + 1]; + NMEtherAddr addr_bin; const char * connection_uuid; + SeenBssidEntry entry_stack; + SeenBssidEntry * entry; + guint i; - g_return_if_fail(seen_bssid != NULL); + g_return_if_fail(seen_bssid); - if (!priv->seen_bssids) - priv->seen_bssids = _seen_bssids_hash_new(); + if (!_nm_utils_hwaddr_aton_exact(seen_bssid, &addr_bin, sizeof(addr_bin))) + g_return_if_reached(); - g_hash_table_add(priv->seen_bssids, g_strdup(seen_bssid)); + _seen_bssid_entry_init_stale(&entry_stack, &addr_bin); + + if (!priv->seen_bssids_hash) { + priv->seen_bssids_hash = _seen_bssids_hash_new(); + entry = NULL; + } else + entry = g_hash_table_lookup(priv->seen_bssids_hash, &entry_stack); + + if (entry) { + if (!nm_c_list_move_front(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst)) { + /* no change. */ + return; + } + } else { + entry = _seen_bssid_entry_new_stale_copy(&entry_stack); + c_list_link_front(&priv->seen_bssids_lst_head, &entry->seen_bssids_lst); + if (!g_hash_table_add(priv->seen_bssids_hash, entry)) + nm_assert_not_reached(); + + if (g_hash_table_size(priv->seen_bssids_hash) > SEEN_BSSIDS_MAX) { + g_hash_table_remove( + priv->seen_bssids_hash, + c_list_last_entry(&priv->seen_bssids_lst_head, SeenBssidEntry, seen_bssids_lst)); + } + } + + nm_assert(g_hash_table_size(priv->seen_bssids_hash) <= SEEN_BSSIDS_MAX); + nm_assert(g_hash_table_size(priv->seen_bssids_hash) + == c_list_length(&priv->seen_bssids_lst_head)); if (!priv->kf_db_seen_bssids) return; @@ -2394,12 +2499,8 @@ nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *se if (!connection_uuid) return; - strv = nm_utils_strdict_get_keys(priv->seen_bssids, TRUE, NULL); - - nm_key_file_db_set_string_list(priv->kf_db_seen_bssids, - connection_uuid, - strv ?: NM_PTRARRAY_EMPTY(const char *), - -1); + i = _get_seen_bssids(self, seen_bssids_strv); + nm_key_file_db_set_string_list(priv->kf_db_seen_bssids, connection_uuid, seen_bssids_strv, i); } /*****************************************************************************/ @@ -2610,7 +2711,7 @@ nm_settings_connection_init(NMSettingsConnection *self) self->_priv = priv; c_list_init(&self->_connections_lst); - + c_list_init(&priv->seen_bssids_lst_head); c_list_init(&priv->call_ids_lst_head); c_list_init(&priv->auth_lst_head); @@ -2648,7 +2749,7 @@ dispose(GObject *object) nm_clear_pointer(&priv->agent_secrets, g_variant_unref); - nm_clear_pointer(&priv->seen_bssids, g_hash_table_destroy); + nm_clear_pointer(&priv->seen_bssids_hash, g_hash_table_destroy); g_clear_object(&priv->agent_mgr); diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h index 83a6a7f62b..fa3dbcfbcf 100644 --- a/src/core/settings/nm-settings-connection.h +++ b/src/core/settings/nm-settings-connection.h @@ -340,8 +340,6 @@ gboolean nm_settings_connection_get_timestamp(NMSettingsConnection *self, guint6 void nm_settings_connection_update_timestamp(NMSettingsConnection *self, guint64 timestamp); -const char **nm_settings_connection_get_seen_bssids(NMSettingsConnection *self); - gboolean nm_settings_connection_has_seen_bssid(NMSettingsConnection *self, const char *bssid); void nm_settings_connection_add_seen_bssid(NMSettingsConnection *self, const char *seen_bssid);