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.
This commit is contained in:
Thomas Haller 2021-06-25 16:03:29 +02:00
parent 15a0271781
commit 8278719840
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
2 changed files with 155 additions and 56 deletions

View file

@ -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);

View file

@ -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);