about summary refs log tree commit diff
path: root/src/core/settings/nm-settings-connection.c
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2021-08-25 15:24:42 +0200
committerSebastien Bacher <seb128@ubuntu.com>2021-08-25 15:24:42 +0200
commitdbb91282fa488964fb20595f9494a9f0e4f36a58 (patch)
tree142bc942e5320b35514cdf03a5f9f47a9b89df0e /src/core/settings/nm-settings-connection.c
parent5f2ede3a2813b0e9204befdcfc67509d34be71c6 (diff)
parentcfb80376641fa49137b9996130352697e7f8b436 (diff)
Update upstream source from tag 'upstream/1.32.10'
Update to upstream version '1.32.10'
with Debian dir fcf2778b50b013ede3e7375bc1d829e984175658
Diffstat (limited to 'src/core/settings/nm-settings-connection.c')
-rw-r--r--src/core/settings/nm-settings-connection.c227
1 files changed, 152 insertions, 75 deletions
diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c
index 641f3297..36ef6acb 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,68 +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);
-        } else {
-            NMSettingWireless *s_wifi;
+        for (result_len = 0, i = 0; i < len; i++) {
+            NMEtherAddr     addr_bin;
+            SeenBssidEntry *entry;
 
-            _LOGT("no seen-bssids from keyfile database \"%s\"",
-                  nm_key_file_db_get_filename(priv->kf_db_seen_bssids));
+            nm_assert(result_len == nm_g_hash_table_size(priv->seen_bssids_hash));
+            if (result_len >= SEEN_BSSIDS_MAX)
+                break;
 
-            /* 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_settings_connection_get_connection(self));
-            if (s_wifi) {
-                len = nm_setting_wireless_get_num_seen_bssids(s_wifi);
-                if (len > 0) {
-                    priv->seen_bssids = _seen_bssids_hash_new();
-                    for (i = 0; i < len; i++) {
-                        const char *bssid = nm_setting_wireless_get_seen_bssid(s_wifi, i);
-
-                        g_hash_table_add(priv->seen_bssids, g_strdup(bssid));
-                    }
-                }
+            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;
 }
 
 /**
@@ -2379,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);
 }
 
 /**
@@ -2401,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);
 
-    g_return_if_fail(seen_bssid != NULL);
+    if (!_nm_utils_hwaddr_aton_exact(seen_bssid, &addr_bin, sizeof(addr_bin)))
+        g_return_if_reached();
 
-    if (!priv->seen_bssids)
-        priv->seen_bssids = _seen_bssids_hash_new();
+    _seen_bssid_entry_init_stale(&entry_stack, &addr_bin);
 
-    g_hash_table_add(priv->seen_bssids, g_strdup(seen_bssid));
+    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;
@@ -2418,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);
 }
 
 /*****************************************************************************/
@@ -2634,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);
 
@@ -2672,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);