summary refs log tree commit diff
path: root/src/core/nm-policy.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-08-09 21:55:35 +0200
committerMichael Biebl <biebl@debian.org>2023-08-09 21:55:35 +0200
commit05e4a733f2141995181a551854d5df929f084adf (patch)
tree83bb937740a6667525ba0df046748ecaa829c269 /src/core/nm-policy.c
parent14b0f3a9dc9ea90d60a3b057350fd4d637dc021a (diff)
New upstream version 1.44.0 upstream/1.44.0
Diffstat (limited to 'src/core/nm-policy.c')
-rw-r--r--src/core/nm-policy.c533
1 files changed, 276 insertions, 257 deletions
diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c
index d7e05b7b..efdb0636 100644
--- a/src/core/nm-policy.c
+++ b/src/core/nm-policy.c
@@ -51,7 +51,7 @@ typedef struct {
     NMManager          *manager;
     NMNetns            *netns;
     NMFirewalldManager *firewalld_manager;
-    CList               pending_activation_checks;
+    CList               policy_auto_activate_lst_head;
 
     NMAgentManager *agent_mgr;
 
@@ -62,6 +62,10 @@ typedef struct {
 
     NMSettings *settings;
 
+    GSource *device_recheck_auto_activate_all_idle_source;
+
+    GSource *reset_connections_retries_idle_source;
+
     NMHostnameManager *hostname_manager;
 
     NMActiveConnection *default_ac4, *activating_ac4;
@@ -70,10 +74,6 @@ typedef struct {
     NMDnsManager *dns_manager;
     gulong        config_changed_id;
 
-    guint reset_retries_id; /* idle handler for resetting the retries count */
-
-    guint schedule_activate_all_id; /* idle handler for schedule_activate_all(). */
-
     NMPolicyHostnameMode hostname_mode;
     char                *orig_hostname;     /* hostname at NM start time */
     char                *cur_hostname;      /* hostname we want to assign */
@@ -135,8 +135,7 @@ _PRIV_TO_SELF(NMPolicyPrivate *priv)
 /*****************************************************************************/
 
 static void      update_system_hostname(NMPolicy *self, const char *msg);
-static void      schedule_activate_all(NMPolicy *self);
-static void      schedule_activate_check(NMPolicy *self, NMDevice *device);
+static void      nm_policy_device_recheck_auto_activate_all_schedule(NMPolicy *self);
 static NMDevice *get_default_device(NMPolicy *self, int addr_family);
 
 /*****************************************************************************/
@@ -1283,23 +1282,6 @@ check_activating_active_connections(NMPolicy *self)
     g_object_thaw_notify(G_OBJECT(self));
 }
 
-typedef struct {
-    CList     pending_lst;
-    NMPolicy *policy;
-    NMDevice *device;
-    guint     autoactivate_id;
-} ActivateData;
-
-static void
-activate_data_free(ActivateData *data)
-{
-    nm_device_remove_pending_action(data->device, NM_PENDING_ACTION_AUTOACTIVATE, TRUE);
-    c_list_unlink_stale(&data->pending_lst);
-    nm_clear_g_source(&data->autoactivate_id);
-    g_object_unref(data->device);
-    g_slice_free(ActivateData, data);
-}
-
 static void
 pending_ac_gone(gpointer data, GObject *where_the_object_was)
 {
@@ -1326,13 +1308,17 @@ pending_ac_state_changed(NMActiveConnection *ac, guint state, guint reason, NMPo
          * device, but block the current connection to avoid an activation
          * loop.
          */
-        if (reason != NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED) {
+        if (reason != NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED
+            && reason != NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED) {
             con = nm_active_connection_get_settings_connection(ac);
-            nm_settings_connection_autoconnect_blocked_reason_set(
+            nm_manager_devcon_autoconnect_blocked_reason_set(
+                priv->manager,
+                nm_active_connection_get_device(ac),
                 con,
-                NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+                NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
                 TRUE);
-            schedule_activate_check(self, nm_active_connection_get_device(ac));
+            nm_policy_device_recheck_auto_activate_schedule(self,
+                                                            nm_active_connection_get_device(ac));
         }
 
         /* Cleanup */
@@ -1345,7 +1331,7 @@ pending_ac_state_changed(NMActiveConnection *ac, guint state, guint reason, NMPo
 }
 
 static void
-auto_activate_device(NMPolicy *self, NMDevice *device)
+_auto_activate_device(NMPolicy *self, NMDevice *device)
 {
     NMPolicyPrivate               *priv;
     NMSettingsConnection          *best_connection;
@@ -1391,7 +1377,7 @@ auto_activate_device(NMPolicy *self, NMDevice *device)
         NMSettingConnection  *s_con;
         const char           *permission;
 
-        if (nm_settings_connection_autoconnect_is_blocked(candidate))
+        if (nm_manager_devcon_autoconnect_is_blocked(priv->manager, device, candidate))
             continue;
 
         cand_conn = nm_settings_connection_get_connection(candidate);
@@ -1435,11 +1421,13 @@ auto_activate_device(NMPolicy *self, NMDevice *device)
               "connection '%s' auto-activation failed: %s",
               nm_settings_connection_get_id(best_connection),
               error->message);
-        nm_settings_connection_autoconnect_blocked_reason_set(
+        nm_manager_devcon_autoconnect_blocked_reason_set(
+            priv->manager,
+            device,
             best_connection,
-            NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+            NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
             TRUE);
-        schedule_activate_check(self, device);
+        nm_policy_device_recheck_auto_activate_schedule(self, device);
         return;
     }
 
@@ -1456,32 +1444,33 @@ auto_activate_device(NMPolicy *self, NMDevice *device)
     }
 }
 
-static gboolean
-auto_activate_device_cb(gpointer user_data)
+static void
+_auto_activate_device_clear(NMPolicy *self, NMDevice *device, gboolean do_activate)
 {
-    ActivateData *data = user_data;
+    nm_assert(NM_IS_DEVICE(device));
+    nm_assert(NM_IS_POLICY(self));
+    nm_assert(c_list_is_linked(&device->policy_auto_activate_lst));
+    nm_assert(c_list_contains(&NM_POLICY_GET_PRIVATE(self)->policy_auto_activate_lst_head,
+                              &device->policy_auto_activate_lst));
 
-    g_assert(data);
-    g_assert(NM_IS_POLICY(data->policy));
-    g_assert(NM_IS_DEVICE(data->device));
+    c_list_unlink(&device->policy_auto_activate_lst);
+    nm_clear_g_source_inst(&device->policy_auto_activate_idle_source);
 
-    data->autoactivate_id = 0;
-    auto_activate_device(data->policy, data->device);
-    activate_data_free(data);
-    return G_SOURCE_REMOVE;
+    if (do_activate)
+        _auto_activate_device(self, device);
+
+    nm_device_remove_pending_action(device, NM_PENDING_ACTION_AUTOACTIVATE, TRUE);
 }
 
-static ActivateData *
-find_pending_activation(NMPolicy *self, NMDevice *device)
+static gboolean
+_auto_activate_idle_cb(gpointer user_data)
 {
-    NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
-    ActivateData    *data;
+    NMDevice *device = user_data;
 
-    c_list_for_each_entry (data, &priv->pending_activation_checks, pending_lst) {
-        if (data->device == device)
-            return data;
-    }
-    return NULL;
+    nm_assert(NM_IS_DEVICE(device));
+
+    _auto_activate_device_clear(nm_manager_get_policy(nm_device_get_manager(device)), device, TRUE);
+    return G_SOURCE_CONTINUE;
 }
 
 /*****************************************************************************/
@@ -1600,10 +1589,12 @@ nm_policy_unblock_failed_ovs_interfaces(NMPolicy *self)
         NMConnection         *connection = nm_settings_connection_get_connection(sett_conn);
 
         if (nm_connection_get_setting_ovs_interface(connection)) {
-            nm_settings_connection_autoconnect_retries_reset(sett_conn);
-            nm_settings_connection_autoconnect_blocked_reason_set(
+            nm_manager_devcon_autoconnect_retries_reset(priv->manager, NULL, sett_conn);
+            nm_manager_devcon_autoconnect_blocked_reason_set(
+                priv->manager,
+                NULL,
                 sett_conn,
-                NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+                NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
                 FALSE);
         }
     }
@@ -1634,36 +1625,15 @@ reset_autoconnect_all(
             && !nm_device_check_connection_compatible(
                 device,
                 nm_settings_connection_get_connection(sett_conn),
+                TRUE,
                 NULL))
             continue;
 
-        if (only_no_secrets) {
-            /* we only reset the no-secrets blocked flag. */
-            if (nm_settings_connection_autoconnect_blocked_reason_set(
-                    sett_conn,
-                    NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS,
-                    FALSE)) {
-                /* maybe the connection is still blocked afterwards for other reasons
-                 * and in the larger picture nothing changed. But it's too complicated
-                 * to find out exactly. Just assume, something changed to be sure. */
-                if (!nm_settings_connection_autoconnect_is_blocked(sett_conn))
-                    changed = TRUE;
-            }
-        } else {
-            /* we reset the tries-count and any blocked-reason */
-            if (nm_settings_connection_autoconnect_retries_get(sett_conn) == 0)
-                changed = TRUE;
-            nm_settings_connection_autoconnect_retries_reset(sett_conn);
-
-            if (nm_settings_connection_autoconnect_blocked_reason_set(
-                    sett_conn,
-                    NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_ALL
-                        & ~NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
-                    FALSE)) {
-                if (!nm_settings_connection_autoconnect_is_blocked(sett_conn))
-                    changed = TRUE;
-            }
-        }
+        if (nm_manager_devcon_autoconnect_reset_reconnect_all(priv->manager,
+                                                              device,
+                                                              sett_conn,
+                                                              only_no_secrets))
+            changed = TRUE;
     }
     return changed;
 }
@@ -1683,21 +1653,35 @@ sleeping_changed(NMManager *manager, GParamSpec *pspec, gpointer user_data)
         reset_autoconnect_all(self, NULL, FALSE);
 }
 
-static void
-schedule_activate_check(NMPolicy *self, NMDevice *device)
+void
+nm_policy_device_recheck_auto_activate_schedule(NMPolicy *self, NMDevice *device)
 {
-    NMPolicyPrivate    *priv = NM_POLICY_GET_PRIVATE(self);
-    ActivateData       *data;
+    NMPolicyPrivate    *priv;
     NMActiveConnection *ac;
     const CList        *tmp_list;
 
-    if (nm_manager_get_state(priv->manager) == NM_STATE_ASLEEP)
+    g_return_if_fail(NM_IS_POLICY(self));
+    g_return_if_fail(NM_IS_DEVICE(device));
+    nm_assert(g_signal_handler_find(device,
+                                    G_SIGNAL_MATCH_DATA,
+                                    0,
+                                    0,
+                                    NULL,
+                                    NULL,
+                                    NM_POLICY_GET_PRIVATE(self))
+              != 0);
+
+    if (!c_list_is_empty(&device->policy_auto_activate_lst)) {
+        /* already queued. Return. */
         return;
+    }
 
-    if (!nm_device_autoconnect_allowed(device))
+    priv = NM_POLICY_GET_PRIVATE(self);
+
+    if (nm_manager_get_state(priv->manager) == NM_STATE_ASLEEP)
         return;
 
-    if (find_pending_activation(self, device))
+    if (!nm_device_autoconnect_allowed(device))
         return;
 
     nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
@@ -1712,11 +1696,8 @@ schedule_activate_check(NMPolicy *self, NMDevice *device)
 
     nm_device_add_pending_action(device, NM_PENDING_ACTION_AUTOACTIVATE, TRUE);
 
-    data                  = g_slice_new0(ActivateData);
-    data->policy          = self;
-    data->device          = g_object_ref(device);
-    data->autoactivate_id = g_idle_add(auto_activate_device_cb, data);
-    c_list_link_tail(&priv->pending_activation_checks, &data->pending_lst);
+    c_list_link_tail(&priv->policy_auto_activate_lst_head, &device->policy_auto_activate_lst);
+    device->policy_auto_activate_idle_source = nm_g_idle_add_source(_auto_activate_idle_cb, device);
 }
 
 static gboolean
@@ -1729,7 +1710,7 @@ reset_connections_retries(gpointer user_data)
     gint32                       con_stamp, min_stamp, now;
     gboolean                     changed = FALSE;
 
-    priv->reset_retries_id = 0;
+    nm_clear_g_source_inst(&priv->reset_connections_retries_idle_source);
 
     min_stamp   = 0;
     now         = nm_utils_get_monotonic_timestamp_sec();
@@ -1737,91 +1718,83 @@ reset_connections_retries(gpointer user_data)
     for (i = 0; connections[i]; i++) {
         NMSettingsConnection *connection = connections[i];
 
-        con_stamp = nm_settings_connection_autoconnect_retries_blocked_until(connection);
+        con_stamp =
+            nm_manager_devcon_autoconnect_retries_blocked_until(priv->manager, NULL, connection);
         if (con_stamp == 0)
             continue;
 
         if (con_stamp <= now) {
-            nm_settings_connection_autoconnect_retries_reset(connection);
+            nm_manager_devcon_autoconnect_retries_reset(priv->manager, NULL, connection);
             changed = TRUE;
         } else if (min_stamp == 0 || min_stamp > con_stamp)
             min_stamp = con_stamp;
     }
 
     /* Schedule the handler again if there are some stamps left */
-    if (min_stamp != 0)
-        priv->reset_retries_id =
-            g_timeout_add_seconds(min_stamp - now, reset_connections_retries, self);
+    if (min_stamp != 0) {
+        priv->reset_connections_retries_idle_source =
+            nm_g_timeout_add_seconds_source(min_stamp - now, reset_connections_retries, self);
+    }
 
     /* If anything changed, try to activate the newly re-enabled connections */
     if (changed)
-        schedule_activate_all(self);
+        nm_policy_device_recheck_auto_activate_all_schedule(self);
 
-    return FALSE;
+    return G_SOURCE_CONTINUE;
 }
 
 static void
-_connection_autoconnect_retries_set(NMPolicy *self, NMSettingsConnection *connection, int tries)
+_connection_autoconnect_retries_set(NMPolicy             *self,
+                                    NMDevice             *device,
+                                    NMSettingsConnection *connection,
+                                    guint32               tries)
 {
     NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
 
     nm_assert(NM_IS_SETTINGS_CONNECTION(connection));
-    nm_assert(tries >= 0);
 
-    nm_settings_connection_autoconnect_retries_set(connection, tries);
+    nm_manager_devcon_autoconnect_retries_set(priv->manager, device, connection, tries);
 
     if (tries == 0) {
         /* Schedule a handler to reset retries count */
-        if (!priv->reset_retries_id) {
-            gint32 retry_time =
-                nm_settings_connection_autoconnect_retries_blocked_until(connection);
-
-            g_warn_if_fail(retry_time != 0);
-            priv->reset_retries_id =
-                g_timeout_add_seconds(MAX(0, retry_time - nm_utils_get_monotonic_timestamp_sec()),
-                                      reset_connections_retries,
-                                      self);
+        if (!priv->reset_connections_retries_idle_source) {
+            gint32 retry_time;
+
+            retry_time = nm_manager_devcon_autoconnect_retries_blocked_until(priv->manager,
+                                                                             device,
+                                                                             connection);
+            nm_assert(retry_time != 0);
+
+            priv->reset_connections_retries_idle_source = nm_g_timeout_add_seconds_source(
+                MAX(0, retry_time - nm_utils_get_monotonic_timestamp_sec()),
+                reset_connections_retries,
+                self);
         }
     }
 }
 
 static void
-activate_slave_connections(NMPolicy *self, NMDevice *device)
+unblock_autoconnect_for_ports(NMPolicy   *self,
+                              const char *master_device,
+                              const char *master_uuid_settings,
+                              const char *master_uuid_applied,
+                              gboolean    reset_devcon_autoconnect)
 {
     NMPolicyPrivate             *priv = NM_POLICY_GET_PRIVATE(self);
-    const char                  *master_device;
-    const char                  *master_uuid_settings = NULL;
-    const char                  *master_uuid_applied  = NULL;
-    guint                        i;
-    NMActRequest                *req;
-    gboolean                     internal_activation = FALSE;
     NMSettingsConnection *const *connections;
     gboolean                     changed;
+    guint                        i;
 
-    master_device = nm_device_get_iface(device);
-    g_assert(master_device);
-
-    req = nm_device_get_act_request(device);
-    if (req) {
-        NMConnection         *connection;
-        NMSettingsConnection *sett_conn;
-        NMAuthSubject        *subject;
-
-        connection = nm_active_connection_get_applied_connection(NM_ACTIVE_CONNECTION(req));
-        if (connection)
-            master_uuid_applied = nm_connection_get_uuid(connection);
-
-        sett_conn = nm_active_connection_get_settings_connection(NM_ACTIVE_CONNECTION(req));
-        if (sett_conn) {
-            master_uuid_settings = nm_settings_connection_get_uuid(sett_conn);
-            if (nm_streq0(master_uuid_settings, master_uuid_applied))
-                master_uuid_settings = NULL;
-        }
-
-        subject = nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(req));
-        internal_activation =
-            subject && (nm_auth_subject_get_subject_type(subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL);
-    }
+    _LOGT(LOGD_CORE,
+          "block-autoconnect: unblocking port profiles for controller ifname=%s%s%s, uuid=%s%s%s"
+          "%s%s%s",
+          NM_PRINT_FMT_QUOTE_STRING(master_device),
+          NM_PRINT_FMT_QUOTE_STRING(master_uuid_settings),
+          NM_PRINT_FMT_QUOTED(master_uuid_applied,
+                              ", applied-uuid=\"",
+                              master_uuid_applied,
+                              "\"",
+                              ""));
 
     changed     = FALSE;
     connections = nm_settings_get_connections(priv->settings, NULL);
@@ -1831,21 +1804,25 @@ activate_slave_connections(NMPolicy *self, NMDevice *device)
         const char           *slave_master;
 
         s_slave_con =
-            nm_connection_get_setting_connection(nm_settings_connection_get_connection(sett_conn));
+            nm_settings_connection_get_setting(sett_conn, NM_META_SETTING_TYPE_CONNECTION);
         slave_master = nm_setting_connection_get_master(s_slave_con);
         if (!slave_master)
             continue;
+
         if (!NM_IN_STRSET(slave_master, master_device, master_uuid_applied, master_uuid_settings))
             continue;
 
-        if (!internal_activation) {
-            if (nm_settings_connection_autoconnect_retries_get(sett_conn) == 0)
+        if (reset_devcon_autoconnect) {
+            if (nm_manager_devcon_autoconnect_retries_reset(priv->manager, NULL, sett_conn))
                 changed = TRUE;
-            nm_settings_connection_autoconnect_retries_reset(sett_conn);
         }
-        if (nm_settings_connection_autoconnect_blocked_reason_set(
+
+        /* unblock the devices associated with that connection */
+        if (nm_manager_devcon_autoconnect_blocked_reason_set(
+                priv->manager,
+                NULL,
                 sett_conn,
-                NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+                NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
                 FALSE)) {
             if (!nm_settings_connection_autoconnect_is_blocked(sett_conn))
                 changed = TRUE;
@@ -1853,7 +1830,68 @@ activate_slave_connections(NMPolicy *self, NMDevice *device)
     }
 
     if (changed)
-        schedule_activate_all(self);
+        nm_policy_device_recheck_auto_activate_all_schedule(self);
+}
+
+static void
+unblock_autoconnect_for_ports_for_sett_conn(NMPolicy *self, NMSettingsConnection *sett_conn)
+{
+    const char          *master_device;
+    const char          *master_uuid_settings;
+    NMSettingConnection *s_con;
+
+    nm_assert(NM_IS_POLICY(self));
+    nm_assert(NM_IS_SETTINGS_CONNECTION(sett_conn));
+
+    s_con = nm_settings_connection_get_setting(sett_conn, NM_META_SETTING_TYPE_CONNECTION);
+
+    nm_assert(NM_IS_SETTING_CONNECTION(s_con));
+
+    master_uuid_settings = nm_setting_connection_get_uuid(s_con);
+    master_device        = nm_setting_connection_get_interface_name(s_con);
+
+    unblock_autoconnect_for_ports(self, master_device, master_uuid_settings, NULL, TRUE);
+}
+
+static void
+activate_slave_connections(NMPolicy *self, NMDevice *device)
+{
+    const char   *master_device;
+    const char   *master_uuid_settings = NULL;
+    const char   *master_uuid_applied  = NULL;
+    NMActRequest *req;
+    gboolean      internal_activation = FALSE;
+
+    master_device = nm_device_get_iface(device);
+    nm_assert(master_device);
+
+    req = nm_device_get_act_request(device);
+    if (req) {
+        NMConnection         *connection;
+        NMSettingsConnection *sett_conn;
+        NMAuthSubject        *subject;
+
+        sett_conn = nm_active_connection_get_settings_connection(NM_ACTIVE_CONNECTION(req));
+        if (sett_conn)
+            master_uuid_settings = nm_settings_connection_get_uuid(sett_conn);
+
+        connection = nm_active_connection_get_applied_connection(NM_ACTIVE_CONNECTION(req));
+        if (connection)
+            master_uuid_applied = nm_connection_get_uuid(connection);
+
+        if (nm_streq0(master_uuid_settings, master_uuid_applied))
+            master_uuid_applied = NULL;
+
+        subject = nm_active_connection_get_subject(NM_ACTIVE_CONNECTION(req));
+        internal_activation =
+            subject && (nm_auth_subject_get_subject_type(subject) == NM_AUTH_SUBJECT_TYPE_INTERNAL);
+    }
+
+    unblock_autoconnect_for_ports(self,
+                                  master_device,
+                                  master_uuid_settings,
+                                  master_uuid_applied,
+                                  !internal_activation);
 }
 
 static gboolean
@@ -1968,9 +2006,11 @@ device_state_changed(NMDevice           *device,
          * a missing SIM or wrong modem initialization).
          */
         if (sett_conn) {
-            nm_settings_connection_autoconnect_blocked_reason_set(
+            nm_manager_devcon_autoconnect_blocked_reason_set(
+                priv->manager,
+                device,
                 sett_conn,
-                NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+                NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
                 TRUE);
         }
         break;
@@ -1988,10 +2028,10 @@ device_state_changed(NMDevice           *device,
         if (sett_conn && old_state >= NM_DEVICE_STATE_PREPARE
             && old_state <= NM_DEVICE_STATE_ACTIVATED) {
             gboolean blocked = FALSE;
-            int      tries;
             guint64  con_v;
 
-            if (nm_device_state_reason_check(reason) == NM_DEVICE_STATE_REASON_NO_SECRETS) {
+            switch (nm_device_state_reason_check(reason)) {
+            case NM_DEVICE_STATE_REASON_NO_SECRETS:
                 /* we want to block the connection from auto-connect if it failed due to no-secrets.
                  * However, if a secret-agent registered, since the connection made the last
                  * secret-request, we do not block it. The new secret-agent might not yet
@@ -2008,16 +2048,17 @@ device_state_changed(NMDevice           *device,
                 con_v = nm_settings_connection_get_last_secret_agent_version_id(sett_conn);
                 if (con_v == 0 || con_v == nm_agent_manager_get_agent_version_id(priv->agent_mgr)) {
                     _LOGD(LOGD_DEVICE,
-                          "connection '%s' now blocked from autoconnect due to no secrets",
+                          "block-autoconnect: connection '%s' now blocked from autoconnect due to "
+                          "no secrets",
                           nm_settings_connection_get_id(sett_conn));
                     nm_settings_connection_autoconnect_blocked_reason_set(
                         sett_conn,
-                        NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS,
+                        NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_NO_SECRETS,
                         TRUE);
                     blocked = TRUE;
                 }
-            } else if (nm_device_state_reason_check(reason)
-                       == NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED) {
+                break;
+            case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
                 /* A connection that fails due to dependency-failed is not
                  * able to reconnect until the master connection activates
                  * again; when this happens, the master clears the blocked
@@ -2027,26 +2068,41 @@ device_state_changed(NMDevice           *device,
                  * dependency-failed.
                  */
                 _LOGD(LOGD_DEVICE,
-                      "connection '%s' now blocked from autoconnect due to failed dependency",
+                      "block-autoconnect: connection[%p] (%s) now blocked from autoconnect due to "
+                      "failed "
+                      "dependency",
+                      sett_conn,
                       nm_settings_connection_get_id(sett_conn));
-                nm_settings_connection_autoconnect_blocked_reason_set(
+                nm_manager_devcon_autoconnect_blocked_reason_set(
+                    priv->manager,
+                    device,
                     sett_conn,
-                    NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+                    NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
                     TRUE);
                 blocked = TRUE;
+                break;
+            default:
+                break;
             }
 
             if (!blocked) {
-                tries = nm_settings_connection_autoconnect_retries_get(sett_conn);
-                if (tries > 0) {
+                guint32 tries;
+
+                tries = nm_manager_devcon_autoconnect_retries_get(priv->manager, device, sett_conn);
+                if (tries == 0) {
+                    /* blocked */
+                } else if (tries != NM_AUTOCONNECT_RETRIES_FOREVER) {
                     _LOGD(LOGD_DEVICE,
-                          "connection '%s' failed to autoconnect; %d tries left",
+                          "autoconnect: connection[%p] (%s): failed to autoconnect; %u tries left",
+                          sett_conn,
                           nm_settings_connection_get_id(sett_conn),
-                          tries - 1);
-                    _connection_autoconnect_retries_set(self, sett_conn, tries - 1);
-                } else if (tries != 0) {
+                          tries - 1u);
+                    _connection_autoconnect_retries_set(self, device, sett_conn, tries - 1u);
+                } else {
                     _LOGD(LOGD_DEVICE,
-                          "connection '%s' failed to autoconnect; infinite tries left",
+                          "autoconnect: connection[%p] (%s) failed to autoconnect; infinite tries "
+                          "left",
+                          sett_conn,
                           nm_settings_connection_get_id(sett_conn));
                 }
             }
@@ -2055,7 +2111,7 @@ device_state_changed(NMDevice           *device,
     case NM_DEVICE_STATE_ACTIVATED:
         if (sett_conn) {
             /* Reset auto retries back to default since connection was successful */
-            nm_settings_connection_autoconnect_retries_reset(sett_conn);
+            nm_manager_devcon_autoconnect_retries_reset(priv->manager, device, sett_conn);
         }
 
         /* Since there is no guarantee that device_l3cd_changed() is called
@@ -2087,27 +2143,34 @@ device_state_changed(NMDevice           *device,
     case NM_DEVICE_STATE_DEACTIVATING:
         if (sett_conn) {
             NMSettingsAutoconnectBlockedReason blocked_reason =
-                NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE;
+                NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_NONE;
 
             switch (nm_device_state_reason_check(reason)) {
             case NM_DEVICE_STATE_REASON_USER_REQUESTED:
-                blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST;
+                blocked_reason = NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_USER_REQUEST;
                 break;
             case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
-                blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED;
+                blocked_reason = NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED;
                 break;
             default:
                 break;
             }
-            if (blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE) {
+            if (blocked_reason != NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_NONE) {
                 _LOGD(LOGD_DEVICE,
-                      "blocking autoconnect of connection '%s': %s",
+                      "block-autoconnect: blocking autoconnect of connection '%s': %s",
                       nm_settings_connection_get_id(sett_conn),
                       NM_UTILS_LOOKUP_STR_A(nm_device_state_reason_to_string,
                                             nm_device_state_reason_check(reason)));
-                nm_settings_connection_autoconnect_blocked_reason_set(sett_conn,
-                                                                      blocked_reason,
-                                                                      TRUE);
+                if (blocked_reason == NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED)
+                    nm_manager_devcon_autoconnect_blocked_reason_set(priv->manager,
+                                                                     device,
+                                                                     sett_conn,
+                                                                     blocked_reason,
+                                                                     TRUE);
+                else
+                    nm_settings_connection_autoconnect_blocked_reason_set(sett_conn,
+                                                                          blocked_reason,
+                                                                          TRUE);
             }
         }
         ip6_remove_device_prefix_delegations(self, device);
@@ -2126,7 +2189,7 @@ device_state_changed(NMDevice           *device,
             update_routing_and_dns(self, FALSE, device);
 
         /* Device is now available for auto-activation */
-        schedule_activate_check(self, device);
+        nm_policy_device_recheck_auto_activate_schedule(self, device);
         break;
 
     case NM_DEVICE_STATE_PREPARE:
@@ -2146,9 +2209,11 @@ device_state_changed(NMDevice           *device,
     case NM_DEVICE_STATE_IP_CONFIG:
         /* We must have secrets if we got here. */
         if (sett_conn)
-            nm_settings_connection_autoconnect_blocked_reason_set(
+            nm_manager_devcon_autoconnect_blocked_reason_set(
+                priv->manager,
+                device,
                 sett_conn,
-                NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_ALL,
+                NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED,
                 FALSE);
         break;
     case NM_DEVICE_STATE_SECONDARIES:
@@ -2248,16 +2313,7 @@ device_autoconnect_changed(NMDevice *device, GParamSpec *pspec, gpointer user_da
     NMPolicyPrivate *priv = user_data;
     NMPolicy        *self = _PRIV_TO_SELF(priv);
 
-    schedule_activate_check(self, device);
-}
-
-static void
-device_recheck_auto_activate(NMDevice *device, gpointer user_data)
-{
-    NMPolicyPrivate *priv = user_data;
-    NMPolicy        *self = _PRIV_TO_SELF(priv);
-
-    schedule_activate_check(self, device);
+    nm_policy_device_recheck_auto_activate_schedule(self, device);
 }
 
 static void
@@ -2292,10 +2348,6 @@ devices_list_register(NMPolicy *self, NMDevice *device)
                      "notify::" NM_DEVICE_AUTOCONNECT,
                      G_CALLBACK(device_autoconnect_changed),
                      priv);
-    g_signal_connect(device,
-                     NM_DEVICE_RECHECK_AUTO_ACTIVATE,
-                     G_CALLBACK(device_recheck_auto_activate),
-                     priv);
 }
 
 static void
@@ -2319,16 +2371,13 @@ device_removed(NMManager *manager, NMDevice *device, gpointer user_data)
 {
     NMPolicyPrivate *priv = user_data;
     NMPolicy        *self = _PRIV_TO_SELF(priv);
-    ActivateData    *data;
 
     /* TODO: is this needed? The delegations are cleaned up
      * on transition to deactivated too. */
     ip6_remove_device_prefix_delegations(self, device);
 
-    /* Clear any idle callbacks for this device */
-    data = find_pending_activation(self, device);
-    if (data && data->autoactivate_id)
-        activate_data_free(data);
+    if (c_list_is_linked(&device->policy_auto_activate_lst))
+        _auto_activate_device_clear(self, device, FALSE);
 
     if (g_hash_table_remove(priv->devices, device))
         devices_list_unregister(self, device);
@@ -2508,39 +2557,45 @@ active_connection_removed(NMManager *manager, NMActiveConnection *active, gpoint
 /*****************************************************************************/
 
 static gboolean
-schedule_activate_all_cb(gpointer user_data)
+_device_recheck_auto_activate_all_cb(gpointer user_data)
 {
     NMPolicy        *self = user_data;
     NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
     const CList     *tmp_lst;
     NMDevice        *device;
 
-    priv->schedule_activate_all_id = 0;
+    nm_clear_g_source_inst(&priv->device_recheck_auto_activate_all_idle_source);
 
     nm_manager_for_each_device (priv->manager, device, tmp_lst)
-        schedule_activate_check(self, device);
+        nm_policy_device_recheck_auto_activate_schedule(self, device);
 
-    return G_SOURCE_REMOVE;
+    return G_SOURCE_CONTINUE;
 }
 
 static void
-schedule_activate_all(NMPolicy *self)
+nm_policy_device_recheck_auto_activate_all_schedule(NMPolicy *self)
 {
     NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
 
     /* always restart the idle handler. That way, we settle
      * all other events before restarting to activate them. */
-    nm_clear_g_source(&priv->schedule_activate_all_id);
-    priv->schedule_activate_all_id = g_idle_add(schedule_activate_all_cb, self);
+    nm_clear_g_source_inst(&priv->device_recheck_auto_activate_all_idle_source);
+
+    priv->device_recheck_auto_activate_all_idle_source =
+        nm_g_idle_add_source(_device_recheck_auto_activate_all_cb, self);
 }
 
+/*****************************************************************************/
+
 static void
 connection_added(NMSettings *settings, NMSettingsConnection *connection, gpointer user_data)
 {
     NMPolicyPrivate *priv = user_data;
     NMPolicy        *self = _PRIV_TO_SELF(priv);
 
-    schedule_activate_all(self);
+    unblock_autoconnect_for_ports_for_sett_conn(self, connection);
+
+    nm_policy_device_recheck_auto_activate_all_schedule(self);
 }
 
 static void
@@ -2597,6 +2652,8 @@ connection_updated(NMSettings           *settings,
     NMPolicy                        *self          = _PRIV_TO_SELF(priv);
     NMSettingsConnectionUpdateReason update_reason = update_reason_u;
 
+    unblock_autoconnect_for_ports_for_sett_conn(self, connection);
+
     if (NM_FLAGS_HAS(update_reason, NM_SETTINGS_CONNECTION_UPDATE_REASON_REAPPLY_PARTIAL)) {
         const CList *tmp_lst;
         NMDevice    *device;
@@ -2608,44 +2665,15 @@ connection_updated(NMSettings           *settings,
         }
     }
 
-    schedule_activate_all(self);
-}
-
-static void
-_deactivate_if_active(NMPolicy *self, NMSettingsConnection *connection)
-{
-    NMPolicyPrivate    *priv = NM_POLICY_GET_PRIVATE(self);
-    NMActiveConnection *ac;
-    const CList        *tmp_list, *tmp_safe;
-    GError             *error = NULL;
-
-    nm_assert(NM_IS_SETTINGS_CONNECTION(connection));
-
-    nm_manager_for_each_active_connection_safe (priv->manager, ac, tmp_list, tmp_safe) {
-        if (nm_active_connection_get_settings_connection(ac) == connection
-            && (nm_active_connection_get_state(ac) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
-            if (!nm_manager_deactivate_connection(priv->manager,
-                                                  ac,
-                                                  NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,
-                                                  &error)) {
-                _LOGW(LOGD_DEVICE,
-                      "connection '%s' disappeared, but error deactivating it: (%d) %s",
-                      nm_settings_connection_get_id(connection),
-                      error ? error->code : -1,
-                      error ? error->message : "(unknown)");
-                g_clear_error(&error);
-            }
-        }
-    }
+    nm_policy_device_recheck_auto_activate_all_schedule(self);
 }
 
 static void
 connection_removed(NMSettings *settings, NMSettingsConnection *connection, gpointer user_data)
 {
     NMPolicyPrivate *priv = user_data;
-    NMPolicy        *self = _PRIV_TO_SELF(priv);
 
-    _deactivate_if_active(self, connection);
+    nm_manager_deactivate_ac(priv->manager, connection);
 }
 
 static void
@@ -2657,7 +2685,7 @@ connection_flags_changed(NMSettings *settings, NMSettingsConnection *connection,
     if (NM_FLAGS_HAS(nm_settings_connection_get_flags(connection),
                      NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE)) {
         if (!nm_settings_connection_autoconnect_is_blocked(connection))
-            schedule_activate_all(self);
+            nm_policy_device_recheck_auto_activate_all_schedule(self);
     }
 }
 
@@ -2671,7 +2699,7 @@ secret_agent_registered(NMSettings *settings, NMSecretAgent *agent, gpointer use
      * connections failed due to missing secrets may re-try auto-connection.
      */
     if (reset_autoconnect_all(self, NULL, TRUE))
-        schedule_activate_all(self);
+        nm_policy_device_recheck_auto_activate_all_schedule(self);
 }
 
 NMActiveConnection *
@@ -2765,7 +2793,7 @@ nm_policy_init(NMPolicy *self)
     NMPolicyPrivate *priv          = NM_POLICY_GET_PRIVATE(self);
     gs_free char    *hostname_mode = NULL;
 
-    c_list_init(&priv->pending_activation_checks);
+    c_list_init(&priv->policy_auto_activate_lst_head);
 
     priv->netns = g_object_ref(nm_netns_get());
 
@@ -2901,9 +2929,9 @@ dispose(GObject *object)
 {
     NMPolicy        *self = NM_POLICY(object);
     NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
-    GHashTableIter   h_iter;
-    NMDevice        *device;
-    ActivateData    *data, *data_safe;
+
+    nm_assert(!c_list_is_empty(&priv->policy_auto_activate_lst_head));
+    nm_assert(g_hash_table_size(priv->devices) == 0);
 
     nm_clear_g_object(&priv->default_ac4);
     nm_clear_g_object(&priv->default_ac6);
@@ -2911,9 +2939,6 @@ dispose(GObject *object)
     nm_clear_g_object(&priv->activating_ac6);
     nm_clear_pointer(&priv->pending_active_connections, g_hash_table_unref);
 
-    c_list_for_each_entry_safe (data, data_safe, &priv->pending_activation_checks, pending_lst)
-        activate_data_free(data);
-
     g_slist_free_full(priv->pending_secondaries, (GDestroyNotify) pending_secondary_data_free);
     priv->pending_secondaries = NULL;
 
@@ -2932,20 +2957,14 @@ dispose(GObject *object)
         g_clear_object(&priv->dns_manager);
     }
 
-    g_hash_table_iter_init(&h_iter, priv->devices);
-    while (g_hash_table_iter_next(&h_iter, (gpointer *) &device, NULL)) {
-        g_hash_table_iter_remove(&h_iter);
-        devices_list_unregister(self, device);
-    }
-
     /* The manager should have disposed of ActiveConnections already, which
      * will have called active_connection_removed() and thus we don't need
      * to clean anything up.  Assert that this is TRUE.
      */
     nm_assert(c_list_is_empty(nm_manager_get_active_connections(priv->manager)));
 
-    nm_clear_g_source(&priv->reset_retries_id);
-    nm_clear_g_source(&priv->schedule_activate_all_id);
+    nm_clear_g_source_inst(&priv->reset_connections_retries_idle_source);
+    nm_clear_g_source_inst(&priv->device_recheck_auto_activate_all_idle_source);
 
     nm_clear_g_free(&priv->orig_hostname);
     nm_clear_g_free(&priv->cur_hostname);