about summary refs log tree commit diff
path: root/src/core/devices/nm-device.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2022-08-16 18:24:19 +0200
committerMichael Biebl <biebl@debian.org>2022-08-16 18:24:19 +0200
commit0018d1f3cf71d680d7b6bceda55a5717244d8b26 (patch)
treea058f1d106d172d3354179437ef034c9355cdf9c /src/core/devices/nm-device.c
parent6accbd3ec0e42d8633bbde4d47ed7bfe854e7e0b (diff)
New upstream version 1.39.90 upstream/1.39.90
Diffstat (limited to 'src/core/devices/nm-device.c')
-rw-r--r--src/core/devices/nm-device.c582
1 files changed, 437 insertions, 145 deletions
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 6b854517..c51da9ac 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -41,7 +41,7 @@
 #include "libnm-platform/nm-platform.h"
 #include "libnm-platform/nm-platform-utils.h"
 #include "libnm-platform/nmp-object.h"
-#include "libnm-platform/nmp-route-manager.h"
+#include "libnm-platform/nmp-global-tracker.h"
 #include "ndisc/nm-ndisc.h"
 #include "ndisc/nm-lndp-ndisc.h"
 
@@ -120,11 +120,12 @@ typedef enum _nm_packed {
 } AddrMethodState;
 
 typedef struct {
-    CList     lst_slave;
-    NMDevice *slave;
-    gulong    watch_id;
-    bool      slave_is_enslaved;
-    bool      configure;
+    CList         lst_slave;
+    NMDevice     *slave;
+    GCancellable *cancellable;
+    gulong        watch_id;
+    bool          slave_is_enslaved;
+    bool          configure;
 } SlaveInfo;
 
 typedef struct {
@@ -392,6 +393,8 @@ typedef struct _NMDevicePrivate {
     guint device_link_changed_id;
     guint device_ip_link_changed_id;
 
+    GSource *delay_activation_source;
+
     NMDeviceState       state;
     NMDeviceStateReason state_reason;
     struct {
@@ -604,6 +607,7 @@ typedef struct _NMDevicePrivate {
             const NMDeviceIPState state;
             NMDeviceIPState       state_;
         };
+        gulong dnsmgr_update_pending_signal_id;
     } ip_data;
 
     union {
@@ -1394,6 +1398,56 @@ _prop_get_connection_dns_over_tls(NMDevice *self)
                                                        NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT);
 }
 
+static NMMptcpFlags
+_prop_get_connection_mptcp_flags(NMDevice *self)
+{
+    NMConnection *connection;
+    NMMptcpFlags  mptcp_flags = NM_MPTCP_FLAGS_NONE;
+
+    g_return_val_if_fail(NM_IS_DEVICE(self), NM_MPTCP_FLAGS_DISABLED);
+
+    connection = nm_device_get_applied_connection(self);
+    if (connection) {
+        mptcp_flags =
+            nm_setting_connection_get_mptcp_flags(nm_connection_get_setting_connection(connection));
+        if (mptcp_flags != NM_MPTCP_FLAGS_NONE)
+            mptcp_flags = nm_mptcp_flags_normalize(mptcp_flags);
+    }
+
+    if (mptcp_flags == NM_MPTCP_FLAGS_NONE) {
+        guint64 v;
+
+        v = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                        NM_CON_DEFAULT("connection.mptcp-flags"),
+                                                        self,
+                                                        0,
+                                                        G_MAXINT64,
+                                                        NM_MPTCP_FLAGS_NONE);
+        /* We filter out all invalid settings and accept it. Somewhat intentionally, we don't do a
+         * strict parsing of the value to support forward compatibility. */
+        if (v != NM_MPTCP_FLAGS_NONE)
+            mptcp_flags = nm_mptcp_flags_normalize(v);
+    }
+
+    if (mptcp_flags == NM_MPTCP_FLAGS_NONE) {
+        gint32 v;
+
+        v = nm_platform_sysctl_get_int32(nm_device_get_platform(self),
+                                         NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/mptcp/enabled"),
+                                         -1);
+        if (v > 0) {
+            /* if MPTCP is enabled via the sysctl, we use the default. */
+            mptcp_flags = _NM_MPTCP_FLAGS_DEFAULT;
+        } else
+            mptcp_flags = NM_MPTCP_FLAGS_DISABLED;
+    }
+
+    nm_assert(mptcp_flags != NM_MPTCP_FLAGS_NONE
+              && mptcp_flags == nm_mptcp_flags_normalize(mptcp_flags));
+
+    return mptcp_flags;
+}
+
 static guint32
 _prop_get_ipvx_route_table(NMDevice *self, int addr_family)
 {
@@ -1508,6 +1562,53 @@ _prop_get_connection_lldp(NMDevice *self)
     return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX;
 }
 
+static NMSettingIP4LinkLocal
+_prop_get_ipv4_link_local(NMDevice *self)
+{
+    NMSettingIP4Config   *s_ip4;
+    NMSettingIP4LinkLocal link_local;
+
+    s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG);
+    if (!s_ip4)
+        return NM_SETTING_IP4_LL_DISABLED;
+
+    link_local = nm_setting_ip4_config_get_link_local(s_ip4);
+
+    if (link_local == NM_SETTING_IP4_LL_DEFAULT) {
+        /* For connections without a ipv4.link-local property configured the global configuration
+           might defines the default value for ipv4.link-local. */
+        link_local = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                                 NM_CON_DEFAULT("ipv4.link-local"),
+                                                                 self,
+                                                                 NM_SETTING_IP4_LL_AUTO,
+                                                                 NM_SETTING_IP4_LL_ENABLED,
+                                                                 NM_SETTING_IP4_LL_DEFAULT);
+        if (link_local == NM_SETTING_IP4_LL_DEFAULT) {
+            /* If there is no global configuration for ipv4.link-local assume auto */
+            link_local = NM_SETTING_IP4_LL_AUTO;
+        } else if (link_local == NM_SETTING_IP4_LL_ENABLED
+                   && nm_streq(nm_setting_ip_config_get_method((NMSettingIPConfig *) s_ip4),
+                               NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
+            /* ipv4.method=disabled has higher priority than the global ipv4.link-local=enabled */
+            link_local = NM_SETTING_IP4_LL_DISABLED;
+        } else if (link_local == NM_SETTING_IP4_LL_DISABLED
+                   && nm_streq(nm_setting_ip_config_get_method((NMSettingIPConfig *) s_ip4),
+                               NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
+            /* ipv4.method=link-local has higher priority than the global ipv4.link-local=disabled */
+            link_local = NM_SETTING_IP4_LL_ENABLED;
+        }
+    }
+
+    if (link_local == NM_SETTING_IP4_LL_AUTO) {
+        link_local = nm_streq(nm_setting_ip_config_get_method((NMSettingIPConfig *) s_ip4),
+                              NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)
+                         ? NM_SETTING_IP4_LL_ENABLED
+                         : NM_SETTING_IP4_LL_DISABLED;
+    }
+
+    return link_local;
+}
+
 static guint32
 _prop_get_ipv4_dad_timeout(NMDevice *self)
 {
@@ -2118,6 +2219,50 @@ _prop_get_ipv6_ip6_privacy(NMDevice *self)
     return _ip6_privacy_clamp(ip6_privacy);
 }
 
+static NMSettingIP6ConfigAddrGenMode
+_prop_get_ipv6_addr_gen_mode(NMDevice *self)
+{
+    NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+    NMSettingIP6Config           *s_ip6;
+    gint64                        c;
+
+    g_return_val_if_fail(self, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY);
+
+    s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG);
+    if (s_ip6) {
+        addr_gen_mode = nm_setting_ip6_config_get_addr_gen_mode(s_ip6);
+        if (NM_IN_SET(addr_gen_mode,
+                      NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+                      NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY))
+            return addr_gen_mode;
+    } else
+        addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT;
+
+    nm_assert(NM_IN_SET(addr_gen_mode,
+                        NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64,
+                        NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT));
+
+    c = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                    NM_CON_DEFAULT("ipv6.addr-gen-mode"),
+                                                    self,
+                                                    NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+                                                    NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT,
+                                                    -1);
+    if (c != -1)
+        addr_gen_mode = c;
+
+    if (addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT)
+        addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY;
+    else if (addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_DEFAULT_OR_EUI64)
+        addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64;
+
+    nm_assert(NM_IN_SET(addr_gen_mode,
+                        NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
+                        NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY));
+
+    return addr_gen_mode;
+}
+
 static const char *
 _prop_get_x_cloned_mac_address(NMDevice *self, NMConnection *connection, gboolean is_wifi)
 {
@@ -2678,6 +2823,7 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co
     nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self));
     nm_l3_config_data_set_dns_over_tls(l3cd, _prop_get_connection_dns_over_tls(self));
     nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self));
+    nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self));
     return l3cd;
 }
 
@@ -2930,6 +3076,13 @@ _add_capabilities(NMDevice *self, NMDeviceCapabilities capabilities)
 /*****************************************************************************/
 
 static void
+_dev_ip_state_dnsmgr_update_pending_changed(NMDnsManager *dnsmgr, GParamSpec *pspec, NMDevice *self)
+{
+    _dev_ip_state_check(self, AF_INET);
+    _dev_ip_state_check(self, AF_INET6);
+}
+
+static void
 _dev_ip_state_req_timeout_cancel(NMDevice *self, int addr_family)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
@@ -3321,6 +3474,27 @@ got_ip_state:
             combinedip_state = priv->ip_data.state;
     }
 
+    if (combinedip_state == NM_DEVICE_IP_STATE_READY
+        && priv->ip_data.state <= NM_DEVICE_IP_STATE_PENDING
+        && nm_dns_manager_get_update_pending(nm_manager_get_dns_manager(priv->manager))) {
+        /* We would be ready, but a DNS update is pending. That prevents us from getting fully ready. */
+        if (priv->ip_data.dnsmgr_update_pending_signal_id == 0) {
+            priv->ip_data.dnsmgr_update_pending_signal_id =
+                g_signal_connect(nm_manager_get_dns_manager(priv->manager),
+                                 "notify::" NM_DNS_MANAGER_UPDATE_PENDING,
+                                 G_CALLBACK(_dev_ip_state_dnsmgr_update_pending_changed),
+                                 self);
+            _LOGT_ip(AF_UNSPEC,
+                     "check-state: (combined) state: wait for DNS before becoming ready");
+        }
+        combinedip_state = NM_DEVICE_IP_STATE_PENDING;
+    }
+    if (combinedip_state != NM_DEVICE_IP_STATE_PENDING
+        && priv->ip_data.dnsmgr_update_pending_signal_id != 0) {
+        nm_clear_g_signal_handler(nm_manager_get_dns_manager(priv->manager),
+                                  &priv->ip_data.dnsmgr_update_pending_signal_id);
+    }
+
     _LOGT_ip(AF_UNSPEC,
              "check-state: (combined) state %s => %s",
              nm_device_ip_state_to_string(priv->ip_data.state),
@@ -5896,43 +6070,16 @@ find_slave_info(NMDevice *self, NMDevice *slave)
     return NULL;
 }
 
-/**
- * nm_device_master_enslave_slave:
- * @self: the master device
- * @slave: the slave device to enslave
- * @connection: (allow-none): the slave device's connection
- *
- * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
- * etc) then this function enslaves @slave.
- *
- * Returns: %TRUE on success, %FALSE on failure or if this device cannot enslave
- *  other devices.
- */
-static gboolean
-nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection)
+static void
+attach_port_done(NMDevice *self, NMDevice *slave, gboolean success)
 {
     SlaveInfo *info;
-    gboolean   success = FALSE;
-    gboolean   configure;
-
-    g_return_val_if_fail(self != NULL, FALSE);
-    g_return_val_if_fail(slave != NULL, FALSE);
-    g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->enslave_slave != NULL, FALSE);
 
     info = find_slave_info(self, slave);
     if (!info)
-        return FALSE;
+        return;
 
-    if (info->slave_is_enslaved)
-        success = TRUE;
-    else {
-        configure = (info->configure && connection != NULL);
-        if (configure)
-            g_return_val_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
-
-        success = NM_DEVICE_GET_CLASS(self)->enslave_slave(self, slave, connection, configure);
-        info->slave_is_enslaved = success;
-    }
+    info->slave_is_enslaved = success;
 
     nm_device_slave_notify_enslave(info->slave, success);
 
@@ -5952,8 +6099,71 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
      */
     if (success)
         nm_device_activate_schedule_stage3_ip_config(self, FALSE);
+}
 
-    return success;
+static void
+attach_port_cb(NMDevice *self, GError *error, gpointer user_data)
+{
+    NMDevice  *slave = user_data;
+    SlaveInfo *info;
+
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    info = find_slave_info(self, slave);
+    if (!info)
+        return;
+
+    nm_clear_g_cancellable(&info->cancellable);
+    attach_port_done(self, slave, !error);
+}
+
+/**
+ * nm_device_master_enslave_slave:
+ * @self: the master device
+ * @slave: the slave device to enslave
+ * @connection: (allow-none): the slave device's connection
+ *
+ * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
+ * etc) then this function enslaves @slave.
+ */
+static void
+nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *connection)
+{
+    SlaveInfo *info;
+    NMTernary  success;
+    gboolean   configure;
+
+    g_return_if_fail(self);
+    g_return_if_fail(slave);
+    g_return_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port);
+
+    info = find_slave_info(self, slave);
+    if (!info)
+        return;
+
+    if (info->slave_is_enslaved)
+        success = TRUE;
+    else {
+        configure = (info->configure && connection != NULL);
+        if (configure)
+            g_return_if_fail(nm_device_get_state(slave) >= NM_DEVICE_STATE_DISCONNECTED);
+
+        nm_clear_g_cancellable(&info->cancellable);
+        info->cancellable = g_cancellable_new();
+        success           = NM_DEVICE_GET_CLASS(self)->attach_port(self,
+                                                         slave,
+                                                         connection,
+                                                         configure,
+                                                         info->cancellable,
+                                                         attach_port_cb,
+                                                         slave);
+
+        if (success == NM_TERNARY_DEFAULT)
+            return;
+    }
+
+    attach_port_done(self, slave, success);
 }
 
 /**
@@ -5986,7 +6196,7 @@ nm_device_master_release_slave(NMDevice           *self,
                         RELEASE_SLAVE_TYPE_NO_CONFIG,
                         RELEASE_SLAVE_TYPE_CONFIG,
                         RELEASE_SLAVE_TYPE_CONFIG_FORCE));
-    g_return_if_fail(NM_DEVICE_GET_CLASS(self)->release_slave != NULL);
+    g_return_if_fail(NM_DEVICE_GET_CLASS(self)->detach_port != NULL);
 
     info = find_slave_info(self, slave);
 
@@ -6007,13 +6217,14 @@ nm_device_master_release_slave(NMDevice           *self,
 
     g_return_if_fail(self == slave_priv->master);
     nm_assert(slave == info->slave);
+    nm_clear_g_cancellable(&info->cancellable);
 
     /* first, let subclasses handle the release ... */
     if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave)
         || release_type >= RELEASE_SLAVE_TYPE_CONFIG_FORCE)
-        NM_DEVICE_GET_CLASS(self)->release_slave(self,
-                                                 slave,
-                                                 release_type >= RELEASE_SLAVE_TYPE_CONFIG);
+        NM_DEVICE_GET_CLASS(self)->detach_port(self,
+                                               slave,
+                                               release_type >= RELEASE_SLAVE_TYPE_CONFIG);
 
     /* raise notifications about the release, including clearing is_enslaved. */
     nm_device_slave_notify_release(slave, reason);
@@ -6344,7 +6555,7 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink)
                                        NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
     }
 
-    if (master && NM_DEVICE_GET_CLASS(master)->enslave_slave) {
+    if (master && NM_DEVICE_GET_CLASS(master)->attach_port) {
         nm_device_master_add_slave(master, self, FALSE);
         goto out;
     }
@@ -7540,8 +7751,9 @@ slave_state_changed(NMDevice           *slave,
     }
 
     if (release) {
-        configure = priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED
-                    && nm_device_sys_iface_state_get(slave) != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL;
+        configure = (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED
+                     && nm_device_sys_iface_state_get(slave) != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL)
+                    || nm_device_sys_iface_state_get(slave) == NM_DEVICE_SYS_IFACE_STATE_MANAGED;
 
         nm_device_master_release_slave(self,
                                        slave,
@@ -7577,7 +7789,7 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
 
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
     g_return_val_if_fail(NM_IS_DEVICE(slave), FALSE);
-    g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->enslave_slave != NULL, FALSE);
+    g_return_val_if_fail(NM_DEVICE_GET_CLASS(self)->attach_port, FALSE);
 
     priv       = NM_DEVICE_GET_PRIVATE(self);
     slave_priv = NM_DEVICE_GET_PRIVATE(slave);
@@ -7806,6 +8018,9 @@ nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason)
 
     g_return_if_fail(priv->master);
 
+    if (!priv->is_enslaved)
+        return;
+
     if (priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state <= NM_DEVICE_STATE_ACTIVATED) {
         switch (nm_device_state_reason_check(reason)) {
         case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
@@ -7835,14 +8050,12 @@ nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason)
     } else
         _LOGI(LOGD_DEVICE, "released from master device %s", nm_device_get_iface(priv->master));
 
-    if (priv->is_enslaved) {
-        priv->is_enslaved = FALSE;
+    priv->is_enslaved = FALSE;
 
-        _notify(self, PROP_MASTER);
+    _notify(self, PROP_MASTER);
 
-        nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref);
-        nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES);
-    }
+    nm_clear_pointer(&NM_DEVICE_GET_PRIVATE(priv->master)->ports_variant, g_variant_unref);
+    nm_gobject_notify_together(priv->master, PROP_PORTS, PROP_SLAVES);
 }
 
 /**
@@ -8161,13 +8374,13 @@ device_has_config(NMDevice *self)
 
     head_entry = nm_platform_lookup(
         nm_device_get_platform(self),
-        nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, pllink->ifindex));
+        nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, pllink->ifindex));
     if (head_entry)
         return TRUE;
 
     head_entry = nm_platform_lookup(
         nm_device_get_platform(self),
-        nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, pllink->ifindex));
+        nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, pllink->ifindex));
     if (head_entry)
         return TRUE;
 
@@ -9329,13 +9542,13 @@ lldp_setup(NMDevice *self, NMTernary enabled)
 static void
 _routing_rules_sync(NMDevice *self, NMTernary set_mode)
 {
-    NMDevicePrivate *priv               = NM_DEVICE_GET_PRIVATE(self);
-    NMPRouteManager *route_manager      = nm_netns_get_route_manager(nm_device_get_netns(self));
-    NMDeviceClass   *klass              = NM_DEVICE_GET_CLASS(self);
-    gboolean         untrack_only_dirty = FALSE;
-    gboolean         keep_deleted_rules;
-    gpointer         user_tag_1;
-    gpointer         user_tag_2;
+    NMDevicePrivate  *priv               = NM_DEVICE_GET_PRIVATE(self);
+    NMPGlobalTracker *global_tracker     = nm_netns_get_global_tracker(nm_device_get_netns(self));
+    NMDeviceClass    *klass              = NM_DEVICE_GET_CLASS(self);
+    gboolean          untrack_only_dirty = FALSE;
+    gboolean          keep_deleted_rules;
+    gpointer          user_tag_1;
+    gpointer          user_tag_2;
 
     /* take two arbitrary user-tag pointers that belong to @self. */
     user_tag_1 = &priv->v4_route_table;
@@ -9367,13 +9580,13 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode)
                 nm_ip_routing_rule_to_platform(rule, &plrule);
 
                 /* We track this rule, but we also make it explicitly not weakly-tracked
-                 * (meaning to untrack NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG at
+                 * (meaning to untrack NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG at
                  * the same time). */
-                nmp_route_manager_track_rule(route_manager,
-                                             &plrule,
-                                             10,
-                                             user_tag_1,
-                                             NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG);
+                nmp_global_tracker_track_rule(global_tracker,
+                                              &plrule,
+                                              10,
+                                              user_tag_1,
+                                              NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG);
             }
         }
 
@@ -9383,25 +9596,25 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode)
             extra_rules = klass->get_extra_rules(self);
             if (extra_rules) {
                 for (i = 0; i < extra_rules->len; i++) {
-                    nmp_route_manager_track_rule(
-                        route_manager,
+                    nmp_global_tracker_track_rule(
+                        global_tracker,
                         NMP_OBJECT_CAST_ROUTING_RULE(extra_rules->pdata[i]),
                         10,
                         user_tag_2,
-                        NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG);
+                        NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG);
                 }
             }
         }
     }
 
-    nmp_route_manager_untrack_all(route_manager, user_tag_1, !untrack_only_dirty, TRUE);
+    nmp_global_tracker_untrack_all(global_tracker, user_tag_1, !untrack_only_dirty, TRUE);
     if (klass->get_extra_rules)
-        nmp_route_manager_untrack_all(route_manager, user_tag_2, !untrack_only_dirty, TRUE);
+        nmp_global_tracker_untrack_all(global_tracker, user_tag_2, !untrack_only_dirty, TRUE);
 
     keep_deleted_rules = FALSE;
     if (set_mode == NM_TERNARY_DEFAULT) {
         /* when exiting NM, we leave the device up and the rules configured.
-         * We just call nmp_route_manager_sync() to forget about the synced rules,
+         * We just call nmp_global_tracker_sync() to forget about the synced rules,
          * but we don't actually delete them.
          *
          * FIXME: that is a problem after restart of NetworkManager, because these
@@ -9415,7 +9628,7 @@ _routing_rules_sync(NMDevice *self, NMTernary set_mode)
          * file and track them after restart again. */
         keep_deleted_rules = TRUE;
     }
-    nmp_route_manager_sync(route_manager, NMP_OBJECT_TYPE_ROUTING_RULE, keep_deleted_rules);
+    nmp_global_tracker_sync(global_tracker, NMP_OBJECT_TYPE_ROUTING_RULE, keep_deleted_rules);
 }
 
 static gboolean
@@ -10045,6 +10258,7 @@ _dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_d
                                             FALSE);
 
         if (notify_data->lease_update.accepted) {
+            nm_manager_write_device_state(priv->manager, self, NULL);
             if (priv->ipdhcp_data_x[IS_IPv4].state != NM_DEVICE_IP_STATE_READY) {
                 _dev_ipdhcpx_set_state(self, addr_family, NM_DEVICE_IP_STATE_READY);
                 nm_dispatcher_call_device(NM_DISPATCHER_ACTION_DHCP_CHANGE_X(IS_IPv4),
@@ -10190,7 +10404,11 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family)
             .vendor_class_identifier = vendor_class_identifier,
             .use_fqdn                = hostname_is_fqdn,
             .reject_servers          = reject_servers,
-            .v4.request_broadcast    = request_broadcast,
+            .v4 =
+                {
+                    .request_broadcast = request_broadcast,
+                    .acd_timeout_msec  = _prop_get_ipv4_dad_timeout(self),
+                },
         };
 
         priv->ipdhcp_data_4.client =
@@ -10205,22 +10423,25 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family)
         duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, &enforce_duid);
 
         config = (NMDhcpClientConfig){
-            .addr_family        = AF_INET6,
-            .l3cfg              = nm_device_get_l3cfg(self),
-            .iface              = nm_device_get_ip_iface(self),
-            .uuid               = nm_connection_get_uuid(connection),
-            .send_hostname      = nm_setting_ip_config_get_dhcp_send_hostname(s_ip),
-            .hostname           = nm_setting_ip_config_get_dhcp_hostname(s_ip),
-            .hostname_flags     = _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6),
-            .client_id          = duid,
-            .mud_url            = _prop_get_connection_mud_url(self, s_con),
-            .timeout            = no_lease_timeout_sec,
-            .anycast_address    = _device_get_dhcp_anycast_address(self),
-            .v6.enforce_duid    = enforce_duid,
-            .v6.iaid            = iaid,
-            .v6.iaid_explicit   = iaid_explicit,
-            .v6.info_only       = (priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF),
-            .v6.needed_prefixes = priv->ipdhcp_data_6.v6.needed_prefixes,
+            .addr_family     = AF_INET6,
+            .l3cfg           = nm_device_get_l3cfg(self),
+            .iface           = nm_device_get_ip_iface(self),
+            .uuid            = nm_connection_get_uuid(connection),
+            .send_hostname   = nm_setting_ip_config_get_dhcp_send_hostname(s_ip),
+            .hostname        = nm_setting_ip_config_get_dhcp_hostname(s_ip),
+            .hostname_flags  = _prop_get_ipvx_dhcp_hostname_flags(self, AF_INET6),
+            .client_id       = duid,
+            .mud_url         = _prop_get_connection_mud_url(self, s_con),
+            .timeout         = no_lease_timeout_sec,
+            .anycast_address = _device_get_dhcp_anycast_address(self),
+            .v6 =
+                {
+                    .enforce_duid  = enforce_duid,
+                    .iaid          = iaid,
+                    .iaid_explicit = iaid_explicit,
+                    .info_only     = (priv->ipdhcp_data_6.v6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF),
+                    .needed_prefixes = priv->ipdhcp_data_6.v6.needed_prefixes,
+                },
         };
 
         priv->ipdhcp_data_6.client =
@@ -10635,7 +10856,6 @@ _dev_ipll6_start(NMDevice *self)
 {
     NMDevicePrivate       *priv = NM_DEVICE_GET_PRIVATE(self);
     NMConnection          *connection;
-    NMSettingIP6Config    *s_ip6 = NULL;
     gboolean               assume;
     const char            *ifname;
     NML3IPv6LLState        llstate;
@@ -10656,14 +10876,10 @@ _dev_ipll6_start(NMDevice *self)
     }
 
     connection = nm_device_get_applied_connection(self);
-    if (connection)
-        s_ip6 = NM_SETTING_IP6_CONFIG(nm_connection_get_setting_ip6_config(connection));
 
     assume = nm_device_sys_iface_state_is_external_or_assume(self);
 
-    if (s_ip6
-        && nm_setting_ip6_config_get_addr_gen_mode(s_ip6)
-               == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
+    if (_prop_get_ipv6_addr_gen_mode(self) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY) {
         NMUtilsStableType stable_type;
         const char       *stable_id;
 
@@ -10866,9 +11082,10 @@ _commit_mtu(NMDevice *self)
 {
     NMDevicePrivate      *priv   = NM_DEVICE_GET_PRIVATE(self);
     NMDeviceMtuSource     source = NM_DEVICE_MTU_SOURCE_NONE;
+    NMSettingIPConfig    *s_ip6;
     const NML3ConfigData *l3cd;
     guint32               ip6_mtu_orig;
-    guint32               ip6_mtu;
+    guint32               ip6_mtu = 0;
     guint32               mtu_desired_orig;
     guint32               mtu_desired;
     guint32               mtu_plat;
@@ -10953,10 +11170,9 @@ _commit_mtu(NMDevice *self)
         }
     }
 
-    if (mtu_desired && mtu_desired < 1280) {
-        NMSettingIPConfig *s_ip6;
+    s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG);
 
-        s_ip6 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP6_CONFIG);
+    if (mtu_desired && mtu_desired < 1280) {
         if (s_ip6
             && !NM_IN_STRSET(nm_setting_ip_config_get_method(s_ip6),
                              NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
@@ -10971,7 +11187,12 @@ _commit_mtu(NMDevice *self)
         }
     }
 
-    ip6_mtu = priv->ip6_mtu;
+    if (s_ip6)
+        ip6_mtu = nm_setting_ip6_config_get_mtu(NM_SETTING_IP6_CONFIG(s_ip6));
+
+    if (!ip6_mtu)
+        ip6_mtu = priv->ip6_mtu;
+
     if (!ip6_mtu && priv->mtu_source == NM_DEVICE_MTU_SOURCE_NONE) {
         /* initially, if the IPv6 MTU is not specified, grow it as large as the
          * link MTU @mtu_desired. Only exception is, if @mtu_desired is so small
@@ -11353,7 +11574,7 @@ _dev_ipac6_start(NMDevice *self)
             .ifname                       = nm_device_get_ip_iface(self),
             .stable_type                  = stable_type,
             .network_id                   = stable_id,
-            .addr_gen_mode                = nm_setting_ip6_config_get_addr_gen_mode(s_ip),
+            .addr_gen_mode                = _prop_get_ipv6_addr_gen_mode(self),
             .node_type                    = node_type,
             .max_addresses                = max_addresses,
             .router_solicitations         = router_solicitations,
@@ -11640,11 +11861,14 @@ activate_stage3_ip_config_for_addr_family(NMDevice *self, int addr_family, const
         goto out_devip;
 
     if (IS_IPv4) {
+        if (_prop_get_ipv4_link_local(self) == NM_SETTING_IP4_LL_ENABLED)
+            _dev_ipll4_start(self);
+
         if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
             _dev_ipdhcpx_start(self, AF_INET);
-        else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
-            _dev_ipll4_start(self);
-        else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+        else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) {
+            /* pass */
+        } else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
             _dev_ipshared4_start(self);
         else if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
             priv->ip_data_x[IS_IPv4].is_disabled = TRUE;
@@ -12344,7 +12568,8 @@ delete_on_deactivate_check_and_schedule(NMDevice *self)
 static void
 _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gboolean from_reapply)
 {
-    const int IS_IPv4 = NM_IS_IPv4(addr_family);
+    const int        IS_IPv4 = NM_IS_IPv4(addr_family);
+    NMDevicePrivate *priv    = NM_DEVICE_GET_PRIVATE(self);
 
     _dev_ipsharedx_cleanup(self, addr_family);
 
@@ -12360,6 +12585,9 @@ _cleanup_ip_pre(NMDevice *self, int addr_family, CleanupType cleanup_type, gbool
 
     _dev_ipmanual_cleanup(self);
 
+    nm_clear_g_signal_handler(nm_manager_get_dns_manager(priv->manager),
+                              &priv->ip_data.dnsmgr_update_pending_signal_id);
+
     _dev_ip_state_cleanup(self, AF_UNSPEC, from_reapply);
     _dev_ip_state_cleanup(self, addr_family, from_reapply);
 }
@@ -12479,12 +12707,15 @@ can_reapply_change(NMDevice   *self,
                                                  NM_SETTING_CONNECTION_STABLE_ID,
                                                  NM_SETTING_CONNECTION_AUTOCONNECT,
                                                  NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
+                                                 NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY,
                                                  NM_SETTING_CONNECTION_ZONE,
                                                  NM_SETTING_CONNECTION_METERED,
                                                  NM_SETTING_CONNECTION_LLDP,
                                                  NM_SETTING_CONNECTION_MDNS,
                                                  NM_SETTING_CONNECTION_LLMNR,
-                                                 NM_SETTING_CONNECTION_DNS_OVER_TLS);
+                                                 NM_SETTING_CONNECTION_DNS_OVER_TLS,
+                                                 NM_SETTING_CONNECTION_MPTCP_FLAGS,
+                                                 NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY);
     }
 
     if (NM_IN_STRSET(setting_name,
@@ -12700,7 +12931,8 @@ check_and_reapply_connection(NMDevice     *self,
                 NM_SETTING_CONNECTION_LLDP,
                 NM_SETTING_CONNECTION_MDNS,
                 NM_SETTING_CONNECTION_LLMNR,
-                NM_SETTING_CONNECTION_DNS_OVER_TLS)) {
+                NM_SETTING_CONNECTION_DNS_OVER_TLS,
+                NM_SETTING_CONNECTION_MPTCP_FLAGS)) {
             priv->ip_data_4.do_reapply = TRUE;
             priv->ip_data_6.do_reapply = TRUE;
         }
@@ -13361,29 +13593,71 @@ _dispatcher_cleanup(NMDevice *self)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
+    nm_clear_g_source_inst(&priv->delay_activation_source);
+
     if (!priv->dispatcher.call_id)
         return FALSE;
 
     nm_dispatcher_call_cancel(g_steal_pointer(&priv->dispatcher.call_id));
     priv->dispatcher.post_state        = NM_DEVICE_STATE_UNKNOWN;
     priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
+
     return TRUE;
 }
 
 static void
-_dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data)
+_queue_dispatcher_post_state(NMDevice *self)
 {
-    NMDevice        *self = NM_DEVICE(user_data);
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
 
-    g_return_if_fail(call_id == priv->dispatcher.call_id);
-
-    priv->dispatcher.call_id = NULL;
     nm_device_queue_state(self, priv->dispatcher.post_state, priv->dispatcher.post_state_reason);
     priv->dispatcher.post_state        = NM_DEVICE_STATE_UNKNOWN;
     priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
 }
 
+static gboolean
+_wait_activation_delay_timeout(gpointer user_data)
+{
+    NMDevice        *self = user_data;
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
+
+    nm_clear_g_source_inst(&priv->delay_activation_source);
+
+    _LOGD(LOGD_DEVICE, "finished waiting on activation delay");
+    _queue_dispatcher_post_state(self);
+
+    return G_SOURCE_REMOVE;
+}
+
+static void
+_dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_data)
+{
+    NMDevice            *self = NM_DEVICE(user_data);
+    NMDevicePrivate     *priv = NM_DEVICE_GET_PRIVATE(self);
+    NMConnection        *conn;
+    NMSettingConnection *s_conn;
+    gint32               delay_timeout;
+
+    g_return_if_fail(call_id == priv->dispatcher.call_id);
+    nm_assert(!priv->delay_activation_source);
+
+    priv->dispatcher.call_id = NULL;
+    conn                     = nm_device_get_applied_connection(self);
+    if (conn) {
+        s_conn = nm_connection_get_setting_connection(conn);
+        if (s_conn) {
+            delay_timeout = nm_setting_connection_get_wait_activation_delay(s_conn);
+            if (delay_timeout > 0) {
+                priv->delay_activation_source =
+                    nm_g_timeout_add_source(delay_timeout, _wait_activation_delay_timeout, self);
+                return;
+            }
+        }
+    }
+
+    _queue_dispatcher_post_state(self);
+}
+
 /*****************************************************************************/
 
 static void
@@ -13437,14 +13711,14 @@ spawn_ping(NMDevice *self)
     gs_free char         *str_timeout = NULL;
     gs_free char         *tmp_str     = NULL;
     const char           *args[]      = {priv->gw_ping.binary,
-                          "-I",
-                          nm_device_get_ip_iface(self),
-                          "-c",
-                          "1",
-                          "-w",
-                          NULL,
-                          priv->gw_ping.address,
-                          NULL};
+                                         "-I",
+                                         nm_device_get_ip_iface(self),
+                                         "-c",
+                                         "1",
+                                         "-w",
+                                         NULL,
+                                         priv->gw_ping.address,
+                                         NULL};
     gs_free_error GError *error       = NULL;
     gboolean              ret;
 
@@ -13665,10 +13939,38 @@ _get_carrier_wait_ms(NMDevice *self)
                                                   CARRIER_WAIT_TIME_MS);
 }
 
+/*
+ * Devices that support carrier detect must be IFF_UP to report carrier
+ * changes; so after setting the device IFF_UP we must suppress startup
+ * complete (via a pending action) until either the carrier turns on, or
+ * a timeout is reached.
+ */
+static void
+carrier_detect_wait(NMDevice *self)
+{
+    NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
+    gint64           now_ms, until_ms;
+
+    if (!nm_device_has_capability(self, NM_DEVICE_CAP_CARRIER_DETECT))
+        return;
+
+    /* we start a grace period of 5 seconds during which we will schedule
+     * a pending action whenever we have no carrier.
+     *
+     * If during that time carrier goes away, we declare the interface
+     * as not ready. */
+    nm_clear_g_source(&priv->carrier_wait_id);
+    if (!priv->carrier)
+        nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE);
+
+    now_ms   = nm_utils_get_monotonic_timestamp_msec();
+    until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_ms);
+    priv->carrier_wait_id = g_timeout_add(until_ms - now_ms, carrier_wait_timeout, self);
+}
+
 gboolean
 nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware)
 {
-    NMDevicePrivate     *priv         = NM_DEVICE_GET_PRIVATE(self);
     gboolean             device_is_up = FALSE;
     NMDeviceCapabilities capabilities;
     int                  ifindex;
@@ -13724,27 +14026,7 @@ nm_device_bring_up(NMDevice *self, gboolean block, gboolean *no_firmware)
         capabilities |= NM_DEVICE_GET_CLASS(self)->get_generic_capabilities(self);
     _add_capabilities(self, capabilities);
 
-    /* Devices that support carrier detect must be IFF_UP to report carrier
-     * changes; so after setting the device IFF_UP we must suppress startup
-     * complete (via a pending action) until either the carrier turns on, or
-     * a timeout is reached.
-     */
-    if (nm_device_has_capability(self, NM_DEVICE_CAP_CARRIER_DETECT)) {
-        gint64 now_ms, until_ms;
-
-        /* we start a grace period of 5 seconds during which we will schedule
-         * a pending action whenever we have no carrier.
-         *
-         * If during that time carrier goes away, we declare the interface
-         * as not ready. */
-        nm_clear_g_source(&priv->carrier_wait_id);
-        if (!priv->carrier)
-            nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE);
-
-        now_ms   = nm_utils_get_monotonic_timestamp_msec();
-        until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_ms);
-        priv->carrier_wait_id = g_timeout_add(until_ms - now_ms, carrier_wait_timeout, self);
-    }
+    carrier_detect_wait(self);
 
     /* Can only get HW address of some devices when they are up */
     nm_device_update_hw_address(self);
@@ -14997,6 +15279,9 @@ _cancel_activation(NMDevice *self)
     _dispatcher_cleanup(self);
     ip_check_gw_ping_cleanup(self);
 
+    _dev_ip_state_cleanup(self, AF_INET, FALSE);
+    _dev_ip_state_cleanup(self, AF_INET6, FALSE);
+
     /* Break the activation chain */
     activation_source_clear(self);
 }
@@ -15500,6 +15785,13 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
                 if (!nm_device_bring_up(self, TRUE, &no_firmware) && no_firmware)
                     _LOGW(LOGD_PLATFORM, "firmware may be missing.");
                 nm_device_set_firmware_missing(self, no_firmware ? TRUE : FALSE);
+            } else {
+                /* We didn't bring the device up and we have little idea
+                 * when was it brought up. Play it safe and assume it could
+                 * have been brought up very recently and it might one of
+                 * those who take time to detect carrier.
+                 */
+                carrier_detect_wait(self);
             }
 
             /* Ensure the device gets deactivated in response to stuff like
@@ -16804,7 +17096,7 @@ get_address_for_hostname_dns_lookup(NMDevice *self, int addr_family)
      *   opposed to an address that is configured? */
     head_entry = nm_platform_lookup(
         nm_device_get_platform(self),
-        nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex));
+        nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex));
 
     if (head_entry) {
         c_list_for_each_entry (iter, &head_entry->lst_entries_head, lst_entries) {