summary refs log tree commit diff
path: root/src/core
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2022-05-04 15:35:24 +0200
committerMichael Biebl <biebl@debian.org>2022-05-04 15:35:24 +0200
commit9959fdb2e8ddd06f2161798ca0a39c77d67c652d (patch)
tree2ce24a336d2b1c5fd5dec3090db312eded6c78ba /src/core
parent8c623dddbdebe354cb94bfc559a5371a14865317 (diff)
New upstream version 1.37.92 upstream/1.37.92
Diffstat (limited to 'src/core')
-rw-r--r--src/core/NetworkManagerUtils.c18
-rw-r--r--src/core/NetworkManagerUtils.h30
-rw-r--r--src/core/devices/bluetooth/nm-bluez-manager.c1
-rw-r--r--src/core/devices/nm-device-bond.c24
-rw-r--r--src/core/devices/nm-device-bridge.c124
-rw-r--r--src/core/devices/nm-device-ethernet.c2
-rw-r--r--src/core/devices/nm-device-private.h2
-rw-r--r--src/core/devices/nm-device-tun.c6
-rw-r--r--src/core/devices/nm-device.c223
-rw-r--r--src/core/devices/nm-device.h53
-rw-r--r--src/core/devices/ovs/nm-device-ovs-interface.c95
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c53
-rw-r--r--src/core/devices/wifi/nm-device-iwd-p2p.c4
-rw-r--r--src/core/devices/wifi/nm-device-iwd.c4
-rw-r--r--src/core/devices/wifi/nm-device-wifi-p2p.c4
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c133
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.c1
-rw-r--r--src/core/devices/wifi/nm-wifi-ap.c7
-rw-r--r--src/core/devices/wifi/nm-wifi-p2p-peer.c9
-rw-r--r--src/core/devices/wwan/nm-device-modem.c8
-rw-r--r--src/core/devices/wwan/nm-modem-broadband.c2
-rw-r--r--src/core/devices/wwan/nm-modem-ofono.c2
-rw-r--r--src/core/dhcp/nm-dhcp-client.c32
-rw-r--r--src/core/dhcp/nm-dhcp-dhcpcd.c3
-rw-r--r--src/core/dhcp/nm-dhcp-nettools.c27
-rw-r--r--src/core/dhcp/nm-dhcp-systemd.c103
-rw-r--r--src/core/dhcp/nm-dhcp-utils.c38
-rw-r--r--src/core/dns/nm-dns-dnsmasq.c10
-rw-r--r--src/core/dns/nm-dns-manager.c73
-rw-r--r--src/core/dns/nm-dns-manager.h1
-rw-r--r--src/core/dns/nm-dns-plugin.c4
-rw-r--r--src/core/dns/nm-dns-plugin.h2
-rw-r--r--src/core/dns/nm-dns-systemd-resolved.c2
-rw-r--r--src/core/dns/nm-dns-unbound.c2
-rw-r--r--src/core/dnsmasq/nm-dnsmasq-manager.c7
-rw-r--r--src/core/main.c4
-rw-r--r--src/core/ndisc/nm-ndisc.c3
-rw-r--r--src/core/nm-checkpoint-manager.c9
-rw-r--r--src/core/nm-checkpoint.c2
-rw-r--r--src/core/nm-config.c7
-rw-r--r--src/core/nm-connectivity.c209
-rw-r--r--src/core/nm-core-utils.c90
-rw-r--r--src/core/nm-core-utils.h3
-rw-r--r--src/core/nm-dbus-manager.c15
-rw-r--r--src/core/nm-dbus-manager.h3
-rw-r--r--src/core/nm-firewall-utils.c4
-rw-r--r--src/core/nm-l3-config-data.c38
-rw-r--r--src/core/nm-l3-config-data.h1
-rw-r--r--src/core/nm-l3-ipv4ll.c13
-rw-r--r--src/core/nm-l3-ipv6ll.c9
-rw-r--r--src/core/nm-l3cfg.c50
-rw-r--r--src/core/nm-l3cfg.h9
-rw-r--r--src/core/nm-manager.c655
-rw-r--r--src/core/nm-manager.h1
-rw-r--r--src/core/nm-pacrunner-manager.c4
-rw-r--r--src/core/nm-policy.c59
-rw-r--r--src/core/nm-rfkill-manager.c264
-rw-r--r--src/core/nm-rfkill-manager.h44
-rw-r--r--src/core/nm-session-monitor.c7
-rw-r--r--src/core/nm-test-utils-core.h14
-rw-r--r--src/core/platform/tests/test-common.c16
-rw-r--r--src/core/platform/tests/test-platform-general.c545
-rw-r--r--src/core/platform/tests/test-route.c28
-rw-r--r--src/core/ppp/nm-ppp-manager.c2
-rw-r--r--src/core/settings/nm-secret-agent.c2
-rw-r--r--src/core/settings/nm-settings-connection.c43
-rw-r--r--src/core/settings/nm-settings-connection.h1
-rw-r--r--src/core/settings/nm-settings.c88
-rw-r--r--src/core/settings/nm-settings.h3
-rw-r--r--src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c28
-rw-r--r--src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c2
-rw-r--r--src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c5
-rw-r--r--src/core/supplicant/nm-supplicant-config.c51
-rw-r--r--src/core/supplicant/nm-supplicant-config.h1
-rw-r--r--src/core/supplicant/nm-supplicant-interface.c28
-rw-r--r--src/core/supplicant/nm-supplicant-manager.c9
-rw-r--r--src/core/supplicant/nm-supplicant-types.h6
-rw-r--r--src/core/supplicant/tests/test-supplicant-config.c1
-rw-r--r--src/core/tests/test-core.c29
-rw-r--r--src/core/tests/test-l3cfg.c14
-rw-r--r--src/core/tests/test-utils.c47
81 files changed, 2187 insertions, 1388 deletions
diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c
index c50dbada..5727aac8 100644
--- a/src/core/NetworkManagerUtils.c
+++ b/src/core/NetworkManagerUtils.c
@@ -34,6 +34,11 @@
 
 /*****************************************************************************/
 
+G_STATIC_ASSERT(NM_SHUTDOWN_TIMEOUT_1500_MSEC <= NM_SHUTDOWN_TIMEOUT_MAX_MSEC);
+G_STATIC_ASSERT(NM_SHUTDOWN_TIMEOUT_5000_MSEC <= NM_SHUTDOWN_TIMEOUT_MAX_MSEC);
+
+/*****************************************************************************/
+
 /**
  * nm_utils_get_shared_wifi_permission:
  * @connection: the NMConnection to lookup the permission.
@@ -1037,7 +1042,7 @@ _shutdown_waitobj_cb(gpointer user_data, GObject *where_the_object_was)
  * is still used.
  *
  * If @wait_type is %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE, then during shutdown
- * (after %NM_SHUTDOWN_TIMEOUT_MS), the cancellable will be cancelled to notify
+ * (after %NM_SHUTDOWN_TIMEOUT_MAX_MSEC), the cancellable will be cancelled to notify
  * the source of the shutdown. Note that otherwise, in this mode also @watched_obj
  * is only tracked with a weak-pointer. Especially, it does not register to the
  * "cancelled" signal to automatically unregister (otherwise, you would never
@@ -1046,7 +1051,7 @@ _shutdown_waitobj_cb(gpointer user_data, GObject *where_the_object_was)
  * FIXME(shutdown): proper shutdown is not yet implemented, and registering
  *   an object (currently) has no effect.
  *
- * FIXME(shutdown): during shutdown, after %NM_SHUTDOWN_TIMEOUT_MS timeout, cancel
+ * FIXME(shutdown): during shutdown, after %NM_SHUTDOWN_TIMEOUT_MAX_MSEC timeout, cancel
  *   all remaining %NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE instances. Also, when somebody
  *   enqueues a cancellable after that point, cancel it right away on an idle handler.
  *
@@ -1347,8 +1352,13 @@ nm_utils_ip_route_attribute_to_platform(int                addr_family,
         int type;
 
         type = nm_net_aux_rtnl_rtntype_a2n(g_variant_get_string(variant, NULL));
-        nm_assert(
-            NM_IN_SET(type, RTN_UNICAST, RTN_LOCAL, RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT));
+        nm_assert(NM_IN_SET(type,
+                            RTN_UNICAST,
+                            RTN_LOCAL,
+                            RTN_BLACKHOLE,
+                            RTN_UNREACHABLE,
+                            RTN_PROHIBIT,
+                            RTN_THROW));
 
         r->type_coerced = nm_platform_route_type_coerce(type);
     } else
diff --git a/src/core/NetworkManagerUtils.h b/src/core/NetworkManagerUtils.h
index c9210ba8..67c9cba4 100644
--- a/src/core/NetworkManagerUtils.h
+++ b/src/core/NetworkManagerUtils.h
@@ -103,23 +103,37 @@ NMPlatformRoutingRule *nm_ip_routing_rule_to_platform(const NMIPRoutingRule *rul
 /*****************************************************************************/
 
 /* during shutdown, there are two relevant timeouts. One is
- * NM_SHUTDOWN_TIMEOUT_MS which is plenty of time, that we give for all
+ * NM_SHUTDOWN_TIMEOUT_MAX_MSEC which is plenty of time, that we give for all
  * actions to complete. Of course, during shutdown components should hurry
  * to cleanup.
  *
  * When we initiate shutdown, we should start killing child processes
- * with SIGTERM. If they don't complete within NM_SHUTDOWN_TIMEOUT_MS, we send
+ * with SIGTERM. If they don't complete within NM_SHUTDOWN_TIMEOUT_MAX_MSEC, we send
  * SIGKILL.
  *
- * After NM_SHUTDOWN_TIMEOUT_MS, NetworkManager will however not yet terminate right
- * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG. This
+ * After NM_SHUTDOWN_TIMEOUT_MAX_MSEC, NetworkManager will however not yet terminate right
+ * away. It iterates the mainloop for another NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC. This
  * should give time to reap the child process (after SIGKILL).
  *
  * So, the maximum time we should wait before sending SIGKILL should be at most
- * NM_SHUTDOWN_TIMEOUT_MS.
+ * NM_SHUTDOWN_TIMEOUT_MAX_MSEC.
  */
-#define NM_SHUTDOWN_TIMEOUT_MS          1500
-#define NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG 500
+#define NM_SHUTDOWN_TIMEOUT_MAX_MSEC        5000
+#define NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC 500
+
+/**
+ * NM_SHUTDOWN_TIMEOUT_1500_MSEC: this is just 1500 msec. The special
+ *   thing about the define is that you are guaranteed that this is not
+ *   longer than NM_SHUTDOWN_TIMEOUT_MAX_MSEC.
+ *   When you perform an async operation, it must either be cancellable
+ *   (and complete fast) or never take longer than NM_SHUTDOWN_TIMEOUT_MAX_MSEC.
+ *   The usage of this macro makes that relation to NM_SHUTDOWN_TIMEOUT_MAX_MSEC
+ *   explicit.
+ */
+#define NM_SHUTDOWN_TIMEOUT_1500_MSEC 1500
+
+/* See NM_SHUTDOWN_TIMEOUT_1500_MSEC. */
+#define NM_SHUTDOWN_TIMEOUT_5000_MSEC 5000
 
 typedef enum {
     /* There is no watched_obj argument, and the shutdown is delayed until the user
@@ -131,7 +145,7 @@ typedef enum {
     NM_SHUTDOWN_WAIT_TYPE_OBJECT,
 
     /* The watched_obj argument is a GCancellable, and shutdown is delayed until the object
-     * gets destroyed (or unregistered). Note that after NM_SHUTDOWN_TIMEOUT_MS, the
+     * gets destroyed (or unregistered). Note that after NM_SHUTDOWN_TIMEOUT_MAX_MSEC, the
      * cancellable will be cancelled to notify listeners about the shutdown. */
     NM_SHUTDOWN_WAIT_TYPE_CANCELLABLE,
 } NMShutdownWaitType;
diff --git a/src/core/devices/bluetooth/nm-bluez-manager.c b/src/core/devices/bluetooth/nm-bluez-manager.c
index 05f85297..6bc00faf 100644
--- a/src/core/devices/bluetooth/nm-bluez-manager.c
+++ b/src/core/devices/bluetooth/nm-bluez-manager.c
@@ -1352,6 +1352,7 @@ _conn_create_panu_connection(NMBluezManager *self, BzDBusObj *bzobj)
           bzobj->d_device.address);
 
     nm_settings_add_connection(priv->settings,
+                               NULL,
                                connection,
                                NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
                                NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index 56b729e9..16896d57 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -37,17 +37,17 @@
         NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,       \
         NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY
 
-#define OPTIONS_REAPPLY_SUBSET                                                            \
-    NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY,                        \
-        NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL,            \
-        NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY,              \
-        NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO, \
-        NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS, \
-        NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, NM_SETTING_BOND_OPTION_LP_INTERVAL,         \
-        NM_SETTING_BOND_OPTION_MIN_LINKS, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE,       \
-        NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, NM_SETTING_BOND_OPTION_RESEND_IGMP,      \
-        NM_SETTING_BOND_OPTION_USE_CARRIER, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,      \
-        NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY
+#define OPTIONS_REAPPLY_SUBSET                                                             \
+    NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY,                         \
+        NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_ARP_INTERVAL,             \
+        NM_SETTING_BOND_OPTION_ARP_VALIDATE, NM_SETTING_BOND_OPTION_PRIMARY,               \
+        NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO,  \
+        NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS,  \
+        NM_SETTING_BOND_OPTION_LP_INTERVAL, NM_SETTING_BOND_OPTION_MIN_LINKS,              \
+        NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \
+        NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_USE_CARRIER,            \
+        NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP,      \
+        NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY
 
 #define OPTIONS_REAPPLY_FULL                                     \
     OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \
@@ -373,7 +373,7 @@ apply_bonding_config(NMDeviceBond *self)
                                                             NM_SETTING_BOND_OPTION_MODE);
     /* Need to release all slaves before we can change bond mode */
     if (!nm_streq0(device_bond_mode, mode_str))
-        nm_device_master_release_slaves(device);
+        nm_device_master_release_slaves_all(device);
 
     set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_MODE);
 
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index 4e4c2637..f11c172a 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -22,11 +22,17 @@
 
 /*****************************************************************************/
 
+enum _NMBtCbState {
+    _NM_BT_CB_STATE_NONE    = 0, /* Registration not done    */
+    _NM_BT_CB_STATE_WAIT    = 1, /* Waiting for the callback */
+    _NM_BT_CB_STATE_SUCCESS = 2, /* Callback succeeded       */
+};
+
 struct _NMDeviceBridge {
     NMDevice      parent;
     GCancellable *bt_cancellable;
     bool          vlan_configured : 1;
-    bool          bt_registered : 1;
+    unsigned      bt_cb_state : 2;
 };
 
 struct _NMDeviceBridgeClass {
@@ -76,7 +82,8 @@ check_connection_available(NMDevice                      *device,
         if (!nm_bt_vtable_network_server->is_available(
                 nm_bt_vtable_network_server,
                 bdaddr,
-                (self->bt_cancellable || self->bt_registered) ? device : NULL)) {
+                (self->bt_cancellable || self->bt_cb_state != _NM_BT_CB_STATE_NONE) ? device
+                                                                                    : NULL)) {
             if (bdaddr)
                 nm_utils_error_set(error,
                                    NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
@@ -791,23 +798,68 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge)
     return TRUE;
 }
 
+static void
+_platform_lnk_bridge_init_from_setting(NMSettingBridge *s_bridge, NMPlatformLnkBridge *props)
+{
+    *props = (NMPlatformLnkBridge){
+        .forward_delay = _DEFAULT_IF_ZERO(nm_setting_bridge_get_forward_delay(s_bridge) * 100u,
+                                          NM_BRIDGE_FORWARD_DELAY_DEF_SYS),
+        .hello_time    = _DEFAULT_IF_ZERO(nm_setting_bridge_get_hello_time(s_bridge) * 100u,
+                                       NM_BRIDGE_HELLO_TIME_DEF_SYS),
+        .max_age       = _DEFAULT_IF_ZERO(nm_setting_bridge_get_max_age(s_bridge) * 100u,
+                                    NM_BRIDGE_MAX_AGE_DEF_SYS),
+        .ageing_time   = nm_setting_bridge_get_ageing_time(s_bridge) * 100u,
+        .stp_state     = nm_setting_bridge_get_stp(s_bridge),
+        .priority      = nm_setting_bridge_get_priority(s_bridge),
+        .vlan_protocol = to_sysfs_vlan_protocol_sys(nm_setting_bridge_get_vlan_protocol(s_bridge)),
+        .vlan_stats_enabled = nm_setting_bridge_get_vlan_stats_enabled(s_bridge),
+        .group_fwd_mask     = nm_setting_bridge_get_group_forward_mask(s_bridge),
+        .mcast_snooping     = nm_setting_bridge_get_multicast_snooping(s_bridge),
+        .mcast_router =
+            to_sysfs_multicast_router_sys(nm_setting_bridge_get_multicast_router(s_bridge)),
+        .mcast_query_use_ifaddr    = nm_setting_bridge_get_multicast_query_use_ifaddr(s_bridge),
+        .mcast_querier             = nm_setting_bridge_get_multicast_querier(s_bridge),
+        .mcast_hash_max            = nm_setting_bridge_get_multicast_hash_max(s_bridge),
+        .mcast_last_member_count   = nm_setting_bridge_get_multicast_last_member_count(s_bridge),
+        .mcast_startup_query_count = nm_setting_bridge_get_multicast_startup_query_count(s_bridge),
+        .mcast_last_member_interval =
+            nm_setting_bridge_get_multicast_last_member_interval(s_bridge),
+        .mcast_membership_interval = nm_setting_bridge_get_multicast_membership_interval(s_bridge),
+        .mcast_querier_interval    = nm_setting_bridge_get_multicast_querier_interval(s_bridge),
+        .mcast_query_interval      = nm_setting_bridge_get_multicast_query_interval(s_bridge),
+        .mcast_query_response_interval =
+            nm_setting_bridge_get_multicast_query_response_interval(s_bridge),
+        .mcast_startup_query_interval =
+            nm_setting_bridge_get_multicast_startup_query_interval(s_bridge),
+    };
+
+    to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props->group_addr);
+}
+
 static NMActStageReturn
 act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
-    NMConnection *connection;
-    NMSetting    *s_bridge;
-    const Option *option;
+    NMConnection       *connection;
+    NMSettingBridge    *s_bridge;
+    NMPlatformLnkBridge props;
+    int                 r;
+    int                 ifindex = nm_device_get_ifindex(device);
 
     connection = nm_device_get_applied_connection(device);
     g_return_val_if_fail(connection, NM_ACT_STAGE_RETURN_FAILURE);
 
-    s_bridge = (NMSetting *) nm_connection_get_setting_bridge(connection);
+    s_bridge = nm_connection_get_setting_bridge(connection);
     g_return_val_if_fail(s_bridge, NM_ACT_STAGE_RETURN_FAILURE);
 
-    for (option = master_options; option->name; option++)
-        commit_option(device, s_bridge, option, FALSE);
+    _platform_lnk_bridge_init_from_setting(s_bridge, &props);
+
+    r = nm_platform_link_bridge_change(nm_device_get_platform(device), ifindex, &props);
+    if (r < 0) {
+        NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+        return NM_ACT_STAGE_RETURN_FAILURE;
+    }
 
-    if (!bridge_set_vlan_options(device, (NMSettingBridge *) s_bridge)) {
+    if (!bridge_set_vlan_options(device, s_bridge)) {
         NM_SET_OUT(out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
         return NM_ACT_STAGE_RETURN_FAILURE;
     }
@@ -835,6 +887,7 @@ _bt_register_bridge_cb(GError *error, gpointer user_data)
         return;
     }
 
+    self->bt_cb_state = _NM_BT_CB_STATE_SUCCESS;
     nm_device_activate_schedule_stage2_device_config(NM_DEVICE(self), FALSE);
 }
 
@@ -846,12 +899,12 @@ _nm_device_bridge_notify_unregister_bt_nap(NMDevice *device, const char *reason)
     _LOGD(LOGD_DEVICE,
           "bluetooth NAP server unregistered from bridge: %s%s",
           reason,
-          self->bt_registered ? "" : " (was no longer registered)");
+          self->bt_cb_state != _NM_BT_CB_STATE_NONE ? "" : " (was no longer registered)");
 
     nm_clear_g_cancellable(&self->bt_cancellable);
 
-    if (self->bt_registered) {
-        self->bt_registered = FALSE;
+    if (self->bt_cb_state != _NM_BT_CB_STATE_NONE) {
+        self->bt_cb_state = _NM_BT_CB_STATE_NONE;
         nm_device_state_changed(device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED);
     }
 }
@@ -879,9 +932,12 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     if (self->bt_cancellable)
         return NM_ACT_STAGE_RETURN_POSTPONE;
 
-    if (self->bt_registered)
+    if (self->bt_cb_state == _NM_BT_CB_STATE_WAIT)
         return NM_ACT_STAGE_RETURN_POSTPONE;
 
+    if (self->bt_cb_state == _NM_BT_CB_STATE_SUCCESS)
+        return NM_ACT_STAGE_RETURN_SUCCESS;
+
     self->bt_cancellable = g_cancellable_new();
     if (!nm_bt_vtable_network_server->register_bridge(nm_bt_vtable_network_server,
                                                       nm_setting_bluetooth_get_bdaddr(s_bt),
@@ -895,7 +951,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
         return NM_ACT_STAGE_RETURN_FAILURE;
     }
 
-    self->bt_registered = TRUE;
+    self->bt_cb_state = _NM_BT_CB_STATE_WAIT;
     return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
@@ -906,14 +962,14 @@ deactivate(NMDevice *device)
 
     _LOGD(LOGD_DEVICE,
           "deactivate bridge%s",
-          self->bt_registered ? " (registered as NAP bluetooth device)" : "");
+          self->bt_cb_state != _NM_BT_CB_STATE_NONE ? " (registered as NAP bluetooth device)" : "");
 
     self->vlan_configured = FALSE;
 
     nm_clear_g_cancellable(&self->bt_cancellable);
 
-    if (self->bt_registered) {
-        self->bt_registered = FALSE;
+    if (self->bt_cb_state != _NM_BT_CB_STATE_NONE) {
+        self->bt_cb_state = _NM_BT_CB_STATE_NONE;
         nm_bt_vtable_network_server->unregister_bridge(nm_bt_vtable_network_server, device);
     }
 }
@@ -1054,39 +1110,7 @@ create_and_realize(NMDevice              *device,
         }
     }
 
-    props = (NMPlatformLnkBridge){
-        .forward_delay = _DEFAULT_IF_ZERO(nm_setting_bridge_get_forward_delay(s_bridge) * 100u,
-                                          NM_BRIDGE_FORWARD_DELAY_DEF_SYS),
-        .hello_time    = _DEFAULT_IF_ZERO(nm_setting_bridge_get_hello_time(s_bridge) * 100u,
-                                       NM_BRIDGE_HELLO_TIME_DEF_SYS),
-        .max_age       = _DEFAULT_IF_ZERO(nm_setting_bridge_get_max_age(s_bridge) * 100u,
-                                    NM_BRIDGE_MAX_AGE_DEF_SYS),
-        .ageing_time   = nm_setting_bridge_get_ageing_time(s_bridge) * 100u,
-        .stp_state     = nm_setting_bridge_get_stp(s_bridge),
-        .priority      = nm_setting_bridge_get_priority(s_bridge),
-        .vlan_protocol = to_sysfs_vlan_protocol_sys(nm_setting_bridge_get_vlan_protocol(s_bridge)),
-        .vlan_stats_enabled = nm_setting_bridge_get_vlan_stats_enabled(s_bridge),
-        .group_fwd_mask     = nm_setting_bridge_get_group_forward_mask(s_bridge),
-        .mcast_snooping     = nm_setting_bridge_get_multicast_snooping(s_bridge),
-        .mcast_router =
-            to_sysfs_multicast_router_sys(nm_setting_bridge_get_multicast_router(s_bridge)),
-        .mcast_query_use_ifaddr    = nm_setting_bridge_get_multicast_query_use_ifaddr(s_bridge),
-        .mcast_querier             = nm_setting_bridge_get_multicast_querier(s_bridge),
-        .mcast_hash_max            = nm_setting_bridge_get_multicast_hash_max(s_bridge),
-        .mcast_last_member_count   = nm_setting_bridge_get_multicast_last_member_count(s_bridge),
-        .mcast_startup_query_count = nm_setting_bridge_get_multicast_startup_query_count(s_bridge),
-        .mcast_last_member_interval =
-            nm_setting_bridge_get_multicast_last_member_interval(s_bridge),
-        .mcast_membership_interval = nm_setting_bridge_get_multicast_membership_interval(s_bridge),
-        .mcast_querier_interval    = nm_setting_bridge_get_multicast_querier_interval(s_bridge),
-        .mcast_query_interval      = nm_setting_bridge_get_multicast_query_interval(s_bridge),
-        .mcast_query_response_interval =
-            nm_setting_bridge_get_multicast_query_response_interval(s_bridge),
-        .mcast_startup_query_interval =
-            nm_setting_bridge_get_multicast_startup_query_interval(s_bridge),
-    };
-
-    to_sysfs_group_address_sys(nm_setting_bridge_get_group_address(s_bridge), &props.group_addr);
+    _platform_lnk_bridge_init_from_setting(s_bridge, &props);
 
     /* If mtu != 0, we set the MTU of the new bridge at creation time. However, kernel will still
      * automatically adjust the MTU of the bridge based on the minimum of the slave's MTU.
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index 407f8ab8..32f2fbf9 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -1014,7 +1014,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
      * get confused and fail to negotiate the new connection. (rh #1023503)
      *
      * FIXME(shutdown): when exiting, we also need to wait before quitting,
-     * at least for additional NM_SHUTDOWN_TIMEOUT_MS seconds because
+     * at least for additional NM_SHUTDOWN_TIMEOUT_MAX_MSEC seconds because
      * otherwise after restart the device won't work for the first seconds.
      */
     if (priv->ppp_data.last_pppoe_time_msec != 0) {
diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h
index 790bb823..b54aed6a 100644
--- a/src/core/devices/nm-device-private.h
+++ b/src/core/devices/nm-device-private.h
@@ -61,7 +61,7 @@ void nm_device_recheck_available_connections(NMDevice *device);
 void
 nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLogDomain log_domain);
 
-void nm_device_master_release_slaves(NMDevice *self);
+void nm_device_master_release_slaves_all(NMDevice *self);
 
 void nm_device_set_carrier(NMDevice *self, gboolean carrier);
 
diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c
index b6c4f88b..e623e9c0 100644
--- a/src/core/devices/nm-device-tun.c
+++ b/src/core/devices/nm-device-tun.c
@@ -542,6 +542,12 @@ create_device(NMDeviceFactory      *factory,
                                           NM_SETTING_TUN_SETTING_NAME),
                          NULL);
 
+    /* OpenvSwitch will create a tun device named ovs-netdev when the datapath selected is netdev */
+    if (nm_streq0(iface, "ovs-netdev")) {
+        *out_ignore = TRUE;
+        return NULL;
+    }
+
     return g_object_new(NM_TYPE_DEVICE_TUN,
                         NM_DEVICE_IFACE,
                         iface,
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index a11486d5..1d9f235a 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -101,6 +101,12 @@
 typedef void (*ActivationHandleFunc)(NMDevice *self);
 
 typedef enum {
+    RELEASE_SLAVE_TYPE_NO_CONFIG,
+    RELEASE_SLAVE_TYPE_CONFIG,
+    RELEASE_SLAVE_TYPE_CONFIG_FORCE,
+} ReleaseSlaveType;
+
+typedef enum {
     CLEANUP_TYPE_KEEP,
     CLEANUP_TYPE_REMOVED,
     CLEANUP_TYPE_DECONFIGURE,
@@ -362,7 +368,6 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMDevice,
                              PROP_FIRMWARE_MISSING,
                              PROP_NM_PLUGIN_MISSING,
                              PROP_TYPE_DESC,
-                             PROP_RFKILL_TYPE,
                              PROP_IFINDEX,
                              PROP_AVAILABLE_CONNECTIONS,
                              PROP_PHYSICAL_PORT_ID,
@@ -474,7 +479,6 @@ typedef struct _NMDevicePrivate {
     char                *driver;
     char                *driver_version;
     char                *firmware_version;
-    RfKillType           rfkill_type;
     bool                 firmware_missing : 1;
     bool                 nm_plugin_missing : 1;
     bool
@@ -1135,7 +1139,7 @@ _prop_get_ipv6_dhcp_duid(NMDevice     *self,
             duid_out = nm_utils_generate_duid_llt(arp_type,
                                                   hwaddr_bin,
                                                   hwaddr_len,
-                                                  nm_utils_host_id_get_timestamp_ns()
+                                                  nm_utils_host_id_get_timestamp_nsec()
                                                       / NM_UTILS_NSEC_PER_SEC);
         }
 
@@ -1236,7 +1240,7 @@ _prop_get_ipv6_dhcp_duid(NMDevice     *self,
              * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll
              * subtract it from the host_id timestamp.
              */
-            time = nm_utils_host_id_get_timestamp_ns() / NM_UTILS_NSEC_PER_SEC;
+            time = nm_utils_host_id_get_timestamp_nsec() / NM_UTILS_NSEC_PER_SEC;
 
             /* don't use too old timestamps. They cannot be expressed in DUID-LLT and
              * would all be truncated to zero. */
@@ -3855,6 +3859,7 @@ update_external_connection(NMDevice *self)
 
     if (connection_new) {
         nm_settings_connection_update(settings_connection,
+                                      NULL,
                                       connection_new,
                                       NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
                                       NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
@@ -3893,12 +3898,15 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N
     case NM_L3_CONFIG_NOTIFY_TYPE_PRE_COMMIT:
     {
         const NML3ConfigData *l3cd;
+        NMDeviceState         state = nm_device_get_state(self);
 
-        /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */
-        l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE);
-        if (l3cd)
-            priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd);
-        _commit_mtu(self);
+        if (state >= NM_DEVICE_STATE_IP_CONFIG && state < NM_DEVICE_STATE_DEACTIVATING) {
+            /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */
+            l3cd = nm_l3cfg_get_combined_l3cd(l3cfg, TRUE);
+            if (l3cd)
+                priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd);
+            _commit_mtu(self);
+        }
         return;
     }
     case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT:
@@ -3976,7 +3984,9 @@ _dev_l3_cfg_commit_type_reset(NMDevice *self)
         commit_type = NM_L3_CFG_COMMIT_TYPE_NONE;
         goto do_set;
     case NM_DEVICE_SYS_IFACE_STATE_ASSUME:
-        commit_type = NM_L3_CFG_COMMIT_TYPE_ASSUME;
+        /* TODO: NM_DEVICE_SYS_IFACE_STATE_ASSUME, will be dropped from the code.
+         * Meanwhile, the commit type must be updated. */
+        commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE;
         goto do_set;
     case NM_DEVICE_SYS_IFACE_STATE_MANAGED:
         commit_type = NM_L3_CFG_COMMIT_TYPE_UPDATE;
@@ -4455,9 +4465,9 @@ _parent_set_ifindex(NMDevice *self, int parent_ifindex, gboolean force_check)
             _LOGD(LOGD_DEVICE, "parent: ifindex %d, no device", priv->parent_ifindex);
         else {
             _LOGD(LOGD_DEVICE,
-                  "parent: ifindex %d, device %p, %s",
+                  "parent: ifindex %d, device " NM_HASH_OBFUSCATE_PTR_FMT ", %s",
                   priv->parent_ifindex,
-                  priv->parent_device.obj,
+                  NM_HASH_OBFUSCATE_PTR(priv->parent_device.obj),
                   nm_device_get_iface(priv->parent_device.obj));
         }
 
@@ -4719,7 +4729,7 @@ nm_device_get_ip_iface_identifier(NMDevice           *self,
                                   gboolean           *out_is_token)
 {
     NMSettingIP6Config *s_ip6;
-    const char         *token = NULL;
+    const char         *token;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
 
@@ -4731,13 +4741,12 @@ nm_device_get_ip_iface_identifier(NMDevice           *self,
         g_return_val_if_fail(s_ip6, FALSE);
 
         token = nm_setting_ip6_config_get_token(s_ip6);
-        if (token)
+        if (token) {
             NM_SET_OUT(out_is_token, TRUE);
+            return nm_utils_ipv6_interface_identifier_get_from_token(iid, token);
+        }
     }
-    if (token)
-        return nm_utils_ipv6_interface_identifier_get_from_token(iid, token);
-    else
-        return NM_DEVICE_GET_CLASS(self)->get_ip_iface_identifier(self, iid);
+    return NM_DEVICE_GET_CLASS(self)->get_ip_iface_identifier(self, iid);
 }
 
 const char *
@@ -5162,12 +5171,17 @@ nm_device_get_applied_setting(NMDevice *self, GType setting_type)
     return connection ? nm_connection_get_setting(connection, setting_type) : NULL;
 }
 
-RfKillType
+NMRfkillType
 nm_device_get_rfkill_type(NMDevice *self)
 {
+    NMRfkillType t;
+
     g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
 
-    return NM_DEVICE_GET_PRIVATE(self)->rfkill_type;
+    t = NM_DEVICE_GET_CLASS(self)->rfkill_type;
+
+    nm_assert(NM_IN_SET(t, NM_RFKILL_TYPE_UNKNOWN, NM_RFKILL_TYPE_WLAN, NM_RFKILL_TYPE_WWAN));
+    return t;
 }
 
 static const char *
@@ -5875,11 +5889,9 @@ static SlaveInfo *
 find_slave_info(NMDevice *self, NMDevice *slave)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
-    CList           *iter;
     SlaveInfo       *info;
 
-    c_list_for_each (iter, &priv->slaves) {
-        info = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         if (info->slave == slave)
             return info;
     }
@@ -5947,12 +5959,12 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
 }
 
 /**
- * nm_device_master_release_one_slave:
+ * nm_device_master_release_slave:
  * @self: the master device
  * @slave: the slave device to release
  * @configure: whether @self needs to actually release @slave
- * @force: force the release of @slave even if it wasn't added
- *         to @master by NetworkManager
+ * @release_type: whether @self needs to actually release slave
+ *   and whether that is forced.
  * @reason: the state change reason for the @slave
  *
  * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
@@ -5960,11 +5972,10 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co
  * updates the state of @self and @slave to reflect its release.
  */
 static void
-nm_device_master_release_one_slave(NMDevice           *self,
-                                   NMDevice           *slave,
-                                   gboolean            configure,
-                                   gboolean            force,
-                                   NMDeviceStateReason reason)
+nm_device_master_release_slave(NMDevice           *self,
+                               NMDevice           *slave,
+                               ReleaseSlaveType    release_type,
+                               NMDeviceStateReason reason)
 {
     NMDevicePrivate          *priv;
     NMDevicePrivate          *slave_priv;
@@ -5973,17 +5984,22 @@ nm_device_master_release_one_slave(NMDevice           *self,
 
     g_return_if_fail(NM_DEVICE(self));
     g_return_if_fail(NM_DEVICE(slave));
-    g_return_if_fail(!force || configure);
+    nm_assert(NM_IN_SET(release_type,
+                        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);
 
     info = find_slave_info(self, slave);
 
     _LOGT(LOGD_CORE,
-          "master: release one slave %p/%s %s%s",
-          slave,
+          "master: release one slave " NM_HASH_OBFUSCATE_PTR_FMT "/%s %s%s",
+          NM_HASH_OBFUSCATE_PTR(slave),
           nm_device_get_iface(slave),
           !info ? "(not registered)" : (info->slave_is_enslaved ? "(enslaved)" : "(not enslaved)"),
-          force ? " (force-configure)" : (configure ? " (configure)" : ""));
+          release_type == RELEASE_SLAVE_TYPE_CONFIG_FORCE
+              ? " (force-configure)"
+              : (release_type == RELEASE_SLAVE_TYPE_CONFIG ? " (configure)" : "(no-config)"));
 
     if (!info)
         g_return_if_reached();
@@ -5995,8 +6011,11 @@ nm_device_master_release_one_slave(NMDevice           *self,
     nm_assert(slave == info->slave);
 
     /* first, let subclasses handle the release ... */
-    if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave) || force)
-        NM_DEVICE_GET_CLASS(self)->release_slave(self, slave, configure);
+    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);
 
     /* raise notifications about the release, including clearing is_enslaved. */
     nm_device_slave_notify_release(slave, reason);
@@ -6314,7 +6333,8 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink)
     plink_master            = nm_platform_link_get(nm_device_get_platform(self), plink->master);
     plink_master_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink_master));
 
-    if (master == NULL && plink_master && nm_streq0(plink_master->name, "ovs-system")
+    if (master == NULL && plink_master
+        && NM_IN_STRSET(plink_master->name, "ovs-system", "ovs-netdev")
         && plink_master->type == NM_LINK_TYPE_OPENVSWITCH) {
         _LOGD(LOGD_DEVICE, "the device claimed by openvswitch");
         goto out;
@@ -6330,11 +6350,10 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink)
             goto out;
         }
 
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
     }
 
     if (master && NM_DEVICE_GET_CLASS(master)->enslave_slave) {
@@ -6638,7 +6657,7 @@ static void
 link_changed_cb(NMPlatform     *platform,
                 int             obj_type_i,
                 int             ifindex,
-                NMPlatformLink *info,
+                NMPlatformLink *pllink,
                 int             change_type_i,
                 NMDevice       *self)
 {
@@ -6651,7 +6670,7 @@ link_changed_cb(NMPlatform     *platform,
     priv = NM_DEVICE_GET_PRIVATE(self);
 
     if (ifindex == nm_device_get_ifindex(self)) {
-        if (!(info->n_ifi_flags & IFF_UP))
+        if (!(pllink->n_ifi_flags & IFF_UP))
             priv->device_link_changed_down = TRUE;
         if (!priv->device_link_changed_id) {
             priv->device_link_changed_id = g_idle_add(device_link_changed, self);
@@ -7004,7 +7023,7 @@ sriov_op_queue(NMDevice               *self,
      *
      * FIXME(shutdown): However, during shutdown we don't have a follow-up write request to cancel
      * this operation and we have to give it at least some time to complete. The solution is that
-     * we register a way to abort the last call during shutdown, and after NM_SHUTDOWN_TIMEOUT_MS
+     * we register a way to abort the last call during shutdown, and after NM_SHUTDOWN_TIMEOUT_MAX_MSEC
      * grace period we pull the plug and cancel it. */
 
     op  = g_slice_new(SriovOp);
@@ -7527,7 +7546,11 @@ slave_state_changed(NMDevice           *slave,
         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_master_release_one_slave(self, slave, configure, FALSE, reason);
+        nm_device_master_release_slave(self,
+                                       slave,
+                                       configure ? RELEASE_SLAVE_TYPE_CONFIG
+                                                 : RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       reason);
         /* Bridge/bond/team interfaces are left up until manually deactivated */
         if (c_list_is_empty(&priv->slaves) && priv->state == NM_DEVICE_STATE_ACTIVATED)
             _LOGD(LOGD_DEVICE, "last slave removed; remaining activated");
@@ -7565,8 +7588,8 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure)
     info = find_slave_info(self, slave);
 
     _LOGT(LOGD_CORE,
-          "master: add one slave %p/%s%s",
-          slave,
+          "master: add one slave " NM_HASH_OBFUSCATE_PTR_FMT "/%s%s",
+          NM_HASH_OBFUSCATE_PTR(slave),
           nm_device_get_iface(slave),
           info ? " (already registered)" : "");
 
@@ -7624,14 +7647,12 @@ nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLo
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
     const char      *slave_physical_port_id, *existing_physical_port_id;
     SlaveInfo       *info;
-    CList           *iter;
 
     slave_physical_port_id = nm_device_get_physical_port_id(slave);
     if (!slave_physical_port_id)
         return;
 
-    c_list_for_each (iter, &priv->slaves) {
-        info = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         if (info->slave == slave)
             continue;
 
@@ -7651,13 +7672,13 @@ nm_device_master_check_slave_physical_port(NMDevice *self, NMDevice *slave, NMLo
     }
 }
 
-/* release all slaves */
 void
-nm_device_master_release_slaves(NMDevice *self)
+nm_device_master_release_slaves_all(NMDevice *self)
 {
     NMDevicePrivate    *priv = NM_DEVICE_GET_PRIVATE(self);
     NMDeviceStateReason reason;
-    CList              *iter, *safe;
+    SlaveInfo          *info;
+    SlaveInfo          *safe;
 
     /* Don't release the slaves if this connection doesn't belong to NM. */
     if (nm_device_sys_iface_state_is_external(self))
@@ -7667,9 +7688,7 @@ nm_device_master_release_slaves(NMDevice *self)
     if (priv->state == NM_DEVICE_STATE_FAILED)
         reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
 
-    c_list_for_each_safe (iter, safe, &priv->slaves) {
-        SlaveInfo *info = c_list_entry(iter, SlaveInfo, lst_slave);
-
+    c_list_for_each_entry_safe (info, safe, &priv->slaves, lst_slave) {
         if (priv->activation_state_preserve_external_ports
             && nm_device_sys_iface_state_is_external(info->slave)) {
             _LOGT(LOGD_DEVICE,
@@ -7677,7 +7696,7 @@ nm_device_master_release_slaves(NMDevice *self)
                   nm_device_get_iface(info->slave));
             continue;
         }
-        nm_device_master_release_one_slave(self, info->slave, TRUE, FALSE, reason);
+        nm_device_master_release_slave(self, info->slave, RELEASE_SLAVE_TYPE_CONFIG, reason);
     }
 
     /* We only need this flag for a short time. It served its purpose. Clear
@@ -7852,11 +7871,10 @@ nm_device_removed(NMDevice *self, gboolean unconfigure_ip_config)
     if (priv->master) {
         /* this is called when something externally messes with the slave or during shut-down.
          * Release the slave from master, but don't touch the device. */
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
     }
 
     _dev_l3_register_l3cds(self, priv->l3cfg, FALSE, unconfigure_ip_config);
@@ -8928,11 +8946,10 @@ master_ready(NMDevice *self, NMActiveConnection *active)
     _LOGD(LOGD_DEVICE, "master connection ready; master device %s", nm_device_get_iface(master));
 
     if (priv->master && priv->master != master)
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
     /* If the master didn't change, add-slave only rechecks whether to assume a connection. */
     nm_device_master_add_slave(master,
@@ -9214,11 +9231,10 @@ activate_stage1_device_prepare(NMDevice *self)
     if (master)
         master_ready(self, active);
     else if (priv->master) {
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           TRUE,
-                                           TRUE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_CONFIG_FORCE,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
     }
 
     nm_device_activate_schedule_stage2_device_config(self, TRUE);
@@ -9434,7 +9450,7 @@ activate_stage2_device_config(NMDevice *self)
     NMActStageReturn ret;
     NMSettingWired  *s_wired;
     gboolean         no_firmware = FALSE;
-    CList           *iter;
+    SlaveInfo       *info;
     NMTernary        accept_all_mac_addresses;
 
     nm_device_state_changed(self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
@@ -9481,8 +9497,7 @@ activate_stage2_device_config(NMDevice *self)
     }
 
     /* If we have slaves that aren't yet enslaved, do that now */
-    c_list_for_each (iter, &priv->slaves) {
-        SlaveInfo    *info        = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         NMDeviceState slave_state = nm_device_get_state(info->slave);
 
         if (slave_state == NM_DEVICE_STATE_IP_CONFIG)
@@ -9517,8 +9532,6 @@ activate_stage2_device_config(NMDevice *self)
 
     lldp_setup(self, NM_TERNARY_DEFAULT);
 
-    _commit_mtu(self);
-
     nm_device_activate_schedule_stage3_ip_config(self, TRUE);
 }
 
@@ -10410,14 +10423,12 @@ have_any_ready_slaves(NMDevice *self)
 {
     NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
     SlaveInfo       *info;
-    CList           *iter;
 
     /* Any enslaved slave is "ready" in the generic case as it's
-     * at least >= NM_DEVCIE_STATE_IP_CONFIG and has had Layer 2
+     * at least >= NM_DEVICE_STATE_IP_CONFIG and has had Layer 2
      * properties set up.
      */
-    c_list_for_each (iter, &priv->slaves) {
-        info = c_list_entry(iter, SlaveInfo, lst_slave);
+    c_list_for_each_entry (info, &priv->slaves, lst_slave) {
         if (NM_DEVICE_GET_PRIVATE(info->slave)->is_enslaved)
             return TRUE;
     }
@@ -10440,7 +10451,7 @@ void
 nm_device_use_ip6_subnet(NMDevice *self, const NMPlatformIP6Address *subnet)
 {
     nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
-    char                                    sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char                                    sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     NMPlatformIP6Address                    address;
 
     l3cd = nm_device_create_l3_config_data(self, NM_IP_CONFIG_SOURCE_SHARED);
@@ -11864,6 +11875,15 @@ activate_stage3_ip_config(NMDevice *self)
                   nm_device_get_ip_iface(self));
     }
 
+    /* We currently will attach ports in the state change NM_DEVICE_STATE_IP_CONFIG above.
+     * Note that kernel changes the MTU of bond ports, so we want to commit the MTU
+     * afterwards!
+     *
+     * This might reset the MTU to something different from the bond controller and
+     * it might not be a working configuration. But it's what the user asked for, so
+     * let's do it! */
+    _commit_mtu(self);
+
     ipv4_method = nm_device_get_effective_ip_config_method(self, AF_INET);
     if (nm_streq(ipv4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
         /* "auto" usually means DHCPv4 or autoconf6, but it doesn't have to be. Subclasses
@@ -12455,6 +12475,7 @@ can_reapply_change(NMDevice   *self,
                                                  NM_SETTING_CONNECTION_UUID,
                                                  NM_SETTING_CONNECTION_STABLE_ID,
                                                  NM_SETTING_CONNECTION_AUTOCONNECT,
+                                                 NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
                                                  NM_SETTING_CONNECTION_ZONE,
                                                  NM_SETTING_CONNECTION_METERED,
                                                  NM_SETTING_CONNECTION_LLDP,
@@ -15085,7 +15106,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
 
     if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
         /* master: release slaves */
-        nm_device_master_release_slaves(self);
+        nm_device_master_release_slaves_all(self);
 
         /* Take out any entries in the routing table and any IP address the device had. */
         if (ifindex > 0) {
@@ -15109,12 +15130,12 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
 
     /* slave: mark no longer enslaved */
     if (priv->master && priv->ifindex > 0
-        && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0)
-        nm_device_master_release_one_slave(priv->master,
-                                           self,
-                                           FALSE,
-                                           FALSE,
-                                           NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+        && nm_platform_link_get_master(nm_device_get_platform(self), priv->ifindex) <= 0) {
+        nm_device_master_release_slave(priv->master,
+                                       self,
+                                       RELEASE_SLAVE_TYPE_NO_CONFIG,
+                                       NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+    }
 
     lldp_setup(self, NM_TERNARY_FALSE);
 
@@ -15614,7 +15635,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason,
               sett_conn ? nm_settings_connection_get_id(sett_conn) : "<unknown>");
 
         /* Notify any slaves of the unexpected failure */
-        nm_device_master_release_slaves(self);
+        nm_device_master_release_slaves_all(self);
 
         /* If the connection doesn't yet have a timestamp, set it to zero so that
          * we can distinguish between connections we've tried to activate and have
@@ -17070,9 +17091,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_TYPE_DESC:
         g_value_set_string(value, priv->type_desc);
         break;
-    case PROP_RFKILL_TYPE:
-        g_value_set_uint(value, priv->rfkill_type);
-        break;
     case PROP_AVAILABLE_CONNECTIONS:
         nm_dbus_utils_g_value_set_object_path_from_hash(value, priv->available_connections, TRUE);
         break;
@@ -17208,10 +17226,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         /* construct-only */
         priv->type_desc = g_value_dup_string(value);
         break;
-    case PROP_RFKILL_TYPE:
-        /* construct-only */
-        priv->rfkill_type = g_value_get_uint(value);
-        break;
     case PROP_PERM_HW_ADDRESS:
         /* construct-only */
         priv->hw_addr_perm = g_value_dup_string(value);
@@ -17261,7 +17275,6 @@ nm_device_init(NMDevice *self)
     priv->capabilities          = NM_DEVICE_CAP_NM_SUPPORTED;
     priv->state                 = NM_DEVICE_STATE_UNMANAGED;
     priv->state_reason          = NM_DEVICE_STATE_REASON_NONE;
-    priv->rfkill_type           = RFKILL_TYPE_UNKNOWN;
     priv->unmanaged_flags       = NM_UNMANAGED_PLATFORM_INIT;
     priv->unmanaged_mask        = priv->unmanaged_flags;
     priv->available_connections = g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL);
@@ -17664,6 +17677,8 @@ nm_device_class_init(NMDeviceClass *klass)
     klass->reapply_connection            = reapply_connection;
     klass->set_platform_mtu              = set_platform_mtu;
 
+    klass->rfkill_type = NM_RFKILL_TYPE_UNKNOWN;
+
     obj_properties[PROP_UDI] =
         g_param_spec_string(NM_DEVICE_UDI,
                             "",
@@ -17821,14 +17836,6 @@ nm_device_class_init(NMDeviceClass *klass)
                             "",
                             NULL,
                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
-    obj_properties[PROP_RFKILL_TYPE] =
-        g_param_spec_uint(NM_DEVICE_RFKILL_TYPE,
-                          "",
-                          "",
-                          RFKILL_TYPE_WLAN,
-                          RFKILL_TYPE_MAX,
-                          RFKILL_TYPE_UNKNOWN,
-                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
     obj_properties[PROP_IFINDEX] = g_param_spec_int(NM_DEVICE_IFINDEX,
                                                     "",
                                                     "",
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index a7badb86..80def125 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -65,7 +65,6 @@
 #define NM_DEVICE_SLAVES "slaves" /* partially internal */
 
 #define NM_DEVICE_TYPE_DESC          "type-desc"          /* Internal only */
-#define NM_DEVICE_RFKILL_TYPE        "rfkill-type"        /* Internal only */
 #define NM_DEVICE_IFINDEX            "ifindex"            /* Internal only */
 #define NM_DEVICE_MASTER             "master"             /* Internal only */
 #define NM_DEVICE_HAS_PENDING_ACTION "has-pending-action" /* Internal only */
@@ -105,38 +104,38 @@ typedef enum NMActStageReturn NMActStageReturn;
  * a condition, so that adding a flag might make a connection available that would
  * not be available otherwise. Adding a flag should never make a connection
  * not available if it would be available otherwise. */
-typedef enum { /*< skip >*/
-               NM_DEVICE_CHECK_CON_AVAILABLE_NONE = 0,
+typedef enum {
+    NM_DEVICE_CHECK_CON_AVAILABLE_NONE = 0,
 
-               /* since NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST is a collection of flags with more fine grained
+    /* since NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST is a collection of flags with more fine grained
      * parts, this flag in general indicates that this is a user-request. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = (1L << 0),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = (1L << 0),
 
-               /* we also consider devices which have no carrier but are still waiting for the driver
+    /* we also consider devices which have no carrier but are still waiting for the driver
      * to detect carrier. Usually, such devices are not yet available, however for a user-request
      * they are. They might fail later if carrier doesn't come. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER = (1L << 1),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER = (1L << 1),
 
-               /* usually, a profile is only available if the Wi-Fi AP is in range. For an
+    /* usually, a profile is only available if the Wi-Fi AP is in range. For an
      * explicit user request, we also consider profiles for APs that are not (yet)
      * visible. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP = (1L << 2),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP = (1L << 2),
 
-               /* a device can be marked as unmanaged for various reasons. Some of these reasons
+    /* a device can be marked as unmanaged for various reasons. Some of these reasons
      * are authoritative, others not. Non-authoritative reasons can be overruled by
      * `nmcli device set $DEVICE managed yes`. Also, for an explicit user activation
      * request we may want to consider the device as managed. This flag makes devices
      * that are unmanaged appear available. */
-               _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED = (1L << 3),
+    _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED = (1L << 3),
 
-               /* a collection of flags, that are commonly set for an explicit user-request. */
-               NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST =
-                   _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST
-                   | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER
-                   | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP
-                   | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED,
+    /* a collection of flags, that are commonly set for an explicit user-request. */
+    NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST =
+        _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST
+        | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER
+        | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP
+        | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED,
 
-               NM_DEVICE_CHECK_CON_AVAILABLE_ALL = (1L << 4) - 1,
+    NM_DEVICE_CHECK_CON_AVAILABLE_ALL = (1L << 4) - 1,
 } NMDeviceCheckConAvailableFlags;
 
 struct _NMDevicePrivate;
@@ -149,19 +148,18 @@ struct _NMDevice {
 
 /* The flags have an relaxing meaning, that means, specifying more flags, can make
  * a device appear more available. It can never make a device less available. */
-typedef enum { /*< skip >*/
-               NM_DEVICE_CHECK_DEV_AVAILABLE_NONE = 0,
+typedef enum {
+    NM_DEVICE_CHECK_DEV_AVAILABLE_NONE = 0,
 
-               /* the device is considered available, even if it has no carrier.
+    /* the device is considered available, even if it has no carrier.
      *
      * For various device types (software devices) we ignore carrier based
      * on the type. So, for them, this flag has no effect anyway. */
-               _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER = (1L << 0),
+    _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER = (1L << 0),
 
-               NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST =
-                   _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER,
+    NM_DEVICE_CHECK_DEV_AVAILABLE_FOR_USER_REQUEST = _NM_DEVICE_CHECK_DEV_AVAILABLE_IGNORE_CARRIER,
 
-               NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1,
+    NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1,
 } NMDeviceCheckDevAvailableFlags;
 
 typedef void (*NMDeviceDeactivateCallback)(NMDevice *self, GError *error, gpointer user_data);
@@ -209,6 +207,8 @@ typedef struct _NMDeviceClass {
 
     bool can_reapply_change_ovs_external_ids : 1;
 
+    NMRfkillType rfkill_type : 4;
+
     void (*state_changed)(NMDevice           *device,
                           NMDeviceState       new_state,
                           NMDeviceState       old_state,
@@ -410,7 +410,6 @@ typedef struct _NMDeviceClass {
     gboolean (*set_platform_mtu)(NMDevice *self, guint32 mtu);
 
     const char *(*get_dhcp_anycast_address)(NMDevice *self);
-
 } NMDeviceClass;
 
 GType nm_device_get_type(void);
@@ -538,7 +537,7 @@ gboolean nm_device_get_enabled(NMDevice *device);
 
 void nm_device_set_enabled(NMDevice *device, gboolean enabled);
 
-RfKillType nm_device_get_rfkill_type(NMDevice *device);
+NMRfkillType nm_device_get_rfkill_type(NMDevice *device);
 
 /* IPv6 prefix delegation */
 
diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c
index 74707d24..6b9d3d7e 100644
--- a/src/core/devices/ovs/nm-device-ovs-interface.c
+++ b/src/core/devices/ovs/nm-device-ovs-interface.c
@@ -13,6 +13,7 @@
 #include "devices/nm-device-private.h"
 #include "nm-active-connection.h"
 #include "nm-setting-connection.h"
+#include "nm-setting-ovs-bridge.h"
 #include "nm-setting-ovs-interface.h"
 #include "nm-setting-ovs-port.h"
 
@@ -23,7 +24,10 @@
 
 typedef struct {
     NMOvsdb *ovsdb;
-    bool     waiting_for_interface : 1;
+    GSource *wait_link_idle_source;
+    gulong   wait_link_signal_id;
+    int      wait_link_ifindex;
+    bool     wait_link_is_waiting : 1;
 } NMDeviceOvsInterfacePrivate;
 
 struct _NMDeviceOvsInterface {
@@ -115,10 +119,10 @@ link_changed(NMDevice *device, const NMPlatformLink *pllink)
 {
     NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(device);
 
-    if (!pllink || !priv->waiting_for_interface)
+    if (!pllink || !priv->wait_link_is_waiting)
         return;
 
-    priv->waiting_for_interface = FALSE;
+    priv->wait_link_is_waiting = FALSE;
 
     if (nm_device_get_state(device) == NM_DEVICE_STATE_IP_CONFIG) {
         if (!nm_device_hw_addr_set_cloned(device,
@@ -199,17 +203,83 @@ ready_for_ip_config(NMDevice *device)
     return nm_device_get_ip_ifindex(device) > 0;
 }
 
+static gboolean
+_set_ip_ifindex_tun(gpointer user_data)
+{
+    NMDevice                    *device = user_data;
+    NMDeviceOvsInterface        *self   = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv   = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+
+    priv->wait_link_is_waiting = FALSE;
+    nm_device_set_ip_ifindex(device, priv->wait_link_ifindex);
+
+    nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_PENDING, NULL);
+    nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_PENDING, NULL);
+    nm_device_activate_schedule_stage3_ip_config(device, FALSE);
+
+    return G_SOURCE_CONTINUE;
+}
+
+static void
+_netdev_tun_link_cb(NMPlatform     *platform,
+                    int             obj_type_i,
+                    int             ifindex,
+                    NMPlatformLink *pllink,
+                    int             change_type_i,
+                    NMDevice       *device)
+{
+    const NMPlatformSignalChangeType change_type = change_type_i;
+    NMDeviceOvsInterface            *self        = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate     *priv        = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+
+    if (change_type == NM_PLATFORM_SIGNAL_ADDED) {
+        if (pllink->type == NM_LINK_TYPE_TUN
+            && nm_streq0(pllink->name, nm_device_get_iface(device))) {
+            nm_clear_g_signal_handler(platform, &priv->wait_link_signal_id);
+
+            priv->wait_link_ifindex = ifindex;
+
+            priv->wait_link_idle_source = nm_g_idle_add_source(_set_ip_ifindex_tun, device);
+        }
+    }
+}
+
 static void
 act_stage3_ip_config(NMDevice *device, int addr_family)
 {
-    NMDeviceOvsInterface        *self = NM_DEVICE_OVS_INTERFACE(device);
-    NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
+    NMActiveConnection          *controller_act = NULL;
+    NMSettingOvsBridge          *s_ovs_bridge   = NULL;
+    NMDeviceOvsInterface        *self           = NM_DEVICE_OVS_INTERFACE(device);
+    NMDeviceOvsInterfacePrivate *priv           = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
 
     if (!_is_internal_interface(device)) {
         nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL);
         return;
     }
 
+    /* When the ovs-bridge controller is using netdev datapath, the interface
+     * link created is a tun device instead of a ovs-interface. NetworkManager must
+     * detect the creation of the tun link and attach the ifindex to the
+     * ovs-interface device. */
+    controller_act = NM_ACTIVE_CONNECTION(nm_device_get_act_request(device));
+    if (controller_act && nm_device_get_ip_ifindex(device) <= 0 && priv->wait_link_signal_id == 0) {
+        controller_act = nm_active_connection_get_master(controller_act);
+        if (controller_act) {
+            controller_act = nm_active_connection_get_master(controller_act);
+            if (controller_act)
+                s_ovs_bridge = nm_connection_get_setting_ovs_bridge(
+                    nm_active_connection_get_applied_connection(controller_act));
+            if (s_ovs_bridge
+                && nm_streq0(nm_setting_ovs_bridge_get_datapath_type(s_ovs_bridge), "netdev"))
+                priv->wait_link_signal_id = g_signal_connect(nm_device_get_platform(device),
+                                                             NM_PLATFORM_SIGNAL_LINK_CHANGED,
+                                                             G_CALLBACK(_netdev_tun_link_cb),
+                                                             self);
+        }
+    }
+
     /* FIXME(l3cfg): we should create the IP ifindex before stage3 start.
      *
      * For now it's here because when the ovs-interface enters stage3, then it's added to the
@@ -219,11 +289,15 @@ act_stage3_ip_config(NMDevice *device, int addr_family)
      * This should change. */
     if (nm_device_get_ip_ifindex(device) <= 0) {
         _LOGT(LOGD_DEVICE, "waiting for link to appear");
-        priv->waiting_for_interface = TRUE;
+        priv->wait_link_is_waiting = TRUE;
         nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_PENDING, NULL);
         return;
     }
 
+    priv->wait_link_is_waiting = FALSE;
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+    nm_clear_g_signal_handler(nm_device_get_platform(device), &priv->wait_link_signal_id);
+
     if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) {
         nm_device_devip_set_failed(device, addr_family, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
         return;
@@ -244,7 +318,8 @@ deactivate(NMDevice *device)
     NMDeviceOvsInterface        *self = NM_DEVICE_OVS_INTERFACE(device);
     NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE(self);
 
-    priv->waiting_for_interface = FALSE;
+    priv->wait_link_is_waiting = FALSE;
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
 }
 
 typedef struct {
@@ -351,7 +426,7 @@ deactivate_async(NMDevice                  *device,
         .callback_user_data = callback_user_data,
     };
 
-    if (!priv->waiting_for_interface
+    if (!priv->wait_link_is_waiting
         && !nm_platform_link_get_by_ifname(nm_device_get_platform(device),
                                            nm_device_get_iface(device))) {
         _LOGT(LOGD_CORE, "deactivate: link not present, proceeding");
@@ -360,7 +435,9 @@ deactivate_async(NMDevice                  *device,
         return;
     }
 
-    if (priv->waiting_for_interface) {
+    nm_clear_g_source_inst(&priv->wait_link_idle_source);
+
+    if (priv->wait_link_is_waiting) {
         /* At this point we have issued an INSERT and a DELETE
          * command for the interface to ovsdb. We don't know if
          * vswitchd will see the two updates or only one. We
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index 7c45e0e4..44e16cb7 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -104,6 +104,7 @@ typedef struct {
     OvsdbMethodCallback callback;
     gpointer            user_data;
     OvsdbMethodPayload  payload;
+    GObject            *shutdown_wait_obj;
 } OvsdbMethodCall;
 
 /*****************************************************************************/
@@ -245,6 +246,8 @@ static NM_UTILS_LOOKUP_STR_DEFINE(_device_type_to_table,
 static void
 _call_complete(OvsdbMethodCall *call, json_t *response, GError *error)
 {
+    g_clear_object(&call->shutdown_wait_obj);
+
     if (response) {
         gs_free char *str = NULL;
 
@@ -378,12 +381,14 @@ ovsdb_call_method(NMOvsdb                  *self,
 
     call  = g_slice_new(OvsdbMethodCall);
     *call = (OvsdbMethodCall){
-        .self      = self,
-        .call_id   = CALL_ID_UNSPEC,
-        .command   = command,
-        .callback  = callback,
-        .user_data = user_data,
+        .self              = self,
+        .call_id           = CALL_ID_UNSPEC,
+        .command           = command,
+        .callback          = callback,
+        .user_data         = user_data,
+        .shutdown_wait_obj = g_object_new(G_TYPE_OBJECT, NULL),
     };
+    nm_shutdown_wait_obj_register_object(call->shutdown_wait_obj, "ovsdb-call");
 
     if (add_first)
         c_list_link_front(&priv->calls_lst_head, &call->calls_lst);
@@ -2481,29 +2486,27 @@ typedef struct {
 static void
 _transact_cb(NMOvsdb *self, json_t *result, GError *error, gpointer user_data)
 {
-    OvsdbCall  *call = user_data;
-    const char *err;
-    const char *err_details;
-    size_t      index;
-    json_t     *value;
-
-    if (error)
-        goto out;
-
-    json_array_foreach (result, index, value) {
-        if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
-            g_set_error(&error,
-                        G_IO_ERROR,
-                        G_IO_ERROR_FAILED,
-                        "Error running the transaction: %s: %s",
-                        err,
-                        err_details);
-            goto out;
+    OvsdbCall            *call  = user_data;
+    gs_free_error GError *local = NULL;
+    const char           *err;
+    const char           *err_details;
+    size_t                index;
+    json_t               *value;
+
+    if (!error) {
+        json_array_foreach (result, index, value) {
+            if (json_unpack(value, "{s:s, s:s}", "error", &err, "details", &err_details) == 0) {
+                local = g_error_new(G_IO_ERROR,
+                                    G_IO_ERROR_FAILED,
+                                    "Error running the transaction: %s: %s",
+                                    err,
+                                    err_details);
+                break;
+            }
         }
     }
 
-out:
-    call->callback(error, call->user_data);
+    call->callback(local ?: error, call->user_data);
     nm_g_slice_free(call);
 }
 
diff --git a/src/core/devices/wifi/nm-device-iwd-p2p.c b/src/core/devices/wifi/nm-device-iwd-p2p.c
index 01774b12..40e38321 100644
--- a/src/core/devices/wifi/nm-device-iwd-p2p.c
+++ b/src/core/devices/wifi/nm-device-iwd-p2p.c
@@ -1196,8 +1196,6 @@ nm_device_iwd_p2p_new(GDBusObject *dbus_obj)
                         NM_DEVICE_TYPE_WIFI_P2P,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NULL);
 
     if (!self || !nm_device_iwd_p2p_set_dbus_obj(self, dbus_obj))
@@ -1252,6 +1250,8 @@ nm_device_iwd_p2p_class_init(NMDeviceIwdP2PClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_IWD_P2P_PEERS,
                                                     "",
                                                     "",
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c
index 7543d9da..ab37cbec 100644
--- a/src/core/devices/wifi/nm-device-iwd.c
+++ b/src/core/devices/wifi/nm-device-iwd.c
@@ -3426,8 +3426,6 @@ nm_device_iwd_new(const char *iface)
                         NM_DEVICE_TYPE_WIFI,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NULL);
 }
 
@@ -3490,6 +3488,8 @@ nm_device_iwd_class_init(NMDeviceIwdClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_IWD_MODE,
                                                   "",
                                                   "",
diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c
index dfbf8978..4576af95 100644
--- a/src/core/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/core/devices/wifi/nm-device-wifi-p2p.c
@@ -1229,8 +1229,6 @@ nm_device_wifi_p2p_new(const char *iface)
                         NM_DEVICE_TYPE_WIFI_P2P,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NULL);
 }
 
@@ -1293,6 +1291,8 @@ nm_device_wifi_p2p_class_init(NMDeviceWifiP2PClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_PEERS] = g_param_spec_boxed(NM_DEVICE_WIFI_P2P_PEERS,
                                                     "",
                                                     "",
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index 010cee08..d83b1f35 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -978,14 +978,16 @@ deactivate_reset_hw_addr(NMDevice *device)
 static gboolean
 check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error)
 {
-    NMDeviceWifi        *self = NM_DEVICE_WIFI(device);
-    NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
-    NMSettingWireless   *s_wireless;
-    const char          *mac;
-    const char *const   *mac_blacklist;
-    int                  i;
-    const char          *mode;
-    const char          *perm_hw_addr;
+    NMDeviceWifi              *self = NM_DEVICE_WIFI(device);
+    NMDeviceWifiPrivate       *priv = NM_DEVICE_WIFI_GET_PRIVATE(self);
+    NMSettingWireless         *s_wireless;
+    NMSettingWirelessSecurity *s_wsec;
+    const char                *mac;
+    const char *const         *mac_blacklist;
+    int                        i;
+    const char                *mode;
+    const char                *perm_hw_addr;
+    const char                *key_mgmt;
 
     if (!NM_DEVICE_CLASS(nm_device_wifi_parent_class)
              ->check_connection_compatible(device, connection, error))
@@ -1070,6 +1072,20 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
         }
     }
 
+    s_wsec = nm_connection_get_setting_wireless_security(connection);
+    if (s_wsec) {
+        key_mgmt = nm_setting_wireless_security_get_key_mgmt(s_wsec);
+
+        if (nm_supplicant_interface_get_capability(priv->sup_iface, NM_SUPPL_CAP_TYPE_WEP)
+                == NM_TERNARY_FALSE
+            && NM_IN_STRSET(key_mgmt, "ieee8021x", "none")) {
+            nm_utils_error_set_literal(error,
+                                       NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+                                       "wpa_supplicant does not support WEP encryption");
+            return FALSE;
+        }
+    }
+
     // FIXME: check channel/freq/band against bands the hardware supports
     // FIXME: check encryption against device capabilities
     // FIXME: check bitrate against device capabilities
@@ -2936,14 +2952,16 @@ build_supplicant_config(NMDeviceWifi *self,
         }
 
         s_8021x = nm_connection_get_setting_802_1x(connection);
-        if (!nm_supplicant_config_add_setting_wireless_security(config,
-                                                                s_wireless_sec,
-                                                                s_8021x,
-                                                                con_uuid,
-                                                                mtu,
-                                                                pmf,
-                                                                fils,
-                                                                error)) {
+        if (!nm_supplicant_config_add_setting_wireless_security(
+                config,
+                s_wireless_sec,
+                s_8021x,
+                con_uuid,
+                nm_setting_wireless_get_mode(s_wireless),
+                mtu,
+                pmf,
+                fils,
+                error)) {
             g_prefix_error(error, "802-11-wireless-security: ");
             goto error;
         }
@@ -3103,28 +3121,75 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
 static void
 ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP *ap)
 {
-    NMDevice     *device     = NM_DEVICE(self);
-    const char   *band       = nm_setting_wireless_get_band(s_wifi);
-    const guint32 a_freqs[]  = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0};
-    const guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0};
-    guint32       freq       = 0;
+    guint32     a_freqs[]  = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0};
+    guint32     bg_freqs[] = {2412, 2437, 2462, 2472, 0};
+    guint32    *rnd_freqs;
+    guint       rnd_freqs_len;
+    NMDevice   *device = NM_DEVICE(self);
+    const char *band   = nm_setting_wireless_get_band(s_wifi);
+    guint32     freq;
+    guint64     rnd;
+    guint       i;
+    guint       l;
 
-    g_assert(ap);
+    nm_assert(ap);
+    nm_assert(NM_IN_STRSET(band, NULL, "a", "bg"));
 
     if (nm_wifi_ap_get_freq(ap))
         return;
 
-    if (g_strcmp0(band, "a") == 0)
-        freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device),
-                                               nm_device_get_ifindex(device),
-                                               a_freqs);
-    else
-        freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device),
-                                               nm_device_get_ifindex(device),
-                                               bg_freqs);
+    {
+        GBytes       *ssid;
+        gsize         ssid_len;
+        const guint8 *ssid_data;
+        const guint8  random_seed[16] = {0x9a,
+                                        0xdc,
+                                        0x86,
+                                        0x9a,
+                                        0xa8,
+                                        0xa2,
+                                        0x07,
+                                        0x97,
+                                        0xbe,
+                                        0x6d,
+                                        0xe6,
+                                        0x99,
+                                        0x9f,
+                                        0xa8,
+                                        0x09,
+                                        0x2b};
+
+        /* Calculate a stable "random" number based on the SSID. */
+        ssid      = nm_setting_wireless_get_ssid(s_wifi);
+        ssid_data = g_bytes_get_data(ssid, &ssid_len);
+        rnd       = c_siphash_hash(random_seed, ssid_data, ssid_len);
+    }
+
+    if (nm_streq0(band, "a")) {
+        rnd_freqs     = a_freqs;
+        rnd_freqs_len = G_N_ELEMENTS(a_freqs) - 1;
+    } else {
+        rnd_freqs     = bg_freqs;
+        rnd_freqs_len = G_N_ELEMENTS(bg_freqs) - 1;
+    }
+
+    /* shuffle the frequencies (inplace). The idea is to choose
+     * a different frequency depending on the SSID. */
+    for (i = 0, l = rnd_freqs_len; l > 1; i++, l--) {
+        /* Add an arbitrary chosen (prime) number to rnd, to get more "random"
+         * numbers. Since we only shuffle a handful of elements, that's good
+         * enough (and stable). */
+        rnd += 5630246189u;
+        NM_SWAP(&rnd_freqs[i], &rnd_freqs[i + (rnd % l)]);
+    }
+
+    freq = nm_platform_wifi_find_frequency(nm_device_get_platform(device),
+                                           nm_device_get_ifindex(device),
+                                           rnd_freqs);
+    if (freq == 0)
+        freq = rnd_freqs[0];
 
-    if (!freq)
-        freq = (g_strcmp0(band, "a") == 0) ? 5180 : 2462;
+    _LOGD(LOGD_WIFI, "set frequency for hotspot AP to %u", freq);
 
     if (nm_wifi_ap_set_freq(ap, freq))
         _ap_dump(self, LOGL_DEBUG, ap, "updated", 0);
@@ -3663,8 +3728,6 @@ nm_device_wifi_new(const char *iface, _NMDeviceWifiCapabilities capabilities)
                         NM_DEVICE_TYPE_WIFI,
                         NM_DEVICE_LINK_TYPE,
                         NM_LINK_TYPE_WIFI,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WLAN,
                         NM_DEVICE_WIFI_CAPABILITIES,
                         (guint) capabilities,
                         NULL);
@@ -3756,6 +3819,8 @@ nm_device_wifi_class_init(NMDeviceWifiClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WLAN;
+
     obj_properties[PROP_MODE] = g_param_spec_uint(NM_DEVICE_WIFI_MODE,
                                                   "",
                                                   "",
diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c
index 5563ebf8..27222aae 100644
--- a/src/core/devices/wifi/nm-iwd-manager.c
+++ b/src/core/devices/wifi/nm-iwd-manager.c
@@ -948,6 +948,7 @@ mirror_connection(NMIwdManager         *self,
 
     if (!nm_settings_add_connection(
             priv->settings,
+            NULL,
             connection,
             NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
             NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c
index ac0b748a..825a5aa8 100644
--- a/src/core/devices/wifi/nm-wifi-ap.c
+++ b/src/core/devices/wifi/nm-wifi-ap.c
@@ -1037,9 +1037,10 @@ nm_wifi_ap_lookup_for_device(NMDevice *device, const char *exported_path)
 
     g_return_val_if_fail(NM_IS_DEVICE(device), NULL);
 
-    ap = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
-                                       exported_path);
-    if (!ap || !NM_IS_WIFI_AP(ap) || ap->wifi_device != device)
+    ap = nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
+                                                 NM_TYPE_WIFI_AP,
+                                                 exported_path);
+    if (!ap || ap->wifi_device != device)
         return NULL;
 
     return ap;
diff --git a/src/core/devices/wifi/nm-wifi-p2p-peer.c b/src/core/devices/wifi/nm-wifi-p2p-peer.c
index 0a174270..8ccb2a4d 100644
--- a/src/core/devices/wifi/nm-wifi-p2p-peer.c
+++ b/src/core/devices/wifi/nm-wifi-p2p-peer.c
@@ -139,10 +139,11 @@ nm_wifi_p2p_peer_lookup_for_device(NMDevice *device, const char *exported_path)
 
     g_return_val_if_fail(NM_IS_DEVICE(device), NULL);
 
-    peer = (NMWifiP2PPeer *) nm_dbus_manager_lookup_object(
-        nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
-        exported_path);
-    if (!peer || !NM_IS_WIFI_P2P_PEER(peer) || peer->wifi_device != device)
+    peer =
+        nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(device)),
+                                                NM_TYPE_WIFI_P2P_PEER,
+                                                exported_path);
+    if (!peer || peer->wifi_device != device)
         return NULL;
 
     return peer;
diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c
index ef802a44..77ba1b24 100644
--- a/src/core/devices/wwan/nm-device-modem.c
+++ b/src/core/devices/wwan/nm-device-modem.c
@@ -181,8 +181,10 @@ modem_new_config(NMModem                  *modem,
         return;
     }
 
-    if (!IS_IPv4)
+    if (!IS_IPv4) {
         priv->iid = iid ? *iid : ((NMUtilsIPv6IfaceId) NM_UTILS_IPV6_IFACE_ID_INIT);
+        nm_device_sysctl_ip_conf_set(device, AF_INET6, "disable_ipv6", "0");
+    }
 
     if (do_auto) {
         if (IS_IPv4)
@@ -757,8 +759,6 @@ nm_device_modem_new(NMModem *modem)
                         "Broadband",
                         NM_DEVICE_DEVICE_TYPE,
                         NM_DEVICE_TYPE_MODEM,
-                        NM_DEVICE_RFKILL_TYPE,
-                        RFKILL_TYPE_WWAN,
                         NM_DEVICE_MODEM_MODEM,
                         modem,
                         NM_DEVICE_MODEM_CAPABILITIES,
@@ -840,6 +840,8 @@ nm_device_modem_class_init(NMDeviceModemClass *klass)
 
     device_class->state_changed = device_state_changed;
 
+    device_class->rfkill_type = NM_RFKILL_TYPE_WWAN;
+
     obj_properties[PROP_MODEM] =
         g_param_spec_object(NM_DEVICE_MODEM_MODEM,
                             "",
diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c
index b585652e..cbf30f56 100644
--- a/src/core/devices/wwan/nm-modem-broadband.c
+++ b/src/core/devices/wwan/nm-modem-broadband.c
@@ -957,7 +957,7 @@ stage3_ip_config_start(NMModem *modem, int addr_family, NMModemIPMethod ip_metho
     const int                               IS_IPv4 = NM_IS_IPv4(addr_family);
     NMModemBroadband                       *self    = NM_MODEM_BROADBAND(modem);
     nm_auto_unref_l3cd_init NML3ConfigData *l3cd    = NULL;
-    char                                    sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char                                    sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     gs_free_error GError                   *error = NULL;
     const char                             *data_port;
     const char                             *address_string;
diff --git a/src/core/devices/wwan/nm-modem-ofono.c b/src/core/devices/wwan/nm-modem-ofono.c
index cac90d32..0db30046 100644
--- a/src/core/devices/wwan/nm-modem-ofono.c
+++ b/src/core/devices/wwan/nm-modem-ofono.c
@@ -734,7 +734,7 @@ handle_settings(GVariant *v_dict, gpointer user_data)
 {
     NMModemOfono        *self = NM_MODEM_OFONO(user_data);
     NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE(self);
-    char                 sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char                 sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     NMPlatformIP4Address address;
     gboolean             ret = FALSE;
     const char          *interface;
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
index 85e48ff7..2bfd7e01 100644
--- a/src/core/dhcp/nm-dhcp-client.c
+++ b/src/core/dhcp/nm-dhcp-client.c
@@ -109,25 +109,22 @@ nm_dhcp_client_get_pid(NMDhcpClient *self)
     return NM_DHCP_CLIENT_GET_PRIVATE(self)->pid;
 }
 
-static void
-_set_effective_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take)
+void
+nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
 {
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
 
-    nm_assert(!client_id || g_bytes_get_size(client_id) >= 2);
+    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
+    g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2);
+
+    priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
 
-    if (priv->effective_client_id == client_id
-        || (priv->effective_client_id && client_id
-            && g_bytes_equal(priv->effective_client_id, client_id))) {
-        if (take && client_id)
-            g_bytes_unref(client_id);
+    if (nm_g_bytes_equal0(priv->effective_client_id, client_id))
         return;
-    }
 
-    if (priv->effective_client_id)
-        g_bytes_unref(priv->effective_client_id);
+    g_bytes_unref(priv->effective_client_id);
     priv->effective_client_id = client_id;
-    if (!take && client_id)
+    if (client_id)
         g_bytes_ref(client_id);
 
     {
@@ -141,15 +138,6 @@ _set_effective_client_id(NMDhcpClient *self, GBytes *client_id, gboolean take)
     }
 }
 
-void
-nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
-{
-    g_return_if_fail(NM_IS_DHCP_CLIENT(self));
-    g_return_if_fail(!client_id || g_bytes_get_size(client_id) >= 2);
-
-    _set_effective_client_id(self, client_id, FALSE);
-}
-
 /*****************************************************************************/
 
 NM_UTILS_LOOKUP_STR_DEFINE(nm_dhcp_state_to_string,
@@ -678,7 +666,7 @@ nm_dhcp_client_start_ip6(NMDhcpClient *self, GError **error)
     if (!priv->config.v6.enforce_duid)
         own_client_id = NM_DHCP_CLIENT_GET_CLASS(self)->get_duid(self);
 
-    _set_effective_client_id(self, own_client_id ?: priv->config.client_id, FALSE);
+    nm_dhcp_client_set_effective_client_id(self, own_client_id ?: priv->config.client_id);
 
     addr = ipv6_lladdr_find(self);
     if (!addr) {
diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c
index 4b3429ef..280d9ce6 100644
--- a/src/core/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/core/dhcp/nm-dhcp-dhcpcd.c
@@ -70,7 +70,7 @@ ip4_start(NMDhcpClient *client, GError **error)
     const NMDhcpClientConfig    *client_config;
     gs_unref_ptrarray GPtrArray *argv = NULL;
     pid_t                        pid;
-    GError                      *local   = NULL;
+    gs_free_error GError        *local   = NULL;
     gs_free char                *cmd_str = NULL;
     const char                  *dhcpcd_path;
 
@@ -143,7 +143,6 @@ ip4_start(NMDhcpClient *client, GError **error)
                            NM_UTILS_ERROR_UNKNOWN,
                            "dhcpcd failed to start: %s",
                            local->message);
-        g_error_free(local);
         return FALSE;
     }
 
diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c
index d7fbe356..aac18967 100644
--- a/src/core/dhcp/nm-dhcp-nettools.c
+++ b/src/core/dhcp/nm-dhcp-nettools.c
@@ -154,6 +154,7 @@ static gboolean
 lease_parse_address(NDhcp4ClientLease *lease,
                     NML3ConfigData    *l3cd,
                     GHashTable        *options,
+                    in_addr_t         *out_address,
                     GError           **error)
 {
     struct in_addr a_address;
@@ -268,6 +269,8 @@ lease_parse_address(NDhcp4ClientLease *lease,
                                         .preferred    = a_lifetime,
                                     }));
 
+    NM_SET_OUT(out_address, a_address.s_addr);
+
     return TRUE;
 }
 
@@ -326,6 +329,7 @@ lease_parse_address_list(NDhcp4ClientLease       *lease,
 static void
 lease_parse_routes(NDhcp4ClientLease *lease,
                    NML3ConfigData    *l3cd,
+                   in_addr_t          lease_address,
                    GHashTable        *options,
                    NMStrBuf          *sbuf)
 {
@@ -343,6 +347,17 @@ lease_parse_routes(NDhcp4ClientLease *lease,
     int           r;
     guint         i;
 
+    /* Routes can be in option 33 (static-route), 121 (classless-static-route) and 249 (a non-standard classless-static-route).
+     * Option 249 (Microsoft Classless Static Route), is described here:
+     * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dhcpe/f9c19c79-1c7f-4746-b555-0c0fc523f3f9
+     *
+     * We will anyway parse all these 3 options and add them to the "options" hash (as distinct entries).
+     * We will however also parse one of the options into the "l3cd" for configuring routing.
+     * Thereby we prefer 121 over 249 over 33.
+     *
+     * Preferring 121 over 33 is defined by RFC 3443.
+     * Preferring 121 over 249 over 33 is made up as it makes sense (the MS docs are not very clear).
+     */
     for (i = 0; i < 2; i++) {
         const guint8 option_code = (i == 0) ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
                                             : NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE;
@@ -373,10 +388,11 @@ lease_parse_routes(NDhcp4ClientLease *lease,
 
             nm_l3_config_data_add_route_4(l3cd,
                                           &((const NMPlatformIP4Route){
+                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .network       = dest,
                                               .plen          = plen,
                                               .gateway       = gateway,
-                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
+                                              .pref_src      = lease_address,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
@@ -416,10 +432,11 @@ lease_parse_routes(NDhcp4ClientLease *lease,
 
             nm_l3_config_data_add_route_4(l3cd,
                                           &((const NMPlatformIP4Route){
+                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .network       = dest,
                                               .plen          = plen,
                                               .gateway       = gateway,
-                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
+                                              .pref_src      = lease_address,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
@@ -464,6 +481,7 @@ lease_parse_routes(NDhcp4ClientLease *lease,
                                           &((const NMPlatformIP4Route){
                                               .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .gateway       = gateway,
+                                              .pref_src      = lease_address,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
@@ -547,6 +565,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
     const char                             *v_str;
     guint16                                 v_u16;
     in_addr_t                               v_inaddr;
+    in_addr_t                               lease_address;
     struct in_addr                          v_inaddr_s;
     int                                     r;
 
@@ -556,7 +575,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
 
     options = nm_dhcp_option_create_options_dict();
 
-    if (!lease_parse_address(lease, l3cd, options, error))
+    if (!lease_parse_address(lease, l3cd, options, &lease_address, error))
         return NULL;
 
     r = n_dhcp4_client_lease_get_server_identifier(lease, &v_inaddr_s);
@@ -575,7 +594,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
                                           v_inaddr);
     }
 
-    lease_parse_routes(lease, l3cd, options, &sbuf);
+    lease_parse_routes(lease, l3cd, lease_address, options, &sbuf);
 
     lease_parse_address_list(lease, l3cd, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER, options, &sbuf);
 
diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c
index 0884def3..14a121e7 100644
--- a/src/core/dhcp/nm-dhcp-systemd.c
+++ b/src/core/dhcp/nm-dhcp-systemd.c
@@ -81,16 +81,20 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
     const struct in_addr                   *addr_list;
     char                                    addr_str[NM_UTILS_INET_ADDRSTRLEN];
     const char                             *s;
-    nm_auto_free_gstring GString           *str            = NULL;
-    nm_auto_free sd_dhcp_route            **routes         = NULL;
-    const char *const                      *search_domains = NULL;
+    nm_auto_free_gstring GString           *str              = NULL;
+    nm_auto_free sd_dhcp_route            **routes_static    = NULL;
+    nm_auto_free sd_dhcp_route            **routes_classless = NULL;
+    const char *const                      *search_domains   = NULL;
+    guint32                                 default_route_metric_offset;
     guint16                                 mtu;
-    int                                     i, num;
+    int                                     i;
+    int                                     num;
+    int                                     is_classless;
+    int                                     n_routes_static;
+    int                                     n_routes_classless;
     const void                             *data;
     gsize                                   data_len;
     gboolean                                has_router_from_classless = FALSE;
-    gboolean                                has_classless_route       = FALSE;
-    gboolean                                has_static_route          = FALSE;
     const gint32                            ts      = nm_utils_get_monotonic_timestamp_sec();
     gint64                                  ts_time = time(NULL);
     struct in_addr                          a_address;
@@ -226,44 +230,27 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
         nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_HOST_NAME, s);
     }
 
-    num = sd_dhcp_lease_get_routes(lease, &routes);
-    if (num > 0) {
-        nm_auto_free_gstring GString *str_classless               = NULL;
-        nm_auto_free_gstring GString *str_static                  = NULL;
-        guint32                       default_route_metric_offset = 0;
+    default_route_metric_offset = 0;
+    n_routes_static             = sd_dhcp_lease_get_static_routes(lease, &routes_static);
+    n_routes_classless          = sd_dhcp_lease_get_classless_routes(lease, &routes_classless);
+    for (is_classless = 1; is_classless >= 0; is_classless--) {
+        int                   n_routes = (is_classless ? n_routes_classless : n_routes_static);
+        sd_dhcp_route *const *routes   = (is_classless ? routes_classless : routes_static);
 
-        for (i = 0; i < num; i++) {
-            switch (sd_dhcp_route_get_option(routes[i])) {
-            case NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE:
-                has_classless_route = TRUE;
-                break;
-            case NM_DHCP_OPTION_DHCP4_STATIC_ROUTE:
-                has_static_route = TRUE;
-                break;
-            }
-        }
+        if (n_routes <= 0)
+            continue;
 
-        if (has_classless_route)
-            str_classless = g_string_sized_new(30);
-        if (has_static_route)
-            str_static = g_string_sized_new(30);
+        nm_gstring_prepare(&str);
 
-        for (i = 0; i < num; i++) {
+        for (i = 0; i < n_routes; i++) {
             char           network_net_str[NM_UTILS_INET_ADDRSTRLEN];
             char           gateway_str[NM_UTILS_INET_ADDRSTRLEN];
             guint8         r_plen;
             struct in_addr r_network;
             struct in_addr r_gateway;
             in_addr_t      network_net;
-            int            option;
             guint32        m;
 
-            option = sd_dhcp_route_get_option(routes[i]);
-            if (!NM_IN_SET(option,
-                           NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE,
-                           NM_DHCP_OPTION_DHCP4_STATIC_ROUTE))
-                continue;
-
             if (sd_dhcp_route_get_destination(routes[i], &r_network) < 0)
                 continue;
             if (sd_dhcp_route_get_destination_prefix_length(routes[i], &r_plen) < 0 || r_plen > 32)
@@ -275,31 +262,28 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
             _nm_utils_inet4_ntop(network_net, network_net_str);
             _nm_utils_inet4_ntop(r_gateway.s_addr, gateway_str);
 
-            g_string_append_printf(
-                nm_gstring_add_space_delimiter(option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
-                                                   ? str_classless
-                                                   : str_static),
-                "%s/%d %s",
-                network_net_str,
-                (int) r_plen,
-                gateway_str);
+            g_string_append_printf(nm_gstring_add_space_delimiter(str),
+                                   "%s/%d %s",
+                                   network_net_str,
+                                   (int) r_plen,
+                                   gateway_str);
 
-            if (option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE && has_classless_route) {
+            if (!is_classless && n_routes_classless > 0) {
                 /* RFC 3443: if the DHCP server returns both a Classless Static Routes
                  * option and a Static Routes option, the DHCP client MUST ignore the
                  * Static Routes option. */
                 continue;
             }
 
-            if (r_plen == 0 && option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE) {
-                /* for option 33 (static route), RFC 2132 says:
-                 *
-                 * The default route (0.0.0.0) is an illegal destination for a static
-                 * route. */
-                continue;
-            }
-
             if (r_plen == 0) {
+                if (!is_classless) {
+                    /* for option 33 (static route), RFC 2132 says:
+                     *
+                     * The default route (0.0.0.0) is an illegal destination for a static
+                     * route. */
+                    continue;
+                }
+
                 /* if there are multiple default routes, we add them with differing
                  * metrics. */
                 m                         = default_route_metric_offset++;
@@ -309,10 +293,11 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
 
             nm_l3_config_data_add_route_4(l3cd,
                                           &((const NMPlatformIP4Route){
+                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .network       = network_net,
                                               .plen          = r_plen,
                                               .gateway       = r_gateway.s_addr,
-                                              .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
+                                              .pref_src      = a_address.s_addr,
                                               .metric_any    = TRUE,
                                               .metric        = m,
                                               .table_any     = TRUE,
@@ -320,21 +305,18 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
                                           }));
         }
 
-        if (str_classless && str_classless->len > 0)
-            nm_dhcp_option_add_option(options,
-                                      AF_INET,
-                                      NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE,
-                                      str_classless->str);
-        if (str_static && str_static->len > 0)
+        if (str->len > 0) {
             nm_dhcp_option_add_option(options,
                                       AF_INET,
-                                      NM_DHCP_OPTION_DHCP4_STATIC_ROUTE,
-                                      str_static->str);
+                                      is_classless ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE
+                                                   : NM_DHCP_OPTION_DHCP4_STATIC_ROUTE,
+                                      str->str);
+        }
     }
 
     num = sd_dhcp_lease_get_router(lease, &a_router);
     if (num > 0) {
-        guint32 default_route_metric_offset = 0;
+        default_route_metric_offset = 0;
 
         nm_gstring_prepare(&str);
         for (i = 0; i < num; i++) {
@@ -366,6 +348,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
                                           &((const NMPlatformIP4Route){
                                               .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
                                               .gateway       = a_router[i].s_addr,
+                                              .pref_src      = a_address.s_addr,
                                               .table_any     = TRUE,
                                               .table_coerced = 0,
                                               .metric_any    = TRUE,
diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c
index 214e94cd..a0eec6e0 100644
--- a/src/core/dhcp/nm-dhcp-utils.c
+++ b/src/core/dhcp/nm-dhcp-utils.c
@@ -28,7 +28,8 @@ static gboolean
 ip4_process_dhcpcd_rfc3442_routes(const char     *iface,
                                   const char     *str,
                                   NML3ConfigData *l3cd,
-                                  guint32        *gwaddr)
+                                  in_addr_t       address,
+                                  guint32        *out_gwaddr)
 {
     gs_free const char **routes = NULL;
     const char         **r;
@@ -79,7 +80,7 @@ ip4_process_dhcpcd_rfc3442_routes(const char     *iface,
         have_routes = TRUE;
         if (rt_cidr == 0 && rt_addr == 0) {
             /* FIXME: how to handle multiple routers? */
-            *gwaddr = rt_route;
+            *out_gwaddr = rt_route;
         } else {
             _LOG2I(LOGD_DHCP4,
                    iface,
@@ -91,13 +92,13 @@ ip4_process_dhcpcd_rfc3442_routes(const char     *iface,
             nm_l3_config_data_add_route_4(
                 l3cd,
                 &((const NMPlatformIP4Route){
+                    .rt_source  = NM_IP_CONFIG_SOURCE_DHCP,
                     .network    = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr),
                     .plen       = rt_cidr,
                     .gateway    = rt_route,
-                    .rt_source  = NM_IP_CONFIG_SOURCE_DHCP,
+                    .pref_src   = address,
                     .metric_any = TRUE,
                     .table_any  = TRUE,
-
                 }));
         }
     }
@@ -158,7 +159,8 @@ static gboolean
 ip4_process_dhclient_rfc3442_routes(const char     *iface,
                                     const char     *str,
                                     NML3ConfigData *l3cd,
-                                    guint32        *gwaddr)
+                                    in_addr_t       address,
+                                    guint32        *out_gwaddr)
 {
     gs_free const char **octets = NULL;
     const char *const   *o;
@@ -182,13 +184,14 @@ ip4_process_dhclient_rfc3442_routes(const char     *iface,
         have_routes = TRUE;
         if (!route.plen) {
             /* gateway passed as classless static route */
-            *gwaddr = route.gateway;
+            *out_gwaddr = route.gateway;
         } else {
             char b1[INET_ADDRSTRLEN];
             char b2[INET_ADDRSTRLEN];
 
             /* normal route */
             route.rt_source     = NM_IP_CONFIG_SOURCE_DHCP;
+            route.pref_src      = address;
             route.table_any     = TRUE;
             route.table_coerced = 0;
             route.metric_any    = TRUE;
@@ -212,14 +215,15 @@ static gboolean
 ip4_process_classless_routes(const char     *iface,
                              GHashTable     *options,
                              NML3ConfigData *l3cd,
-                             guint32        *gwaddr)
+                             in_addr_t       address,
+                             guint32        *out_gwaddr)
 {
     const char *str, *p;
 
     g_return_val_if_fail(options != NULL, FALSE);
     g_return_val_if_fail(l3cd != NULL, FALSE);
 
-    *gwaddr = 0;
+    *out_gwaddr = 0;
 
     /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a
      * slightly different format:
@@ -266,14 +270,17 @@ ip4_process_classless_routes(const char     *iface,
 
     if (strchr(str, '/')) {
         /* dhcpcd format */
-        return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, gwaddr);
+        return ip4_process_dhcpcd_rfc3442_routes(iface, str, l3cd, address, out_gwaddr);
     }
 
-    return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, gwaddr);
+    return ip4_process_dhclient_rfc3442_routes(iface, str, l3cd, address, out_gwaddr);
 }
 
 static void
-process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *l3cd)
+process_classful_routes(const char     *iface,
+                        GHashTable     *options,
+                        NML3ConfigData *l3cd,
+                        in_addr_t       address)
 {
     gs_free const char **searches = NULL;
     const char         **s;
@@ -293,6 +300,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *
     }
 
     for (s = searches; *s; s += 2) {
+        char               sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
         NMPlatformIP4Route route;
         guint32            rt_addr, rt_route;
 
@@ -321,6 +329,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *
             route.plen = 32;
         }
         route.gateway       = rt_route;
+        route.pref_src      = address;
         route.rt_source     = NM_IP_CONFIG_SOURCE_DHCP;
         route.table_any     = TRUE;
         route.table_coerced = 0;
@@ -334,7 +343,7 @@ process_classful_routes(const char *iface, GHashTable *options, NML3ConfigData *
         _LOG2I(LOGD_DHCP,
                iface,
                "  static route %s",
-               nm_platform_ip4_route_to_string(&route, NULL, 0));
+               nm_platform_ip4_route_to_string(&route, sbuf, sizeof(sbuf)));
     }
 }
 
@@ -422,8 +431,8 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
     /* Routes: if the server returns classless static routes, we MUST ignore
      * the 'static_routes' option.
      */
-    if (!ip4_process_classless_routes(iface, options, l3cd, &gateway))
-        process_classful_routes(iface, options, l3cd);
+    if (!ip4_process_classless_routes(iface, options, l3cd, address.address, &gateway))
+        process_classful_routes(iface, options, l3cd, address.address);
 
     if (gateway) {
         _LOG2I(LOGD_DHCP4, iface, "  gateway %s", _nm_utils_inet4_ntop(gateway, sbuf));
@@ -453,6 +462,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
         const NMPlatformIP4Route r = {
             .rt_source     = NM_IP_CONFIG_SOURCE_DHCP,
             .gateway       = gateway,
+            .pref_src      = address.address,
             .table_any     = TRUE,
             .table_coerced = 0,
             .metric_any    = TRUE,
diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c
index f0abf564..43426882 100644
--- a/src/core/dns/nm-dns-dnsmasq.c
+++ b/src/core/dns/nm-dns-dnsmasq.c
@@ -39,10 +39,10 @@
 #define _NMLOG(level, ...) __NMLOG_DEFAULT(level, _NMLOG_DOMAIN, "dnsmasq", __VA_ARGS__)
 
 #define WAIT_MSEC_AFTER_SIGTERM 1000
-G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGTERM <= NM_SHUTDOWN_TIMEOUT_MS);
+G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGTERM <= NM_SHUTDOWN_TIMEOUT_MAX_MSEC);
 
 #define WAIT_MSEC_AFTER_SIGKILL 400
-G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGKILL + 100 <= NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG);
+G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGKILL + 100 <= NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC);
 
 typedef void (*GlPidSpawnAsyncNotify)(GCancellable *cancellable,
                                       GPid          pid,
@@ -848,7 +848,7 @@ static GVariant *
 create_update_args(NMDnsDnsmasq            *self,
                    const NMGlobalDnsConfig *global_config,
                    const CList             *ip_data_lst_head,
-                   const char              *hostname)
+                   const char              *hostdomain)
 {
     GVariantBuilder          servers;
     const NMDnsConfigIPData *ip_data;
@@ -1124,7 +1124,7 @@ static gboolean
 update(NMDnsPlugin             *plugin,
        const NMGlobalDnsConfig *global_config,
        const CList             *ip_data_lst_head,
-       const char              *hostname,
+       const char              *hostdomain,
        GError                 **error)
 {
     NMDnsDnsmasq        *self = NM_DNS_DNSMASQ(plugin);
@@ -1135,7 +1135,7 @@ update(NMDnsPlugin             *plugin,
 
     nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref);
     priv->set_server_ex_args =
-        g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostname));
+        g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostdomain));
 
     send_dnsmasq_update(self);
     return TRUE;
diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c
index efbf847b..566f3d66 100644
--- a/src/core/dns/nm-dns-manager.c
+++ b/src/core/dns/nm-dns-manager.c
@@ -26,6 +26,7 @@
 
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device.h"
@@ -97,7 +98,7 @@ typedef struct {
 
     bool config_changed : 1;
 
-    char *hostname;
+    char *hostdomain;
     guint updates_queue;
 
     guint8 hash[HASH_LEN];      /* SHA1 hash of current DNS config */
@@ -1260,24 +1261,8 @@ _collect_resolv_conf_data(NMDnsManager      *self,
         }
     }
 
-    /* If the hostname is a FQDN ("dcbw.example.com"), then add the domain part of it
-     * ("example.com") to the searches list, to ensure that we can still resolve its
-     * non-FQ form ("dcbw") too. (Also, if there are no other search domains specified,
-     * this makes a good default.) However, if the hostname is the top level of a domain
-     * (eg, "example.com"), then use the hostname itself as the search (since the user is
-     * unlikely to want "com" as a search domain).
-     */
-    if (priv->hostname) {
-        const char *hostdomain = strchr(priv->hostname, '.');
-
-        if (hostdomain && !nm_utils_ipaddr_is_valid(AF_UNSPEC, priv->hostname)) {
-            hostdomain++;
-            if (domain_is_valid(hostdomain, TRUE))
-                add_string_item(rc.searches, hostdomain, TRUE);
-            else if (domain_is_valid(priv->hostname, TRUE))
-                add_string_item(rc.searches, priv->hostname, TRUE);
-        }
-    }
+    if (priv->hostdomain)
+        add_string_item(rc.searches, priv->hostdomain, TRUE);
 
     if (rc.has_trust_ad == NM_TERNARY_TRUE)
         g_ptr_array_add(rc.options, g_strdup(NM_SETTING_DNS_OPTION_TRUST_AD));
@@ -1695,7 +1680,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError
         nm_dns_plugin_update(priv->sd_resolve_plugin,
                              global_config,
                              _mgr_get_ip_data_lst_head(self),
-                             priv->hostname,
+                             priv->hostdomain,
                              NULL);
     }
 
@@ -1717,7 +1702,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError
         if (!nm_dns_plugin_update(plugin,
                                   global_config,
                                   _mgr_get_ip_data_lst_head(self),
-                                  priv->hostname,
+                                  priv->hostdomain,
                                   &plugin_error)) {
             _LOGW("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message);
 
@@ -2003,27 +1988,39 @@ done:
 }
 
 void
-nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname)
-{
-    NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
-
-    g_free(priv->hostname);
-    priv->hostname = g_strdup(hostname);
-}
-
-void
 nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update)
 {
-    NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+    NMDnsManagerPrivate *priv   = NM_DNS_MANAGER_GET_PRIVATE(self);
+    const char          *domain = NULL;
 
     /* Certain hostnames we don't want to include in resolv.conf 'searches' */
-    if (hostname && nm_utils_is_specific_hostname(hostname) && !strstr(hostname, ".in-addr.arpa")
-        && strchr(hostname, '.')) {
-        /* pass */
-    } else
-        hostname = NULL;
+    if (hostname && nm_utils_is_specific_hostname(hostname)
+        && !g_str_has_suffix(hostname, ".in-addr.arpa")
+        && !nm_utils_ipaddr_is_valid(AF_UNSPEC, hostname)) {
+        domain = strchr(hostname, '.');
+        if (domain) {
+            domain++;
+            /* If the hostname is a FQDN ("dcbw.example.com"), then add
+             * the domain part of it ("example.com") to the searches list,
+             * to ensure that we can still resolve its non-FQ form
+             * ("dcbw") too. (Also, if there are no other search domains
+             * specified, this makes a good default.) However, if the
+             * hostname is the top level of a domain (eg, "example.com"),
+             * then use the hostname itself as the search (since the user
+             * is unlikely to want "com" as a search domain).a
+             */
+            if (domain_is_valid(domain, TRUE)) {
+                /* pass */
+            } else if (domain_is_valid(hostname, TRUE)) {
+                domain = hostname;
+            }
+
+            if (!nm_sd_hostname_is_valid(domain, FALSE))
+                domain = NULL;
+        }
+    }
 
-    if (!nm_strdup_reset(&priv->hostname, hostname))
+    if (!nm_strdup_reset(&priv->hostdomain, domain))
         return;
 
     if (skip_update)
@@ -2668,7 +2665,7 @@ finalize(GObject *object)
     NMDnsManager        *self = NM_DNS_MANAGER(object);
     NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
-    g_free(priv->hostname);
+    g_free(priv->hostdomain);
     g_free(priv->mode);
 
     G_OBJECT_CLASS(nm_dns_manager_parent_class)->finalize(object);
diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h
index 84821ece..c30d4b3a 100644
--- a/src/core/dns/nm-dns-manager.h
+++ b/src/core/dns/nm-dns-manager.h
@@ -105,7 +105,6 @@ gboolean nm_dns_manager_set_ip_config(NMDnsManager         *self,
                                       NMDnsIPConfigType     ip_config_type,
                                       gboolean              replace_all);
 
-void nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname);
 void nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update);
 
 /**
diff --git a/src/core/dns/nm-dns-plugin.c b/src/core/dns/nm-dns-plugin.c
index 2126da77..847d7839 100644
--- a/src/core/dns/nm-dns-plugin.c
+++ b/src/core/dns/nm-dns-plugin.c
@@ -60,7 +60,7 @@ gboolean
 nm_dns_plugin_update(NMDnsPlugin             *self,
                      const NMGlobalDnsConfig *global_config,
                      const CList             *ip_config_lst_head,
-                     const char              *hostname,
+                     const char              *hostdomain,
                      GError                 **error)
 {
     g_return_val_if_fail(NM_DNS_PLUGIN_GET_CLASS(self)->update != NULL, FALSE);
@@ -68,7 +68,7 @@ nm_dns_plugin_update(NMDnsPlugin             *self,
     return NM_DNS_PLUGIN_GET_CLASS(self)->update(self,
                                                  global_config,
                                                  ip_config_lst_head,
-                                                 hostname,
+                                                 hostdomain,
                                                  error);
 }
 
diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h
index bc854cce..f9c424ab 100644
--- a/src/core/dns/nm-dns-plugin.h
+++ b/src/core/dns/nm-dns-plugin.h
@@ -34,7 +34,7 @@ typedef struct {
     gboolean (*update)(NMDnsPlugin             *self,
                        const NMGlobalDnsConfig *global_config,
                        const CList             *ip_config_lst_head,
-                       const char              *hostname,
+                       const char              *hostdomain,
                        GError                 **error);
 
     void (*stop)(NMDnsPlugin *self);
diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c
index a043e855..c4993884 100644
--- a/src/core/dns/nm-dns-systemd-resolved.c
+++ b/src/core/dns/nm-dns-systemd-resolved.c
@@ -551,7 +551,7 @@ static gboolean
 update(NMDnsPlugin             *plugin,
        const NMGlobalDnsConfig *global_config,
        const CList             *ip_data_lst_head,
-       const char              *hostname,
+       const char              *hostdomain,
        GError                 **error)
 {
     NMDnsSystemdResolved          *self            = NM_DNS_SYSTEMD_RESOLVED(plugin);
diff --git a/src/core/dns/nm-dns-unbound.c b/src/core/dns/nm-dns-unbound.c
index f5a53332..8a75cf08 100644
--- a/src/core/dns/nm-dns-unbound.c
+++ b/src/core/dns/nm-dns-unbound.c
@@ -28,7 +28,7 @@ static gboolean
 update(NMDnsPlugin             *plugin,
        const NMGlobalDnsConfig *global_config,
        const CList             *ip_config_lst_head,
-       const char              *hostname,
+       const char              *hostdomain,
        GError                 **error)
 {
     char                 *argv[] = {DNSSEC_TRIGGER_PATH, "--async", "--update", NULL};
diff --git a/src/core/dnsmasq/nm-dnsmasq-manager.c b/src/core/dnsmasq/nm-dnsmasq-manager.c
index 4ab91e7e..be463da3 100644
--- a/src/core/dnsmasq/nm-dnsmasq-manager.c
+++ b/src/core/dnsmasq/nm-dnsmasq-manager.c
@@ -67,9 +67,12 @@ dm_watch_cb(GPid pid, int status, gpointer user_data)
     guint                    err;
 
     if (WIFEXITED(status)) {
+        char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
+
         err = WEXITSTATUS(status);
         if (err != 0) {
-            _LOGW("dnsmasq exited with error: %s", nm_utils_dnsmasq_status_to_string(err, NULL, 0));
+            _LOGW("dnsmasq exited with error: %s",
+                  nm_utils_dnsmasq_status_to_string(err, sbuf, sizeof(sbuf)));
         }
     } else if (WIFSTOPPED(status)) {
         _LOGW("dnsmasq stopped unexpectedly with signal %d", WSTOPSIG(status));
@@ -188,8 +191,6 @@ create_dm_cmd_line(const char           *iface,
         nm_strv_ptrarray_add_string_dup(cmd, "--dhcp-option-force=43,ANDROID_METERED");
     }
 
-    nm_strv_ptrarray_add_string_dup(cmd, "--dhcp-lease-max=50");
-
     nm_strv_ptrarray_add_string_printf(cmd,
                                        "--dhcp-leasefile=%s/dnsmasq-%s.leases",
                                        NMSTATEDIR,
diff --git a/src/core/main.c b/src/core/main.c
index e39a9614..1a9c3a65 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -302,8 +302,6 @@ main(int argc, char *argv[])
     g_type_ensure(G_TYPE_DBUS_CONNECTION);
     g_type_ensure(NM_TYPE_DBUS_MANAGER);
 
-    main_loop = g_main_loop_new(NULL, FALSE);
-
     /* we determine a first-start (contrary to a restart during the same boot)
      * based on the existence of NM_CONFIG_DEVICE_STATE_DIR directory. */
     config_cli = nm_config_cmd_line_options_new(
@@ -403,6 +401,8 @@ main(int argc, char *argv[])
         wrote_pidfile = nm_main_utils_write_pidfile(global_opt.pidfile);
     }
 
+    main_loop = g_main_loop_new(NULL, FALSE);
+
     /* Set up unix signal handling - before creating threads, but after daemonizing! */
     nm_main_utils_setup_signals(main_loop);
 
diff --git a/src/core/ndisc/nm-ndisc.c b/src/core/ndisc/nm-ndisc.c
index 969eacfa..04b673e5 100644
--- a/src/core/ndisc/nm-ndisc.c
+++ b/src/core/ndisc/nm-ndisc.c
@@ -996,6 +996,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd)
     const NMPObject       *obj;
     guint                  len;
     guint                  i;
+    gint32                 fake_now = NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000;
 
     nm_assert(NM_IS_NDISC(ndisc));
     nm_assert(nm_ndisc_get_node_type(ndisc) == NM_NDISC_NODE_TYPE_ROUTER);
@@ -1018,7 +1019,7 @@ nm_ndisc_set_config(NMNDisc *ndisc, const NML3ConfigData *l3cd)
         lifetime = nmp_utils_lifetime_get(addr->timestamp,
                                           addr->lifetime,
                                           addr->preferred,
-                                          NM_NDISC_EXPIRY_BASE_TIMESTAMP / 1000,
+                                          &fake_now,
                                           &preferred);
         if (!lifetime)
             continue;
diff --git a/src/core/nm-checkpoint-manager.c b/src/core/nm-checkpoint-manager.c
index 4a3e7aaf..6c9ea2a6 100644
--- a/src/core/nm-checkpoint-manager.c
+++ b/src/core/nm-checkpoint-manager.c
@@ -259,10 +259,11 @@ nm_checkpoint_manager_lookup_by_path(NMCheckpointManager *self, const char *path
 
     g_return_val_if_fail(self, NULL);
 
-    checkpoint =
-        nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(GET_MANAGER(self))),
-                                      path);
-    if (!checkpoint || !NM_IS_CHECKPOINT(checkpoint)) {
+    checkpoint = nm_dbus_manager_lookup_object_with_type(
+        nm_dbus_object_get_manager(NM_DBUS_OBJECT(GET_MANAGER(self))),
+        NM_TYPE_CHECKPOINT,
+        path);
+    if (!checkpoint) {
         g_set_error(error,
                     NM_MANAGER_ERROR,
                     NM_MANAGER_ERROR_INVALID_ARGUMENTS,
diff --git a/src/core/nm-checkpoint.c b/src/core/nm-checkpoint.c
index 5b48f91a..15667332 100644
--- a/src/core/nm-checkpoint.c
+++ b/src/core/nm-checkpoint.c
@@ -231,6 +231,7 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
             persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
             nm_settings_connection_update(
                 connection,
+                NULL,
                 dev_checkpoint->settings_connection,
                 persist_mode,
                 sett_flags,
@@ -247,6 +248,7 @@ restore_and_activate_connection(NMCheckpoint *self, DeviceCheckpoint *dev_checkp
 
         persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_TO_DISK;
         if (!nm_settings_add_connection(NM_SETTINGS_GET,
+                                        NULL,
                                         dev_checkpoint->settings_connection,
                                         persist_mode,
                                         NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/nm-config.c b/src/core/nm-config.c
index 60a2f1df..ea1c2ab3 100644
--- a/src/core/nm-config.c
+++ b/src/core/nm-config.c
@@ -2870,6 +2870,7 @@ _set_config_data(NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags rel
     NMConfigData       *old_data = priv->config_data;
     NMConfigChangeFlags changes, changes_diff;
     gboolean            had_new_data = !!new_data;
+    char                sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
 
     nm_assert(reload_flags);
     nm_assert(!NM_FLAGS_ANY(reload_flags, ~NM_CONFIG_CHANGE_CAUSES));
@@ -2901,15 +2902,15 @@ _set_config_data(NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags rel
 
     if (new_data) {
         _LOGI("signal: %s (%s)",
-              nm_config_change_flags_to_string(changes, NULL, 0),
+              nm_config_change_flags_to_string(changes, sbuf, sizeof(sbuf)),
               nm_config_data_get_config_description(new_data));
         nm_config_data_log(new_data, "CONFIG: ", "  ", priv->no_auto_default_file, NULL);
         priv->config_data = new_data;
     } else if (had_new_data)
         _LOGI("signal: %s (no changes from disk)",
-              nm_config_change_flags_to_string(changes, NULL, 0));
+              nm_config_change_flags_to_string(changes, sbuf, sizeof(sbuf)));
     else
-        _LOGI("signal: %s", nm_config_change_flags_to_string(changes, NULL, 0));
+        _LOGI("signal: %s", nm_config_change_flags_to_string(changes, sbuf, sizeof(sbuf)));
     g_signal_emit(self, signals[SIGNAL_CONFIG_CHANGED], 0, new_data ?: old_data, changes, old_data);
     if (new_data)
         g_object_unref(old_data);
diff --git a/src/core/nm-connectivity.c b/src/core/nm-connectivity.c
index c84d8a56..eea8adb2 100644
--- a/src/core/nm-connectivity.c
+++ b/src/core/nm-connectivity.c
@@ -622,6 +622,75 @@ _timeout_cb(gpointer user_data)
     cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "timeout");
     return G_SOURCE_REMOVE;
 }
+
+static gboolean
+easy_debug_enabled(void)
+{
+    static int enabled = 0;
+    int        e;
+
+    /* libcurl debug logging can be useful, but is very verbose.
+     * Only enable it when we have a certain environment variable set. */
+
+again:
+    e = g_atomic_int_get(&enabled);
+    if (G_UNLIKELY(e == 0)) {
+        e = _nm_utils_ascii_str_to_bool(g_getenv("NM_LOG_CONCHECK"), FALSE) ? 1 : -1;
+        if (G_UNLIKELY(!g_atomic_int_compare_and_exchange(&enabled, 0, e)))
+            goto again;
+    }
+    return e >= 0;
+}
+
+static int
+easy_debug_cb(CURL *handle, curl_infotype type, char *data, size_t size, void *userptr)
+{
+    NMConnectivityCheckHandle *cb_data      = userptr;
+    gs_free char              *data_escaped = NULL;
+    const char                *msg;
+    gboolean                   print_data = FALSE;
+
+    switch (type) {
+    case CURLINFO_TEXT:
+        print_data = TRUE;
+        msg        = "== Info: ";
+        break;
+    case CURLINFO_DATA_OUT:
+        msg = "=> Send data";
+        break;
+    case CURLINFO_SSL_DATA_OUT:
+        msg = "=> Send SSL data";
+        break;
+    case CURLINFO_HEADER_IN:
+        msg = "<= Recv header";
+        break;
+    case CURLINFO_DATA_IN:
+        msg = "<= Recv data";
+        break;
+    case CURLINFO_SSL_DATA_IN:
+        msg = "<= Recv SSL data";
+        break;
+    case CURLINFO_HEADER_OUT:
+        print_data = TRUE;
+        msg        = "=> Send header: ";
+        return 0;
+    default:
+        return 0;
+    }
+
+    _LOG2T("libcurl %s%s%s%s",
+           msg,
+           NM_PRINT_FMT_QUOTED(print_data,
+                               "[",
+                               (data_escaped = nm_utils_buf_utf8safe_escape_cp(
+                                    data,
+                                    size,
+                                    NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL))
+                                   ?: "",
+                               "]",
+                               ""));
+    return 0;
+}
 #endif
 
 static gboolean
@@ -641,12 +710,14 @@ _idle_cb(gpointer user_data)
 
 #if WITH_CONCHECK
 static void
-do_curl_request(NMConnectivityCheckHandle *cb_data)
+do_curl_request(NMConnectivityCheckHandle *cb_data, const char *hosts)
 {
     CURLM *mhandle;
     CURL  *ehandle;
     long   resolve;
 
+    _LOG2T("set curl resolve list to '%s'", hosts);
+
     mhandle = curl_multi_init();
     if (!mhandle) {
         cb_data_complete(cb_data, NM_CONNECTIVITY_ERROR, "curl error");
@@ -660,6 +731,8 @@ do_curl_request(NMConnectivityCheckHandle *cb_data)
         return;
     }
 
+    cb_data->concheck.hosts = curl_slist_append(NULL, hosts);
+
     cb_data->concheck.curl_mhandle    = mhandle;
     cb_data->concheck.curl_ehandle    = ehandle;
     cb_data->concheck.request_headers = curl_slist_append(NULL, "Connection: close");
@@ -696,12 +769,103 @@ do_curl_request(NMConnectivityCheckHandle *cb_data)
     curl_easy_setopt(ehandle, CURLOPT_RESOLVE, cb_data->concheck.hosts);
     curl_easy_setopt(ehandle, CURLOPT_IPRESOLVE, resolve);
     curl_easy_setopt(ehandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+    if (_LOGT_ENABLED() && easy_debug_enabled()) {
+        curl_easy_setopt(ehandle, CURLOPT_DEBUGFUNCTION, easy_debug_cb);
+        curl_easy_setopt(ehandle, CURLOPT_DEBUGDATA, cb_data);
+        curl_easy_setopt(ehandle, CURLOPT_VERBOSE, 1L);
+    }
 
     curl_multi_add_handle(mhandle, ehandle);
 }
 
 static void
-resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data)
+system_resolver_resolve_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+    nm_auto_str_buf NMStrBuf   strbuf_hosts = NM_STR_BUF_INIT(0, FALSE);
+    gs_free_error GError      *resolv_error = NULL;
+    GList                     *list;
+    GList                     *iter;
+    NMConnectivityCheckHandle *cb_data;
+
+    list = g_resolver_lookup_by_name_finish(G_RESOLVER(source_object), res, &resolv_error);
+
+    if (nm_utils_error_is_cancelled(resolv_error))
+        return;
+
+    cb_data = user_data;
+
+    nm_assert(cb_data);
+    nm_assert(NM_IS_CONNECTIVITY(cb_data->self));
+
+    if (resolv_error) {
+        _LOG2D("failure to resolve name: %s", resolv_error->message);
+        cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error");
+        return;
+    }
+
+    for (iter = list; iter; iter = iter->next) {
+        GInetAddress *a = iter->data;
+        char          str_addr[NM_UTILS_INET_ADDRSTRLEN];
+        int           addr_family;
+
+        switch (g_inet_address_get_family(a)) {
+        case G_SOCKET_FAMILY_IPV4:
+            addr_family = AF_INET;
+            break;
+        case G_SOCKET_FAMILY_IPV6:
+            addr_family = AF_INET6;
+            break;
+        default:
+            addr_family = AF_UNSPEC;
+            break;
+        }
+        if (cb_data->addr_family != AF_UNSPEC && cb_data->addr_family != addr_family)
+            continue;
+
+        if (strbuf_hosts.len == 0) {
+            nm_str_buf_append_printf(&strbuf_hosts,
+                                     "%s:%s:",
+                                     cb_data->concheck.con_config->host,
+                                     cb_data->concheck.con_config->port ?: "80");
+        } else
+            nm_str_buf_append_c(&strbuf_hosts, ',');
+
+        nm_str_buf_append(&strbuf_hosts,
+                          nm_utils_inet_ntop(addr_family, g_inet_address_to_bytes(a), str_addr));
+    }
+
+    g_list_free_full(list, g_object_unref);
+
+    if (strbuf_hosts.len == 0) {
+        _LOG2D("system resolver returned no usable IPv%c addresses",
+               nm_utils_addr_family_to_char(cb_data->addr_family));
+        cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error");
+        return;
+    }
+
+    do_curl_request(cb_data, nm_str_buf_get_str(&strbuf_hosts));
+}
+
+static void
+system_resolver_resolve(NMConnectivityCheckHandle *cb_data)
+{
+    gs_unref_object GResolver *resolver = NULL;
+
+    _LOG2D("start request to '%s' (try resolving '%s' using system resolver)",
+           cb_data->concheck.con_config->uri,
+           cb_data->concheck.con_config->host);
+
+    resolver = g_resolver_get_default();
+
+    g_resolver_lookup_by_name_async(resolver,
+                                    cb_data->concheck.con_config->host,
+                                    cb_data->concheck.resolve_cancellable,
+                                    system_resolver_resolve_cb,
+                                    cb_data);
+}
+
+static void
+systemd_resolved_resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data)
 {
     NMConnectivityCheckHandle *cb_data;
     gs_unref_variant GVariant *result    = NULL;
@@ -715,17 +879,24 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data)
     nm_auto_str_buf NMStrBuf   strbuf_hosts = NM_STR_BUF_INIT(0, FALSE);
 
     result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
-    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+    if (nm_utils_error_is_cancelled(error))
         return;
 
     cb_data = user_data;
 
-    g_clear_object(&cb_data->concheck.resolve_cancellable);
-
     if (!result) {
-        /* Never mind. Just let do curl do its own resolving. */
+        gs_free char *dbus_error = NULL;
+
         _LOG2D("can't resolve a name via systemd-resolved: %s", error->message);
-        do_curl_request(cb_data);
+
+        dbus_error = g_dbus_error_get_remote_error(error);
+        if (nm_streq0(dbus_error, "org.freedesktop.resolve1.NoNameServers")) {
+            cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error");
+            return;
+        }
+
+        /* Never mind. Fallback to the system resolver. */
+        system_resolver_resolve(cb_data);
         return;
     }
 
@@ -759,14 +930,14 @@ resolve_cb(GObject *object, GAsyncResult *res, gpointer user_data)
 
         nm_str_buf_append(&strbuf_hosts, nm_utils_inet_ntop(addr_family, address_buf, str_addr));
     }
-    if (strbuf_hosts.len > 0) {
-        const char *s = nm_str_buf_get_str(&strbuf_hosts);
-
-        cb_data->concheck.hosts = curl_slist_append(NULL, s);
-        _LOG2T("set curl resolve list to '%s'", s);
+    if (strbuf_hosts.len == 0) {
+        _LOG2D("systemd-resolve returned no usable IPv%c addresses",
+               nm_utils_addr_family_to_char(cb_data->addr_family));
+        cb_data_complete(cb_data, NM_CONNECTIVITY_LIMITED, "resolve-error");
+        return;
     }
 
-    do_curl_request(cb_data);
+    do_curl_request(cb_data, nm_str_buf_get_str(&strbuf_hosts));
 }
 #endif
 
@@ -892,6 +1063,8 @@ nm_connectivity_check_start(NMConnectivity             *self,
             }
         }
 
+        cb_data->concheck.resolve_cancellable = g_cancellable_new();
+
         /* note that we pick up support for systemd-resolved right away when we need it.
          * We don't need to remember the setting, because we can (cheaply) check anew
          * on each request.
@@ -927,8 +1100,6 @@ nm_connectivity_check_start(NMConnectivity             *self,
                 return cb_data;
             }
 
-            cb_data->concheck.resolve_cancellable = g_cancellable_new();
-
             g_dbus_connection_call(dbus_connection,
                                    "org.freedesktop.resolve1",
                                    "/org/freedesktop/resolve1",
@@ -943,17 +1114,15 @@ nm_connectivity_check_start(NMConnectivity             *self,
                                    G_DBUS_CALL_FLAGS_NONE,
                                    -1,
                                    cb_data->concheck.resolve_cancellable,
-                                   resolve_cb,
+                                   systemd_resolved_resolve_cb,
                                    cb_data);
             _LOG2D("start request to '%s' (try resolving '%s' using systemd-resolved)",
                    cb_data->concheck.con_config->uri,
                    cb_data->concheck.con_config->host);
-        } else {
-            _LOG2D("start request to '%s' (systemd-resolved not available)",
-                   cb_data->concheck.con_config->uri);
-            do_curl_request(cb_data);
+            return cb_data;
         }
 
+        system_resolver_resolve(cb_data);
         return cb_data;
     }
 #endif
diff --git a/src/core/nm-core-utils.c b/src/core/nm-core-utils.c
index e05638e4..c8b789b3 100644
--- a/src/core/nm-core-utils.c
+++ b/src/core/nm-core-utils.c
@@ -30,6 +30,7 @@
 #include "libnm-glib-aux/nm-secret-utils.h"
 #include "libnm-glib-aux/nm-time-utils.h"
 #include "libnm-glib-aux/nm-str-buf.h"
+#include "libnm-systemd-shared/nm-sd-utils-shared.h"
 #include "nm-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "nm-setting-connection.h"
@@ -2692,8 +2693,9 @@ _host_id_read_timestamp(gboolean      use_secret_key_file,
      * timestamp. It is wrong to worry about using a fake timestamp (which is tied to
      * the secret_key) if we are unable to access the secret_key file in the first place.
      *
-     * Pick a random timestamp from the past two years. Yes, this timestamp
-     * is not stable across restarts, but apparently neither is the host-id
+     * Pick a timestamp from the past two years, by using a generated timespan
+     * by hashing the host-id. Yes, this timestamp counts back from @now, and is
+     * thus not stable across restarts. But apparently neither is the host-id
      * nor the secret_key itself. */
 
 #define EPOCH_TWO_YEARS (G_GINT64_CONSTANT(2 * 365 * 24 * 3600) * NM_UTILS_NSEC_PER_SEC)
@@ -2874,9 +2876,15 @@ out:
 typedef struct {
     guint8 *host_id;
     gsize   host_id_len;
-    gint64  timestamp_ns;
-    bool    is_good : 1;
-    bool    timestamp_is_good : 1;
+
+    /* The timestamp (in nsec since the Epoch) returned by nm_utils_host_id_get_timestamp_nsec().
+     * It is associated with the host (and the host-id). We currently use this for the LLT DUID
+     * generation for IPv6. Instead of persisting the timestamp separately to disk, we re-use the
+     * file timestamp of the secret_key file. */
+    gint64 timestamp_nsec;
+
+    bool is_good : 1;
+    bool timestamp_is_good : 1;
 } HostIdData;
 
 static const HostIdData *volatile host_id_static;
@@ -2900,7 +2908,7 @@ again:
         host_id_data.timestamp_is_good = _host_id_read_timestamp(host_id_data.is_good,
                                                                  host_id_data.host_id,
                                                                  host_id_data.host_id_len,
-                                                                 &host_id_data.timestamp_ns);
+                                                                 &host_id_data.timestamp_nsec);
         if (!host_id_data.timestamp_is_good && host_id_data.is_good)
             nm_log_warn(LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)");
 
@@ -2939,9 +2947,9 @@ nm_utils_host_id_get(const guint8 **out_host_id, gsize *out_host_id_len)
 }
 
 gint64
-nm_utils_host_id_get_timestamp_ns(void)
+nm_utils_host_id_get_timestamp_nsec(void)
 {
-    return _host_id_get()->timestamp_ns;
+    return _host_id_get()->timestamp_nsec;
 }
 
 static GArray    *nmtst_host_id_stack = NULL;
@@ -2952,7 +2960,7 @@ void
 nmtst_utils_host_id_push(const guint8 *host_id,
                          gssize        host_id_len,
                          gboolean      is_good,
-                         const gint64 *timestamp_ns)
+                         const gint64 *p_timestamp_nsec)
 {
     NM_G_MUTEX_LOCKED(&nmtst_host_id_lock);
     gs_free char *str1_to_free = NULL;
@@ -2971,8 +2979,8 @@ nmtst_utils_host_id_push(const guint8 *host_id,
                                             &str1_to_free),
                (gsize) host_id_len,
                !!is_good,
-               timestamp_ns ? *timestamp_ns : 0,
-               timestamp_ns ? "" : " (not-good)");
+               p_timestamp_nsec ? *p_timestamp_nsec : 0,
+               p_timestamp_nsec ? "" : " (not-good)");
 
     if (!nmtst_host_id_stack) {
         nmtst_host_id_stack    = g_array_new(FALSE, FALSE, sizeof(HostIdData));
@@ -2984,9 +2992,9 @@ nmtst_utils_host_id_push(const guint8 *host_id,
     *h = (HostIdData){
         .host_id           = nm_memdup(host_id, host_id_len),
         .host_id_len       = host_id_len,
-        .timestamp_ns      = timestamp_ns ? *timestamp_ns : 0,
+        .timestamp_nsec    = p_timestamp_nsec ? *p_timestamp_nsec : 0,
         .is_good           = is_good,
-        .timestamp_is_good = !!timestamp_ns,
+        .timestamp_is_good = !!p_timestamp_nsec,
     };
 
     g_atomic_pointer_set(&host_id_static, h);
@@ -3342,8 +3350,13 @@ nm_utils_stable_id_generated_complete(const char *stable_id_generated)
 static void
 _stable_id_append(GString *str, const char *substitution)
 {
-    if (!substitution)
+    if (!substitution) {
+        /* Would have been nicer to append "=NIL;" to differentiate between
+         * empty and NULL.
+         *
+         * Can't do that now, as it would change behavior. */
         substitution = "";
+    }
     g_string_append_printf(str, "=%zu{%s}", strlen(substitution), substitution);
 }
 
@@ -3413,7 +3426,7 @@ nm_utils_stable_id_parse(const char *stable_id,
     ({                                                                        \
         gboolean _match = FALSE;                                              \
                                                                               \
-        if (g_str_has_prefix(&stable_id[i], "" prefix "")) {                  \
+        if (NM_STR_HAS_PREFIX(&stable_id[i], "" prefix "")) {                 \
             _match = TRUE;                                                    \
             if (!str)                                                         \
                 str = g_string_sized_new(256);                                \
@@ -5201,3 +5214,50 @@ again:
 
     return g;
 }
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_shorten_hostname:
+ * @hostname: the input hostname
+ * @shortened: (out) (transfer full): on return, the shortened hostname
+ *
+ * Checks whether the input hostname is valid. If not, tries to shorten it
+ * to HOST_NAME_MAX or to the first dot, whatever comes earlier.
+ * The new hostname is returned in @shortened.
+ *
+ * Returns: %TRUE if the input hostname was already valid or if was shortened
+ * successfully; %FALSE otherwise
+ */
+gboolean
+nm_utils_shorten_hostname(const char *hostname, char **shortened)
+{
+    gs_free char *s = NULL;
+    const char   *dot;
+    gsize         l;
+
+    nm_assert(hostname);
+    nm_assert(shortened);
+
+    if (nm_sd_hostname_is_valid(hostname, FALSE)) {
+        *shortened = NULL;
+        return TRUE;
+    }
+
+    dot = strchr(hostname, '.');
+    if (dot)
+        l = (dot - hostname);
+    else
+        l = strlen(hostname);
+    l = MIN(l, (gsize) HOST_NAME_MAX);
+
+    s = g_strndup(hostname, l);
+
+    if (!nm_sd_hostname_is_valid(s, FALSE)) {
+        *shortened = NULL;
+        return FALSE;
+    }
+
+    *shortened = g_steal_pointer(&s);
+    return TRUE;
+}
diff --git a/src/core/nm-core-utils.h b/src/core/nm-core-utils.h
index 7079ab2f..d1a62a60 100644
--- a/src/core/nm-core-utils.h
+++ b/src/core/nm-core-utils.h
@@ -233,6 +233,7 @@ void nm_utils_log_connection_diff(NMConnection *connection,
                                   const char   *dbus_path);
 
 gboolean nm_utils_is_specific_hostname(const char *name);
+gboolean nm_utils_shorten_hostname(const char *hostname, char **shortened);
 
 struct _NMUuid;
 
@@ -246,7 +247,7 @@ const char           *nm_utils_proc_cmdline(void);
 const char *const    *nm_utils_proc_cmdline_split(void);
 
 gboolean nm_utils_host_id_get(const guint8 **out_host_id, gsize *out_host_id_len);
-gint64   nm_utils_host_id_get_timestamp_ns(void);
+gint64   nm_utils_host_id_get_timestamp_nsec(void);
 
 void nmtst_utils_host_id_push(const guint8 *host_id,
                               gssize        host_id_len,
diff --git a/src/core/nm-dbus-manager.c b/src/core/nm-dbus-manager.c
index 2c5f7ace..7fcbf6ca 100644
--- a/src/core/nm-dbus-manager.c
+++ b/src/core/nm-dbus-manager.c
@@ -1082,6 +1082,21 @@ nm_dbus_manager_lookup_object(NMDBusManager *self, const char *path)
     return obj;
 }
 
+gpointer
+nm_dbus_manager_lookup_object_with_type(NMDBusManager *self, GType gtype, const char *path)
+{
+    gpointer ptr;
+
+    nm_assert(g_type_is_a(gtype, NM_TYPE_DBUS_OBJECT));
+    nm_assert(gtype != NM_TYPE_DBUS_OBJECT);
+
+    ptr = nm_dbus_manager_lookup_object(self, path);
+    if (!ptr || !G_TYPE_CHECK_INSTANCE_TYPE(ptr, gtype))
+        return NULL;
+
+    return ptr;
+}
+
 void
 _nm_dbus_manager_obj_export(NMDBusObject *obj)
 {
diff --git a/src/core/nm-dbus-manager.h b/src/core/nm-dbus-manager.h
index d7ca78d1..d977f99f 100644
--- a/src/core/nm-dbus-manager.h
+++ b/src/core/nm-dbus-manager.h
@@ -52,6 +52,9 @@ gboolean nm_dbus_manager_is_stopping(NMDBusManager *self);
 
 gpointer nm_dbus_manager_lookup_object(NMDBusManager *self, const char *path);
 
+gpointer
+nm_dbus_manager_lookup_object_with_type(NMDBusManager *self, GType gtype, const char *path);
+
 void _nm_dbus_manager_obj_export(NMDBusObject *obj);
 void _nm_dbus_manager_obj_unexport(NMDBusObject *obj);
 void
diff --git a/src/core/nm-firewall-utils.c b/src/core/nm-firewall-utils.c
index ddac1137..8fa8059e 100644
--- a/src/core/nm-firewall-utils.c
+++ b/src/core/nm-firewall-utils.c
@@ -422,7 +422,7 @@ _fw_nft_call_communicate_cb(GObject *source, GAsyncResult *result, gpointer user
                 nm_g_main_context_push_thread_default_if_necessary(NULL);
 
             nm_shutdown_wait_obj_register_object(call_data->subprocess, "nft-terminate");
-            G_STATIC_ASSERT_EXPR(200 < NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG * 2 / 3);
+            G_STATIC_ASSERT_EXPR(200 < NM_SHUTDOWN_TIMEOUT_ADDITIONAL_MSEC * 2 / 3);
             nm_g_subprocess_terminate_in_background(call_data->subprocess, 200);
         }
     } else if (g_subprocess_get_successful(call_data->subprocess)) {
@@ -546,7 +546,7 @@ _fw_nft_call(GBytes             *stdin_buf,
                                    call_data);
 
     call_data->timeout_source =
-        nm_g_source_attach(nm_g_timeout_source_new((NM_SHUTDOWN_TIMEOUT_MS * 2) / 3,
+        nm_g_source_attach(nm_g_timeout_source_new((NM_SHUTDOWN_TIMEOUT_1500_MSEC * 2) / 3,
                                                    G_PRIORITY_DEFAULT,
                                                    _fw_nft_call_timeout_cb,
                                                    call_data,
diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c
index 03593ea2..5ab2f6b8 100644
--- a/src/core/nm-l3-config-data.c
+++ b/src/core/nm-l3-config-data.c
@@ -349,7 +349,7 @@ nm_l3_config_data_log(const NML3ConfigData *self,
                       NMLogLevel            log_level,
                       NMLogDomain           log_domain)
 {
-    char  sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char  sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     char  sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
     int   IS_IPv4;
     guint i;
@@ -1172,13 +1172,6 @@ _l3_config_data_add_obj(NMDedupMultiIndex      *multi_idx,
                     modified                                 = TRUE;
                 }
 
-                /* OR assume_config_once flag */
-                if (obj_new->ip_address.a_assume_config_once
-                    && !obj_old->ip_address.a_assume_config_once) {
-                    obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
-                    obj_new_stackinit.ip_address.a_assume_config_once = TRUE;
-                    modified                                          = TRUE;
-                }
                 break;
             case NMP_OBJECT_TYPE_IP4_ROUTE:
             case NMP_OBJECT_TYPE_IP6_ROUTE:
@@ -1189,13 +1182,6 @@ _l3_config_data_add_obj(NMDedupMultiIndex      *multi_idx,
                     modified                             = TRUE;
                 }
 
-                /* OR assume_config_once flag */
-                if (obj_new->ip_route.r_assume_config_once
-                    && !obj_old->ip_route.r_assume_config_once) {
-                    obj_new = nmp_object_stackinit_obj(&obj_new_stackinit, obj_new);
-                    obj_new_stackinit.ip_route.r_assume_config_once = TRUE;
-                    modified                                        = TRUE;
-                }
                 break;
             default:
                 nm_assert_not_reached();
@@ -3056,9 +3042,8 @@ nm_l3_config_data_merge(NML3ConfigData       *self,
             const NMPlatformIPAddress *a_src = NMP_OBJECT_CAST_IP_ADDRESS(obj);
             NMPlatformIPXAddress       a;
             NML3ConfigMergeHookResult  hook_result = {
-                 .ip4acd_not_ready   = NM_OPTION_BOOL_DEFAULT,
-                 .assume_config_once = NM_OPTION_BOOL_DEFAULT,
-                 .force_commit       = NM_OPTION_BOOL_DEFAULT,
+                 .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT,
+                 .force_commit     = NM_OPTION_BOOL_DEFAULT,
             };
 
 #define _ensure_a()                                       \
@@ -3091,12 +3076,6 @@ nm_l3_config_data_merge(NML3ConfigData       *self,
                 a.a4.a_acd_not_ready = (!!hook_result.ip4acd_not_ready);
             }
 
-            if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT
-                && (!!hook_result.assume_config_once) != a_src->a_assume_config_once) {
-                _ensure_a();
-                a.ax.a_assume_config_once = (!!hook_result.assume_config_once);
-            }
-
             if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT
                 && (!!hook_result.force_commit) != a_src->a_force_commit) {
                 _ensure_a();
@@ -3121,9 +3100,8 @@ nm_l3_config_data_merge(NML3ConfigData       *self,
                 const NMPlatformIPRoute  *r_src = NMP_OBJECT_CAST_IP_ROUTE(obj);
                 NMPlatformIPXRoute        r;
                 NML3ConfigMergeHookResult hook_result = {
-                    .ip4acd_not_ready   = NM_OPTION_BOOL_DEFAULT,
-                    .assume_config_once = NM_OPTION_BOOL_DEFAULT,
-                    .force_commit       = NM_OPTION_BOOL_DEFAULT,
+                    .ip4acd_not_ready = NM_OPTION_BOOL_DEFAULT,
+                    .force_commit     = NM_OPTION_BOOL_DEFAULT,
                 };
 
 #define _ensure_r()                                     \
@@ -3149,12 +3127,6 @@ nm_l3_config_data_merge(NML3ConfigData       *self,
                     r.rx.ifindex = self->ifindex;
                 }
 
-                if (hook_result.assume_config_once != NM_OPTION_BOOL_DEFAULT
-                    && (!!hook_result.assume_config_once) != r_src->r_assume_config_once) {
-                    _ensure_r();
-                    r.rx.r_assume_config_once = (!!hook_result.assume_config_once);
-                }
-
                 if (hook_result.force_commit != NM_OPTION_BOOL_DEFAULT
                     && (!!hook_result.force_commit) != r_src->r_force_commit) {
                     _ensure_r();
diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h
index b7a1bb32..20a32c62 100644
--- a/src/core/nm-l3-config-data.h
+++ b/src/core/nm-l3-config-data.h
@@ -137,7 +137,6 @@ NML3ConfigData *nm_l3_config_data_new_from_platform(NMDedupMultiIndex        *mu
 
 typedef struct {
     NMOptionBool ip4acd_not_ready;
-    NMOptionBool assume_config_once;
     NMOptionBool force_commit;
 } NML3ConfigMergeHookResult;
 
diff --git a/src/core/nm-l3-ipv4ll.c b/src/core/nm-l3-ipv4ll.c
index 68cb17fb..2aedab56 100644
--- a/src/core/nm-l3-ipv4ll.c
+++ b/src/core/nm-l3-ipv4ll.c
@@ -600,7 +600,7 @@ _l3cd_config_add(NML3IPv4LL *self)
         nm_assert_not_reached();
 
     self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg,
-                                                              NM_L3_CFG_COMMIT_TYPE_ASSUME,
+                                                              NM_L3_CFG_COMMIT_TYPE_UPDATE,
                                                               self->l3cfg_commit_handle,
                                                               "ipv4ll");
     nm_l3cfg_commit_on_idle_schedule(self->l3cfg, NM_L3_CFG_COMMIT_TYPE_AUTO);
@@ -748,12 +748,8 @@ _ipv4ll_set_timed_out_update(NML3IPv4LL *self, TimedOutState new_state)
         if (self->timed_out_expiry_msec == 0 || self->timed_out_expiry_msec < expiry_msec) {
             self->timed_out_expiry_msec = expiry_msec;
             nm_clear_g_source_inst(&self->timed_out_source);
-            self->timed_out_source = nm_g_timeout_source_new(timeout_msec,
-                                                             G_PRIORITY_DEFAULT,
-                                                             _ipv4ll_set_timed_out_timeout_cb,
-                                                             self,
-                                                             NULL);
-            g_source_attach(self->timed_out_source, NULL);
+            self->timed_out_source =
+                nm_g_timeout_add_source(timeout_msec, _ipv4ll_set_timed_out_timeout_cb, self);
         }
         break;
     }
@@ -922,8 +918,7 @@ _ipv4ll_state_change_on_idle(NML3IPv4LL *self)
 
     if (!self->state_change_on_idle_source) {
         self->state_change_on_idle_source =
-            nm_g_idle_source_new(G_PRIORITY_DEFAULT, _ipv4ll_state_change_on_idle_cb, self, NULL);
-        g_source_attach(self->state_change_on_idle_source, NULL);
+            nm_g_idle_add_source(_ipv4ll_state_change_on_idle_cb, self);
     }
 }
 
diff --git a/src/core/nm-l3-ipv6ll.c b/src/core/nm-l3-ipv6ll.c
index 60da4ee8..2b9a1a0e 100644
--- a/src/core/nm-l3-ipv6ll.c
+++ b/src/core/nm-l3-ipv6ll.c
@@ -398,10 +398,7 @@ _lladdr_handle_changed(NML3IPv6LL *self)
      * NML3IPv4LL, where we use NM_L3_CONFIG_MERGE_FLAGS_ONLY_FOR_ACD. The difference
      * is that for IPv6 we let kernel do DAD, so we need to actually configure the
      * address. For IPv4, we can run ACD without configuring anything in kernel,
-     * and let the user decide how to proceed.
-     *
-     * Also in this case, we use the most graceful commit-type (NM_L3_CFG_COMMIT_TYPE_ASSUME),
-     * but for that to work, we also need NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE flag. */
+     * and let the user decide how to proceed. */
 
     l3cd = nm_l3_ipv6ll_get_l3cd(self);
 
@@ -421,7 +418,7 @@ _lladdr_handle_changed(NML3IPv6LL *self)
                                 NM_DNS_PRIORITY_DEFAULT_NORMAL,
                                 NM_L3_ACD_DEFEND_TYPE_ALWAYS,
                                 0,
-                                NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE,
+                                NM_L3CFG_CONFIG_FLAGS_NONE,
                                 NM_L3_CONFIG_MERGE_FLAGS_NONE))
             changed = TRUE;
     } else {
@@ -430,7 +427,7 @@ _lladdr_handle_changed(NML3IPv6LL *self)
     }
 
     self->l3cfg_commit_handle = nm_l3cfg_commit_type_register(self->l3cfg,
-                                                              l3cd ? NM_L3_CFG_COMMIT_TYPE_ASSUME
+                                                              l3cd ? NM_L3_CFG_COMMIT_TYPE_UPDATE
                                                                    : NM_L3_CFG_COMMIT_TYPE_NONE,
                                                               self->l3cfg_commit_handle,
                                                               "ipv6ll");
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index eeb041d0..6b7e6f4b 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -363,7 +363,6 @@ static NM_UTILS_ENUM2STR_DEFINE(_l3_cfg_commit_type_to_string,
                                 NML3CfgCommitType,
                                 NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_AUTO, "auto"),
                                 NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_NONE, "none"),
-                                NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_ASSUME, "assume"),
                                 NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_UPDATE, "update"),
                                 NM_UTILS_ENUM2STR(NM_L3_CFG_COMMIT_TYPE_REAPPLY, "reapply"), );
 
@@ -585,7 +584,7 @@ _l3_config_notify_data_to_string(const NML3ConfigNotifyData *notify_data,
 void
 _nm_l3cfg_emit_signal_notify(NML3Cfg *self, const NML3ConfigNotifyData *notify_data)
 {
-    char sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
 
     nm_assert(notify_data);
     nm_assert(_NM_INT_NOT_NEGATIVE(notify_data->notify_type));
@@ -768,14 +767,6 @@ _nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gp
     }                                                                                             \
     G_STMT_END
 
-static gboolean
-_obj_state_data_get_assume_config_once(const ObjStateData *obj_state)
-{
-    nm_assert_obj_state(NULL, obj_state);
-
-    return nmp_object_get_assume_config_once(obj_state->obj);
-}
-
 static ObjStateData *
 _obj_state_data_new(const NMPObject *obj, const NMPObject *plobj)
 {
@@ -885,7 +876,7 @@ _obj_state_data_update(ObjStateData *obj_state, const NMPObject *obj)
 static void
 _obj_states_externally_removed_track(NML3Cfg *self, const NMPObject *obj, gboolean in_platform)
 {
-    char          sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char          sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     ObjStateData *obj_state;
 
     nm_assert(NM_IS_L3CFG(self));
@@ -946,7 +937,7 @@ _obj_states_update_all(NML3Cfg *self)
         NMP_OBJECT_TYPE_IP4_ROUTE,
         NMP_OBJECT_TYPE_IP6_ROUTE,
     };
-    char          sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char          sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     ObjStateData *obj_state;
     int           i;
     gboolean      any_dirty = FALSE;
@@ -1037,7 +1028,7 @@ typedef struct {
 static gboolean
 _obj_states_sync_filter(NML3Cfg *self, const NMPObject *obj, NML3CfgCommitType commit_type)
 {
-    char          sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char          sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     NMPObjectType obj_type;
     ObjStateData *obj_state;
 
@@ -1054,10 +1045,6 @@ _obj_states_sync_filter(NML3Cfg *self, const NMPObject *obj, NML3CfgCommitType c
     nm_assert(c_list_is_empty(&obj_state->os_zombie_lst));
 
     if (!obj_state->os_nm_configured) {
-        if (commit_type == NM_L3_CFG_COMMIT_TYPE_ASSUME
-            && !_obj_state_data_get_assume_config_once(obj_state))
-            return FALSE;
-
         obj_state->os_nm_configured = TRUE;
 
         _LOGD("obj-state: configure-first-time: %s",
@@ -1152,7 +1139,7 @@ _obj_state_zombie_lst_get_prune_lists(NML3Cfg    *self,
     const int           IS_IPv4          = NM_IS_IPv4(addr_family);
     const NMPObjectType obj_type_route   = NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4);
     const NMPObjectType obj_type_address = NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4);
-    char                sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char                sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     ObjStateData       *obj_state;
     ObjStateData       *obj_state_safe;
 
@@ -1198,7 +1185,7 @@ _obj_state_zombie_lst_get_prune_lists(NML3Cfg    *self,
 static void
 _obj_state_zombie_lst_prune_all(NML3Cfg *self, int addr_family)
 {
-    char          sbuf[sizeof(_nm_utils_to_string_buffer)];
+    char          sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
     ObjStateData *obj_state;
     ObjStateData *obj_state_safe;
 
@@ -1743,9 +1730,7 @@ again:
 
     n_acd_get_fd(self->priv.p->nacd, &fd);
 
-    self->priv.p->nacd_source =
-        nm_g_unix_fd_source_new(fd, G_IO_IN, G_PRIORITY_DEFAULT, _l3_acd_nacd_event, self, NULL);
-    nm_g_source_attach(self->priv.p->nacd_source, NULL);
+    self->priv.p->nacd_source = nm_g_unix_fd_add_source(fd, G_IO_IN, _l3_acd_nacd_event, self);
 
     NM_SET_OUT(out_acd_not_supported, FALSE);
     return self->priv.p->nacd;
@@ -3088,7 +3073,6 @@ nm_l3cfg_commit_on_idle_schedule(NML3Cfg *self, NML3CfgCommitType commit_type)
     nm_assert(NM_IS_L3CFG(self));
     nm_assert(NM_IN_SET(commit_type,
                         NM_L3_CFG_COMMIT_TYPE_AUTO,
-                        NM_L3_CFG_COMMIT_TYPE_ASSUME,
                         NM_L3_CFG_COMMIT_TYPE_UPDATE,
                         NM_L3_CFG_COMMIT_TYPE_REAPPLY));
 
@@ -3503,7 +3487,6 @@ out_clear:
 typedef struct {
     NML3Cfg      *self;
     gconstpointer tag;
-    bool          assume_config_once;
     bool          to_commit;
     bool          force_commit_once;
 } L3ConfigMergeHookAddObjData;
@@ -3523,11 +3506,9 @@ _l3_hook_add_obj_cb(const NML3ConfigData      *l3cd,
     nm_assert(obj);
     nm_assert(hook_result);
     nm_assert(hook_result->ip4acd_not_ready == NM_OPTION_BOOL_DEFAULT);
-    nm_assert(hook_result->assume_config_once == NM_OPTION_BOOL_DEFAULT);
     nm_assert(hook_result->force_commit == NM_OPTION_BOOL_DEFAULT);
 
-    hook_result->assume_config_once = hook_data->assume_config_once;
-    hook_result->force_commit       = hook_data->force_commit_once;
+    hook_result->force_commit = hook_data->force_commit_once;
 
     switch (NMP_OBJECT_GET_TYPE(obj)) {
     case NMP_OBJECT_TYPE_IP4_ADDRESS:
@@ -3683,9 +3664,7 @@ _l3cfg_update_combined_config(NML3Cfg               *self,
             if (NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ONLY_FOR_ACD))
                 continue;
 
-            hook_data.tag = l3cd_data->tag_confdata;
-            hook_data.assume_config_once =
-                NM_FLAGS_HAS(l3cd_data->config_flags, NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE);
+            hook_data.tag               = l3cd_data->tag_confdata;
             hook_data.force_commit_once = l3cd_data->force_commit_once;
 
             nm_l3_config_data_merge(l3cd,
@@ -3859,7 +3838,7 @@ _routes_temporary_not_available_update(NML3Cfg   *self,
 
     for (i = 0; i < routes_temporary_not_available_arr->len; i++) {
         const NMPObject *o = routes_temporary_not_available_arr->pdata[i];
-        char             sbuf[sizeof(_nm_utils_to_string_buffer)];
+        char             sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
 
         nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)));
 
@@ -4212,8 +4191,7 @@ _l3_commit_one(NML3Cfg              *self,
     nm_assert(NM_IN_SET(commit_type,
                         NM_L3_CFG_COMMIT_TYPE_NONE,
                         NM_L3_CFG_COMMIT_TYPE_REAPPLY,
-                        NM_L3_CFG_COMMIT_TYPE_UPDATE,
-                        NM_L3_CFG_COMMIT_TYPE_ASSUME));
+                        NM_L3_CFG_COMMIT_TYPE_UPDATE));
     nm_assert_addr_family(addr_family);
 
     _LOGT("committing IPv%c configuration (%s)",
@@ -4299,7 +4277,6 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle)
     nm_assert(NM_IN_SET(commit_type,
                         NM_L3_CFG_COMMIT_TYPE_NONE,
                         NM_L3_CFG_COMMIT_TYPE_AUTO,
-                        NM_L3_CFG_COMMIT_TYPE_ASSUME,
                         NM_L3_CFG_COMMIT_TYPE_UPDATE,
                         NM_L3_CFG_COMMIT_TYPE_REAPPLY));
     nm_assert(self->priv.p->commit_reentrant_count == 0);
@@ -4423,10 +4400,7 @@ nm_l3cfg_commit_type_register(NML3Cfg                 *self,
     char                     buf[64];
 
     nm_assert(NM_IS_L3CFG(self));
-    nm_assert(NM_IN_SET(commit_type,
-                        NM_L3_CFG_COMMIT_TYPE_NONE,
-                        NM_L3_CFG_COMMIT_TYPE_ASSUME,
-                        NM_L3_CFG_COMMIT_TYPE_UPDATE));
+    nm_assert(NM_IN_SET(commit_type, NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_UPDATE));
 
     /* It would be easy (and maybe convenient) to allow that @existing_handle
      * can currently be registered on another NML3Cfg instance. But then we couldn't
diff --git a/src/core/nm-l3cfg.h b/src/core/nm-l3cfg.h
index 0ea68646..f6ec39ce 100644
--- a/src/core/nm-l3cfg.h
+++ b/src/core/nm-l3cfg.h
@@ -363,15 +363,6 @@ typedef enum _nm_packed {
     /* Don't touch the interface. */
     NM_L3_CFG_COMMIT_TYPE_NONE,
 
-    /* ASSUME means to keep any pre-existing extra routes/addresses, while
-     * also not adding routes/addresses that are not present yet. This is to
-     * gracefully take over after restart, where the existing IP configuration
-     * should not change.
-     *
-     * The flag NM_L3CFG_CONFIG_FLAGS_ASSUME_CONFIG_ONCE can make certain addresses/
-     * routes commitable also during "assume". */
-    NM_L3_CFG_COMMIT_TYPE_ASSUME,
-
     /* UPDATE means to add new addresses/routes, while also removing addresses/routes
      * that are no longer present (but were previously configured by NetworkManager).
      * Routes/addresses that were removed externally won't be re-added, and routes/addresses
diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c
index 6c73d237..52153697 100644
--- a/src/core/nm-manager.c
+++ b/src/core/nm-manager.c
@@ -54,15 +54,18 @@
 /*****************************************************************************/
 
 typedef struct {
-    gboolean                     user_enabled;
-    gboolean                     sw_enabled;
-    gboolean                     hw_enabled;
-    RfKillType                   rtype;
+    guint                        prop_id;
+    guint                        hw_prop_id;
     NMConfigRunStatePropertyType key;
-    const char                  *desc;
-    const char                  *prop;
-    const char                  *hw_prop;
-} RadioState;
+} RfkillTypeDesc;
+
+typedef struct {
+    bool available : 1;
+    bool user_enabled : 1;
+    bool sw_enabled : 1;
+    bool hw_enabled : 1;
+    bool os_owner : 1;
+} RfkillRadioState;
 
 typedef enum {
     ASYNC_OP_TYPE_AC_AUTH_ACTIVATE_INTERNAL,
@@ -120,6 +123,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMManager,
                              PROP_WWAN_HARDWARE_ENABLED,
                              PROP_WIMAX_ENABLED,
                              PROP_WIMAX_HARDWARE_ENABLED,
+                             PROP_RADIO_FLAGS,
                              PROP_ACTIVE_CONNECTIONS,
                              PROP_CONNECTIVITY,
                              PROP_CONNECTIVITY_CHECK_AVAILABLE,
@@ -169,7 +173,8 @@ typedef struct {
 
     NMSettings *settings;
 
-    RadioState    radio_states[RFKILL_TYPE_MAX];
+    RfkillRadioState radio_states[NM_RFKILL_TYPE_MAX];
+
     NMVpnManager *vpn_manager;
 
     NMSleepMonitor *sleep_monitor;
@@ -190,6 +195,8 @@ typedef struct {
 
     guint devices_inited_id;
 
+    guint radio_flags;
+
     NMConnectivityState connectivity_state;
 
     guint8 device_state_prune_ratelimit_count;
@@ -393,6 +400,8 @@ static void _activation_auth_done(NMManager             *self,
                                   gboolean               success,
                                   const char            *error_desc);
 
+static void _rfkill_update(NMManager *self, NMRfkillType rtype);
+
 /*****************************************************************************/
 
 static NM_CACHED_QUARK_FCN("autoconnect-root", autoconnect_root_quark);
@@ -1156,8 +1165,10 @@ active_connection_get_by_path(NMManager *self, const char *path)
     NMManagerPrivate   *priv = NM_MANAGER_GET_PRIVATE(self);
     NMActiveConnection *ac;
 
-    ac = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path);
-    if (!ac || !NM_IS_ACTIVE_CONNECTION(ac) || c_list_is_empty(&ac->active_connections_lst))
+    ac = nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)),
+                                                 NM_TYPE_ACTIVE_CONNECTION,
+                                                 path);
+    if (!ac || c_list_is_empty(&ac->active_connections_lst))
         return NULL;
 
     nm_assert(c_list_contains(&priv->active_connections_lst_head, &ac->active_connections_lst));
@@ -1289,8 +1300,11 @@ nm_manager_get_device_by_path(NMManager *self, const char *path)
 
     g_return_val_if_fail(path, NULL);
 
-    device = nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path);
-    if (!device || !NM_IS_DEVICE(device) || c_list_is_empty(&device->devices_lst))
+    device =
+        nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)),
+                                                NM_TYPE_DEVICE,
+                                                path);
+    if (!device || c_list_is_empty(&device->devices_lst))
         return NULL;
 
     nm_assert(c_list_contains(&priv->devices_lst_head, &device->devices_lst));
@@ -1637,6 +1651,9 @@ check_if_startup_complete(NMManager *self)
         return;
     }
 
+    /* Most of our logging is not API/stable, but this line is kinda important and
+     * what people will look for when debugging NetworkManager-wait-online.service.
+     * Take care before rewording this message. */
     _LOGI(LOGD_CORE, "startup complete");
 
     priv->startup = FALSE;
@@ -1704,6 +1721,7 @@ remove_device(NMManager *self, NMDevice *device, gboolean quitting)
 {
     NMManagerPrivate *priv     = NM_MANAGER_GET_PRIVATE(self);
     gboolean          unmanage = FALSE;
+    NMRfkillType      rtype;
 
     _LOG2D(LOGD_DEVICE,
            device,
@@ -1744,6 +1762,10 @@ remove_device(NMManager *self, NMDevice *device, gboolean quitting)
 
     _parent_notify_changed(self, device, TRUE);
 
+    rtype = nm_device_get_rfkill_type(device);
+    if (rtype != NM_RFKILL_TYPE_UNKNOWN)
+        _rfkill_update(self, rtype);
+
     if (nm_device_is_real(device)) {
         gboolean unconfigure_ip_config = !quitting || unmanage;
 
@@ -2276,39 +2298,104 @@ _static_hostname_changed_cb(NMHostnameManager *hostname_manager, GParamSpec *psp
 }
 
 /*****************************************************************************/
-/* General NMManager stuff                                         */
-/*****************************************************************************/
+
+static const RfkillTypeDesc _rfkill_type_desc[NM_RFKILL_TYPE_MAX] = {
+    [NM_RFKILL_TYPE_WLAN] =
+        {
+            .prop_id    = PROP_WIRELESS_ENABLED,
+            .hw_prop_id = PROP_WIRELESS_HARDWARE_ENABLED,
+            .key        = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED,
+        },
+    [NM_RFKILL_TYPE_WWAN] =
+        {
+            .prop_id    = PROP_WWAN_ENABLED,
+            .hw_prop_id = PROP_WWAN_HARDWARE_ENABLED,
+            .key        = NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED,
+        },
+};
 
 static gboolean
-radio_enabled_for_rstate(RadioState *rstate, gboolean check_changeable)
+_rfkill_radio_state_get_enabled(const RfkillRadioState *rstate, gboolean check_changeable)
 {
     gboolean enabled;
 
+    /* If the device is not owned by the os, hw_enabled will be FALSE, hence
+     * we don't need to consider os_owner here.
+     */
     enabled = rstate->user_enabled && rstate->hw_enabled;
     if (check_changeable)
         enabled &= rstate->sw_enabled;
     return enabled;
 }
 
+static void
+_rfkill_radio_state_set_from_manager(NMManager *self, NMRfkillType rtype, RfkillRadioState *rstate)
+{
+    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
+    NMDevice         *device;
+
+    switch (nm_rfkill_manager_get_rfkill_state(priv->rfkill_mgr, rtype)) {
+    case NM_RFKILL_STATE_UNAVAILABLE:
+        rstate->sw_enabled = TRUE;
+        rstate->hw_enabled = TRUE;
+        rstate->os_owner   = TRUE;
+
+        /* A rfkill-type is available when there is a compatible
+         * killswitch or a compatible device. */
+        c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+            if (nm_device_get_rfkill_type(device) == rtype) {
+                rstate->available = TRUE;
+                return;
+            }
+        }
+        rstate->available = FALSE;
+        return;
+    case NM_RFKILL_STATE_UNBLOCKED:
+        rstate->available  = TRUE;
+        rstate->sw_enabled = TRUE;
+        rstate->hw_enabled = TRUE;
+        rstate->os_owner   = TRUE;
+        return;
+    case NM_RFKILL_STATE_SOFT_BLOCKED:
+        rstate->available  = TRUE;
+        rstate->sw_enabled = FALSE;
+        rstate->hw_enabled = TRUE;
+        rstate->os_owner   = TRUE;
+        return;
+    case NM_RFKILL_STATE_HARD_BLOCKED:
+        rstate->available  = TRUE;
+        rstate->sw_enabled = FALSE;
+        rstate->hw_enabled = FALSE;
+        /* In case the OS doesn't own the NIC, we would be in NM_RFKILL_STATE_HARD_BLOCKED */
+        rstate->os_owner = TRUE;
+        return;
+    case NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER:
+        rstate->available  = TRUE;
+        rstate->sw_enabled = FALSE;
+        rstate->hw_enabled = FALSE;
+        rstate->os_owner   = FALSE;
+        return;
+    }
+    nm_assert_not_reached();
+}
+
 static gboolean
-radio_enabled_for_type(NMManager *self, RfKillType rtype, gboolean check_changeable)
+_rfkill_radio_state_get(NMManager *self, NMRfkillType rtype)
 {
     NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
 
-    return radio_enabled_for_rstate(&priv->radio_states[rtype], check_changeable);
+    nm_assert(_NM_INT_NOT_NEGATIVE(rtype) && rtype < G_N_ELEMENTS(priv->radio_states));
+
+    return _rfkill_radio_state_get_enabled(&priv->radio_states[rtype], TRUE);
 }
 
 static void
-manager_update_radio_enabled(NMManager *self, RadioState *rstate, gboolean enabled)
+_rfkill_update_devices(NMManager *self, NMRfkillType rtype, gboolean enabled)
 {
     NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
     NMDevice         *device;
 
-    /* Do nothing for radio types not yet implemented */
-    if (!rstate->prop)
-        return;
-
-    g_object_notify(G_OBJECT(self), rstate->prop);
+    _notify(self, _rfkill_type_desc[rtype].prop_id);
 
     /* Don't touch devices if asleep/networking disabled */
     if (manager_sleeping(self))
@@ -2316,7 +2403,7 @@ manager_update_radio_enabled(NMManager *self, RadioState *rstate, gboolean enabl
 
     /* enable/disable wireless devices as required */
     c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-        if (nm_device_get_rfkill_type(device) == rstate->rtype) {
+        if (nm_device_get_rfkill_type(device) == rtype) {
             _LOG2D(LOGD_RFKILL,
                    device,
                    "rfkill: setting radio %s",
@@ -2327,89 +2414,195 @@ manager_update_radio_enabled(NMManager *self, RadioState *rstate, gboolean enabl
 }
 
 static void
-update_rstate_from_rfkill(NMRfkillManager *rfkill_mgr, RadioState *rstate)
+_rfkill_update_one_type(NMManager *self, NMRfkillType rtype)
 {
-    switch (nm_rfkill_manager_get_rfkill_state(rfkill_mgr, rstate->rtype)) {
-    case RFKILL_UNBLOCKED:
-        rstate->sw_enabled = TRUE;
-        rstate->hw_enabled = TRUE;
-        break;
-    case RFKILL_SOFT_BLOCKED:
-        rstate->sw_enabled = FALSE;
-        rstate->hw_enabled = TRUE;
-        break;
-    case RFKILL_HARD_BLOCKED:
-        rstate->sw_enabled = FALSE;
-        rstate->hw_enabled = FALSE;
-        break;
-    default:
-        g_warn_if_reached();
-        break;
-    }
-}
+    NMManagerPrivate *priv   = NM_MANAGER_GET_PRIVATE(self);
+    RfkillRadioState *rstate = &priv->radio_states[rtype];
+    gboolean          old_enabled;
+    gboolean          new_enabled;
+    gboolean          old_rfkilled;
+    gboolean          new_rfkilled;
+    gboolean          old_hwe;
+    guint             old_radio_flags;
 
-static void
-manager_rfkill_update_one_type(NMManager *self, RadioState *rstate, RfKillType rtype)
-{
-    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
-    gboolean          old_enabled, new_enabled, old_rfkilled, new_rfkilled, old_hwe;
+    nm_assert(_NM_INT_NOT_NEGATIVE(rtype) && rtype < G_N_ELEMENTS(priv->radio_states));
 
-    old_enabled  = radio_enabled_for_rstate(rstate, TRUE);
-    old_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
-    old_hwe      = rstate->hw_enabled;
+    old_enabled     = _rfkill_radio_state_get_enabled(rstate, TRUE);
+    old_rfkilled    = rstate->hw_enabled && rstate->sw_enabled;
+    old_hwe         = rstate->hw_enabled;
+    old_radio_flags = priv->radio_flags;
 
     /* recheck kernel rfkill state */
-    update_rstate_from_rfkill(priv->rfkill_mgr, rstate);
+    _rfkill_radio_state_set_from_manager(self, rtype, rstate);
 
     /* Print out all states affecting device enablement */
-    if (rstate->desc) {
-        _LOGD(LOGD_RFKILL,
-              "rfkill: %s hw-enabled %d sw-enabled %d",
-              rstate->desc,
-              rstate->hw_enabled,
-              rstate->sw_enabled);
-    }
+    _LOGD(LOGD_RFKILL,
+          "rfkill: %s available %d hw-enabled %d sw-enabled %d os-owner %d",
+          nm_rfkill_type_to_string(rtype),
+          rstate->available,
+          rstate->hw_enabled,
+          rstate->sw_enabled,
+          rstate->os_owner);
 
     /* Log new killswitch state */
     new_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
     if (old_rfkilled != new_rfkilled) {
         _LOGI(LOGD_RFKILL,
               "rfkill: %s now %s by radio killswitch",
-              rstate->desc,
+              nm_rfkill_type_to_string(rtype),
               new_rfkilled ? "enabled" : "disabled");
     }
 
-    /* Send out property changed signal for HW enabled */
-    if (rstate->hw_enabled != old_hwe) {
-        if (rstate->hw_prop)
-            g_object_notify(G_OBJECT(self), rstate->hw_prop);
-    }
+    priv->radio_flags = NM_FLAGS_ASSIGN(priv->radio_flags,
+                                        (guint) nm_rfkill_type_to_radio_available_flag(rtype),
+                                        rstate->available);
+
+    /* Send out property changed signal for HW available and enabled */
+    nm_gobject_notify_together(self,
+                               rstate->hw_enabled != old_hwe ? _rfkill_type_desc[rtype].hw_prop_id
+                                                             : PROP_0,
+                               priv->radio_flags != old_radio_flags ? PROP_RADIO_FLAGS : PROP_0);
 
     /* And finally update the actual device radio state itself; respect the
      * daemon state here because this is never called from user-triggered
      * radio changes and we only want to ignore the daemon enabled state when
      * handling user radio change requests.
      */
-    new_enabled = radio_enabled_for_rstate(rstate, TRUE);
+    new_enabled = _rfkill_radio_state_get_enabled(rstate, TRUE);
     if (new_enabled != old_enabled)
-        manager_update_radio_enabled(self, rstate, new_enabled);
+        _rfkill_update_devices(self, rtype, new_enabled);
 }
 
 static void
-nm_manager_rfkill_update(NMManager *self, RfKillType rtype)
+_rfkill_update(NMManager *self, NMRfkillType rtype)
 {
-    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
-    guint             i;
+    guint i;
 
-    if (rtype != RFKILL_TYPE_UNKNOWN)
-        manager_rfkill_update_one_type(self, &priv->radio_states[rtype], rtype);
+    if (rtype != NM_RFKILL_TYPE_UNKNOWN)
+        _rfkill_update_one_type(self, rtype);
     else {
         /* Otherwise, sync all radio types */
-        for (i = 0; i < RFKILL_TYPE_MAX; i++)
-            manager_rfkill_update_one_type(self, &priv->radio_states[i], i);
+        for (i = 0; i < NM_RFKILL_TYPE_MAX; i++)
+            _rfkill_update_one_type(self, i);
     }
 }
 
+/*****************************************************************************/
+
+#define KERN_RFKILL_OP_CHANGE_ALL 3
+#define KERN_RFKILL_TYPE_WLAN     1
+#define KERN_RFKILL_TYPE_WWAN     5
+
+struct rfkill_event {
+    uint32_t idx;
+    uint8_t  type;
+    uint8_t  op;
+    uint8_t  soft;
+    uint8_t  hard;
+} _nm_packed;
+
+static void
+_rfkill_update_system(NMManager *self, NMRfkillType rtype, gboolean enabled)
+{
+    nm_auto_close int   fd = -1;
+    struct rfkill_event event;
+    ssize_t             len;
+    int                 errsv;
+
+    nm_assert(NM_IN_SET(rtype, NM_RFKILL_TYPE_WLAN, NM_RFKILL_TYPE_WWAN));
+
+    fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
+    if (fd < 0) {
+        if (errno == EACCES)
+            _LOGW(LOGD_RFKILL,
+                  "rfkill: (%s): failed to open killswitch device",
+                  nm_rfkill_type_to_string(rtype));
+        return;
+    }
+
+    if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+        _LOGW(LOGD_RFKILL,
+              "rfkill: (%s): failed to set killswitch device for "
+              "non-blocking operation",
+              nm_rfkill_type_to_string(rtype));
+        return;
+    }
+
+    memset(&event, 0, sizeof(event));
+    event.op = KERN_RFKILL_OP_CHANGE_ALL;
+    switch (rtype) {
+    case NM_RFKILL_TYPE_WLAN:
+        event.type = KERN_RFKILL_TYPE_WLAN;
+        break;
+    case NM_RFKILL_TYPE_WWAN:
+        event.type = KERN_RFKILL_TYPE_WWAN;
+        break;
+    default:
+        nm_assert_not_reached();
+    }
+    event.soft = enabled ? 0 : 1;
+
+    len = write(fd, &event, sizeof(event));
+    if (len < 0) {
+        errsv = errno;
+        _LOGW(LOGD_RFKILL,
+              "rfkill: (%s): failed to change Wi-Fi killswitch state: (%d) %s",
+              nm_rfkill_type_to_string(rtype),
+              errsv,
+              nm_strerror_native(errsv));
+    } else if (len == sizeof(event)) {
+        _LOGI(LOGD_RFKILL,
+              "rfkill: %s hardware radio set %s",
+              nm_rfkill_type_to_string(rtype),
+              enabled ? "enabled" : "disabled");
+    } else {
+        /* Failed to write full structure */
+        _LOGW(LOGD_RFKILL,
+              "rfkill: (%s): failed to change Wi-Fi killswitch state",
+              nm_rfkill_type_to_string(rtype));
+    }
+}
+
+static void
+_rfkill_update_from_user(NMManager *self, NMRfkillType rtype, gboolean enabled)
+{
+    NMManagerPrivate *priv   = NM_MANAGER_GET_PRIVATE(self);
+    RfkillRadioState *rstate = &priv->radio_states[rtype];
+    gboolean          old_enabled, new_enabled;
+
+    /* Don't touch devices if asleep/networking disabled */
+    if (manager_sleeping(self))
+        return;
+
+    _LOGD(LOGD_RFKILL,
+          "rfkill: (%s): setting radio %s by user",
+          nm_rfkill_type_to_string(rtype),
+          enabled ? "enabled" : "disabled");
+
+    /* Update enabled key in state file */
+    nm_config_state_set(priv->config, TRUE, FALSE, _rfkill_type_desc[rtype].key, enabled);
+
+    /* When the user toggles the radio, their request should override any
+     * daemon (like ModemManager) enabled state that can be changed.  For WWAN
+     * for example, we want the WwanEnabled property to reflect the daemon state
+     * too so that users can toggle the modem powered, but we don't want that
+     * daemon state to affect whether or not the user *can* turn it on, which is
+     * what the kernel rfkill state does.  So we ignore daemon enabled state
+     * when determining what the new state should be since it shouldn't block
+     * the user's request.
+     */
+    old_enabled          = _rfkill_radio_state_get_enabled(rstate, TRUE);
+    rstate->user_enabled = enabled;
+    new_enabled          = _rfkill_radio_state_get_enabled(rstate, FALSE);
+    if (new_enabled != old_enabled) {
+        /* Try to change the kernel rfkill state */
+        _rfkill_update_system(self, rtype, new_enabled);
+        _rfkill_update_devices(self, rtype, new_enabled);
+    }
+}
+
+/*****************************************************************************/
+
 static void
 device_auth_done_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data)
 {
@@ -2599,7 +2792,7 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat
         int master_ifindex = nm_platform_link_get_master(priv->platform, ifindex);
 
         /* Check that the master is activating before assuming a
-         * slave connection. However, ignore ovs-system master as
+         * slave connection. However, ignore ovs-system/ovs-netdev master as
          * we never manage it.
          */
         if (master_ifindex
@@ -2786,6 +2979,7 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat
     nm_device_assume_state_reset(device);
 
     if (!nm_settings_add_connection(priv->settings,
+                                    NULL,
                                     connection,
                                     NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
                                     NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
@@ -2906,6 +3100,7 @@ recheck_assume_connection(NMManager *self, NMDevice *device)
 
                 nm_settings_connection_update(
                     sett_conn,
+                    NULL,
                     con2,
                     NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
                     NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
@@ -3207,7 +3402,7 @@ add_device(NMManager *self, NMDevice *device, GError **error)
 {
     NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
     const char       *iface, *type_desc;
-    RfKillType        rtype;
+    NMRfkillType      rtype;
     GSList           *iter, *remove = NULL;
     int               ifindex;
     const char       *dbus_path;
@@ -3294,9 +3489,9 @@ add_device(NMManager *self, NMDevice *device, GError **error)
      * global state.
      */
     rtype = nm_device_get_rfkill_type(device);
-    if (rtype != RFKILL_TYPE_UNKNOWN) {
-        nm_manager_rfkill_update(self, rtype);
-        nm_device_set_enabled(device, radio_enabled_for_type(self, rtype, TRUE));
+    if (rtype != NM_RFKILL_TYPE_UNKNOWN) {
+        _rfkill_update(self, rtype);
+        nm_device_set_enabled(device, _rfkill_radio_state_get(self, rtype));
     }
 
     iface = nm_device_get_iface(device);
@@ -3671,12 +3866,14 @@ platform_query_devices(NMManager *self)
 }
 
 static void
-rfkill_manager_rfkill_changed_cb(NMRfkillManager *rfkill_mgr,
-                                 RfKillType       rtype,
-                                 RfKillState      udev_state,
-                                 gpointer         user_data)
+rfkill_manager_rfkill_changed_cb(NMRfkillManager          *rfkill_mgr,
+                                 /* NMRfkillType */ guint  rtype,
+                                 /* NMRfkillState */ guint udev_state,
+                                 gpointer                  user_data)
 {
-    nm_manager_rfkill_update(NM_MANAGER(user_data), rtype);
+    nm_assert(rtype < NM_RFKILL_TYPE_MAX);
+
+    _rfkill_update(NM_MANAGER(user_data), rtype);
 }
 
 const CList *
@@ -5760,6 +5957,7 @@ _add_and_activate_auth_done(NMManager                      *self,
      * shutdown. */
     nm_settings_add_connection_dbus(
         priv->settings,
+        NULL,
         connection,
         persist_mode,
         NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
@@ -6313,7 +6511,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
         /* Ensure rfkill state is up-to-date since we don't respond to state
          * changes during sleep.
          */
-        nm_manager_rfkill_update(self, RFKILL_TYPE_UNKNOWN);
+        _rfkill_update(self, NM_RFKILL_TYPE_UNKNOWN);
 
         /* Re-manage managed devices */
         c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
@@ -6330,20 +6528,21 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
             /* enable/disable wireless devices since that we don't respond
              * to killswitch changes during sleep.
              */
-            for (i = 0; i < RFKILL_TYPE_MAX; i++) {
-                RadioState *rstate  = &priv->radio_states[i];
-                gboolean    enabled = radio_enabled_for_rstate(rstate, TRUE);
-
-                if (rstate->desc) {
-                    _LOGD(LOGD_RFKILL,
-                          "rfkill: %s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)",
-                          enabled ? "enabling" : "disabling",
-                          rstate->desc,
-                          rstate->hw_enabled,
-                          rstate->sw_enabled,
-                          rstate->user_enabled);
-                }
-                if (nm_device_get_rfkill_type(device) == rstate->rtype)
+            for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) {
+                const NMRfkillType      rtype   = i;
+                const RfkillRadioState *rstate  = &priv->radio_states[rtype];
+                gboolean                enabled = _rfkill_radio_state_get_enabled(rstate, TRUE);
+
+                _LOGD(LOGD_RFKILL,
+                      "rfkill: %s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d, "
+                      "os_owner %d)",
+                      enabled ? "enabling" : "disabling",
+                      nm_rfkill_type_to_string(rtype),
+                      rstate->hw_enabled,
+                      rstate->sw_enabled,
+                      rstate->user_enabled,
+                      rstate->os_owner);
+                if (nm_device_get_rfkill_type(device) == rtype)
                     nm_device_set_enabled(device, enabled);
             }
 
@@ -6929,25 +7128,21 @@ nm_manager_start(NMManager *self, GError **error)
     nm_device_factory_manager_for_each_factory(start_factory, NULL);
 
     /* Set initial radio enabled/disabled state */
-    for (i = 0; i < RFKILL_TYPE_MAX; i++) {
-        RadioState *rstate = &priv->radio_states[i];
-        gboolean    enabled;
-
-        if (!rstate->desc)
-            continue;
+    for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) {
+        const NMRfkillType rtype  = i;
+        RfkillRadioState  *rstate = &priv->radio_states[rtype];
+        gboolean           enabled;
 
         /* recheck kernel rfkill state */
-        update_rstate_from_rfkill(priv->rfkill_mgr, rstate);
-
-        if (rstate->desc) {
-            _LOGI(LOGD_RFKILL,
-                  "rfkill: %s %s by radio killswitch; %s by state file",
-                  rstate->desc,
-                  (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
-                  rstate->user_enabled ? "enabled" : "disabled");
-        }
-        enabled = radio_enabled_for_rstate(rstate, TRUE);
-        manager_update_radio_enabled(self, rstate, enabled);
+        _rfkill_radio_state_set_from_manager(self, rtype, rstate);
+
+        _LOGI(LOGD_RFKILL,
+              "rfkill: %s %s by radio killswitch; %s by state file",
+              nm_rfkill_type_to_string(rtype),
+              (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
+              rstate->user_enabled ? "enabled" : "disabled");
+        enabled = _rfkill_radio_state_get_enabled(rstate, TRUE);
+        _rfkill_update_devices(self, rtype, enabled);
     }
 
     _LOGI(LOGD_CORE, "Networking is %s by state file", priv->net_enabled ? "enabled" : "disabled");
@@ -7605,141 +7800,7 @@ auth_mgr_changed(NMAuthManager *auth_manager, gpointer user_data)
                                "()");
 }
 
-#define KERN_RFKILL_OP_CHANGE_ALL 3
-#define KERN_RFKILL_TYPE_WLAN     1
-#define KERN_RFKILL_TYPE_WWAN     5
-struct rfkill_event {
-    uint32_t idx;
-    uint8_t  type;
-    uint8_t  op;
-    uint8_t  soft;
-    uint8_t  hard;
-} _nm_packed;
-
-static void
-rfkill_change(NMManager *self, const char *desc, RfKillType rtype, gboolean enabled)
-{
-    int                 fd;
-    struct rfkill_event event;
-    ssize_t             len;
-    int                 errsv;
-
-    g_return_if_fail(rtype == RFKILL_TYPE_WLAN || rtype == RFKILL_TYPE_WWAN);
-
-    fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
-    if (fd < 0) {
-        if (errno == EACCES)
-            _LOGW(LOGD_RFKILL, "rfkill: (%s): failed to open killswitch device", desc);
-        return;
-    }
-
-    if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
-        _LOGW(LOGD_RFKILL,
-              "rfkill: (%s): failed to set killswitch device for "
-              "non-blocking operation",
-              desc);
-        nm_close(fd);
-        return;
-    }
-
-    memset(&event, 0, sizeof(event));
-    event.op = KERN_RFKILL_OP_CHANGE_ALL;
-    switch (rtype) {
-    case RFKILL_TYPE_WLAN:
-        event.type = KERN_RFKILL_TYPE_WLAN;
-        break;
-    case RFKILL_TYPE_WWAN:
-        event.type = KERN_RFKILL_TYPE_WWAN;
-        break;
-    default:
-        g_assert_not_reached();
-    }
-    event.soft = enabled ? 0 : 1;
-
-    len = write(fd, &event, sizeof(event));
-    if (len < 0) {
-        errsv = errno;
-        _LOGW(LOGD_RFKILL,
-              "rfkill: (%s): failed to change Wi-Fi killswitch state: (%d) %s",
-              desc,
-              errsv,
-              nm_strerror_native(errsv));
-    } else if (len == sizeof(event)) {
-        _LOGI(LOGD_RFKILL,
-              "rfkill: %s hardware radio set %s",
-              desc,
-              enabled ? "enabled" : "disabled");
-    } else {
-        /* Failed to write full structure */
-        _LOGW(LOGD_RFKILL, "rfkill: (%s): failed to change Wi-Fi killswitch state", desc);
-    }
-
-    nm_close(fd);
-}
-
-static void
-manager_radio_user_toggled(NMManager *self, RadioState *rstate, gboolean enabled)
-{
-    NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
-    gboolean          old_enabled, new_enabled;
-
-    /* Don't touch devices if asleep/networking disabled */
-    if (manager_sleeping(self))
-        return;
-
-    if (rstate->desc) {
-        _LOGD(LOGD_RFKILL,
-              "rfkill: (%s): setting radio %s by user",
-              rstate->desc,
-              enabled ? "enabled" : "disabled");
-    }
-
-    /* Update enabled key in state file */
-    nm_config_state_set(priv->config, TRUE, FALSE, rstate->key, enabled);
-
-    /* When the user toggles the radio, their request should override any
-     * daemon (like ModemManager) enabled state that can be changed.  For WWAN
-     * for example, we want the WwanEnabled property to reflect the daemon state
-     * too so that users can toggle the modem powered, but we don't want that
-     * daemon state to affect whether or not the user *can* turn it on, which is
-     * what the kernel rfkill state does.  So we ignore daemon enabled state
-     * when determining what the new state should be since it shouldn't block
-     * the user's request.
-     */
-    old_enabled          = radio_enabled_for_rstate(rstate, TRUE);
-    rstate->user_enabled = enabled;
-    new_enabled          = radio_enabled_for_rstate(rstate, FALSE);
-    if (new_enabled != old_enabled) {
-        /* Try to change the kernel rfkill state */
-        if (rstate->rtype == RFKILL_TYPE_WLAN || rstate->rtype == RFKILL_TYPE_WWAN)
-            rfkill_change(self, rstate->desc, rstate->rtype, new_enabled);
-
-        manager_update_radio_enabled(self, rstate, new_enabled);
-    }
-}
-
-static gboolean
-periodic_update_active_connection_timestamps(gpointer user_data)
-{
-    NMManager          *manager = NM_MANAGER(user_data);
-    NMManagerPrivate   *priv    = NM_MANAGER_GET_PRIVATE(manager);
-    NMActiveConnection *ac;
-    gboolean            has_time = FALSE;
-    guint64             t        = 0;
-
-    c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
-        if (nm_active_connection_get_state(ac) != NM_ACTIVE_CONNECTION_STATE_ACTIVATED)
-            continue;
-
-        if (!has_time) {
-            t        = time(NULL);
-            has_time = TRUE;
-        }
-        nm_settings_connection_update_timestamp(nm_active_connection_get_settings_connection(ac),
-                                                t);
-    }
-    return G_SOURCE_CONTINUE;
-}
+/*****************************************************************************/
 
 void
 nm_manager_unblock_failed_ovs_interfaces(NMManager *self)
@@ -7893,8 +7954,8 @@ constructed(GObject *object)
 
     priv->net_enabled = state->net_enabled;
 
-    priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = state->wifi_enabled;
-    priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = state->wwan_enabled;
+    priv->radio_states[NM_RFKILL_TYPE_WLAN].user_enabled = state->wifi_enabled;
+    priv->radio_states[NM_RFKILL_TYPE_WWAN].user_enabled = state->wwan_enabled;
 
     priv->rfkill_mgr = nm_rfkill_manager_new();
     g_signal_connect(priv->rfkill_mgr,
@@ -7907,21 +7968,18 @@ constructed(GObject *object)
      * changes to the WirelessEnabled/WWANEnabled properties which toggle kernel
      * rfkill.
      */
-    rfkill_change(self,
-                  priv->radio_states[RFKILL_TYPE_WLAN].desc,
-                  RFKILL_TYPE_WLAN,
-                  priv->radio_states[RFKILL_TYPE_WLAN].user_enabled);
-    rfkill_change(self,
-                  priv->radio_states[RFKILL_TYPE_WWAN].desc,
-                  RFKILL_TYPE_WWAN,
-                  priv->radio_states[RFKILL_TYPE_WWAN].user_enabled);
+    _rfkill_update_system(self,
+                          NM_RFKILL_TYPE_WLAN,
+                          priv->radio_states[NM_RFKILL_TYPE_WLAN].user_enabled);
+    _rfkill_update_system(self,
+                          NM_RFKILL_TYPE_WWAN,
+                          priv->radio_states[NM_RFKILL_TYPE_WWAN].user_enabled);
 }
 
 static void
 nm_manager_init(NMManager *self)
 {
     NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
-    guint             i;
     GFile            *file;
 
     c_list_init(&priv->auth_lst_head);
@@ -7935,25 +7993,18 @@ nm_manager_init(NMManager *self)
 
     priv->capabilities = g_array_new(FALSE, FALSE, sizeof(guint32));
 
-    /* Initialize rfkill structures and states */
-    memset(priv->radio_states, 0, sizeof(priv->radio_states));
-
-    priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE;
-    priv->radio_states[RFKILL_TYPE_WLAN].key          = NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED;
-    priv->radio_states[RFKILL_TYPE_WLAN].prop         = NM_MANAGER_WIRELESS_ENABLED;
-    priv->radio_states[RFKILL_TYPE_WLAN].hw_prop      = NM_MANAGER_WIRELESS_HARDWARE_ENABLED;
-    priv->radio_states[RFKILL_TYPE_WLAN].desc         = "Wi-Fi";
-    priv->radio_states[RFKILL_TYPE_WLAN].rtype        = RFKILL_TYPE_WLAN;
-
-    priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE;
-    priv->radio_states[RFKILL_TYPE_WWAN].key          = NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED;
-    priv->radio_states[RFKILL_TYPE_WWAN].prop         = NM_MANAGER_WWAN_ENABLED;
-    priv->radio_states[RFKILL_TYPE_WWAN].hw_prop      = NM_MANAGER_WWAN_HARDWARE_ENABLED;
-    priv->radio_states[RFKILL_TYPE_WWAN].desc         = "WWAN";
-    priv->radio_states[RFKILL_TYPE_WWAN].rtype        = RFKILL_TYPE_WWAN;
-
-    for (i = 0; i < RFKILL_TYPE_MAX; i++)
-        priv->radio_states[i].hw_enabled = TRUE;
+    priv->radio_states[NM_RFKILL_TYPE_WLAN] = (RfkillRadioState){
+        .user_enabled = TRUE,
+        .sw_enabled   = FALSE,
+        .hw_enabled   = TRUE,
+        .os_owner     = TRUE,
+    };
+    priv->radio_states[NM_RFKILL_TYPE_WWAN] = (RfkillRadioState){
+        .user_enabled = TRUE,
+        .sw_enabled   = FALSE,
+        .hw_enabled   = TRUE,
+        .os_owner     = TRUE,
+    };
 
     priv->sleeping = FALSE;
     priv->state    = NM_STATE_DISCONNECTED;
@@ -7984,12 +8035,6 @@ nm_manager_init(NMManager *self)
         _LOGW(LOGD_CORE, "failed to monitor kernel firmware directory '%s'.", KERNEL_FIRMWARE_DIR);
     }
 
-    /* Update timestamps in active connections */
-    priv->timestamp_update_id =
-        g_timeout_add_seconds(300,
-                              (GSourceFunc) periodic_update_active_connection_timestamps,
-                              self);
-
     priv->metered       = NM_METERED_UNKNOWN;
     priv->sleep_devices = g_hash_table_new(nm_direct_hash, NULL);
 }
@@ -8025,16 +8070,16 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_boolean(value, priv->net_enabled);
         break;
     case PROP_WIRELESS_ENABLED:
-        g_value_set_boolean(value, radio_enabled_for_type(self, RFKILL_TYPE_WLAN, TRUE));
+        g_value_set_boolean(value, _rfkill_radio_state_get(self, NM_RFKILL_TYPE_WLAN));
         break;
     case PROP_WIRELESS_HARDWARE_ENABLED:
-        g_value_set_boolean(value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled);
+        g_value_set_boolean(value, priv->radio_states[NM_RFKILL_TYPE_WLAN].hw_enabled);
         break;
     case PROP_WWAN_ENABLED:
-        g_value_set_boolean(value, radio_enabled_for_type(self, RFKILL_TYPE_WWAN, TRUE));
+        g_value_set_boolean(value, _rfkill_radio_state_get(self, NM_RFKILL_TYPE_WWAN));
         break;
     case PROP_WWAN_HARDWARE_ENABLED:
-        g_value_set_boolean(value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled);
+        g_value_set_boolean(value, priv->radio_states[NM_RFKILL_TYPE_WWAN].hw_enabled);
         break;
     case PROP_WIMAX_ENABLED:
         g_value_set_boolean(value, FALSE);
@@ -8042,6 +8087,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_WIMAX_HARDWARE_ENABLED:
         g_value_set_boolean(value, FALSE);
         break;
+    case PROP_RADIO_FLAGS:
+        g_value_set_uint(value, priv->radio_flags);
+        break;
     case PROP_ACTIVE_CONNECTIONS:
         ptrarr = g_ptr_array_new();
         c_list_for_each_entry_prev (ac,
@@ -8125,14 +8173,14 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
     switch (prop_id) {
     case PROP_WIRELESS_ENABLED:
-        manager_radio_user_toggled(NM_MANAGER(object),
-                                   &priv->radio_states[RFKILL_TYPE_WLAN],
-                                   g_value_get_boolean(value));
+        _rfkill_update_from_user(NM_MANAGER(object),
+                                 NM_RFKILL_TYPE_WLAN,
+                                 g_value_get_boolean(value));
         break;
     case PROP_WWAN_ENABLED:
-        manager_radio_user_toggled(NM_MANAGER(object),
-                                   &priv->radio_states[RFKILL_TYPE_WWAN],
-                                   g_value_get_boolean(value));
+        _rfkill_update_from_user(NM_MANAGER(object),
+                                 NM_RFKILL_TYPE_WWAN,
+                                 g_value_get_boolean(value));
         break;
     case PROP_WIMAX_ENABLED:
         /* WIMAX is deprecated. This does nothing. */
@@ -8497,6 +8545,9 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("WimaxHardwareEnabled",
                                                            "b",
                                                            NM_MANAGER_WIMAX_HARDWARE_ENABLED),
+            NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("RadioFlags",
+                                                           "u",
+                                                           NM_MANAGER_RADIO_FLAGS),
             NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("ActiveConnections",
                                                            "ao",
                                                            NM_MANAGER_ACTIVE_CONNECTIONS),
@@ -8641,6 +8692,14 @@ nm_manager_class_init(NMManagerClass *manager_class)
                              TRUE,
                              G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
+    obj_properties[PROP_RADIO_FLAGS] = g_param_spec_uint(NM_MANAGER_RADIO_FLAGS,
+                                                         "",
+                                                         "",
+                                                         0,
+                                                         G_MAXUINT32,
+                                                         NM_RADIO_FLAG_NONE,
+                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
     obj_properties[PROP_ACTIVE_CONNECTIONS] =
         g_param_spec_boxed(NM_MANAGER_ACTIVE_CONNECTIONS,
                            "",
diff --git a/src/core/nm-manager.h b/src/core/nm-manager.h
index 385cdfe0..f8563c3a 100644
--- a/src/core/nm-manager.h
+++ b/src/core/nm-manager.h
@@ -30,6 +30,7 @@
 #define NM_MANAGER_WWAN_HARDWARE_ENABLED        "wwan-hardware-enabled"
 #define NM_MANAGER_WIMAX_ENABLED                "wimax-enabled"
 #define NM_MANAGER_WIMAX_HARDWARE_ENABLED       "wimax-hardware-enabled"
+#define NM_MANAGER_RADIO_FLAGS                  "radio-flags"
 #define NM_MANAGER_ACTIVE_CONNECTIONS           "active-connections"
 #define NM_MANAGER_CONNECTIVITY                 "connectivity"
 #define NM_MANAGER_CONNECTIVITY_CHECK_AVAILABLE "connectivity-check-available"
diff --git a/src/core/nm-pacrunner-manager.c b/src/core/nm-pacrunner-manager.c
index 5ef425bb..86a40632 100644
--- a/src/core/nm-pacrunner-manager.c
+++ b/src/core/nm-pacrunner-manager.c
@@ -291,7 +291,7 @@ _call_destroy_proxy_configuration(NMPacrunnerManager *self,
                            g_variant_new("(o)", path),
                            G_VARIANT_TYPE("()"),
                            G_DBUS_CALL_FLAGS_NO_AUTO_START,
-                           NM_SHUTDOWN_TIMEOUT_MS,
+                           NM_SHUTDOWN_TIMEOUT_1500_MSEC,
                            priv->cancellable,
                            _call_destroy_proxy_configuration_cb,
                            conf_id_ref(conf_id));
@@ -315,7 +315,7 @@ _call_create_proxy_configuration(NMPacrunnerManager *self,
                            conf_id->parameters,
                            G_VARIANT_TYPE("(o)"),
                            G_DBUS_CALL_FLAGS_NO_AUTO_START,
-                           NM_SHUTDOWN_TIMEOUT_MS,
+                           NM_SHUTDOWN_TIMEOUT_1500_MSEC,
                            priv->cancellable,
                            _call_create_proxy_configuration_cb,
                            conf_id_ref(conf_id));
diff --git a/src/core/nm-policy.c b/src/core/nm-policy.c
index d77fc0a0..2d47772d 100644
--- a/src/core/nm-policy.c
+++ b/src/core/nm-policy.c
@@ -75,8 +75,9 @@ typedef struct {
     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 */
+    char                *orig_hostname;     /* hostname at NM start time */
+    char                *cur_hostname;      /* hostname we want to assign */
+    char                *cur_hostname_full; /* similar to @last_hostname, but before shortening */
     char *
         last_hostname; /* last hostname NM set (to detect if someone else changed it in the meanwhile) */
 
@@ -560,6 +561,7 @@ _set_hostname(NMPolicy *self, const char *new_hostname, const char *msg)
 {
     NMPolicyPrivate *priv         = NM_POLICY_GET_PRIVATE(self);
     gs_free char    *old_hostname = NULL;
+    gboolean         cur_hostname_full_changed;
     const char      *name;
 
     /* The incoming hostname *can* be NULL, which will get translated to
@@ -568,32 +570,50 @@ _set_hostname(NMPolicy *self, const char *new_hostname, const char *msg)
      * there was no valid hostname to start with.
      */
 
+    if (nm_strdup_reset(&priv->cur_hostname_full, new_hostname)) {
+        gs_free char *shortened = NULL;
+
+        cur_hostname_full_changed = TRUE;
+
+        if (priv->cur_hostname_full
+            && !nm_utils_shorten_hostname(priv->cur_hostname_full, &shortened)) {
+            _LOGW(LOGD_DNS,
+                  "set-hostname: hostname '%s' %s is invalid",
+                  priv->cur_hostname_full,
+                  msg);
+            return;
+        }
+
+        if (shortened) {
+            _LOGI(LOGD_DNS,
+                  "set-hostname: shortened hostname %s from '%s' to '%s'",
+                  msg,
+                  priv->cur_hostname_full,
+                  shortened);
+            nm_strdup_reset_take(&priv->cur_hostname, g_steal_pointer(&shortened));
+        } else
+            nm_strdup_reset(&priv->cur_hostname, priv->cur_hostname_full);
+    } else
+        cur_hostname_full_changed = FALSE;
+
     /* Update the DNS only if the hostname is actually
      * going to change.
      */
-    if (!nm_streq0(priv->cur_hostname, new_hostname)) {
-        g_free(priv->cur_hostname);
-        priv->cur_hostname = g_strdup(new_hostname);
-
+    if (cur_hostname_full_changed) {
         /* Notify the DNS manager of the hostname change so that the domain part, if
          * present, can be added to the search list. Set the @updating_dns flag
          * so that dns_config_changed() doesn't try again to restart DNS lookup.
          */
         priv->updating_dns = TRUE;
         nm_dns_manager_set_hostname(priv->dns_manager,
-                                    priv->cur_hostname,
+                                    priv->cur_hostname_full,
                                     all_devices_not_active(self));
         priv->updating_dns = FALSE;
     }
 
     /* Finally, set kernel hostname */
-    if (!new_hostname)
-        name = FALLBACK_HOSTNAME4;
-    else if (!new_hostname[0]) {
-        g_warn_if_reached();
-        name = FALLBACK_HOSTNAME4;
-    } else
-        name = new_hostname;
+    nm_assert(!priv->cur_hostname || priv->cur_hostname[0]);
+    name = priv->cur_hostname ?: FALLBACK_HOSTNAME4;
 
     /* Don't set the hostname if it isn't actually changing */
     if ((old_hostname = _get_hostname(self)) && (nm_streq(name, old_hostname))) {
@@ -602,14 +622,16 @@ _set_hostname(NMPolicy *self, const char *new_hostname, const char *msg)
     }
 
     /* Keep track of the last set hostname */
-    g_free(priv->last_hostname);
-    priv->last_hostname     = g_strdup(name);
+    nm_strdup_reset(&priv->last_hostname, name);
     priv->changing_hostname = TRUE;
 
     _LOGI(LOGD_DNS, "set-hostname: set hostname to '%s' (%s)", name, msg);
 
     /* Ask NMSettings to update the transient hostname using its
-     * systemd-hostnamed proxy */
+     * systemd-hostnamed proxy.
+     *
+     * FIXME(shutdown): an async request must be cancellable, so we are guaranteed
+     *   to tear down in a reasonable time.*/
     nm_hostname_manager_set_transient_hostname(priv->hostname_manager,
                                                name,
                                                settings_set_hostname_cb,
@@ -2761,7 +2783,7 @@ constructed(GObject *object)
                      self);
 
     priv->dns_manager = g_object_ref(nm_dns_manager_get());
-    nm_dns_manager_set_initial_hostname(priv->dns_manager, priv->orig_hostname);
+    nm_dns_manager_set_hostname(priv->dns_manager, priv->orig_hostname, TRUE);
     priv->config_changed_id = g_signal_connect(priv->dns_manager,
                                                NM_DNS_MANAGER_CONFIG_CHANGED,
                                                G_CALLBACK(dns_config_changed),
@@ -2892,6 +2914,7 @@ dispose(GObject *object)
 
     nm_clear_g_free(&priv->orig_hostname);
     nm_clear_g_free(&priv->cur_hostname);
+    nm_clear_g_free(&priv->cur_hostname_full);
     nm_clear_g_free(&priv->last_hostname);
 
     if (priv->hostname_manager) {
diff --git a/src/core/nm-rfkill-manager.c b/src/core/nm-rfkill-manager.c
index f3851e5c..03333899 100644
--- a/src/core/nm-rfkill-manager.c
+++ b/src/core/nm-rfkill-manager.c
@@ -9,6 +9,7 @@
 
 #include <libudev.h>
 
+#include "c-list/src/c-list.h"
 #include "libnm-udev-aux/nm-udev-utils.h"
 
 /*****************************************************************************/
@@ -24,8 +25,9 @@ typedef struct {
     NMUdevClient *udev_client;
 
     /* Authoritative rfkill state (RFKILL_* enum) */
-    RfKillState rfkill_states[RFKILL_TYPE_MAX];
-    GSList     *killswitches;
+    NMRfkillState rfkill_states[NM_RFKILL_TYPE_MAX];
+
+    CList killswitch_lst_head;
 } NMRfkillManagerPrivate;
 
 struct _NMRfkillManager {
@@ -45,60 +47,81 @@ G_DEFINE_TYPE(NMRfkillManager, nm_rfkill_manager, G_TYPE_OBJECT)
 /*****************************************************************************/
 
 typedef struct {
-    char      *name;
-    guint64    seqnum;
-    char      *path;
-    char      *driver;
-    RfKillType rtype;
-    int        state;
-    gboolean   platform;
+    CList        killswitch_lst;
+    char        *name;
+    char        *path;
+    char        *driver;
+    guint64      seqnum;
+    NMRfkillType rtype;
+    int          state;
+    bool         platform : 1;
 } Killswitch;
 
-RfKillState
-nm_rfkill_manager_get_rfkill_state(NMRfkillManager *self, RfKillType rtype)
+NMRfkillState
+nm_rfkill_manager_get_rfkill_state(NMRfkillManager *self, NMRfkillType rtype)
 {
-    g_return_val_if_fail(self != NULL, RFKILL_UNBLOCKED);
-    g_return_val_if_fail(rtype < RFKILL_TYPE_MAX, RFKILL_UNBLOCKED);
+    g_return_val_if_fail(self != NULL, NM_RFKILL_STATE_UNBLOCKED);
+    g_return_val_if_fail(rtype < NM_RFKILL_TYPE_MAX, NM_RFKILL_STATE_UNBLOCKED);
 
     return NM_RFKILL_MANAGER_GET_PRIVATE(self)->rfkill_states[rtype];
 }
 
-static const char *
-rfkill_type_to_desc(RfKillType rtype)
+NMRadioFlags
+nm_rfkill_type_to_radio_available_flag(NMRfkillType type)
+{
+    switch (type) {
+    case NM_RFKILL_TYPE_WLAN:
+        return NM_RADIO_FLAG_WLAN_AVAILABLE;
+    case NM_RFKILL_TYPE_WWAN:
+        return NM_RADIO_FLAG_WWAN_AVAILABLE;
+    case NM_RFKILL_TYPE_UNKNOWN:
+        break;
+    }
+    return nm_assert_unreachable_val(NM_RADIO_FLAG_NONE);
+}
+
+const char *
+nm_rfkill_type_to_string(NMRfkillType type)
 {
-    if (rtype == 0)
+    switch (type) {
+    case NM_RFKILL_TYPE_WLAN:
         return "Wi-Fi";
-    else if (rtype == 1)
+    case NM_RFKILL_TYPE_WWAN:
         return "WWAN";
-    else if (rtype == 2)
-        return "WiMAX";
-    return "unknown";
+    case NM_RFKILL_TYPE_UNKNOWN:
+        break;
+    }
+    return nm_assert_unreachable_val("unknown");
 }
 
 static const char *
-rfkill_state_to_desc(RfKillState rstate)
+nm_rfkill_state_to_string(NMRfkillState state)
 {
-    if (rstate == 0)
+    switch (state) {
+    case NM_RFKILL_STATE_UNAVAILABLE:
+        return "unavailable";
+    case NM_RFKILL_STATE_UNBLOCKED:
         return "unblocked";
-    else if (rstate == 1)
+    case NM_RFKILL_STATE_SOFT_BLOCKED:
         return "soft-blocked";
-    else if (rstate == 2)
+    case NM_RFKILL_STATE_HARD_BLOCKED:
         return "hard-blocked";
-    return "unknown";
+    case NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER:
+        return "hard-blocked-os-not-owner";
+    }
+    return nm_assert_unreachable_val("unknown");
 }
 
 static Killswitch *
-killswitch_new(struct udev_device *device, RfKillType rtype)
+killswitch_new(struct udev_device *device, NMRfkillType rtype)
 {
     Killswitch         *ks;
-    struct udev_device *parent = NULL, *grandparent = NULL;
-    const char         *driver, *subsys, *parent_subsys = NULL;
-
-    ks         = g_malloc0(sizeof(Killswitch));
-    ks->name   = g_strdup(udev_device_get_sysname(device));
-    ks->seqnum = udev_device_get_seqnum(device);
-    ks->path   = g_strdup(udev_device_get_syspath(device));
-    ks->rtype  = rtype;
+    struct udev_device *parent      = NULL;
+    struct udev_device *grandparent = NULL;
+    const char         *driver;
+    const char         *subsys;
+    const char         *parent_subsys = NULL;
+    gboolean            platform;
 
     driver = udev_device_get_property_value(device, "DRIVER");
     subsys = udev_device_get_subsystem(device);
@@ -116,14 +139,23 @@ killswitch_new(struct udev_device *device, RfKillType rtype)
                 driver = udev_device_get_property_value(grandparent, "DRIVER");
         }
     }
-
     if (!driver)
         driver = "(unknown)";
-    ks->driver = g_strdup(driver);
 
-    if (g_strcmp0(subsys, "platform") == 0 || g_strcmp0(parent_subsys, "platform") == 0
-        || g_strcmp0(subsys, "acpi") == 0 || g_strcmp0(parent_subsys, "acpi") == 0)
-        ks->platform = TRUE;
+    platform = FALSE;
+    if (nm_streq0(subsys, "platform") || nm_streq0(parent_subsys, "platform")
+        || nm_streq0(subsys, "acpi") || nm_streq0(parent_subsys, "acpi"))
+        platform = TRUE;
+
+    ks  = g_slice_new(Killswitch);
+    *ks = (Killswitch){
+        .name     = g_strdup(udev_device_get_sysname(device)),
+        .seqnum   = udev_device_get_seqnum(device),
+        .path     = g_strdup(udev_device_get_syspath(device)),
+        .rtype    = rtype,
+        .driver   = g_strdup(driver),
+        .platform = platform,
+    };
 
     return ks;
 }
@@ -131,55 +163,58 @@ killswitch_new(struct udev_device *device, RfKillType rtype)
 static void
 killswitch_destroy(Killswitch *ks)
 {
-    g_return_if_fail(ks != NULL);
-
+    c_list_unlink_stale(&ks->killswitch_lst);
     g_free(ks->name);
     g_free(ks->path);
     g_free(ks->driver);
-    memset(ks, 0, sizeof(Killswitch));
-    g_free(ks);
+    nm_g_slice_free(ks);
 }
 
-static RfKillState
-sysfs_state_to_nm_state(int sysfs_state)
+static NMRfkillState
+sysfs_state_to_nm_state(int sysfs_state, int sysfs_reason)
 {
     switch (sysfs_state) {
     case 0:
-        return RFKILL_SOFT_BLOCKED;
+        return NM_RFKILL_STATE_SOFT_BLOCKED;
     case 1:
-        return RFKILL_UNBLOCKED;
+        return NM_RFKILL_STATE_UNBLOCKED;
     case 2:
-        return RFKILL_HARD_BLOCKED;
+        /* sysfs reason is a bitmap, in case we have both reasons (SIGNAL and NOT_OWNER), we want
+         * to consider the device as not owned.
+         */
+        if (sysfs_reason & 2)
+            return NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER;
+        return NM_RFKILL_STATE_HARD_BLOCKED;
     default:
         nm_log_warn(LOGD_RFKILL, "unhandled rfkill state %d", sysfs_state);
         break;
     }
-    return RFKILL_UNBLOCKED;
+    return NM_RFKILL_STATE_UNBLOCKED;
 }
 
 static void
 recheck_killswitches(NMRfkillManager *self)
 {
     NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self);
-    GSList                 *iter;
-    RfKillState             poll_states[RFKILL_TYPE_MAX];
-    RfKillState             platform_states[RFKILL_TYPE_MAX];
-    gboolean                platform_checked[RFKILL_TYPE_MAX];
+    Killswitch             *ks;
+    NMRfkillState           poll_states[NM_RFKILL_TYPE_MAX];
+    NMRfkillState           platform_states[NM_RFKILL_TYPE_MAX];
+    gboolean                platform_checked[NM_RFKILL_TYPE_MAX];
     int                     i;
 
     /* Default state is unblocked */
-    for (i = 0; i < RFKILL_TYPE_MAX; i++) {
-        poll_states[i]      = RFKILL_UNBLOCKED;
-        platform_states[i]  = RFKILL_UNBLOCKED;
+    for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) {
+        poll_states[i]      = NM_RFKILL_STATE_UNAVAILABLE;
+        platform_states[i]  = NM_RFKILL_STATE_UNAVAILABLE;
         platform_checked[i] = FALSE;
     }
 
     /* Poll the states of all killswitches */
-    for (iter = priv->killswitches; iter; iter = g_slist_next(iter)) {
-        Killswitch         *ks = iter->data;
+    c_list_for_each_entry (ks, &priv->killswitch_lst_head, killswitch_lst) {
         struct udev_device *device;
-        RfKillState         dev_state;
+        NMRfkillState       dev_state;
         int                 sysfs_state;
+        int                 sysfs_reason;
 
         device = udev_device_new_from_subsystem_sysname(nm_udev_client_get_udev(priv->udev_client),
                                                         "rfkill",
@@ -192,15 +227,24 @@ recheck_killswitches(NMRfkillManager *self)
                                          G_MININT,
                                          G_MAXINT,
                                          -1);
-        dev_state = sysfs_state_to_nm_state(sysfs_state);
+
+        sysfs_reason = _nm_utils_ascii_str_to_int64(
+            udev_device_get_property_value(device, "RFKILL_HW_BLOCK_REASON"),
+            16,
+            G_MININT,
+            G_MAXINT,
+            1); /* defaults to SIGNAL in case the kernel does not support this */
+
+        dev_state = sysfs_state_to_nm_state(sysfs_state, sysfs_reason);
 
         nm_log_dbg(LOGD_RFKILL,
-                   "%s rfkill%s switch %s state now %d/%u",
-                   rfkill_type_to_desc(ks->rtype),
+                   "%s rfkill%s switch %s state now %d/%s reason: 0x%x",
+                   nm_rfkill_type_to_string(ks->rtype),
                    ks->platform ? " platform" : "",
                    ks->name,
                    sysfs_state,
-                   dev_state);
+                   nm_rfkill_state_to_string(dev_state),
+                   sysfs_reason);
 
         if (ks->platform == FALSE) {
             if (dev_state > poll_states[ks->rtype])
@@ -210,27 +254,32 @@ recheck_killswitches(NMRfkillManager *self)
             if (dev_state > platform_states[ks->rtype])
                 platform_states[ks->rtype] = dev_state;
         }
+
         udev_device_unref(device);
     }
 
     /* Log and emit change signal for final rfkill states */
-    for (i = 0; i < RFKILL_TYPE_MAX; i++) {
+    for (i = 0; i < NM_RFKILL_TYPE_MAX; i++) {
         if (platform_checked[i] == TRUE) {
             /* blocked platform switch state overrides device state, otherwise
              * let the device state stand. (bgo #655773)
              */
-            if (platform_states[i] != RFKILL_UNBLOCKED)
+            if (platform_states[i] > NM_RFKILL_STATE_UNBLOCKED)
                 poll_states[i] = platform_states[i];
         }
 
         if (poll_states[i] != priv->rfkill_states[i]) {
             nm_log_dbg(LOGD_RFKILL,
                        "%s rfkill state now '%s'",
-                       rfkill_type_to_desc(i),
-                       rfkill_state_to_desc(poll_states[i]));
+                       nm_rfkill_type_to_string(i),
+                       nm_rfkill_state_to_string(poll_states[i]));
 
             priv->rfkill_states[i] = poll_states[i];
-            g_signal_emit(self, signals[RFKILL_CHANGED], 0, i, priv->rfkill_states[i]);
+            g_signal_emit(self,
+                          signals[RFKILL_CHANGED],
+                          0,
+                          (guint) i,
+                          (guint) priv->rfkill_states[i]);
         }
     }
 }
@@ -239,52 +288,48 @@ static Killswitch *
 killswitch_find_by_name(NMRfkillManager *self, const char *name)
 {
     NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self);
-    GSList                 *iter;
-
-    g_return_val_if_fail(name != NULL, NULL);
+    Killswitch             *ks;
 
-    for (iter = priv->killswitches; iter; iter = g_slist_next(iter)) {
-        Killswitch *candidate = iter->data;
+    nm_assert(name);
 
-        if (!strcmp(name, candidate->name))
-            return candidate;
+    c_list_for_each_entry (ks, &priv->killswitch_lst_head, killswitch_lst) {
+        if (nm_streq(name, ks->name))
+            return ks;
     }
     return NULL;
 }
 
-static RfKillType
+static NMRfkillType
 rfkill_type_to_enum(const char *str)
 {
-    g_return_val_if_fail(str != NULL, RFKILL_TYPE_UNKNOWN);
-
-    if (!strcmp(str, "wlan"))
-        return RFKILL_TYPE_WLAN;
-    else if (!strcmp(str, "wwan"))
-        return RFKILL_TYPE_WWAN;
+    if (str) {
+        if (nm_streq(str, "wlan"))
+            return NM_RFKILL_TYPE_WLAN;
+        if (nm_streq(str, "wwan"))
+            return NM_RFKILL_TYPE_WWAN;
+    }
 
-    return RFKILL_TYPE_UNKNOWN;
+    return NM_RFKILL_TYPE_UNKNOWN;
 }
 
 static void
 add_one_killswitch(NMRfkillManager *self, struct udev_device *device)
 {
     NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self);
-    const char             *str_type;
-    RfKillType              rtype;
+    NMRfkillType            rtype;
     Killswitch             *ks;
 
-    str_type = udev_device_get_property_value(device, "RFKILL_TYPE");
-    rtype    = rfkill_type_to_enum(str_type);
-    if (rtype == RFKILL_TYPE_UNKNOWN)
+    rtype = rfkill_type_to_enum(udev_device_get_property_value(device, "RFKILL_TYPE"));
+    if (rtype == NM_RFKILL_TYPE_UNKNOWN)
         return;
 
-    ks                 = killswitch_new(device, rtype);
-    priv->killswitches = g_slist_prepend(priv->killswitches, ks);
+    ks = killswitch_new(device, rtype);
+    c_list_link_front(&priv->killswitch_lst_head, &ks->killswitch_lst);
 
     nm_log_info(LOGD_RFKILL,
                 "%s: found %s radio killswitch (at %s) (%sdriver %s)",
                 ks->name,
-                rfkill_type_to_desc(rtype),
+                nm_rfkill_type_to_string(rtype),
                 ks->path,
                 ks->platform ? "platform " : "",
                 ks->driver ?: "<unknown>");
@@ -297,6 +342,7 @@ rfkill_add(NMRfkillManager *self, struct udev_device *device)
 
     g_return_if_fail(device != NULL);
     name = udev_device_get_sysname(device);
+
     g_return_if_fail(name != NULL);
 
     if (!killswitch_find_by_name(self, name))
@@ -307,21 +353,20 @@ static void
 rfkill_remove(NMRfkillManager *self, struct udev_device *device)
 {
     NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self);
-    GSList                 *iter;
+    Killswitch             *ks;
     const char             *name;
 
     g_return_if_fail(device != NULL);
+
     name = udev_device_get_sysname(device);
-    g_return_if_fail(name != NULL);
 
-    for (iter = priv->killswitches; iter; iter = g_slist_next(iter)) {
-        Killswitch *ks = iter->data;
+    g_return_if_fail(name != NULL);
 
-        if (!strcmp(ks->name, name)) {
+    c_list_for_each_entry (ks, &priv->killswitch_lst_head, killswitch_lst) {
+        if (nm_streq(ks->name, name)) {
             nm_log_info(LOGD_RFKILL, "radio killswitch %s disappeared", ks->path);
-            priv->killswitches = g_slist_remove(priv->killswitches, ks);
             killswitch_destroy(ks);
-            break;
+            return;
         }
     }
 }
@@ -339,16 +384,16 @@ handle_uevent(NMUdevClient *client, struct udev_device *device, gpointer user_da
 
     /* A bit paranoid */
     subsys = udev_device_get_subsystem(device);
-    g_return_if_fail(!g_strcmp0(subsys, "rfkill"));
+    g_return_if_fail(nm_streq0(subsys, "rfkill"));
 
     nm_log_dbg(LOGD_PLATFORM,
                "udev rfkill event: action '%s' device '%s'",
                action,
                udev_device_get_sysname(device));
 
-    if (!strcmp(action, "add"))
+    if (nm_streq(action, "add"))
         rfkill_add(self, device);
-    else if (!strcmp(action, "remove"))
+    else if (nm_streq(action, "remove"))
         rfkill_remove(self, device);
 
     recheck_killswitches(self);
@@ -364,8 +409,10 @@ nm_rfkill_manager_init(NMRfkillManager *self)
     struct udev_list_entry *iter;
     guint                   i;
 
-    for (i = 0; i < RFKILL_TYPE_MAX; i++)
-        priv->rfkill_states[i] = RFKILL_UNBLOCKED;
+    c_list_init(&priv->killswitch_lst_head);
+
+    for (i = 0; i < NM_RFKILL_TYPE_MAX; i++)
+        priv->rfkill_states[i] = NM_RFKILL_STATE_UNAVAILABLE;
 
     priv->udev_client = nm_udev_client_new(NM_MAKE_STRV("rfkill"), handle_uevent, self);
 
@@ -399,11 +446,10 @@ dispose(GObject *object)
 {
     NMRfkillManager        *self = NM_RFKILL_MANAGER(object);
     NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE(self);
+    Killswitch             *ks;
 
-    if (priv->killswitches) {
-        g_slist_free_full(priv->killswitches, (GDestroyNotify) killswitch_destroy);
-        priv->killswitches = NULL;
-    }
+    while ((ks = c_list_first_entry(&priv->killswitch_lst_head, Killswitch, killswitch_lst)))
+        killswitch_destroy(ks);
 
     priv->udev_client = nm_udev_client_destroy(priv->udev_client);
 
@@ -426,6 +472,6 @@ nm_rfkill_manager_class_init(NMRfkillManagerClass *klass)
                                            NULL,
                                            G_TYPE_NONE,
                                            2,
-                                           G_TYPE_UINT,
-                                           G_TYPE_UINT);
+                                           G_TYPE_UINT /* NMRfkillType */,
+                                           G_TYPE_UINT /* NMRfkillState */);
 }
diff --git a/src/core/nm-rfkill-manager.h b/src/core/nm-rfkill-manager.h
index d3f66349..88d0421a 100644
--- a/src/core/nm-rfkill-manager.h
+++ b/src/core/nm-rfkill-manager.h
@@ -7,23 +7,33 @@
 #ifndef __NM_RFKILL_MANAGER_H__
 #define __NM_RFKILL_MANAGER_H__
 
-typedef enum { /*< skip >*/
-               RFKILL_UNBLOCKED    = 0,
-               RFKILL_SOFT_BLOCKED = 1,
-               RFKILL_HARD_BLOCKED = 2
-} RfKillState;
-
-typedef enum { /*< skip >*/
-               RFKILL_TYPE_WLAN = 0,
-               RFKILL_TYPE_WWAN = 1,
-
-               /* UNKNOWN and MAX should always be 1 more than
-     * the last rfkill type since RFKILL_TYPE_MAX is
+typedef enum {
+    NM_RFKILL_STATE_UNAVAILABLE  = 0,
+    NM_RFKILL_STATE_UNBLOCKED    = 1,
+    NM_RFKILL_STATE_SOFT_BLOCKED = 2,
+    NM_RFKILL_STATE_HARD_BLOCKED = 3,
+    /* NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER means that the CSME firmware
+     * is currently controlling the device. This feature is implmented on Intel
+     * wifi devices only.
+     * The NetworkManager can get ownership on the device, but it requires to
+     * first ask ownership through the iwlmei kernel module.
+     */
+    NM_RFKILL_STATE_HARD_BLOCKED_OS_NOT_OWNER = 4,
+} NMRfkillState;
+
+typedef enum {
+    NM_RFKILL_TYPE_WLAN = 0,
+    NM_RFKILL_TYPE_WWAN = 1,
+
+    /* UNKNOWN and MAX should always be 1 more than
+     * the last rfkill type since NM_RFKILL_TYPE_MAX is
      * used as an array size.
      */
-               RFKILL_TYPE_UNKNOWN, /* KEEP LAST */
-               RFKILL_TYPE_MAX = RFKILL_TYPE_UNKNOWN
-} RfKillType;
+    NM_RFKILL_TYPE_UNKNOWN, /* KEEP LAST */
+    NM_RFKILL_TYPE_MAX = NM_RFKILL_TYPE_UNKNOWN,
+} NMRfkillType;
+
+const char *nm_rfkill_type_to_string(NMRfkillType rtype);
 
 #define NM_TYPE_RFKILL_MANAGER (nm_rfkill_manager_get_type())
 #define NM_RFKILL_MANAGER(obj) \
@@ -43,6 +53,8 @@ GType nm_rfkill_manager_get_type(void);
 
 NMRfkillManager *nm_rfkill_manager_new(void);
 
-RfKillState nm_rfkill_manager_get_rfkill_state(NMRfkillManager *manager, RfKillType rtype);
+NMRfkillState nm_rfkill_manager_get_rfkill_state(NMRfkillManager *manager, NMRfkillType rtype);
+
+NMRadioFlags nm_rfkill_type_to_radio_available_flag(NMRfkillType type);
 
 #endif /* __NM_RFKILL_MANAGER_H__ */
diff --git a/src/core/nm-session-monitor.c b/src/core/nm-session-monitor.c
index 08bfedca..a2e9373c 100644
--- a/src/core/nm-session-monitor.c
+++ b/src/core/nm-session-monitor.c
@@ -114,13 +114,10 @@ st_sd_init(NMSessionMonitor *monitor)
         return;
     }
 
-    monitor->sd.watch = nm_g_unix_fd_source_new(sd_login_monitor_get_fd(monitor->sd.monitor),
+    monitor->sd.watch = nm_g_unix_fd_add_source(sd_login_monitor_get_fd(monitor->sd.monitor),
                                                 G_IO_IN,
-                                                G_PRIORITY_DEFAULT,
                                                 st_sd_changed,
-                                                monitor,
-                                                NULL);
-    g_source_attach(monitor->sd.watch, NULL);
+                                                monitor);
 }
 
 static void
diff --git a/src/core/nm-test-utils-core.h b/src/core/nm-test-utils-core.h
index 6f571972..467f9bb2 100644
--- a/src/core/nm-test-utils-core.h
+++ b/src/core/nm-test-utils-core.h
@@ -211,12 +211,13 @@ nmtst_platform_ip4_routes_equal(const NMPlatformIP4Route *a,
 
     for (i = 0; i < len; i++) {
         if (nm_platform_ip4_route_cmp_full(&a[i], &b[i]) != 0) {
-            char buf[sizeof(_nm_utils_to_string_buffer)];
+            char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE];
+            char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
 
             g_error("Error comparing IPv4 route[%lu]: %s vs %s",
                     (unsigned long) i,
-                    nm_platform_ip4_route_to_string(&a[i], NULL, 0),
-                    nm_platform_ip4_route_to_string(&b[i], buf, sizeof(buf)));
+                    nm_platform_ip4_route_to_string(&a[i], buf1, sizeof(buf1)),
+                    nm_platform_ip4_route_to_string(&b[i], buf2, sizeof(buf2)));
             g_assert_not_reached();
         }
     }
@@ -280,12 +281,13 @@ nmtst_platform_ip6_routes_equal(const NMPlatformIP6Route *a,
 
     for (i = 0; i < len; i++) {
         if (nm_platform_ip6_route_cmp_full(&a[i], &b[i]) != 0) {
-            char buf[sizeof(_nm_utils_to_string_buffer)];
+            char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE];
+            char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
 
             g_error("Error comparing IPv6 route[%lu]: %s vs %s",
                     (unsigned long) i,
-                    nm_platform_ip6_route_to_string(&a[i], NULL, 0),
-                    nm_platform_ip6_route_to_string(&b[i], buf, sizeof(buf)));
+                    nm_platform_ip6_route_to_string(&a[i], buf1, sizeof(buf1)),
+                    nm_platform_ip6_route_to_string(&b[i], buf2, sizeof(buf2)));
             g_assert_not_reached();
         }
     }
diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c
index 9053d351..d7fed220 100644
--- a/src/core/platform/tests/test-common.c
+++ b/src/core/platform/tests/test-common.c
@@ -239,8 +239,9 @@ _nmtstp_platform_ip_addresses_assert(const char        *filename,
             }
         }
         if ((IS_IPv4 ? force_exact_4 : force_exact_6) && nm_g_ptr_array_len(plat_addrs) > 0) {
-            char sbuf[sizeof(_nm_utils_to_string_buffer)];
+            char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
 
+            NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
             g_error("%s:%d: %u IPv%c addresses found on ifindex %d that should not be there (one "
                     "is %s)",
                     filename,
@@ -252,6 +253,7 @@ _nmtstp_platform_ip_addresses_assert(const char        *filename,
                                          NMP_OBJECT_TO_STRING_PUBLIC,
                                          sbuf,
                                          sizeof(sbuf)));
+            NM_PRAGMA_WARNING_REENABLE
         }
     }
 }
@@ -580,6 +582,7 @@ _nmtstp_assert_ip4_route_exists(const char *file,
     if (c != c_exists && c_exists != -1) {
         char sbuf[NM_UTILS_INET_ADDRSTRLEN];
 
+        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but "
                 "platform has it %u times",
                 file,
@@ -591,6 +594,7 @@ _nmtstp_assert_ip4_route_exists(const char *file,
                 tos,
                 c_exists,
                 c);
+        NM_PRAGMA_WARNING_REENABLE
     }
 
     return r;
@@ -673,6 +677,7 @@ _nmtstp_assert_ip6_route_exists(const char            *file,
         char s_src[NM_UTILS_INET_ADDRSTRLEN];
         char s_network[NM_UTILS_INET_ADDRSTRLEN];
 
+        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but "
                 "platform has it %u times",
                 file,
@@ -685,6 +690,7 @@ _nmtstp_assert_ip6_route_exists(const char            *file,
                 src_plen,
                 c_exists,
                 c);
+        NM_PRAGMA_WARNING_REENABLE
     }
 
     return r;
@@ -2772,13 +2778,7 @@ nmtstp_acd_defender_new(int ifindex, in_addr_t ip_addr, const NMEtherAddr *mac_a
     n_acd_get_fd(defender->nacd, &fd);
     g_assert_cmpint(fd, >=, 0);
 
-    defender->source = nm_g_source_attach(nm_g_unix_fd_source_new(fd,
-                                                                  G_IO_IN,
-                                                                  G_PRIORITY_DEFAULT,
-                                                                  _l3_acd_nacd_event,
-                                                                  defender,
-                                                                  NULL),
-                                          NULL);
+    defender->source = nm_g_unix_fd_add_source(fd, G_IO_IN, _l3_acd_nacd_event, defender);
 
     return defender;
 }
diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c
index 9629326a..93e525dd 100644
--- a/src/core/platform/tests/test-platform-general.c
+++ b/src/core/platform/tests/test-platform-general.c
@@ -101,9 +101,9 @@ static void
 test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
 {
     static const char *const EXPECTED_BUFFER[3] = {
-        [0] = ("5b1aea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218424e48836dd1cb504e03d53e1124c5"
+        [0] = ("5b1aea34648cabfec7c3523f76cf1ce34ca17a9a32f3f0f218024e48836dd1cb504e03d53e1124c5"
                "0065aeb2e6fbf952902383028e3b47f280f062ea1a7e0b7be218d067530e1b0487b8c3b99f2b8a1a"
-               "8982c42f0000003437c5156e072b2f2f0037c9cfe07c34ddb3980deb14ab7b5af84a034703000000"
+               "8907c42f0000003437c5156e072b2f2f0037c9cfe07c34ddb3980deb14ab7b5af84a034703000000"
                "883b0f3fd6ed84d6c959e553b887edcd6101f5d200000043b809d259e499db7d00f1853bdcb0e4bc"
                "0e2b00b667b7b16d8d1e69c803000000b973972c17a47631c169f11ff9119c40b403b6630000007a"
                "034f43340d01683c0045097aea4a849f060ddf57b24a5be9636360d603000000ad7c499dd538d345"
@@ -115,50 +115,50 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                "6663861eea1bf42c12ea3b9503000000fb95e8332fdfff658483a2d039a7bf148e02481e00000060"
                "e89f7abdb682380a00eae374835b4a49a2b980b6aba92da6409969aa03000000e00473755d31e5b2"
                "de252167c1c91b3a36020c700000007740318db913a353ed006efc068829c0e66ad0143a0554efb4"
-               "40e55b8b03000000c0cfb2b4386bec092fa5757ecde9348bda002ebd000000ab667224dae775e5cc"
-               "0041aca2ff0f576767d3648102b61886d149f07403000000153ece68ade15cec25a59273e7519f34"
-               "c4458d70000000f3819aa46fbe1439340033ae6dec0fb124f264af67eed7c9a8ecc8fb1103000000"
+               "d149f07403000000153ece68ade15cec25a59273e7519f34c40d8d70000000f3819aa46fbe143934"
+               "0033ae6dec0fb124f264af67eed7c9a840e55b8b03000000c0cfb2b4386bec092fa5757ecde9348b"
+               "da002ebd000000ab667224dae775e5cc0041aca2ff0f576767d3648102b61886ecc8fb1103000000"
                "fcebbaeb0c56535923f14874042a8aff1d028e5ec3cc13cc36bbe3c9bb0ec36f00e007bb64a2827b"
                "7cdd38d0314c178e5a06c40b03000000ab90135fa636af4464d210a256be75e0500244770000004b"
                "2e69220d6c0fc09c25d6534c809829af4a9df58dbfef186d416f3a1e030000002c932e655203d82a"
                "3c84c4eb274ed18687030281000000f2235376239daeaacb3cae864b437baaae91921681c2162b9b"
                "69e66142030000004fda8a3e0b841cf76391dd68269b53eccb02a831000000b78c54dda9ac3bb1b1"
                "d43e6505621b9a7f0422ae3fc8979ee0416f95e70300000057d6249b652ba98c7dc7f17f666969e4"
-               "5c02af7a000000ee0b06fa9e988f80f0de6dc8dfcf2a3ad3bbcc0fa3b314f695111d891d03000000"
-               "cd897619f51f44e644d7cf1d06b2b115d800549e62c12fba5b1cdec48d10bbb13b8313d8cd2a24d3"
-               "4fc812bd2f8a59d90fcc00ac030000005292cd32dc096cd5d8a4c5cf3351ee1cfc03056c00000051"
-               "bbce426cfa4b861cc78592be7b14e7ba9c15acb881ae55f0e5fe7d360300000066a3ae3939762df3"
-               "3a2d55060c78d551cb0010fd00000041b9aca07b6e4925dd27943a272c171ed15abbbe1cd911db7b"
-               "86ed271803000000a5edc511c1507a141e0f515638c7ba31f50209450000003357ae79989870ccec"
-               "3def0ad92749e016663fe6ee0228c1da82d1595603000000348352d715cf9d411ea012e5307294b6"
-               "e301dac4000000075efee38dd16f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e26bb20413203000000"
-               "862573c2303dd1d65c7b2cffeca6d1adaccae11f0000000f855ebf3b772eb2b1c896c9a7304f6645"
-               "0a5f6abd850b06e3b10123e6030000001dff045298cfee0636674cdefb57b9ae54cfe8f400000038"
-               "1ba2c4396de60f032bc7f34de2959871c0d4c0d4eb720c4ab550c5db03000000f32f4af595d785d5"
-               "c1b5aad487c192f08bd7a09b00000043288cd9bf66ec305a225a0c71b2ce78bb16104c8eaf18c565"
-               "b16c7376030000005219061ab4c5c79489b2cc6a883c146972decb8b000000f5e6d66df46ea13910"
-               "7754dee62c36d2fc70ccc567df7a49b8585287dc03000000cfb18b2b2cb749e2e03e544d0eb4f73e"
-               "75039fcf251b32fc79685b05ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0beb42b3403000000"
+               "5c02af7a000000ee0b06fa9e988f80f0de6dc8dfcf2a3ad3bbcc0fa3b314f695b550c5db03000000"
+               "f32f4af595d785d5c1b5aad487c192f08b12a09b00000043288cd9bf66ec305a225a0c71b2ce78bb"
+               "16104c8eaf18c565111d891d03000000cd897619f51f44e644d7cf1d06b2b115d800549e62c12fba"
+               "5b1cdec48d10bbb13b8313d8cd2a24d34fc812bd2f8a59d90fcc00ac030000005292cd32dc096cd5"
+               "d8a4c5cf3351ee1cfc03056c00000051bbce426cfa4b861cc78592be7b14e7ba9c15acb881ae55f0"
+               "b16c7376030000005219061ab4c5c79489b2cc6a883c14697205cb8b000000f5e6d66df46ea13910"
+               "7754dee62c36d2fc70ccc567df7a49b8e5fe7d360300000066a3ae3939762df33a2d55060c78d551"
+               "cb0010fd00000041b9aca07b6e4925dd27943a272c171ed15abbbe1cd911db7b86ed271803000000"
+               "a5edc511c1507a141e0f515638c7ba31f50209450000003357ae79989870ccec3def0ad92749e016"
+               "663fe6ee0228c1dabb20413203000000862573c2303dd1d65c7b2cffeca6d1adac11e11f0000000f"
+               "855ebf3b772eb2b1c896c9a7304f66450a5f6abd850b06e382d1595603000000348352d715cf9d41"
+               "1ea012e5307294b6e301dac4000000075efee38dd16f8ee4ccd2f50c30706cae3fdcc2f0ee3d5e26"
+               "b10123e6030000001dff045298cfee0636674cdefb57b9ae541de8f4000000381ba2c4396de60f03"
+               "2bc7f34de2959871c0d4c0d4eb720c4a585287dc03000000cfb18b2b2cb749e2e03e544d0eb4f73e"
+               "751d9fcf251b32fc79685b05ddd3aa9ba511d2e40edb4d758fb554158ae5c7c0beb42b3403000000"
                "895d5f24037d233302ad3b82d639272e4a02eadfbd2146bf8cfdb205f90e54b58a6ee136a779f37c"
-               "30d2c5053c40ecaec38b6b8e03000000bea73223e59bf0193432e9fa7a899f2d8ec7e4b89bf5a5d0"
+               "30d2c5053c40ecaec38b6b8e03000000bea73223e59bf0193432e9fa7a899f2d8e0ae4b89bf5a5d0"
                "6776e66a9d80ab132e1ac921eb76adbb229df32e561fa80a0fc4676703000000a23eb66e720da9e0"
                "7ac998b5690807d52602369ee1af4ca5a6a95581af5fd7ceacdca10f47d7b351a36b178aabc78a4a"
                "1a0dd8c003000000e2815a1a37a52bddd1c2f1018b587eed720358f0e9201f17bd99fcf72909ac9e"
                "7a55299e9bb4fd53bc7417940fcffe3f81cafd6302000000d6732578acd14320aefd4503189f7630"
-               "2403501c0000002b9f3c39f24b0572b100745cb25851429b3bbfb50168dfd04eb62f22ca02000000"
-               "891715df7fc6a902edae579e2e10c7f7a202a0340436242cbeb0248cee3fbc160032d4f28aa28c08"
-               "f80dd50c6712dfb4abba4a32020000008ffe423d01883918039249f398f9b37ea091465100000064"
-               "3722d9b707c0d8a400b7c8307f06b4b29088f20d9ac676d5e4bafc4e02000000fde69eec3af2e6d0"
-               "bd68ab722af14548b29572e504265f6c72923e22594f3f790008ed2e2ebb0771db46a54cadb245ea"
+               "2403501c0000002b9f3c39f24b0572b100745cb25851429b3bbfb50168dfd04eabba4a3202000000"
+               "8ffe423d01883918039249f398f9b37ea0104651000000643722d9b707c0d8a400b7c8307f06b4b2"
+               "9088f20d9ac676d5b62f22ca02000000891715df7fc6a902edae579e2e10c7f7a202a0340436242c"
+               "beb0248cee3fbc160032d4f28aa28c08f80dd50c6712dfb4e4bafc4e02000000fde69eec3af2e6d0"
+               "bd68ab722af14548b20672e504265f6c72923e22594f3f790008ed2e2ebb0771db46a54cadb245ea"
                "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c01a56dfdcfc1155f236c8b9c79a620"
                "00660bbf024b03ff0a8e27c405e64244e36f90d402000000fd41fe47684b370b6ec6584d64496089"
-               "570968ead4d1ae91c819bb068196d59900de3246e43f5e7945aaf95e2ffa3a11d1e1b34902000000"
-               "bcd7be07d78e6222e45aaf61814f703b40125e6b000000cbaaa37b861e6d46dafe7d6ec4ac1ea051"
-               "010911915ddb05f29c64ed0402000000a660ac824b7fae389861419c50da49bf2b02258300000025"
+               "570568ead4d1ae91c819bb068196d59900de3246e43f5e7945aaf95e2ffa3a11641e447502000000"
+               "b9b68c08a5e4351ea349e1ccf662e058b819a45100000045fcb6a035339d504c9726d80d9c2d89df"
+               "765b4d9a130257dc9c64ed0402000000a660ac824b7fae389861419c50da49bf2b02258300000025"
                "9f9f0251becc987907879cca68fec7bbbb5f8edf248b4995d184e82002000000a19cdf6dd1c173f6"
                "078a806d329c9b008b00c972000000f5b2cd3dbddc74e26de958e48d2ab8b0313e7f8933e315130f"
-               "641e447502000000b9b68c08a5e4351ea349e1ccf662e058b879a45100000045fcb6a035339d504c"
-               "9726d80d9c2d89df765b4d9a130257dcc43bd794010000005bfe47c6f53a54e01b0c1d89414d94d0"
+               "d1e1b34902000000bcd7be07d78e6222e45aaf61814f703b401f5e6b000000cbaaa37b861e6d46da"
+               "fe7d6ec4ac1ea051010911915ddb05f2c43bd794010000005bfe47c6f53a54e01b0c1d89414d94d0"
                "e0032ec50000000103781b0f294a2b7300421398f4de67e9cee64b38b56e03e01539ce4101000000"
                "18238487a417f3da01d99dae5f190096cc012cab0000005b2363d13edc5aa115005eb914d8fbe9a4"
                "fdb3d117d76b0de5bd82e9ea010000003d1b91caae8cb60b49ba9be338d856a40c02c3d400000064"
@@ -172,49 +172,49 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                "b900b2061d0c334b009f2dd1cdf64b0f9a60e0e289f08db3fde6b0250100000039dd8f88152a5845"
                "4d9ca9d20f45dfa774028604000000fbcd7db68b9ae586da00b4070c50320427c4dd3d031e33f22c"
                "210aeb09010000007000b96d06992b6a58acd3995b9663d2df02f333ef467092818aa77d6732b678"
-               "00844ef5a943825fcd743f59bd14c89b955e1a13010000007acbce3e3bcdf3824f1b134847ff26ba"
-               "4400774900000017ec852d59f3d17232edd86ad6c3103a68843a9aef34983882d3d3878501000000"
-               "5e0dfe491d1ba96742c7b5e02b2271229c01b02a06d0dc5504b0595daf37deb499996bfb667f072d"
-               "ec1e5d9cdc8a11f4409bcfb30100000099d90a8543961b2ccd47724a3c460ba85103f4c500000063"
-               "eafb1ac4c0982b283aa9986700b2a2b3ed257b8b0489f48f053ec8ca0100000027335a25a364d101"
-               "5ffac03089f45539e1021784000000d7b83579b8da27345a72437f9b6245de39ec9e71ee4b951507"
-               "f121014a01000000efc67bce716c856e3973dc42a1003be94f89d8ee0000009b5d5bbe6c10085f3d"
-               "6176f78a19bb8df1804c122fce5078c156e8f3fe010000000ea8042602a1f8e6f5657f3f9e3eb807"
-               "cbad7645000000b8df6f628a70456d79f25d5895fb57fa60d9279fb2b8fcbac65ad47b8a01000000"
-               "da40d88d40a6d75bc404156225b7eedefc2b44574b15e2ae496ad01bf007eacb0a28aec868282510"
-               "b60291ea6480e356925b568a0000000087bb24e5264fd3ebe9cf9f6df9615189f701e815000000a7"
-               "5c9555876b6a3f13002b6cb8360feaac1d5c302df59dd32a7a859db500000000362956cd46646a0e"
-               "222160e5f769bb295703ed370000005b6a813387e99bb834009da86c64fefab2548759d313a5b92d"
-               "8e47935e0000000034f0386a253c21d94064f6b021281e235f00ae20000000d71fd050bf8d85055b"
-               "00e3756ccdb3455c60ca7b11c66af76e594f24a100000000e143fd52599364e13468f80fd514573f"
-               "b572671c0000006932d1d5f5d0ce2cf6007a70ba5193a162bc92ec1b11d9172c857ae81200000000"
-               "3e29535402e9b690c628d048eacce745ea213cb1000000b632ef3be6070dafa200187470e9da5570"
-               "9427c226d324d9a08487fd0d00000000b7a350f9fc1519defa7db4532545666937c22a3b000000dc"
+               "00844ef5a943825fcd743f59bd14c89bf121014a01000000efc67bce716c856e3973dc42a1003be9"
+               "4f0ed8ee0000009b5d5bbe6c10085f3d6176f78a19bb8df1804c122fce5078c1955e1a1301000000"
+               "7acbce3e3bcdf3824f1b134847ff26ba4400774900000017ec852d59f3d17232edd86ad6c3103a68"
+               "843a9aef34983882d3d38785010000005e0dfe491d1ba96742c7b5e02b2271229c01b02a06d0dc55"
+               "04b0595daf37deb499996bfb667f072dec1e5d9cdc8a11f4409bcfb30100000099d90a8543961b2c"
+               "cd47724a3c460ba85103f4c500000063eafb1ac4c0982b283aa9986700b2a2b3ed257b8b0489f48f"
+               "053ec8ca0100000027335a25a364d1015ffac03089f45539e1021784000000d7b83579b8da27345a"
+               "72437f9b6245de39ec9e71ee4b95150756e8f3fe010000000ea8042602a1f8e6f5657f3f9e3eb807"
+               "cb067645000000b8df6f628a70456d79f25d5895fb57fa60d9279fb2b8fcbac65ad47b8a01000000"
+               "da40d88d40a6d75bc404156225b7eedefc1e44574b15e2ae496ad01bf007eacb0a28aec868282510"
+               "b60291ea6480e356594f24a100000000e143fd52599364e13468f80fd514573fb517671c00000069"
+               "32d1d5f5d0ce2cf6007a70ba5193a162bc92ec1b11d9172c925b568a0000000087bb24e5264fd3eb"
+               "e9cf9f6df9615189f701e815000000a75c9555876b6a3f13002b6cb8360feaac1d5c302df59dd32a"
+               "7a859db500000000362956cd46646a0e222160e5f769bb295703ed370000005b6a813387e99bb834"
+               "009da86c64fefab2548759d313a5b92d8e47935e0000000034f0386a253c21d94064f6b021281e23"
+               "5f00ae20000000d71fd050bf8d85055b00e3756ccdb3455c60ca7b11c66af76e857ae81200000000"
+               "3e29535402e9b690c628d048eacce745ea053cb1000000b632ef3be6070dafa200187470e9da5570"
+               "9427c226d324d9a08487fd0d00000000b7a350f9fc1519defa7db45325456669371e2a3b000000dc"
                "c405cbee5016c25200d8901d7a0165fe20744edb6ba04f14a4c73cf500000000a4bab14874afdf54"
-               "e6aae816430607ca0675e09818e9bbec5918c59068baf76a008940f6fc3bbdc7f6090f756aae660b"
+               "e6aae816430607ca061fe09818e9bbec5918c59068baf76a008940f6fc3bbdc7f6090f756aae660b"
                "6e4c699300000000d9c1e67743efb54e54270e46042e91186a034e38000000376feecb80ac245409"
                "c0becc271d9c2f67179bff0644399ae7df6b3542000000004c5cf8107ba282f4f983821918f93e74"
-               "2d08f0550000006f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51e3c9472e00000000"
+               "2d00f0550000006f2292362e5d68265d9f98c82d9b7a559be3acf4fc36fa6b51e3c9472e00000000"
                "c5cfd9f2343b21362c19a0921dce2f839200fa45000000270b9977e166bee737fe73670c439a644c"
-               "323b59b4cd20eb7dabea74f700000000f6989d2d6a909e986ff7add5df2c93e05459507b0000000f"
-               "466554d2ae4d52a8c67b2e48b47003c81785d3ffdbd9a61759747cf200000000ab2dadc5a39411fd"
-               "4ff1116d478987316a553fc2000000cfc6ebe434a7ae8ff040483e310819e3b10db116431ec6f769"
-               "438a72e1000000002495a609675344f7e2e3a5ebaec3c85f0a1742f70abe95c50345132a61eda239"
-               "d9d083c3bf085387046ef8a36f0e9e696b382ab0000000009a6ce5d906837dbca6a5ee19d6f63fe9"
+               "323b59b4cd20eb7dabea74f700000000f6989d2d6a909e986ff7add5df2c93e05412507b0000000f"
+               "466554d2ae4d52a8c67b2e48b47003c81785d3ffdbd9a617438a72e1000000002495a609675344f7"
+               "e2e3a5ebaec3c85f0a0042f70abe95c50345132a61eda239d9d083c3bf085387046ef8a36f0e9e69"
+               "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a1f3fc2000000cfc6ebe434a7ae8ff0"
+               "40483e310819e3b10db116431ec6f7696b382ab0000000009a6ce5d906837dbca6a5ee19d6f63fe9"
                "cb0301f7246f13b2050424a2b3a45ef7a029c896b4132bd895072cfeffe9d6815997069500000000"
                "0d3c723b91adb0da7c4aa7e7eb5a15bcde035fb98b841fd84cc43c510385b9a4c2aac1d67a909b29"
-               "7c703915312e9c3cae02dfa000000000dd603bd35e7fa0f02f2f3313d8469d09a92409c0b7f0318a"
+               "7c703915312e9c3cae02dfa000000000dd603bd35e7fa0f02f2f3313d8469d09a91709c0b7f0318a"
                "575a4f8e061db3dd7fde25654a4059d565dbc8a91e3b4457b077ddad3108be69f9b97d05c917ad6b"
-               "10e693bb6e26f2ba90c8e909a9fe20e5c7a4c656482a9b0d00625009a40aeb62a42b6a62548e3c38"
+               "10e693bb6e26f2ba900fe909a9fe20e5c7a4c656482a9b0d00625009a40aeb62a42b6a62548e3c38"
                "cd3c72f203000000ca82ac5180101be4f85cef468ea086ea9a01fdc3a9fe1ec787bc45db7c52a52d"
                "00bd39a44e8e8bc17c01ac63eca0c1cf5ff7f03a03000000c9a89192c1c8be55281a59d1fd338f35"
-               "7e00f8cea9fec34573654ea6624f138ef9531cd9367a02e4d241989477a363d53b02239e03000000"
-               "24438387def0f4c6544e4b275d9b7146a70010d2a9fec17647176b7c07d856e3b883efebc09dd9d6"
-               "1966b7ae7412041d57393c6f03000000182c0287822a272bec4501a1e27acfee7a0188ffa9fe6cae"
-               "426de59560fad65d67c624f285d7174177a47579dda0b6eaa9a84c820300000070b1646d8026e9f1"
-               "704f1b16286ba2dabc01f082a9feed33ef60a8b540b26f66761d1f13badfad0fe8fa8f3c1aad2a82"
-               "fa40546c03000000df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea9fe3e7d75a2f42b50c6a363"
-               "40132378b95c59313bacba64dbe996206e6904f50200000047150b9b14010469823acb72bb89182f"
+               "7e00f8cea9fec34573654ea6624f138ef9531cd9367a02e4d241989477a363d5fa40546c03000000"
+               "df2d7c2790d3119a051bb2ee8192ac0cfa00bc1ea9fe3e7d75a2f42b50c6a36340132378b95c5931"
+               "3bacba64dbe996203b02239e0300000024438387def0f4c6544e4b275d9b7146a70010d2a9fec176"
+               "47176b7c07d856e3b883efebc09dd9d61966b7ae7412041d57393c6f03000000182c0287822a272b"
+               "ec4501a1e27acfee7a0188ffa9fe6cae426de59560fad65d67c624f285d7174177a47579dda0b6ea"
+               "a9a84c820300000070b1646d8026e9f1704f1b16286ba2dabc01f082a9feed33ef60a8b540b26f66"
+               "761d1f13badfad0fe8fa8f3c1aad2a826e6904f50200000047150b9b14010469823acb72bb89182f"
                "93002196a9feb9153b36bc60be5b534e006527f67485ab35aca0c7ee419733853cf09e8b02000000"
                "e79c10acfce165e332a62384ec04e5bab40085ada9fe0070a36dd51323b2c54200154d12f86c260a"
                "9edfa7a74c1c83c1050f63f802000000443cacf59c6379a44b7892f487afa98cb102c19ca9fe942f"
@@ -222,7 +222,7 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                "36506ebd455e679cef009bb8a9fecec3f8c8fa6867a982be8a934f852cc3d4d82bc0ec7303f99f8f"
                "def85b7502000000a0bef8675b29a197b7b3cceaf5f1bb12c503256aa9fe6e5d58099ffc4a503a71"
                "2350acbd48411f0dc15d2f0f49dad345d966279502000000e06302aba042aaa218dc091e9aa1477f"
-               "6fdc9830a9fec95829a8838314dff34d24c332219a1b163a732d803e0e2f4f916d06412601000000"
+               "6f059830a9fec95829a8838314dff34d24c332219a1b163a732d803e0e2f4f916d06412601000000"
                "98c39e7cc282208fefc57ff447036b955101cb22a9fe793f797a3c7dadd1c86e009d0c90bc512e13"
                "7dcef5e4a27985bd5cfd5ce601000000152f2b70eaef7443e0f79ab6902dde533601ec71a9fe9f25"
                "4ac95883195580410062ed564153e17478f8c3344d89c0bbfaa100fd01000000be184524a6bdc878"
@@ -230,7 +230,7 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                "0d80684e01000000ba8110fb9733cc24904f288262e6ea77a203a5f8a9feeefa701d120523bd98f2"
                "00098b43cd68be6e3f81268193fd637e9037d7a701000000c47cf0f551e96770a754ac19ef820fe0"
                "2900f2d3a9fe049150b8d10ab700cc3a7cf51be0403b654ba2f56808092069af5f5b481b01000000"
-               "68cb3bc873b04d937a6ed8f7bc51e54066fed098a9fe048a92d3adc69a84eb47622400207799416a"
+               "68cb3bc873b04d937a6ed8f7bc51e5406617d098a9fe048a92d3adc69a84eb47622400207799416a"
                "f1f0a086fbd7e2f7dea0077a00000000c386e9c6e6a2cbfa10ee58bdc75183609900d627a9feb1cb"
                "e491cbbbf9443fd6007eb3c5bf64b671d6f18dbf463f9b83f512dc1c00000000fbab244735d67c61"
                "283031667b2d74a10302b1b1a9fe2aa590a2312e17f1a35900459582f4ef43c780908872746e39ef"
@@ -241,306 +241,306 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                "3215abf8fbdf17c3677e5a3a00000000608df061d45d864d09f4ecf17625f82da1034828a9fe1e37"
                "1051852c972ea7954079884af257b044fd13a6826a4c619f3d136cac000000009402a4c216772167"
                "3f2b02b3256ead1f2f039bc1a9fefca162fb81e733cff620ca7feefe1933631e8e69f6d9d6962d2c"),
-        [1] = ("54270e46020000006a894e387625da376feecb80ac245409c0becc271d9c2f67179bff0644399ae7"
+        [1] = ("54270e46020000006a894e387625da376feecb80ac245409c058cc271d9c2f67179bff0644399ae7"
                "9c64ed0432d599eaa660ac824b7fae389861419c7a899f2d010000009bf5a5d06776e66a9d80ab13"
-               "2e1ac921eb76adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b"
-               "c4e1966800000000b3121cd5ef51e696c816290dbaee0e7726d082e1530ff5397c9125f59577d71c"
+               "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b"
+               "c4e1966800000000b3121cd5ef51e696c816290dbaee0e77264c82e1530ff5397c9125f59577d71c"
                "4b258a005116d11354edff62ceaa458fc75a91c425a5927300000000c4458d70911714f1819aa46f"
-               "be143934c933ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa"
-               "4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5ab90135fa636af44"
+               "be143934c915ae6dec0fb124f264af67eed7c9a8c38b6b8ee2614344bea73223e59bf0193432e9fa"
+               "4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44"
                "64d210a256be75e0509c4477d19ac64b2e69220d12ea3b9501000000fb95e8332fdfff658483a2d0"
-               "39a7bf148ed3481e0bccc460e89f7abdb682380a62eae374835b4a49a2b980b6aba92da6409969aa"
-               "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ecc8fb112087b97cfcebbaeb0c565359"
+               "39a7bf148e4c481e0bccc460e89f7abdb682380a62eae374835b4a49a2b980b6aba92da6409969aa"
+               "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359"
                "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9bb0ec36f0000000064a2827b7cdd38d0314c178e"
-               "925b568ae56bc4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13"
-               "02e9b69000000000eacce745ea213cb1a84035b632ef3be6070dafa2fed87470e9da55709427c226"
+               "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13"
+               "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226"
                "d324d9a086ed27184b443181a5edc511c1507a142fea9bc10300000062fb81e733cff620ca7feefe"
-               "1933631e8e69f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196"
-               "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6f18dbffeff9b8306c83bc026977911"
+               "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196"
+               "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911"
                "4bdade862c224f6f36506ebd455e679cef369bb8d514573f03000000f6932f6932d1d5f5d0ce2cf6"
-               "bf7a70ba5193a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd"
-               "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e45e9c1feb836c0a25ea71f5daaed4d"
+               "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd"
+               "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d"
                "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a41e0f515601000000f57609453df7803357ae7998"
-               "9870ccec3def0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7"
-               "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2fff2f256000000000000000000000000"
+               "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7"
+               "ae8cb60b0300000038d856a40c67c3d4afa9c8649da90bd2ff4cf256000000000000000000000000"
                "00000001955e1a13d1111b067acbce3e3bcdf38200154d12010000009edfa7a74c1c83c18e47935e"
-               "8354846e34f0386a00000000000000000000000000000001ac26c7d41fd050bf8d85055bcfe3756c"
-               "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789cf85178000000000000000000000000"
+               "8354846e346e386a00000000000000000000000000000001ac26c7d41fd050bf8d85055bcfe3756c"
+               "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000"
                "00000000020ffdb0e612098610e52bb2a16a40086f0e9e6903000000ec2e2924348352d715cf9d41"
-               "1ea012e5307294b600000000000000000000000000000000ccd2f50c30706cae3fdcc2f0ee3d5e26"
-               "81ae55f0030000000aa20624fbab244735d67c61283031667b2d74a1000000000000000000000000"
+               "1ea012e5305a94b600000000000000000000000000000000ccd2f50c30706cae3fdcc2f0ee3d5e26"
+               "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000"
                "0000000000459582f4ef43c780908872746e39efaefd545b020000007ced4f8b362956cd46646a0e"
-               "222160e5f769bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d"
-               "f05d7308020000009d82c856ba942455050f63f8282f3464443cacf5000000000000000000000000"
+               "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d"
+               "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000"
                "0000000069d9942f460bcea75481f25e307d0de96e91b91501000000be5b534e0a6527f67485ab35"
-               "aca0c7ee4197338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b"
-               "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99289462000000000000000000000000"
+               "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b"
+               "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000"
                "00000000b49185ad9b7d0070a36dd51323b2c542e5fe7d360100000066a3ae3939762df33a2d5506"
-               "0c78d551cbb110fd000000000000000000000000000000002c171ed15abbbe1cd911db7b56e8f3fe"
-               "9a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7246f13b2050424a2000000000000000000000000"
+               "0c78d551cb7410fd000000000000000000000000000000002c171ed15abbbe1cd911db7b56e8f3fe"
+               "9a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7246f13b2055624a2000000000000000000000000"
                "00000000ffe9d6812231bb02f922259b173c333cbae87d60000000005d31e5b2de252167c1c91b3a"
-               "36ba0c700ee78477000000000000000000000000000000006ad0143a0554efb46b382ab0404cdf02"
-               "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb7213e4000000000000000000000000"
+               "36ba0c700e538477000000000000000000000000000000006ad0143a0554efb46b382ab0404cdf02"
+               "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000"
                "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8"
-               "9b1bc8f415bd4e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec"
-               "7666c05303000000ffa8e89871fb9510b900b2061d0c334b819f2dd1000000000000000000000000"
+               "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec"
+               "7666c05303000000ffa8e89871fb9510b900b2061d0c334b816a2dd1000000000000000000000000"
                "00000000095a3230fde69eec3af2e6d0bd68ab7210613fc90200000016a31dc2e46a234558817151"
-               "b38fc9c909cf8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2"
-               "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b06fa9e000000000000000000000000"
+               "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2"
+               "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000"
                "00000000b314f695bd82e9ea8891ed0a3d1b91cab56e03e0010000002f70249eefc67bce716c856e"
                "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1"
-               "eecc89f601000000d635edea8ffe423d01883918039249f398f9b37e000000000000000000000000"
+               "eecc89f601000000d635edea8ffe423d01883918039249f3984ab37e000000000000000000000000"
                "000000001fb7c8307f06b4b29088f20d9ac676d5f90e54b501000000a779f37c30d2c5053c40ecae"
-               "210aeb0934cb2a2400000000000000000000000000000000df48f333ef467092818aa77d6732b678"
-               "bcd7be0700000000e45aaf61814f703b40125e6baf4648cbaaa37b86000000000000000000000000"
+               "210aeb0934392a2400000000000000000000000000000000df48f333ef467092818aa77d6732b678"
+               "bcd7be0700000000e45aaf61814f703b40125e6baf4648cbaa557b86000000000000000000000000"
                "000000005ddb05f2a84e27c18a06e2c9cb29efdf50da49bf00000000d94beb259f9f0251becc9879"
-               "07879cca68fec7bb000000000000000000000000000000009402a4c2167721673f2b02b3256ead1f"
-               "a349e1cc00000000b879a45176740744fcb6a035339d504c9726d80d000000000000000000000000"
+               "07879cca6857c7bb000000000000000000000000000000009402a4c2167721673f2b02b3256ead1f"
+               "a349e1cc00000000b879a45176740744fcb6a035339d504c972bd80d000000000000000000000000"
                "000000009518862bc9a89192c1c8be55281a59d1ebfdcec30000000067a982be8a934f852cc3d4d8"
-               "2bc0ec7303f99f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a"
-               "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f218424e48000000000000000000000000"
+               "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a"
+               "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000"
                "00000002e6fbf952902383028e3b47f2111d891d536c75bb01000000720da9e07ac998b5690807d5"
-               "2617369ee1af4ca500000000000000000000ffff47d7b351a36b178aabc78a4a0d80684ee5dbf45b"
-               "17a4763102000000f9119c40b468b66331bca67a034f43340d01683c00000000000000000000ffff"
+               "2617369ee16d4ca500000000000000000000ffff47d7b351a36b178aabc78a4a0d80684ee5dbf45b"
+               "17a4763102000000f9119c40b468b66331bca67a034f43340d2c683c00000000000000000000ffff"
                "b24a5be9b10123e6e4f15ecd1dff045298cfee069c88b02a0300000004b0595daf37deb499996bfb"
-               "667f072dec1e5d9cdc8a11f4409bcfb3c167090e99d90a8543961b2ccd47724a3c460ba85183f4c5"
-               "a7afe28302000000d05ff720b16c73761a3fcb675219061ab4c5c79489b2cc6a883c146972decb8b"
+               "667f072dec755d9cdc8a11f4409bcfb3c167090e99d90a8543961b2ccd47724a3c460ba85183f4c5"
+               "a7afe28302000000d05ff720b16c73761a3fcb675219061ab466c79489b2cc6a883c146972decb8b"
                "5adac8f5e6d66df46ea139107754dee62c36d2fcc7a4c65601000000cd625009a40aeb62a42b6a62"
-               "548e3c38b96c20022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb"
-               "e36f90d401000000fd41fe47684b370b6ec6584d64496089570968ead4d1ae91c819bb068196d599"
+               "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb"
+               "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599"
                "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b21cdb3455c00000000c66af76e80f062ea1a7e0b7b"
-               "e218d067530e1b0487b8c3b99f2b8a1a8982c42f53700a3437c5156e072b2f2fb337c9cfe07c34dd"
-               "c896c9a7000000000a5f6abd850b06e3d9662795ce2cc1cee06302aba042aaa218dc091e9aa1477f"
+               "e218d067532d1b0487b8c3b99f2b8a1a8982c42f53700a3437c5156e072b2f2fb337c9cfe07c34dd"
+               "c896c9a7000000000a5f6abd850b06e3d9662795ce2cc1cee03202aba042aaa218dc091e9aa1477f"
                "6fdc9830d7fbc95829a8838314dff34d24c332214d4647f5000000000f88e3759a08e7a56663861e"
-               "ea1bf42ccd3c72f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db"
-               "7625ca4a00000000e682f80f94315c45ef817264c89a736ed55ed637b077ddad3108be69f9b97d05"
+               "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db"
+               "7625ca4a00000000e682f80f94315c45ef817264c89a736ed580d637b077ddad3108be69f9b97d05"
                "c917ad6b10e693bb6e26f2ba90c8e909e85e20e5b0916e5d030000004a503a712350acbd48411f0d"
-               "c15d2f0f49dad345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8"
-               "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e"
+               "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8"
+               "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e"
                "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c"
-               "db544e44df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378"
-               "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c1fa56dfdcfc1155f236c8b9c79a620"
+               "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378"
+               "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620"
                "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2"
-               "ff0f576767d3648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034"
-               "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c93ef054e93f8120abb42316c533d9c9"
+               "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034"
+               "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9"
                "4d88189c471a1ff2f0ce3ff66e782110125ae2eddaae7d6300000000c0982b283aa9986700b2a2b3"
-               "ed257b8b0489f48f053ec8cac72105b327335a25a364d1015ffac03089f45539e1b0178462156cd6"
-               "6855676c00000000017c9148da5892bb4951c3a7ed55689d3391ce7d3fd48469845f0c233dbdd2a9"
+               "ed257b8b042ff48f053ec8cac72105b327335a25a364d1015ffac03089f45539e1b0178462156cd6"
+               "6855676c00000000017c9148da5892bb4951c3a7ed55689d336bce7d3fd48469845f0c233dbdd2a9"
                "7835df1f1782dee88487fd0db5971b46b7a350f998c39e7c00000000efc57ff447036b9551adcb22"
-               "fd50793f797a3c7dadd1c86e759d0c90bc512e137dcef5e4a27985bd8d1e69c83dc21056b973972c"
-               "bc51e540030000006c3d048a92d3adc69a84eb47622400207799416afef0a086fbd7e2f7dea0077a"
+               "fd50793f791c3c7dadd1c86e759d0c90bc512e137dcef5e4a27985bd8d1e69c83dc21056b973972c"
+               "bc51e540030000006c3d048a92d3adc69a84eb47622400207708416afef0a086fbd7e2f7dea0077a"
                "e9c0c0d9c386e9c6e6a2cbfa10ee58bdc75183600422ae3f03000000c43bd794c697895e5bfe47c6"
-               "f53a54e01b0c1d89fecd94d0e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38"
-               "69121aec010000000fcc00ac2daa755e5292cd32dc096cd5d8a4c5cffe91ee1cfcc2056cc1ff8e51"
+               "f53a54e01b211d89fecd94d0e02d2ec587fc330003781b0f294a2b73c4421398f4de67e9cee64b38"
+               "69121aec010000000fcc00ac2daa755e5292cd32dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51"
                "bbce426cfa4b861cc78592be7b14e7ba9c15acb836674cde0200000054cfe8f42bf0e9381ba2c439"
-               "6de60f032bc7f34dfe959871c0d4c0d4eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21362c19a092"
-               "b2cd3dbd01000000e958e48d2ab8b0313e7f8933e315130fa9a84c82feb83f6e70b1646d8026e9f1"
+               "6de60f032b5af34dfe959871c0d4c0d4eb720c4ae3c9472e5f2da5d9c5cfd9f2343b21362c19a092"
+               "b2cd3dbd01000000e958e48d2ab8b0313e7f8933e315130fa9274c82feb83f6e70b1646d8026e9f1"
                "704f1b16286ba2dabc7ef0820c91ed33ef60a8b5bcfe97e1000000007ad742d3d7320a4f880cf47f"
-               "5dd0cf69cb22840ffeb3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f6e8e99cd"
-               "1dce2f83020000003a3efc240b9977e166bee737fe73670c439a644c000000000000000000000000"
+               "5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee30b7e7736a0afc9879ea40e69710fc9f6e8e99cd"
+               "1dce2f83020000003a3efc240b9977e166bee737fe73670c436b644c000000000000000000000000"
                "000000015e0dfe491d1ba96742c7b5e02b227122cf844ef501000000cd743f59bd14c89b5cfd5ce6"
-               "e82e7854152f2b700000000000000000000000000000000127e19f254ac9588319558041fc62ed56"
-               "b83579b80300000072437f9b6245de39ec9e71ee4b951507beb42b34000000000000000000000000"
+               "e82e7854157b2b700000000000000000000000000000000127e19f254ac9588319558041fc62ed56"
+               "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000"
                "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f"
-               "c1ea76e98c9dfc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e"
-               "0436242c03000000ee3fbc164632d4f28aa28c08f80dd50c6712dfb4000000000000000000000000"
+               "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e"
+               "0436242c03000000ee3fbc164632d4f28aa28c08f80dd50c6705dfb4000000000000000000000000"
                "00000000078a806d329c9b008bbfc9723107f3f52af145480300000004265f6c72923e22594f3f79"
-               "e208ed2e2ebb07710000000000000000000000000000000039dd8f88152a58454d9ca9d20f45dfa7"
-               "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f608df061000000000000000000000000"
+               "e208ed2e2e7107710000000000000000000000000000000039dd8f88152a58454d9ca9d20f45dfa7"
+               "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000"
                "000000008ae21e371051852c972ea7954079884a9a1b163a030000000e2f4f911a0dd8c052ed6b13"
-               "e2815a1a37a52bdd00000000000000000000000000000000bd99fcf72909ac9e7a55299e9bb4fd53"
-               "9f3c2c8803000000f51f44e644d7cf1d06b2b115d882549e62c12fba000000000000000000000000"
+               "e2815a1a37322bdd00000000000000000000000000000000bd99fcf72909ac9e7a55299e9bb4fd53"
+               "9f3c2c8803000000f51f44e644d7cf1d06b2b115d882549e62042fba000000000000000000000000"
                "000000004fc812bd2f8a59d96d064126666befbbf257b044030000006a4c619f9037d7a754655354"
-               "c47cf0f551e967700000000000000000000000000000000050b8d10ab700cc3a7cf51be0403b654b"
-               "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4dd3d03000000000000000000000000"
+               "c47cf0f5512167700000000000000000000000000000000050b8d10ab700cc3a7cf51be0403b654b"
+               "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000"
                "00000000d538d34574c038404923e75d0e09e2fcfc1519de010000002545666937c22a3b6e9686dc"
-               "c405cbee5016c252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e"
-               "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d9b7146000000000000000000000000"
+               "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e"
+               "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000"
                "00000000b883efebc09dd9d61966b7ae7412041dd7ac5f9901000000822a272bec4501a1e27acfee"
-               "7a8588ffd5a06cae0000000000000000000000000000000077a47579dda0b6ead1e1b34901eaedd5"
-               "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e2b00b6000000000000000000000000"
+               "7a8588ffd5286cae0000000000000000000000000000000077a47579dda0b6ead1e1b34901eaedd5"
+               "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000"
                "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096"
-               "cc582cabd5ddd15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f"
-               "fc2b445700000000496ad01bf007eacb0a28aec868282510b60291ea000000000000000000000000"
+               "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f"
+               "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000"
                "000000005b29a197b7b3cceaf5f1bb12c535256a74993ade03000000e62112898dab2dade2ab2fc9"
-               "c56a7c86be5f962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8"
-               "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a631480000000000000000000000000"
+               "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8"
+               "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000"
                "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050"
-               "ed7afd741024e83600000000000000000000000000000000f493472201e48d163106cc397446a33f"
-               "bc74179403000000438a72e12ae0436a2495a609675344f7e2e3a5eb000000000000000000000000"
+               "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f"
+               "bc74179403000000438a72e12ae0436a2495a609675344f7e226a5eb000000000000000000000000"
                "0000000061eda239d9d083c3bf085387046ef8a34153e174020000004d89c0bb5408c1b7d17fb084"
-               "d9e1825fa638e1af000000000000000000000000000000006e08cb887464b9344ec3127c750fabdd"
-               "6c0fc09c02000000809829af4a9df58dbfef186d416f3a1ef170d10f000000000000000000000000"
+               "d9e1825fa613e1af000000000000000000000000000000006e08cb887464b9344ec3127c750fabdd"
+               "6c0fc09c02000000809829af4a9df58dbfef186d416f3a1ef154d10f000000000000000000000000"
                "0000000087780281d47a32f2235376239daeaacba9a89f8701000000fcdf6d9be94030b34774d1d7"
-               "dddedd9899f0f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767"
-               "fd338f35010000006e8cc34573654ea6624f138ef9531cd9367a02e4000000000000000000000000"
+               "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767"
+               "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000"
                "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc"
-               "79685b05ddd3aa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515"
-               "b7f0318a01000000061db3dd7fde25654a4059d565dbc8a91e3b4457000000000000000000000000"
+               "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515"
+               "b7f0318a01000000061db3dd7fde25654a4059d565dbc8a91e004457000000000000000000000000"
                "00000000c1b5aad487c192f08bd7a09bdd21444359747cf200000000ab2dadc5a39411fd4ff1116d"
-               "478987316a553fc2000000000000000000000000000000000819e3b10db116431ec6f7691539ce41"
-               "b95c593100000000dbe9962069e66142e22753344fda8a3e0b841cf7000000000000000000000000"
+               "478987316a293fc2000000000000000000000000000000000819e3b10db116431ec6f7691539ce41"
+               "b95c593100000000dbe9962069e66142e22753344fda8a3e0b001cf7000000000000000000000000"
                "000000008c54dda9ac3bb1b1d43e6505621b9a7f672d2fa501000000e276c5661a64192db44ce7af"
-               "5eb888e9eb37bb0400000000000000000000000000000002641e447514ca565fb9b68c08a5e4351e"
-               "9f3c39f201000000de745cb25851429b3bbfb50168dfd04edf6b3542000000000000000000000000"
+               "5eb888e9eb73bb0400000000000000000000000000000002641e447514ca565fb9b68c08a5e4351e"
+               "9f3c39f201000000de745cb25851429b3bbfb50168dfd04edf2a3542000000000000000000000000"
                "0000000218f93e742d08f0550b55726f2292362e70ccc56700000000a4c73cf5ffa4ffc8a4bab148"
-               "74afdf54e6aae8160000000000000000000000000000000268baf76ab08940f6fc3bbdc7f6090f75"
-               "4f1b134803000000449a774929e86716ec852d59f3d17232edd86ad6000000000000000000000000"
+               "74afdf54e64be8160000000000000000000000000000000268baf76ab08940f6fc3bbdc7f6090f75"
+               "4f1b134803000000449a774929e86716ec852d59f3d17232ed3a6ad6000000000000000000000000"
                "000000027dba1ee7e143fd52599364e13468f80f40b26f6603000000badfad0fe8fa8f3c1aad2a82"
-               "530b7447d9c2f401000000000000000000000000000000028147507e1cd59ea1ae6da48b1eba6d16"
-               "dedea2030100000002a1f8e6f5657f3f9e3eb807cbad76451055edb8000000000000000000000000"
+               "530b7447d93bf401000000000000000000000000000000028147507e1cd59ea1ae6da48b1eba6d16"
+               "dedea2030100000002a1f8e6f5657f3f9e3eb807cbad76451013edb8000000000000000000000000"
                "00000002d9279fb2b8fcbac6abea74f7a6df7979f6989d2d000000006ff7add5df2c93e05459507b"
-               "5d8c6a0f466554d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"),
-        [2] = ("4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a06c40b20ea99d5ab90135fa636af44"
+               "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"),
+        [2] = ("4cc43c5103000000c2aac1d67a909b297c703915312e9c3c5a64c40b20ea99d5ab90135fa636af44"
                "64d210a256be75e0509c4477d19ac64b2e69220d54270e46020000006a894e387625da376feecb80"
-               "ac245409c0becc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c"
-               "12ea3b9501000000fb95e8332fdfff658483a2d039a7bf148ed3481e0bccc460e89f7abdb682380a"
+               "ac245409c058cc271d9c2f67179bff0644399ae79c64ed0432d599eaa660ac824b7fae389861419c"
+               "12ea3b9501000000fb95e8332fdfff658483a2d039a7bf148e4c481e0bccc460e89f7abdb682380a"
                "62eae374835b4a49a2b980b6aba92da6409969aa7a899f2d010000009bf5a5d06776e66a9d80ab13"
-               "2e1ac921eb76adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b"
-               "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ecc8fb112087b97cfcebbaeb0c565359"
+               "2e1ac921eb04adbb229df32e561fa80a40e55b8b1dd92e18c0cfb2b4386bec092fa5757ecde9348b"
+               "288cd9bf00000000225a0c71b2ce78bb16104c8eaf18c565ec5ffb112087b97cfcebbaeb0c565359"
                "23f14874042a8aff1d1f8e5ec3cc13cc36bbe3c9c4e1966800000000b3121cd5ef51e696c816290d"
-               "baee0e7726d082e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4"
-               "25a5927300000000c4458d70911714f1819aa46fbe143934c933ae6dec0fb124f264af67eed7c9a8"
+               "baee0e77264c82e1530ff5397c9125f59577d71c4b258a005116d11354edff62ceaa458fc75a91c4"
+               "25a5927300000000c4458d70911714f1819aa46fbe143934c915ae6dec0fb124f264af67eed7c9a8"
                "c38b6b8ee2614344bea73223e59bf0193432e9fa2fea9bc10300000062fb81e733cff620ca7feefe"
-               "1933631e8e69f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196"
-               "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6f18dbffeff9b8306c83bc026977911"
+               "1933631e8e18f6d9fed62d2c6e6904f57239c09c47150b9b14010469823acb72bb89182f93112196"
+               "9985d62700000000e491cbbbf9443fd6a77eb3c5bf64b671d6098dbffeff9b8306c83bc026977911"
                "4bdade862c224f6f36506ebd455e679cef369bb8bb0ec36f0000000064a2827b7cdd38d0314c178e"
-               "925b568ae56bc4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13"
-               "02e9b69000000000eacce745ea213cb1a84035b632ef3be6070dafa2fed87470e9da55709427c226"
+               "925b568ae566c4e9fefb24e5264fd3ebe9cf9f6df9615189f78ee815b2781ea55c9555876b6a3f13"
+               "02e9b69000000000eacce745ea213cb1a84035b632ef3be60755afa2fed87470e9da55709427c226"
                "d324d9a086ed27184b443181a5edc511c1507a14d514573f03000000f6932f6932d1d5f5d0ce2cf6"
-               "bf7a70ba5193a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd"
-               "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e45e9c1feb836c0a25ea71f5daaed4d"
+               "bf7a70ba5111a162fe92ec1b11d9172cf84a03478b82cdd8883b0f3fd6ed84d6c959e553b887edcd"
+               "6297dfa700000000fd0d53bb6215de4bb6f6f3d031a790287e7be9c1feb836c0a25ea71f5daaed4d"
                "99eb2ebebe3c432fec7d3abc4ccf30d3aaaf02a4ae8cb60b0300000038d856a40c67c3d4afa9c864"
-               "9da90bd2fff2f25600000000000000000000000000000001955e1a13d1111b067acbce3e3bcdf382"
-               "00154d12010000009edfa7a74c1c83c18e47935e8354846e34f0386a000000000000000000000000"
+               "9da90bd2ff4cf25600000000000000000000000000000001955e1a13d1111b067acbce3e3bcdf382"
+               "00154d12010000009edfa7a74c1c83c18e47935e8354846e346e386a000000000000000000000000"
                "00000001ac26c7d41fd050bf8d85055bcfe3756c1e0f515601000000f57609453df7803357ae7998"
-               "9870ccec3def0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7"
-               "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb7213e4000000000000000000000000"
+               "9870ccec3d4f0ad9000000000000000000000000000000019f05a16768cb3bc873b04d937a6ed8f7"
+               "7c52a52d030000004e8e8bc17c01ac63eca0c1cfbb204132fb0813e4000000000000000000000000"
                "00000000accae11f8045a80f855ebf3b772eb2b1d03bc90503000000eb760e3f004a2efc8ffc96b8"
-               "9b1bc8f415bd4e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec"
-               "6f0e9e6903000000ec2e2924348352d715cf9d411ea012e5307294b6000000000000000000000000"
+               "9b1bc8f415174e7700000000000000000000000000000000d149f074a378b881153ece68ade15cec"
+               "6f0e9e6903000000ec2e2924348352d715cf9d411ea012e5305a94b6000000000000000000000000"
                "00000000ccd2f50c30706cae3fdcc2f0ee3d5e267666c05303000000ffa8e89871fb9510b900b206"
-               "1d0c334b819f2dd100000000000000000000000000000000095a3230fde69eec3af2e6d0bd68ab72"
-               "81ae55f0030000000aa20624fbab244735d67c61283031667b2d74a1000000000000000000000000"
+               "1d0c334b816a2dd100000000000000000000000000000000095a3230fde69eec3af2e6d0bd68ab72"
+               "81ae55f0030000000aa20624fbab244735d67c61283031667b2274a1000000000000000000000000"
                "0000000000459582f4ef43c780908872746e39ef10613fc90200000016a31dc2e46a234558817151"
-               "b38fc9c909cf8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2"
-               "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b06fa9e000000000000000000000000"
+               "b38fc9c9093c8d7100000000000000000000000000000000ae17bb4515856092b4d9e89b676761b2"
+               "57d6249b020000007dc7f17f666969e45c3baf7a119a65ee0b08fa9e000000000000000000000000"
                "00000000b314f695bd82e9ea8891ed0a3d1b91ca6e91b91501000000be5b534e0a6527f67485ab35"
-               "aca0c7ee4197338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b"
-               "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99289462000000000000000000000000"
+               "aca0c7ee4167338500000000000000000000000000000000aefd4503189f7630248e501c9052c22b"
+               "5d68265d010000009b7a559be3acf4fc36fa6b513cf09e8b99729462000000000000000000000000"
                "00000000b49185ad9b7d0070a36dd51323b2c542b56e03e0010000002f70249eefc67bce716c856e"
                "3973dc42a1003be9000000000000000000000000000000006176f78a19bb8df1804c122fce5078c1"
-               "e5fe7d360100000066a3ae3939762df33a2d55060c78d551cbb110fd000000000000000000000000"
+               "e5fe7d360100000066a3ae3939762df33a2d55060c78d551cb7410fd000000000000000000000000"
                "000000002c171ed15abbbe1cd911db7b56e8f3feeecc89f601000000d635edea8ffe423d01883918"
-               "039249f398f9b37e000000000000000000000000000000001fb7c8307f06b4b29088f20d9ac676d5"
-               "f90e54b501000000a779f37c30d2c5053c40ecae210aeb0934cb2a24000000000000000000000000"
+               "039249f3984ab37e000000000000000000000000000000001fb7c8307f06b4b29088f20d9ac676d5"
+               "f90e54b501000000a779f37c30d2c5053c40ecae210aeb0934392a24000000000000000000000000"
                "00000000df48f333ef467092818aa77d6732b678bcd7be0700000000e45aaf61814f703b40125e6b"
-               "af4648cbaaa37b86000000000000000000000000000000005ddb05f2a84e27c18a06e2c9cb29efdf"
-               "50da49bf00000000d94beb259f9f0251becc987907879cca68fec7bb000000000000000000000000"
+               "af4648cbaa557b86000000000000000000000000000000005ddb05f2a84e27c18a06e2c9cb29efdf"
+               "50da49bf00000000d94beb259f9f0251becc987907879cca6857c7bb000000000000000000000000"
                "000000009402a4c2167721673f2b02b3256ead1fa349e1cc00000000b879a45176740744fcb6a035"
-               "339d504c9726d80d000000000000000000000000000000009518862bc9a89192c1c8be55281a59d1"
-               "bae87d60000000005d31e5b2de252167c1c91b3a36ba0c700ee78477000000000000000000000000"
+               "339d504c972bd80d000000000000000000000000000000009518862bc9a89192c1c8be55281a59d1"
+               "bae87d60000000005d31e5b2de252167c1c91b3a36ba0c700e538477000000000000000000000000"
                "000000006ad0143a0554efb46b382ab0404cdf02ebfdcec30000000067a982be8a934f852cc3d4d8"
-               "2bc0ec7303f99f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a"
-               "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789cf85178000000000000000000000000"
+               "2bc0ec7303579f8f00000000000000000000000000000000901f55f960c55c22f32099a8020bea2a"
+               "a2f56808092069affaa100fdfdd68d34be184524a6bdc8789c575178000000000000000000000000"
                "00000000020ffdb0e612098610e52bb2a16a4008aefd545b020000007ced4f8b362956cd46646a0e"
-               "222160e5f769bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d"
-               "f05d7308020000009d82c856ba942455050f63f8282f3464443cacf5000000000000000000000000"
+               "222160e5f749bb2900000000000000000000000000000000a19da86c64fefab2548759d313a5b92d"
+               "f05d7308020000009d82c856ba942455050f63f8282f34644466acf5000000000000000000000000"
                "0000000069d9942f460bcea75481f25e307d0de99a6ce5d900000000a6a5ee19d6f63fe9cbdd01f7"
-               "246f13b2050424a200000000000000000000000000000000ffe9d6812231bb02f922259b173c333c"
-               "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f218424e48000000000000000000000000"
+               "246f13b2055624a200000000000000000000000000000000ffe9d6812231bb02f922259b173c333c"
+               "5b1aea3401000000c7c3523f76cf1ce34ca17a9a32f3f0f2187c4e48000000000000000000000000"
                "00000002e6fbf952902383028e3b47f2111d891d17a4763102000000f9119c40b468b66331bca67a"
-               "034f43340d01683c00000000000000000000ffffb24a5be9b10123e6e4f15ecd1dff045298cfee06"
-               "536c75bb01000000720da9e07ac998b5690807d52617369ee1af4ca500000000000000000000ffff"
+               "034f43340d2c683c00000000000000000000ffffb24a5be9b10123e6e4f15ecd1dff045298cfee06"
+               "536c75bb01000000720da9e07ac998b5690807d52617369ee16d4ca500000000000000000000ffff"
                "47d7b351a36b178aabc78a4a0d80684ee5dbf45bb0916e5d030000004a503a712350acbd48411f0d"
-               "c15d2f0f49dad345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8"
-               "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa701d120523bd98f29b098b43cd68be6e"
+               "c15d2f0f4952d345599706954d2471f00d3c723b91adb0da7c4aa7e7eb5a15bcde015fb98b841fd8"
+               "ba8110fb03000000904f288262e6ea77a22fa5f863eaeefa700b120523bd98f29b098b43cd68be6e"
                "3f81268193fd637e585287dc6ca972a2cfb18b2b3cae864b0200000091921681c2162b9bfa40546c"
-               "db544e44df2d7c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378"
-               "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c1fa56dfdcfc1155f236c8b9c79a620"
+               "db544e44df587c2790d3119a051bb2ee8192ac0cfa3abc1ea0ab3e7d75a2f42b50c6a36340132378"
+               "8c3b371502000000710c030690f5f18ea125dbf7d7e93bd65c03a56dfdcfc1155f236c8b9c79a620"
                "5f660bbf024b03ff0a8e27c405e6424457393c6fdac12ebd01000000667224dae775e5ccd141aca2"
-               "ff0f576767d3648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034"
-               "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c93ef054e93f8120abb42316c533d9c9"
+               "ff0f57676754648102b61886b62f22ca33478e1f891715df7fc6a902edae579e2e10c7f7a22ba034"
+               "abf85f0c0100000008d33a2ea67776d7f88d69c8c7e3b2d3c951f054e93f8120abb42316c533d9c9"
                "4d88189c471a1ff2f0ce3ff66e782110125ae2edc7a4c65601000000cd625009a40aeb62a42b6a62"
-               "548e3c38b96c20022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb"
-               "daae7d6300000000c0982b283aa9986700b2a2b3ed257b8b0489f48f053ec8cac72105b327335a25"
+               "548e3c38b96620022753c5fbfaf1809becd2506315a6da29b2e94d3ab51fe0e9af98b180a36035fb"
+               "daae7d6300000000c0982b283aa9986700b2a2b3ed257b8b042ff48f053ec8cac72105b327335a25"
                "a364d1015ffac03089f45539e1b0178462156cd66855676c00000000017c9148da5892bb4951c3a7"
-               "ed55689d3391ce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46b7a350f9"
-               "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d067530e1b0487b8c3b99f2b8a1a8982c42f"
+               "ed55689d336bce7d3fd48469845f0c233dbdd2a97835df1f1782dee88487fd0db5971b46b7a350f9"
+               "cdb3455c00000000c66af76e80f062ea1a7e0b7be218d067532d1b0487b8c3b99f2b8a1a8982c42f"
                "53700a3437c5156e072b2f2fb337c9cfe07c34ddc896c9a7000000000a5f6abd850b06e3d9662795"
-               "ce2cc1cee06302aba042aaa218dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d24c33221"
-               "98c39e7c00000000efc57ff447036b9551adcb22fd50793f797a3c7dadd1c86e759d0c90bc512e13"
+               "ce2cc1cee03202aba042aaa218dc091e9aa1477f6fdc9830d7fbc95829a8838314dff34d24c33221"
+               "98c39e7c00000000efc57ff447036b9551adcb22fd50793f791c3c7dadd1c86e759d0c90bc512e13"
                "7dcef5e4a27985bd8d1e69c83dc21056b973972c7625ca4a00000000e682f80f94315c45ef817264"
-               "c89a736ed55ed637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5"
-               "9c88b02a0300000004b0595daf37deb499996bfb667f072dec1e5d9cdc8a11f4409bcfb3c167090e"
+               "c89a736ed580d637b077ddad3108be69f9b97d05c917ad6b10e693bb6e26f2ba90c8e909e85e20e5"
+               "9c88b02a0300000004b0595daf37deb499996bfb667f072dec755d9cdc8a11f4409bcfb3c167090e"
                "99d90a8543961b2ccd47724a3c460ba85183f4c5a7afe28302000000d05ff720b16c73761a3fcb67"
-               "5219061ab4c5c79489b2cc6a883c146972decb8b5adac8f5e6d66df46ea139107754dee62c36d2fc"
-               "e36f90d401000000fd41fe47684b370b6ec6584d64496089570968ead4d1ae91c819bb068196d599"
+               "5219061ab466c79489b2cc6a883c146972decb8b5adac8f5e6d66df46ea139107754dee62c36d2fc"
+               "e36f90d401000000fd41fe47684b370b6ec6584d64496089574568ead4d1ae91c819bb068196d599"
                "efde3246e43f5e7945aaf95e2ffa3a11b9ef1b214d4647f5000000000f88e3759a08e7a56663861e"
-               "ea1bf42ccd3c72f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db"
-               "0422ae3f03000000c43bd794c697895e5bfe47c6f53a54e01b0c1d89fecd94d0e02d2ec587fc3300"
+               "ea1bf42ccd1172f2ad063857ca82ac5180101be4f85cef468ea086ea9aaafdc388811ec787bc45db"
+               "0422ae3f03000000c43bd794c697895e5bfe47c6f53a54e01b211d89fecd94d0e02d2ec587fc3300"
                "03781b0f294a2b73c4421398f4de67e9cee64b38bc51e540030000006c3d048a92d3adc69a84eb47"
-               "622400207799416afef0a086fbd7e2f7dea0077ae9c0c0d9c386e9c6e6a2cbfa10ee58bdc7518360"
-               "36674cde0200000054cfe8f42bf0e9381ba2c4396de60f032bc7f34dfe959871c0d4c0d4eb720c4a"
+               "622400207708416afef0a086fbd7e2f7dea0077ae9c0c0d9c386e9c6e6a2cbfa10ee58bdc7518360"
+               "36674cde0200000054cfe8f42bf0e9381ba2c4396de60f032b5af34dfe959871c0d4c0d4eb720c4a"
                "e3c9472e5f2da5d9c5cfd9f2343b21362c19a092b2cd3dbd01000000e958e48d2ab8b0313e7f8933"
-               "e315130fa9a84c82feb83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5"
-               "bcfe97e1000000007ad742d3d7320a4f880cf47f5dd0cf69cb22840ffeb3fe7749509cb6752b2cee"
+               "e315130fa9274c82feb83f6e70b1646d8026e9f1704f1b16286ba2dabc7ef0820c91ed33ef60a8b5"
+               "bcfe97e1000000007ad742d3d7320a4f880cf47f5dd0cf69cb7f840ffeb3fe7749509cb6752b2cee"
                "30b7e7736a0afc9879ea40e69710fc9f6e8e99cd69121aec010000000fcc00ac2daa755e5292cd32"
-               "dc096cd5d8a4c5cffe91ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8"
-               "b83579b80300000072437f9b6245de39ec9e71ee4b951507beb42b34000000000000000000000000"
+               "dc096cd5d830c5cffe91ee1cfcc2056cc1ff8e51bbce426cfa4b861cc78592be7b14e7ba9c15acb8"
+               "b83579b80300000072437f9b6245de39ec9e71ee4b951507be0a2b34000000000000000000000000"
                "00000001d639272e4a46eadfbd2146bf8cfdb205b3980deb020000006c1de913df1424ce3ee1d76f"
-               "c1ea76e98c9dfc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e"
-               "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e7854152f2b70000000000000000000000000"
+               "c1ea76e98c16fc7800000000000000000000000000000001e44ea74b4f2b83f9b3bee14d861a4c9e"
+               "cf844ef501000000cd743f59bd14c89b5cfd5ce6e82e7854157b2b70000000000000000000000000"
                "0000000127e19f254ac9588319558041fc62ed561dce2f83020000003a3efc240b9977e166bee737"
-               "fe73670c439a644c000000000000000000000000000000015e0dfe491d1ba96742c7b5e02b227122"
-               "2af145480300000004265f6c72923e22594f3f79e208ed2e2ebb0771000000000000000000000000"
+               "fe73670c436b644c000000000000000000000000000000015e0dfe491d1ba96742c7b5e02b227122"
+               "2af145480300000004265f6c72923e22594f3f79e208ed2e2e710771000000000000000000000000"
                "0000000039dd8f88152a58454d9ca9d20f45dfa774993ade03000000e62112898dab2dade2ab2fc9"
-               "c56a7c86be5f962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8"
-               "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f608df061000000000000000000000000"
+               "c56a7c86be73962200000000000000000000000000000000bdd06352cb2c354434819f4b248eb2b8"
+               "922b6cb8030000001d5c302df59dd32a677e5a3af4fe297f6042f061000000000000000000000000"
                "000000008ae21e371051852c972ea7954079884a9f3c2c8803000000f51f44e644d7cf1d06b2b115"
-               "d882549e62c12fba000000000000000000000000000000004fc812bd2f8a59d96d064126666befbb"
-               "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a631480000000000000000000000000"
+               "d882549e62042fba000000000000000000000000000000004fc812bd2f8a59d96d064126666befbb"
+               "dc335c8b030000000a3c8e7c91bf821c4b09cee3c37ff4283a0c1480000000000000000000000000"
                "000000005588456e4ead7cbda620a3abae816e34e3ff1a5f03000000b4425486c619147eb0216050"
-               "ed7afd741024e83600000000000000000000000000000000f493472201e48d163106cc397446a33f"
-               "f257b044030000006a4c619f9037d7a754655354c47cf0f551e96770000000000000000000000000"
+               "ed7afd74105fe83600000000000000000000000000000000f493472201e48d163106cc397446a33f"
+               "f257b044030000006a4c619f9037d7a754655354c47cf0f551216770000000000000000000000000"
                "0000000050b8d10ab700cc3a7cf51be0403b654bbc74179403000000438a72e12ae0436a2495a609"
-               "675344f7e2e3a5eb0000000000000000000000000000000061eda239d9d083c3bf085387046ef8a3"
-               "4153e174020000004d89c0bb5408c1b7d17fb084d9e1825fa638e1af000000000000000000000000"
+               "675344f7e226a5eb0000000000000000000000000000000061eda239d9d083c3bf085387046ef8a3"
+               "4153e174020000004d89c0bb5408c1b7d17fb084d9e1825fa613e1af000000000000000000000000"
                "000000006e08cb887464b9344ec3127c750fabdd6c0fc09c02000000809829af4a9df58dbfef186d"
-               "416f3a1ef170d10f0000000000000000000000000000000087780281d47a32f2235376239daeaacb"
-               "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4dd3d03000000000000000000000000"
+               "416f3a1ef154d10f0000000000000000000000000000000087780281d47a32f2235376239daeaacb"
+               "74e7860402000000cd7db68b9ae586dacdb4070c50320427c4803d03000000000000000000000000"
                "00000000d538d34574c038404923e75d0e09e2fca9a89f8701000000fcdf6d9be94030b34774d1d7"
-               "dddedd9899f0f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767"
-               "fd338f35010000006e8cc34573654ea6624f138ef9531cd9367a02e4000000000000000000000000"
+               "dddedd989972f62700000000000000000000000000000000848518c4e3f6cbf25e5e1b990fc46767"
+               "fd338f35010000006e8cc34573654ea6624f138ef9531cd9361802e4000000000000000000000000"
                "00000000da40d88d40a6d75bc404156225b7eede2cb749e2010000000eb4f73e75039fcf251b32fc"
-               "79685b05ddd3aa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515"
-               "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d9b7146000000000000000000000000"
+               "79685b05dd3caa9b000000000000000000000000000000000d58fbcc58fc4c2fd0d44ff1e51c3515"
+               "6aae660b01000000fc1fb23d24438387def0f4c6544e4b275d4a7146000000000000000000000000"
                "00000000b883efebc09dd9d61966b7ae7412041db7f0318a01000000061db3dd7fde25654a4059d5"
-               "65dbc8a91e3b445700000000000000000000000000000000c1b5aad487c192f08bd7a09bdd214443"
-               "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e2b00b6000000000000000000000000"
+               "65dbc8a91e00445700000000000000000000000000000000c1b5aad487c192f08bd7a09bdd214443"
+               "61c2f5d200000000b809d259e499db7dc8f1853bdcb0e4bc0e0e00b6000000000000000000000000"
                "000000005e7fa0f02f2f3313d8469d09a92409c0b4d54acb00000000a417f3da01d99dae5f190096"
-               "cc582cabd5ddd15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f"
-               "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a553fc2000000000000000000000000"
+               "cc582cabd534d15a00000000000000000000000000000000fdb3d117d76b0de5416f95e7bb75d72f"
+               "59747cf200000000ab2dadc5a39411fd4ff1116d478987316a293fc2000000000000000000000000"
                "000000000819e3b10db116431ec6f7691539ce41b95c593100000000dbe9962069e66142e2275334"
-               "4fda8a3e0b841cf7000000000000000000000000000000008c54dda9ac3bb1b1d43e6505621b9a7f"
-               "fc2b445700000000496ad01bf007eacb0a28aec868282510b60291ea000000000000000000000000"
+               "4fda8a3e0b001cf7000000000000000000000000000000008c54dda9ac3bb1b1d43e6505621b9a7f"
+               "fc2b445700000000496ad01bf007eacb0a28aec868282510b61b91ea000000000000000000000000"
                "000000005b29a197b7b3cceaf5f1bb12c535256a0436242c03000000ee3fbc164632d4f28aa28c08"
-               "f80dd50c6712dfb400000000000000000000000000000000078a806d329c9b008bbfc9723107f3f5"
-               "9a1b163a030000000e2f4f911a0dd8c052ed6b13e2815a1a37a52bdd000000000000000000000000"
+               "f80dd50c6705dfb400000000000000000000000000000000078a806d329c9b008bbfc9723107f3f5"
+               "9a1b163a030000000e2f4f911a0dd8c052ed6b13e2815a1a37322bdd000000000000000000000000"
                "00000000bd99fcf72909ac9e7a55299e9bb4fd53fc1519de010000002545666937c22a3b6e9686dc"
-               "c405cbee5016c252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e"
-               "d7ac5f9901000000822a272bec4501a1e27acfee7a8588ffd5a06cae000000000000000000000000"
+               "c405cbee504dc252000000000000000000000000000000006e4c699341b95f41d9c1e67743efb54e"
+               "d7ac5f9901000000822a272bec4501a1e27acfee7a8588ffd5286cae000000000000000000000000"
                "0000000077a47579dda0b6ead1e1b34901eaedd54f1b134803000000449a774929e86716ec852d59"
-               "f3d17232edd86ad6000000000000000000000000000000027dba1ee7e143fd52599364e13468f80f"
-               "40b26f6603000000badfad0fe8fa8f3c1aad2a82530b7447d9c2f401000000000000000000000000"
+               "f3d17232ed3a6ad6000000000000000000000000000000027dba1ee7e143fd52599364e13468f80f"
+               "40b26f6603000000badfad0fe8fa8f3c1aad2a82530b7447d93bf401000000000000000000000000"
                "000000028147507e1cd59ea1ae6da48b1eba6d16dedea2030100000002a1f8e6f5657f3f9e3eb807"
-               "cbad76451055edb800000000000000000000000000000002d9279fb2b8fcbac6abea74f7a6df7979"
-               "672d2fa501000000e276c5661a64192db44ce7af5eb888e9eb37bb04000000000000000000000000"
+               "cbad76451013edb800000000000000000000000000000002d9279fb2b8fcbac6abea74f7a6df7979"
+               "672d2fa501000000e276c5661a64192db44ce7af5eb888e9eb73bb04000000000000000000000000"
                "00000002641e447514ca565fb9b68c08a5e4351e9f3c39f201000000de745cb25851429b3bbfb501"
-               "68dfd04edf6b35420000000000000000000000000000000218f93e742d08f0550b55726f2292362e"
-               "70ccc56700000000a4c73cf5ffa4ffc8a4bab14874afdf54e6aae816000000000000000000000000"
+               "68dfd04edf2a35420000000000000000000000000000000218f93e742d08f0550b55726f2292362e"
+               "70ccc56700000000a4c73cf5ffa4ffc8a4bab14874afdf54e64be816000000000000000000000000"
                "0000000268baf76ab08940f6fc3bbdc7f6090f75f6989d2d000000006ff7add5df2c93e05459507b"
-               "5d8c6a0f466554d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"),
+               "5d8c6a0f465354d200000000000000000000ffff1785d3ffdbd9a617857ae8126b028c0e3e295354"),
     };
     const int   TEST_DATA_I      = GPOINTER_TO_INT(test_data);
     const int   addr_family      = (TEST_DATA_I == 0 ? AF_INET : AF_INET6);
@@ -584,8 +584,9 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
     nmtst_stable_rand(710086081, rand_map, sizeof(rand_map[0]) * N_ADDRESSES);
 
     for (i = 0; i < N_ADDRESSES; i++) {
-        NMPlatformIPXAddress *a = (gpointer) (&addresses[i * ELM_SIZE]);
-        guint64               r = rand_map[i];
+        NMPlatformIPXAddress *a  = (gpointer) (&addresses[i * ELM_SIZE]);
+        guint64               r  = rand_map[i];
+        guint64               r0 = r;
         struct in6_addr      *a6;
 
 #define CONSUME_BITS(r, nbits)                        \
@@ -664,9 +665,10 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                 a->a4.label[0] = '\0';
             }
         }
+        if (a->ax.plen > 0)
+            a->ax.plen = (r0 + 209284393u) % ((NM_IS_IPv4(addr_family) ? 32 : 128) + 1);
         if (addr_family == AF_INET) {
             if (CONSUME_BITS(r, 2) != 0) {
-                /* randomly make the label empty or not. */
                 a->a4.plen = CONSUME_BITS(r, 2);
             }
         }
@@ -731,15 +733,17 @@ test_platform_ip_address_pretty_sort_cmp(gconstpointer test_data)
                                             &bin_len);
 
         if (bin_len != ELM_SIZE * N_ADDRESSES || memcmp(addresses, bin_arr, bin_len) != 0) {
-            char *addresses_str = nm_utils_bin2hexstr(addresses, ELM_SIZE * N_ADDRESSES, -1);
+            gs_free char *addresses_str = NULL;
 
+            NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
             g_error(">>> test_platform_ip_address_pretty_sort_cmp() will fail:\n"
                     ">>> addresses[%zu]: %s\n"
                     ">>> expected [%zu]: %s\n",
                     ELM_SIZE * N_ADDRESSES,
-                    addresses_str,
+                    (addresses_str = nm_utils_bin2hexstr(addresses, ELM_SIZE * N_ADDRESSES, -1)),
                     bin_len,
                     EXPECTED_BUFFER[TEST_DATA_I]);
+            NM_PRAGMA_WARNING_REENABLE
         }
 
         g_assert_cmpmem(addresses, ELM_SIZE * N_ADDRESSES, bin_arr, bin_len);
@@ -760,6 +764,7 @@ test_route_type_is_nodev(void)
         case RTN_BLACKHOLE:
         case RTN_UNREACHABLE:
         case RTN_PROHIBIT:
+        case RTN_THROW:
             is_nodev = TRUE;
             break;
         default:
diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c
index cf0236e0..2d39cb37 100644
--- a/src/core/platform/tests/test-route.c
+++ b/src/core/platform/tests/test-route.c
@@ -1535,6 +1535,7 @@ _rule_fuzzy_equal(const NMPObject *obj, const NMPObject *obj_comp, int op_type)
 static void
 test_rule(gconstpointer test_data)
 {
+    char                         sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE];
     const int                    TEST_IDX     = GPOINTER_TO_INT(test_data);
     const gboolean               TEST_SYNC    = (TEST_IDX == 4);
     gs_unref_ptrarray GPtrArray *objs         = NULL;
@@ -1763,7 +1764,7 @@ again:
 
                 g_print(">>> failing... errno=%d, rule=%s\n",
                         r,
-                        nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
+                        nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf1, sizeof(sbuf1)));
 
                 nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE);
                 head_entry = nm_platform_lookup(platform, &lookup);
@@ -1775,9 +1776,10 @@ again:
                         && NMP_OBJECT_CAST_ROUTING_RULE(o)->priority
                                == NMP_OBJECT_CAST_ROUTING_RULE(obj)->priority)
                         ch = '*';
-                    g_print(">>> existing rule: %c %s\n",
-                            ch,
-                            nmp_object_to_string(o, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
+                    g_print(
+                        ">>> existing rule: %c %s\n",
+                        ch,
+                        nmp_object_to_string(o, NMP_OBJECT_TO_STRING_ALL, sbuf1, sizeof(sbuf1)));
                 }
 
                 nmtstp_run_command_check("ip rule");
@@ -1836,11 +1838,14 @@ again:
 
                 if (!_rule_fuzzy_equal(obj, objs->pdata[k], RTM_DELRULE)) {
                     g_print(">>> failing...\n");
-                    g_print(">>> no fuzzy match between: %s\n",
-                            nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0));
                     g_print(
-                        ">>>                    and: %s\n",
-                        nmp_object_to_string(objs->pdata[k], NMP_OBJECT_TO_STRING_ALL, NULL, 0));
+                        ">>> no fuzzy match between: %s\n",
+                        nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf1, sizeof(sbuf1)));
+                    g_print(">>>                    and: %s\n",
+                            nmp_object_to_string(objs->pdata[k],
+                                                 NMP_OBJECT_TO_STRING_ALL,
+                                                 sbuf1,
+                                                 sizeof(sbuf1)));
                     g_assert_not_reached();
                 }
 
@@ -1899,14 +1904,17 @@ test_blackhole(gconstpointer test_data)
     NMPlatformIPXRoute           rr = {};
     int                          r  = -1;
     int                          i;
+    guint8                       rtn_type;
+
+    rtn_type = nmtst_rand_select(RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_THROW);
 
     if (IS_IPv4) {
         rr.r4 = (const NMPlatformIP4Route){
-            .type_coerced = nmtst_rand_select(RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT),
+            .type_coerced = nm_platform_route_type_coerce(rtn_type),
         };
     } else {
         rr.r6 = (const NMPlatformIP6Route){
-            .type_coerced = nmtst_rand_select(RTN_BLACKHOLE, RTN_UNREACHABLE, RTN_PROHIBIT),
+            .type_coerced = nm_platform_route_type_coerce(rtn_type),
             .metric       = 1000,
         };
     }
diff --git a/src/core/ppp/nm-ppp-manager.c b/src/core/ppp/nm-ppp-manager.c
index 5761d59d..896c233d 100644
--- a/src/core/ppp/nm-ppp-manager.c
+++ b/src/core/ppp/nm-ppp-manager.c
@@ -1247,7 +1247,7 @@ _ppp_manager_stop(NMPPPManager            *self,
                               SIGTERM,
                               LOGD_PPP,
                               "pppd",
-                              5000,
+                              NM_SHUTDOWN_TIMEOUT_5000_MSEC,
                               _stop_child_cb,
                               handle);
 
diff --git a/src/core/settings/nm-secret-agent.c b/src/core/settings/nm-secret-agent.c
index b222bd5c..a3df4497 100644
--- a/src/core/settings/nm-secret-agent.c
+++ b/src/core/settings/nm-secret-agent.c
@@ -511,7 +511,7 @@ nm_secret_agent_cancel_call(NMSecretAgent *self, NMSecretAgentCallId *call_id)
             g_variant_new("(os)", call_id->path, call_id->setting_name),
             G_VARIANT_TYPE("()"),
             G_DBUS_CALL_FLAGS_NO_AUTO_START,
-            NM_SHUTDOWN_TIMEOUT_MS,
+            NM_SHUTDOWN_TIMEOUT_1500_MSEC,
             NULL, /* this operation is not cancellable. We rely on the timeout. */
             _call_cancel_cb,
             call_id);
diff --git a/src/core/settings/nm-settings-connection.c b/src/core/settings/nm-settings-connection.c
index 55efaebe..1638efcd 100644
--- a/src/core/settings/nm-settings-connection.c
+++ b/src/core/settings/nm-settings-connection.c
@@ -606,6 +606,7 @@ _secrets_update(NMConnection  *connection,
 
 gboolean
 nm_settings_connection_update(NMSettingsConnection            *self,
+                              const char                      *plugin_name,
                               NMConnection                    *new_connection,
                               NMSettingsConnectionPersistMode  persist_mode,
                               NMSettingsConnectionIntFlags     sett_flags,
@@ -618,6 +619,7 @@ nm_settings_connection_update(NMSettingsConnection            *self,
 
     return nm_settings_update_connection(NM_SETTINGS_CONNECTION_GET_PRIVATE(self)->settings,
                                          self,
+                                         plugin_name,
                                          new_connection,
                                          persist_mode,
                                          sett_flags,
@@ -835,6 +837,7 @@ nm_settings_connection_new_secrets(NMSettingsConnection *self,
 
     if (!nm_settings_connection_update(
             self,
+            NULL,
             new_connection ?: connection,
             NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
@@ -980,6 +983,7 @@ get_secrets_done_cb(NMAgentManager              *manager,
     }
     if (!nm_settings_connection_update(
             self,
+            NULL,
             new_connection,
             agent_had_system ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
                              : NM_SETTINGS_CONNECTION_PERSIST_MODE_NO_PERSIST,
@@ -1409,6 +1413,7 @@ typedef struct {
     NMConnection          *new_settings;
     NMSettingsUpdate2Flags flags;
     char                  *audit_args;
+    char                  *plugin_name;
     bool                   is_update2 : 1;
 } UpdateInfo;
 
@@ -1436,6 +1441,7 @@ update_complete(NMSettingsConnection *self, UpdateInfo *info, GError *error)
     g_clear_object(&info->agent_mgr);
     g_clear_object(&info->new_settings);
     g_free(info->audit_args);
+    g_free(info->plugin_name);
     g_slice_free(UpdateInfo, info);
 }
 
@@ -1572,6 +1578,7 @@ update_auth_cb(NMSettingsConnection  *self,
 
     nm_settings_connection_update(
         self,
+        info->plugin_name,
         info->new_settings,
         persist_mode,
         (NM_FLAGS_HAS(info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
@@ -1642,6 +1649,7 @@ settings_connection_update(NMSettingsConnection  *self,
                            gboolean               is_update2,
                            GDBusMethodInvocation *context,
                            GVariant              *new_settings,
+                           const char            *plugin_name,
                            NMSettingsUpdate2Flags flags)
 {
     NMSettingsConnectionPrivate *priv    = NM_SETTINGS_CONNECTION_GET_PRIVATE(self);
@@ -1696,6 +1704,7 @@ settings_connection_update(NMSettingsConnection  *self,
     info->subject      = subject;
     info->flags        = flags;
     info->new_settings = tmp;
+    info->plugin_name  = g_strdup(plugin_name);
 
     permission = get_update_modify_permission(nm_settings_connection_get_connection(self),
                                               tmp ?: nm_settings_connection_get_connection(self));
@@ -1724,7 +1733,12 @@ impl_settings_connection_update(NMDBusObject                      *obj,
     gs_unref_variant GVariant *settings = NULL;
 
     g_variant_get(parameters, "(@a{sa{sv}})", &settings);
-    settings_connection_update(self, FALSE, invocation, settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
+    settings_connection_update(self,
+                               FALSE,
+                               invocation,
+                               settings,
+                               NULL,
+                               NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
 }
 
 static void
@@ -1744,6 +1758,7 @@ impl_settings_connection_update_unsaved(NMDBusObject                      *obj,
                                FALSE,
                                invocation,
                                settings,
+                               NULL,
                                NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY);
 }
 
@@ -1758,7 +1773,12 @@ impl_settings_connection_save(NMDBusObject                      *obj,
 {
     NMSettingsConnection *self = NM_SETTINGS_CONNECTION(obj);
 
-    settings_connection_update(self, FALSE, invocation, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
+    settings_connection_update(self,
+                               FALSE,
+                               invocation,
+                               NULL,
+                               NULL,
+                               NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
 }
 
 static void
@@ -1770,13 +1790,15 @@ impl_settings_connection_update2(NMDBusObject                      *obj,
                                  GDBusMethodInvocation             *invocation,
                                  GVariant                          *parameters)
 {
-    NMSettingsConnection      *self     = NM_SETTINGS_CONNECTION(obj);
-    gs_unref_variant GVariant *settings = NULL;
-    gs_unref_variant GVariant *args     = NULL;
+    NMSettingsConnection      *self        = NM_SETTINGS_CONNECTION(obj);
+    gs_unref_variant GVariant *settings    = NULL;
+    gs_unref_variant GVariant *args        = NULL;
+    gs_free char              *plugin_name = NULL;
     guint32                    flags_u;
     GError                    *error = NULL;
     GVariantIter               iter;
     const char                *args_name;
+    GVariant                  *args_value;
     NMSettingsUpdate2Flags     flags;
 
     g_variant_get(parameters, "(@a{sa{sv}}u@a{sv})", &settings, &flags_u, &args);
@@ -1812,7 +1834,13 @@ impl_settings_connection_update2(NMDBusObject                      *obj,
     nm_assert(g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}")));
 
     g_variant_iter_init(&iter, args);
-    while (g_variant_iter_next(&iter, "{&sv}", &args_name, NULL)) {
+    while (g_variant_iter_next(&iter, "{&sv}", &args_name, &args_value)) {
+        if (plugin_name == NULL && nm_streq(args_name, "plugin")
+            && g_variant_is_of_type(args_value, G_VARIANT_TYPE_STRING)) {
+            plugin_name = g_variant_dup_string(args_value, NULL);
+            continue;
+        }
+
         error = g_error_new(NM_SETTINGS_ERROR,
                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
                             "Unsupported argument '%s'",
@@ -1821,7 +1849,7 @@ impl_settings_connection_update2(NMDBusObject                      *obj,
         return;
     }
 
-    settings_connection_update(self, TRUE, invocation, settings, flags);
+    settings_connection_update(self, TRUE, invocation, settings, plugin_name, flags);
 }
 
 static void
@@ -2010,6 +2038,7 @@ dbus_clear_secrets_auth_cb(NMSettingsConnection  *self,
 
     if (!nm_settings_connection_update(
             self,
+            NULL,
             connection_cloned,
             NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
             NM_SETTINGS_CONNECTION_INT_FLAGS_NONE,
diff --git a/src/core/settings/nm-settings-connection.h b/src/core/settings/nm-settings-connection.h
index 5f8a22e8..893b0d7b 100644
--- a/src/core/settings/nm-settings-connection.h
+++ b/src/core/settings/nm-settings-connection.h
@@ -240,6 +240,7 @@ nm_settings_connection_has_unmodified_applied_connection(NMSettingsConnection *s
                                                          NMSettingCompareFlags compare_flage);
 
 gboolean nm_settings_connection_update(NMSettingsConnection            *self,
+                                       const char                      *plugin_name,
                                        NMConnection                    *new_connection,
                                        NMSettingsConnectionPersistMode  persist_mode,
                                        NMSettingsConnectionIntFlags     sett_flags,
diff --git a/src/core/settings/nm-settings.c b/src/core/settings/nm-settings.c
index 6619d3e0..1ff66e25 100644
--- a/src/core/settings/nm-settings.c
+++ b/src/core/settings/nm-settings.c
@@ -236,12 +236,16 @@ _sett_conn_entry_get_conn(SettConnEntry *sett_conn_entry)
  * update-connection. If this parameter is omitted, then it's about what happens
  * when adding a new profile (add-connection).
  *
+ * @storage_check_ignore is optional, and if given then it skips this particular
+ * storage.
+ *
  * Returns: the conflicting storage or %NULL if there is none.
  */
 static NMSettingsStorage *
 _sett_conn_entry_storage_find_conflicting_storage(SettConnEntry     *sett_conn_entry,
                                                   NMSettingsPlugin  *target_plugin,
                                                   NMSettingsStorage *storage_check_including,
+                                                  NMSettingsStorage *storage_check_ignore,
                                                   const GSList      *plugins)
 {
     StorageData *sd;
@@ -269,6 +273,12 @@ _sett_conn_entry_storage_find_conflicting_storage(SettConnEntry     *sett_conn_e
             continue;
         }
 
+        if (sd->storage == storage_check_ignore) {
+            /* We ignore this one, because we're in the process of
+             * replacing it. */
+            continue;
+        }
+
         if (sd->storage == storage_check_including) {
             /* ok, the storage is the one we are about to check. All other
              * storages are lower priority, so there is no storage that hides
@@ -557,6 +567,8 @@ _startup_complete_check(NMSettings *self, gint64 now_msec)
         return;
     }
 
+    nm_clear_g_source(&priv->startup_complete_timeout_id);
+
     if (c_list_is_empty(&priv->startup_complete_scd_lst_head))
         goto ready;
 
@@ -592,8 +604,6 @@ next_with_ready:
     }
     c_list_splice(&priv->startup_complete_scd_lst_head, &ready_lst);
 
-    nm_clear_g_source(&priv->startup_complete_timeout_id);
-
     if (scd_not_ready) {
         gint64 timeout_msec;
 
@@ -1463,6 +1473,7 @@ _plugin_connections_reload(NMSettings *self)
 
 static gboolean
 _add_connection_to_first_plugin(NMSettings                  *self,
+                                const char                  *plugin_name,
                                 SettConnEntry               *sett_conn_entry,
                                 NMConnection                *new_connection,
                                 gboolean                     in_memory,
@@ -1471,12 +1482,14 @@ _add_connection_to_first_plugin(NMSettings                  *self,
                                 gboolean                     shadowed_owned,
                                 NMSettingsStorage          **out_new_storage,
                                 NMConnection               **out_new_connection,
+                                NMSettingsStorage           *drop_storage,
                                 GError                     **error)
 {
     NMSettingsPrivate    *priv        = NM_SETTINGS_GET_PRIVATE(self);
     gs_free_error GError *first_error = NULL;
     GSList               *iter;
     const char           *uuid;
+    gboolean              no_plugin = TRUE;
 
     uuid = nm_connection_get_uuid(new_connection);
 
@@ -1493,12 +1506,18 @@ _add_connection_to_first_plugin(NMSettings                  *self,
         gboolean                           success;
         const char                        *filename;
 
+        if (plugin_name && strcmp(plugin_name, nm_settings_plugin_get_plugin_name(plugin))) {
+            /* Not the plugin we're confined to. Ignore. */
+            continue;
+        }
+
         if (!in_memory) {
             NMSettingsStorage *conflicting_storage;
 
             conflicting_storage = _sett_conn_entry_storage_find_conflicting_storage(sett_conn_entry,
                                                                                     plugin,
                                                                                     NULL,
+                                                                                    drop_storage,
                                                                                     priv->plugins);
             if (conflicting_storage) {
                 /* we have a connection provided by a plugin with higher priority than the one
@@ -1545,6 +1564,8 @@ _add_connection_to_first_plugin(NMSettings                  *self,
                                                         &add_error);
         }
 
+        no_plugin = FALSE;
+
         if (!success) {
             _LOGT("add-connection: failed to add %s/'%s': %s",
                   nm_connection_get_uuid(new_connection),
@@ -1588,8 +1609,18 @@ _add_connection_to_first_plugin(NMSettings                  *self,
         return TRUE;
     }
 
-    nm_assert(first_error);
-    g_propagate_error(error, g_steal_pointer(&first_error));
+    if (no_plugin) {
+        nm_assert(plugin_name);
+        nm_assert(!first_error);
+        g_set_error(error,
+                    NM_SETTINGS_ERROR,
+                    NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
+                    "a plugin by the name of '%s' is not available",
+                    plugin_name);
+    } else {
+        nm_assert(first_error);
+        g_propagate_error(error, g_steal_pointer(&first_error));
+    }
     return FALSE;
 }
 
@@ -1704,6 +1735,7 @@ _set_nmmeta_tombstone(NMSettings *self,
  */
 gboolean
 nm_settings_add_connection(NMSettings                     *self,
+                           const char                     *plugin,
                            NMConnection                   *connection,
                            NMSettingsConnectionPersistMode persist_mode,
                            NMSettingsConnectionAddReason   add_reason,
@@ -1800,6 +1832,7 @@ nm_settings_add_connection(NMSettings                     *self,
             sett_conn_entry,
             nm_settings_storage_get_plugin(shadowed_storage),
             shadowed_storage,
+            NULL,
             priv->plugins);
         if (conflicting_storage) {
             /* We cannot add the profile as @shadowed_storage, because there is another, existing storage
@@ -1824,6 +1857,7 @@ again_add_connection:
 
     if (!update_storage) {
         success = _add_connection_to_first_plugin(self,
+                                                  plugin,
                                                   sett_conn_entry,
                                                   connection,
                                                   new_in_memory,
@@ -1832,6 +1866,7 @@ again_add_connection:
                                                   FALSE,
                                                   &new_storage,
                                                   &new_connection,
+                                                  NULL,
                                                   &local);
     } else {
         success = _update_connection_to_plugin(self,
@@ -1948,6 +1983,7 @@ again_delete_tombstone:
 gboolean
 nm_settings_update_connection(NMSettings                      *self,
                               NMSettingsConnection            *sett_conn,
+                              const char                      *plugin_name,
                               NMConnection                    *connection,
                               NMSettingsConnectionPersistMode  persist_mode,
                               NMSettingsConnectionIntFlags     sett_flags,
@@ -2154,8 +2190,9 @@ nm_settings_update_connection(NMSettings                      *self,
         } else if (nm_settings_storage_is_keyfile_lib(cur_storage)) {
             /* the profile is a keyfile in /usr/lib. It cannot be overwritten, we must migrate it
              * from /usr/lib to /etc. */
-        } else
+        } else {
             update_storage = cur_storage;
+        }
 
         if (new_in_memory) {
             if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY) {
@@ -2174,8 +2211,22 @@ nm_settings_update_connection(NMSettings                      *self,
             }
         }
 
+        if (update_storage && plugin_name) {
+            NMSettingsPlugin *plugin = nm_settings_storage_get_plugin(update_storage);
+
+            if (strcmp(plugin_name, nm_settings_plugin_get_plugin_name(plugin))) {
+                /* We're updating a connection, we're confined to a particular
+                 * plugin, but the connection is currently using a different one.
+                 * We need to migrate. Drop the existing storage and look out for
+                 * a new one. */
+                drop_storage   = update_storage;
+                update_storage = NULL;
+            }
+        }
+
         if (!update_storage) {
             success = _add_connection_to_first_plugin(self,
+                                                      plugin_name,
                                                       sett_conn_entry,
                                                       connection,
                                                       new_in_memory,
@@ -2184,6 +2235,7 @@ nm_settings_update_connection(NMSettings                      *self,
                                                       new_shadowed_owned,
                                                       &new_storage,
                                                       &new_connection,
+                                                      drop_storage,
                                                       &local);
         } else {
             success = _update_connection_to_plugin(self,
@@ -2462,6 +2514,7 @@ pk_add_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data
         nm_assert(NM_IS_CONNECTION(connection));
 
         nm_settings_add_connection(self,
+                                   nm_auth_chain_get_data(chain, "plugin"),
                                    connection,
                                    GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "persist-mode")),
                                    GPOINTER_TO_UINT(nm_auth_chain_get_data(chain, "add-reason")),
@@ -2489,6 +2542,7 @@ pk_add_cb(NMAuthChain *chain, GDBusMethodInvocation *context, gpointer user_data
 
 void
 nm_settings_add_connection_dbus(NMSettings                     *self,
+                                const char                     *plugin,
                                 NMConnection                   *connection,
                                 NMSettingsConnectionPersistMode persist_mode,
                                 NMSettingsConnectionAddReason   add_reason,
@@ -2549,6 +2603,7 @@ nm_settings_add_connection_dbus(NMSettings                     *self,
     nm_auth_chain_set_data(chain, "persist-mode", GUINT_TO_POINTER(persist_mode), NULL);
     nm_auth_chain_set_data(chain, "add-reason", GUINT_TO_POINTER(add_reason), NULL);
     nm_auth_chain_set_data(chain, "sett-flags", GUINT_TO_POINTER(sett_flags), NULL);
+    nm_auth_chain_set_data(chain, "plugin", g_strdup(plugin), g_free);
     nm_auth_chain_add_call_unsafe(chain, perm, TRUE);
     return;
 
@@ -2601,6 +2656,7 @@ settings_add_connection_helper(NMSettings                   *self,
                                GDBusMethodInvocation        *context,
                                gboolean                      is_add_connection_2,
                                GVariant                     *settings,
+                               const char                   *plugin,
                                NMSettingsAddConnection2Flags flags)
 {
     gs_unref_object NMConnection   *connection = NULL;
@@ -2636,6 +2692,7 @@ settings_add_connection_helper(NMSettings                   *self,
 
     nm_settings_add_connection_dbus(
         self,
+        plugin,
         connection,
         persist_mode,
         NM_FLAGS_HAS(flags, NM_SETTINGS_ADD_CONNECTION2_FLAG_BLOCK_AUTOCONNECT)
@@ -2665,6 +2722,7 @@ impl_settings_add_connection(NMDBusObject                      *obj,
                                    invocation,
                                    FALSE,
                                    settings,
+                                   NULL,
                                    NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK);
 }
 
@@ -2685,6 +2743,7 @@ impl_settings_add_connection_unsaved(NMDBusObject                      *obj,
                                    invocation,
                                    FALSE,
                                    settings,
+                                   NULL,
                                    NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY);
 }
 
@@ -2700,8 +2759,10 @@ impl_settings_add_connection2(NMDBusObject                      *obj,
     NMSettings                   *self     = NM_SETTINGS(obj);
     gs_unref_variant GVariant    *settings = NULL;
     gs_unref_variant GVariant    *args     = NULL;
+    gs_free char                 *plugin   = NULL;
     NMSettingsAddConnection2Flags flags;
     const char                   *args_name;
+    GVariant                     *args_value;
     GVariantIter                  iter;
     guint32                       flags_u;
 
@@ -2745,7 +2806,13 @@ impl_settings_add_connection2(NMDBusObject                      *obj,
     nm_assert(g_variant_is_of_type(args, G_VARIANT_TYPE("a{sv}")));
 
     g_variant_iter_init(&iter, args);
-    while (g_variant_iter_next(&iter, "{&sv}", &args_name, NULL)) {
+    while (g_variant_iter_next(&iter, "{&sv}", &args_name, &args_value)) {
+        if (plugin == NULL && nm_streq(args_name, "plugin")
+            && g_variant_is_of_type(args_value, G_VARIANT_TYPE_STRING)) {
+            plugin = g_variant_dup_string(args_value, NULL);
+            continue;
+        }
+
         g_dbus_method_invocation_take_error(invocation,
                                             g_error_new(NM_SETTINGS_ERROR,
                                                         NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
@@ -2754,7 +2821,7 @@ impl_settings_add_connection2(NMDBusObject                      *obj,
         return;
     }
 
-    settings_add_connection_helper(self, invocation, TRUE, settings, flags);
+    settings_add_connection_helper(self, invocation, TRUE, settings, plugin, flags);
 }
 
 /*****************************************************************************/
@@ -3201,8 +3268,10 @@ nm_settings_get_connection_by_path(NMSettings *self, const char *path)
     priv = NM_SETTINGS_GET_PRIVATE(self);
 
     connection =
-        nm_dbus_manager_lookup_object(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)), path);
-    if (!connection || !NM_IS_SETTINGS_CONNECTION(connection))
+        nm_dbus_manager_lookup_object_with_type(nm_dbus_object_get_manager(NM_DBUS_OBJECT(self)),
+                                                NM_TYPE_SETTINGS_CONNECTION,
+                                                path);
+    if (!connection)
         return NULL;
 
     nm_assert(c_list_contains(&priv->connections_lst_head, &connection->_connections_lst));
@@ -3604,6 +3673,7 @@ device_realized(NMDevice *device, GParamSpec *pspec, NMSettings *self)
           nm_device_get_iface(device));
 
     nm_settings_add_connection(self,
+                               NULL,
                                connection,
                                NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
                                NM_SETTINGS_CONNECTION_ADD_REASON_NONE,
diff --git a/src/core/settings/nm-settings.h b/src/core/settings/nm-settings.h
index 56cebed0..8747d1dc 100644
--- a/src/core/settings/nm-settings.h
+++ b/src/core/settings/nm-settings.h
@@ -68,6 +68,7 @@ typedef void (*NMSettingsAddCallback)(NMSettings            *settings,
                                       gpointer               user_data);
 
 void nm_settings_add_connection_dbus(NMSettings                     *self,
+                                     const char                     *plugin,
                                      NMConnection                   *connection,
                                      NMSettingsConnectionPersistMode persist_mode,
                                      NMSettingsConnectionAddReason   add_reason,
@@ -90,6 +91,7 @@ NMSettingsConnection **nm_settings_get_connections_clone(NMSettings
                                                          gpointer         sort_data);
 
 gboolean nm_settings_add_connection(NMSettings                     *settings,
+                                    const char                     *plugin,
                                     NMConnection                   *connection,
                                     NMSettingsConnectionPersistMode persist_mode,
                                     NMSettingsConnectionAddReason   add_reason,
@@ -99,6 +101,7 @@ gboolean nm_settings_add_connection(NMSettings                     *settings,
 
 gboolean nm_settings_update_connection(NMSettings                      *self,
                                        NMSettingsConnection            *sett_conn,
+                                       const char                      *plugin_name,
                                        NMConnection                    *new_connection,
                                        NMSettingsConnectionPersistMode  persist_mode,
                                        NMSettingsConnectionIntFlags     sett_flags,
diff --git a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index 48c4cae9..551f2b91 100644
--- a/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/core/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -397,7 +397,9 @@ write_8021x_setting(NMConnection *connection,
     if (wired)
         svSetValueStr(ifcfg, "KEY_MGMT", "IEEE8021X");
 
-    /* EAP method */
+    /* EAP method
+     *
+     * FIXME(ifcfg-full-cycle): persist all values of eap-method. */
     if (nm_setting_802_1x_get_num_eap_methods(s_8021x)) {
         value = nm_setting_802_1x_get_eap_method(s_8021x, 0);
         if (value)
@@ -455,11 +457,21 @@ write_8021x_setting(NMConnection *connection,
             value = "allow-auth";
         else if (strcmp(value, "3") == 0)
             value = "allow-unauth allow-auth";
-        else
+        else {
+            /* FIXME(ifcfg-full-cycle): does not handle the value "0". */
             value = NULL;
+        }
     }
     svSetValueStr(ifcfg, "IEEE_8021X_FAST_PROVISIONING", value);
 
+    auth_flags = nm_setting_802_1x_get_phase1_auth_flags(s_8021x);
+    if (auth_flags != NM_SETTING_802_1X_AUTH_FLAGS_NONE) {
+        svSetValueEnum(ifcfg,
+                       "IEEE_8021X_PHASE1_AUTH_FLAGS",
+                       nm_setting_802_1x_auth_flags_get_type(),
+                       auth_flags);
+    }
+
     /* Phase2 auth methods */
     phase2_auth = g_string_new(NULL);
 
@@ -480,14 +492,6 @@ write_8021x_setting(NMConnection *connection,
         g_free(tmp);
     }
 
-    auth_flags = nm_setting_802_1x_get_phase1_auth_flags(s_8021x);
-    if (auth_flags != NM_SETTING_802_1X_AUTH_FLAGS_NONE) {
-        svSetValueEnum(ifcfg,
-                       "IEEE_8021X_PHASE1_AUTH_FLAGS",
-                       nm_setting_802_1x_auth_flags_get_type(),
-                       auth_flags);
-    }
-
     svSetValueStr(ifcfg,
                   "IEEE_8021X_INNER_AUTH_METHODS",
                   phase2_auth->len ? phase2_auth->str : NULL);
@@ -503,6 +507,8 @@ write_8021x_setting(NMConnection *connection,
     str = g_string_new(NULL);
     num = nm_setting_802_1x_get_num_altsubject_matches(s_8021x);
     for (i = 0; i < num; i++) {
+        /* FIXME(ifcfg-full-cycle): this cannot handle values with spaces, which
+         * are not rejected by nm_connection_verify(). */
         if (i > 0)
             g_string_append_c(str, ' ');
         match = nm_setting_802_1x_get_altsubject_match(s_8021x, i);
@@ -515,6 +521,8 @@ write_8021x_setting(NMConnection *connection,
     str = g_string_new(NULL);
     num = nm_setting_802_1x_get_num_phase2_altsubject_matches(s_8021x);
     for (i = 0; i < num; i++) {
+        /* FIXME(ifcfg-full-cycle): this cannot handle values with spaces, which
+         * are not rejected by nm_connection_verify(). */
         if (i > 0)
             g_string_append_c(str, ' ');
         match = nm_setting_802_1x_get_phase2_altsubject_match(s_8021x, i);
diff --git a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 7960ea2a..83e48b81 100644
--- a/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/core/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -221,6 +221,7 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c
             success = g_file_set_contents(expected, content_written, len_written, &error);
             nmtst_assert_success(success, error);
         } else {
+            NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
             g_error(
                 "The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set "
                 "NMTST_IFCFG_RH_UPDATE_EXPECTED=yes (or NM_TEST_REGENERATE=1) to update the files "
@@ -231,6 +232,7 @@ _assert_expected_content(NMConnection *connection, const char *filename, const c
                 len_expectd,
                 content_written,
                 content_expectd);
+            NM_PRAGMA_WARNING_REENABLE
         }
     }
 }
diff --git a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
index b4c30df7..11a8e416 100644
--- a/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
+++ b/src/core/settings/plugins/keyfile/tests/test-keyfile-settings.c
@@ -2635,8 +2635,11 @@ _escape_filename(gboolean with_extension, const char *filename, gboolean would_b
     g_assert(esc && esc[0]);
     g_assert(!strchr(esc, '/'));
 
-    if (nm_keyfile_utils_ignore_filename(esc, with_extension))
+    if (nm_keyfile_utils_ignore_filename(esc, with_extension)) {
+        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
         g_error("Escaping filename \"%s\" yielded \"%s\", but this is ignored", filename, esc);
+        NM_PRAGMA_WARNING_REENABLE
+    }
 }
 
 static void
diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c
index 96c23579..a4e5cc4d 100644
--- a/src/core/supplicant/nm-supplicant-config.c
+++ b/src/core/supplicant/nm-supplicant-config.c
@@ -805,6 +805,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
                                                    NMSettingWirelessSecurity    *setting,
                                                    NMSetting8021x               *setting_8021x,
                                                    const char                   *con_uuid,
+                                                   const char                   *mode,
                                                    guint32                       mtu,
                                                    NMSettingWirelessSecurityPmf  pmf,
                                                    NMSettingWirelessSecurityFils fils,
@@ -815,12 +816,20 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
     const char                   *key_mgmt, *auth_alg;
     const char                   *psk;
     gboolean                      set_pmf, wps_disabled;
+    gboolean                      is_ap;
 
     g_return_val_if_fail(NM_IS_SUPPLICANT_CONFIG(self), FALSE);
     g_return_val_if_fail(setting != NULL, FALSE);
     g_return_val_if_fail(con_uuid != NULL, FALSE);
     g_return_val_if_fail(!error || !*error, FALSE);
 
+    /* Currently wpa_supplicant doesn't support FT in AP mode. Even
+     * if it did, it  would require additional parameters as the nas
+     * identifier and the mobility domain. Therefore we disable all
+     * FT key-mgmts in AP mode.
+     */
+    is_ap = nm_streq0(mode, NM_SETTING_WIRELESS_MODE_AP);
+
     /* Check if we actually support FILS */
     if (!_get_capability(priv, NM_SUPPL_CAP_TYPE_FILS)) {
         if (fils == NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED) {
@@ -852,11 +861,39 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
             g_string_append(key_mgmt_conf, "WPA-PSK");
         if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF))
             g_string_append(key_mgmt_conf, " WPA-PSK-SHA256");
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
+        if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
             g_string_append(key_mgmt_conf, " FT-PSK");
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SAE)) {
+
+        /* For NM "key-mgmt=wpa-psk" doesn't strictly mean WPA1/wPA2 only,
+         * but also allows WPA3 (SAE), so that existing connections can
+         * benefit from the improved security when the AP gets upgraded.
+         *
+         * According to WPA3_Specification_v3.0 section 2.3, when operating
+         * in WPA3-Personal transition mode a STA:
+         *
+         * - should allow AKM suite selector: 00-0F-AC:6 (WPA-PSK-SHA256) to
+         *   be selected for an association;
+         * - shall negotiate PMF when associating to an AP using SAE.
+         *
+         * Those conditions are met when the interface has capabilities
+         * SAE, PMF, BIP.
+         *
+         * According to WPA3_Specification_v3.0 section 2.3, when operating
+         * in WPA3-Personal transition mode an AP:
+         *
+         * - shall set MFPC to 1, MFPR to 0.
+         *
+         * Therefore, do not operate in WPA3-Personal transition mode when PMF
+         * is set to disabled. This also provides a way to be compatible with
+         * some devices that are not fully compatible with WPA3-Personal
+         * transition mode.
+         */
+        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SAE)
+            && _get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)
+            && _get_capability(priv, NM_SUPPL_CAP_TYPE_BIP)
+            && (!is_ap || pmf != NM_SETTING_WIRELESS_SECURITY_PMF_DISABLE)) {
             g_string_append(key_mgmt_conf, " SAE");
-            if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
+            if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
                 g_string_append(key_mgmt_conf, " FT-SAE");
         }
 
@@ -864,13 +901,13 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
         pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED;
 
         g_string_append(key_mgmt_conf, "SAE");
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
+        if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT))
             g_string_append(key_mgmt_conf, " FT-SAE");
 
     } else if (nm_streq(key_mgmt, "wpa-eap")) {
         if (pmf != NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)
             g_string_append(key_mgmt_conf, "WPA-EAP");
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) {
+        if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) {
             g_string_append(key_mgmt_conf, " FT-EAP");
             if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
                 g_string_append(key_mgmt_conf, " FT-EAP-SHA384");
@@ -891,7 +928,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
         case NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL:
             if (_get_capability(priv, NM_SUPPL_CAP_TYPE_PMF)) {
                 g_string_append(key_mgmt_conf, " FILS-SHA256 FILS-SHA384");
-                if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) {
+                if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)) {
                     g_string_append(key_mgmt_conf, " FT-FILS-SHA256");
                     if (_get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
                         g_string_append(key_mgmt_conf, " FT-FILS-SHA384");
@@ -907,7 +944,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
         pmf = NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED;
 
         g_string_append(key_mgmt_conf, "WPA-EAP-SUITE-B-192");
-        if (_get_capability(priv, NM_SUPPL_CAP_TYPE_FT)
+        if (!is_ap && _get_capability(priv, NM_SUPPL_CAP_TYPE_FT)
             && _get_capability(priv, NM_SUPPL_CAP_TYPE_SHA384))
             g_string_append(key_mgmt_conf, " FT-EAP-SHA384");
     }
diff --git a/src/core/supplicant/nm-supplicant-config.h b/src/core/supplicant/nm-supplicant-config.h
index 349c310f..ee7f4dc8 100644
--- a/src/core/supplicant/nm-supplicant-config.h
+++ b/src/core/supplicant/nm-supplicant-config.h
@@ -51,6 +51,7 @@ gboolean nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
                                                             NMSettingWirelessSecurity *setting,
                                                             NMSetting8021x *setting_8021x,
                                                             const char     *con_uuid,
+                                                            const char     *mode,
                                                             guint32         mtu,
                                                             NMSettingWirelessSecurityPmf  pmf,
                                                             NMSettingWirelessSecurityFils fils,
diff --git a/src/core/supplicant/nm-supplicant-interface.c b/src/core/supplicant/nm-supplicant-interface.c
index d8276144..e502ae85 100644
--- a/src/core/supplicant/nm-supplicant-interface.c
+++ b/src/core/supplicant/nm-supplicant-interface.c
@@ -1225,8 +1225,10 @@ parse_capabilities(NMSupplicantInterface *self, GVariant *capabilities)
     const guint32                 old_max_scan_ssids   = priv->max_scan_ssids;
     gboolean                      have_ft              = FALSE;
     gboolean                      have_sae             = FALSE;
+    gboolean                      have_bip             = FALSE;
     gint32                        max_scan_ssids;
     const char                  **array;
+    guint                         i;
 
     nm_assert(capabilities && g_variant_is_of_type(capabilities, G_VARIANT_TYPE_VARDICT));
 
@@ -1236,12 +1238,28 @@ parse_capabilities(NMSupplicantInterface *self, GVariant *capabilities)
         g_free(array);
     }
 
+    if (g_variant_lookup(capabilities, "GroupMgmt", "^a&s", &array)) {
+        for (i = 0; array[i]; i++) {
+            if (NM_IN_STRSET(array[i],
+                             "aes-128-cmac",
+                             "bip-gmac-128",
+                             "bip-gmac-256",
+                             "bip-cmac-256")) {
+                have_bip = TRUE;
+                break;
+            }
+        }
+    }
+
     priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities,
                                                      NM_SUPPL_CAP_TYPE_FT,
                                                      have_ft ? NM_TERNARY_TRUE : NM_TERNARY_FALSE);
     priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities,
                                                      NM_SUPPL_CAP_TYPE_SAE,
                                                      have_sae ? NM_TERNARY_TRUE : NM_TERNARY_FALSE);
+    priv->iface_capabilities = NM_SUPPL_CAP_MASK_SET(priv->iface_capabilities,
+                                                     NM_SUPPL_CAP_TYPE_BIP,
+                                                     have_bip ? NM_TERNARY_TRUE : NM_TERNARY_FALSE);
 
     if (g_variant_lookup(capabilities, "Modes", "^a&s", &array)) {
         /* Setting p2p_capable might toggle _prop_p2p_available_get(). However,
@@ -1317,10 +1335,12 @@ _starting_check_ready(NMSupplicantInterface *self)
           " AP%c"
           " FT%c"
           " SAE%c"
+          " BIP%c"
           "",
           NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_AP),
           NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_FT),
-          NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_SAE));
+          NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_SAE),
+          NM_SUPPL_CAP_TO_CHAR(priv->iface_capabilities, NM_SUPPL_CAP_TYPE_BIP));
 
     /* Other global properties are set in constructed() because they don't
      * depend on interface capabilities. */
@@ -1362,6 +1382,7 @@ _get_capability(NMSupplicantInterfacePrivate *priv, NMSupplCapType type)
         }
         break;
     case NM_SUPPL_CAP_TYPE_SAE:
+    case NM_SUPPL_CAP_TYPE_BIP:
         nm_assert(NM_SUPPL_CAP_MASK_GET(priv->global_capabilities, type) == NM_TERNARY_DEFAULT);
         value = NM_SUPPL_CAP_MASK_GET(priv->iface_capabilities, type);
         break;
@@ -1395,10 +1416,13 @@ nm_supplicant_interface_get_capabilities(NMSupplicantInterface *self)
     caps = NM_SUPPL_CAP_MASK_SET(caps,
                                  NM_SUPPL_CAP_TYPE_SAE,
                                  _get_capability(priv, NM_SUPPL_CAP_TYPE_SAE));
+    caps = NM_SUPPL_CAP_MASK_SET(caps,
+                                 NM_SUPPL_CAP_TYPE_BIP,
+                                 _get_capability(priv, NM_SUPPL_CAP_TYPE_BIP));
 
     nm_assert(!NM_FLAGS_ANY(priv->iface_capabilities,
                             ~(NM_SUPPL_CAP_MASK_T_AP_MASK | NM_SUPPL_CAP_MASK_T_FT_MASK
-                              | NM_SUPPL_CAP_MASK_T_SAE_MASK)));
+                              | NM_SUPPL_CAP_MASK_T_SAE_MASK | NM_SUPPL_CAP_MASK_T_BIP_MASK)));
 
 #if NM_MORE_ASSERTS > 10
     {
diff --git a/src/core/supplicant/nm-supplicant-manager.c b/src/core/supplicant/nm-supplicant-manager.c
index 9de845f0..2ec7db23 100644
--- a/src/core/supplicant/nm-supplicant-manager.c
+++ b/src/core/supplicant/nm-supplicant-manager.c
@@ -920,6 +920,7 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data)
                     _caps_set(priv, NM_SUPPL_CAP_TYPE_PMF, NM_TERNARY_FALSE);
                     _caps_set(priv, NM_SUPPL_CAP_TYPE_FILS, NM_TERNARY_FALSE);
                     _caps_set(priv, NM_SUPPL_CAP_TYPE_SUITEB192, NM_TERNARY_FALSE);
+                    _caps_set(priv, NM_SUPPL_CAP_TYPE_WEP, NM_TERNARY_TRUE);
                     if (array) {
                         for (a = array; *a; a++) {
                             if (nm_streq(*a, "ap")) {
@@ -954,6 +955,10 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data)
                                 _caps_set(priv, NM_SUPPL_CAP_TYPE_SUITEB192, NM_TERNARY_TRUE);
                                 continue;
                             }
+                            if (nm_streq(*a, "wep_disabled")) {
+                                _caps_set(priv, NM_SUPPL_CAP_TYPE_WEP, NM_TERNARY_FALSE);
+                                continue;
+                            }
                         }
                     }
                 }
@@ -994,6 +999,7 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data)
           " FAST%c"
           " WFD%c"
           " SUITEB192%c"
+          " WEP%c"
           "",
           NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_AP),
           NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_PMF),
@@ -1004,7 +1010,8 @@ _dbus_get_capabilities_cb(GVariant *res, GError *error, gpointer user_data)
           NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_MESH),
           NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_FAST),
           NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_WFD),
-          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_SUITEB192));
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_SUITEB192),
+          NM_SUPPL_CAP_TO_CHAR(priv->capabilities, NM_SUPPL_CAP_TYPE_WEP));
 
     nm_assert(g_hash_table_size(priv->supp_ifaces) == 0);
     nm_assert(c_list_is_empty(&priv->supp_lst_head));
diff --git a/src/core/supplicant/nm-supplicant-types.h b/src/core/supplicant/nm-supplicant-types.h
index d5cf1bf7..1ae67e13 100644
--- a/src/core/supplicant/nm-supplicant-types.h
+++ b/src/core/supplicant/nm-supplicant-types.h
@@ -48,6 +48,10 @@ typedef enum {
     NM_SUPPL_CAP_TYPE_FAST,
     NM_SUPPL_CAP_TYPE_WFD,
     NM_SUPPL_CAP_TYPE_SUITEB192,
+    NM_SUPPL_CAP_TYPE_WEP,
+    NM_SUPPL_CAP_TYPE_BIP,
+    /* Note: if you're adding a capability here, log its presence at the
+     * bottom of _dbus_get_capabilities_cb(). */
     _NM_SUPPL_CAP_TYPE_NUM,
 } NMSupplCapType;
 
@@ -75,6 +79,8 @@ typedef enum {
     _NM_SUPPL_CAP_MASK_DEFINE(FT),
     _NM_SUPPL_CAP_MASK_DEFINE(SAE),
     _NM_SUPPL_CAP_MASK_DEFINE(SHA384),
+    _NM_SUPPL_CAP_MASK_DEFINE(WEP),
+    _NM_SUPPL_CAP_MASK_DEFINE(BIP),
 #undef _NM_SUPPL_CAP_MASK_DEFINE
 } NMSupplCapMask;
 
diff --git a/src/core/supplicant/tests/test-supplicant-config.c b/src/core/supplicant/tests/test-supplicant-config.c
index 53c5f70f..237b1a96 100644
--- a/src/core/supplicant/tests/test-supplicant-config.c
+++ b/src/core/supplicant/tests/test-supplicant-config.c
@@ -116,6 +116,7 @@ build_supplicant_config(NMConnection  *connection,
                                                                s_wsec,
                                                                s_8021x,
                                                                nm_connection_get_uuid(connection),
+                                                               nm_setting_wireless_get_mode(s_wifi),
                                                                mtu,
                                                                pmf,
                                                                fils,
diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c
index f61d66d9..b4e1c4d5 100644
--- a/src/core/tests/test-core.c
+++ b/src/core/tests/test-core.c
@@ -2319,10 +2319,25 @@ test_machine_id_read(void)
     char          machine_id_str[33];
     gpointer      logstate;
 
+    /* This unit test checks our functions to read /etc/machine-id. As
+     * the path name is not configurable (and the test does not setup
+     * a chroot/mountns), we read the actual file from the system. That
+     * is ugly, as the test depends on the system where it's running.
+     *
+     * Still, better a bad test, than no test. Patch welcome to fix this
+     * shortcoming.
+     *
+     * Also, if you have a sufficiently broken system, the unit test fails.
+     * In particular, if the machine-id file exists but does not contain
+     * a valid ID. Just don't have that. Fix your system. */
+
     logstate = nmtst_logging_disable(FALSE);
     /* If you run this test as root, without a valid /etc/machine-id,
      * the code will try to get the secret-key. That is a bit ugly,
-     * but no real problem. */
+     * but no real problem.
+     *
+     * The real answer is: don't run our unit tests as root. That's
+     * not the way to do it. */
     machine_id = nm_utils_machine_id_bin();
     nmtst_logging_reenable(logstate);
 
@@ -2338,9 +2353,15 @@ test_machine_id_read(void)
          * is invalid. Our machine-id is fake, and we have nothing to
          * compare against. */
 
-        /* NOTE: this test will fail, if you don't have /etc/machine-id,
-         * but a valid "LOCALSTATEDIR/lib/dbus/machine-id" file.
-         * Just don't do that. */
+        if (g_file_test(LOCALSTATEDIR "/lib/dbus/machine-id", G_FILE_TEST_EXISTS)) {
+            /* Hm. So systemd failed to read /etc/machine-id, but we may have the one from D-Bus.
+             * With LOCALSTATEDIR"/lib/dbus/machine-id", we don't really know whether we
+             * parsed that file. Assume we don't know and skip the test on this system. */
+            g_assert(!nm_utils_machine_id_is_fake());
+            return;
+        }
+
+        /* OK, in this case, our function should have generated a random machine ID. */
         g_assert(nm_utils_machine_id_is_fake());
     } else {
         g_assert(!nm_utils_machine_id_is_fake());
diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c
index 5501079e..924d98f1 100644
--- a/src/core/tests/test-l3cfg.c
+++ b/src/core/tests/test-l3cfg.c
@@ -382,13 +382,11 @@ test_l3cfg(gconstpointer test_data)
         nm_l3cfg_commit_type_register(l3cfg0, NM_L3_CFG_COMMIT_TYPE_UPDATE, NULL, "test1");
 
     if (!nmtst_get_rand_one_case_in(4)) {
-        commit_type_2 =
-            nm_l3cfg_commit_type_register(l3cfg0,
-                                          nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE,
-                                                            NM_L3_CFG_COMMIT_TYPE_ASSUME,
-                                                            NM_L3_CFG_COMMIT_TYPE_UPDATE),
-                                          NULL,
-                                          "test2");
+        commit_type_2 = nm_l3cfg_commit_type_register(
+            l3cfg0,
+            nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE, NM_L3_CFG_COMMIT_TYPE_UPDATE),
+            NULL,
+            "test2");
     } else
         commit_type_2 = NULL;
 
@@ -956,7 +954,7 @@ test_l3_ipv6ll(gconstpointer test_data)
                                        .steps_done = FALSE,
     };
     TestL3IPv6LLData *const tdata = &tdata_stack;
-    char                    sbuf1[sizeof(_nm_utils_to_string_buffer)];
+    char                    sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE];
     int                     r;
 
     _LOGD("test start (/l3-ipv6ll/%d)", TEST_IDX);
diff --git a/src/core/tests/test-utils.c b/src/core/tests/test-utils.c
index 2d2b7340..ad9950dd 100644
--- a/src/core/tests/test-utils.c
+++ b/src/core/tests/test-utils.c
@@ -212,6 +212,52 @@ test_hw_addr_gen_stable_eth(void)
                     "04:0D:CD:0C:9E:2C");
 }
 
+static void
+test_shorten_hostname(void)
+{
+    gs_free char *maxhost = NULL;
+    char         *hostname;
+
+#define do_test_shorten_hostname(_host, _exp_res, _exp_short) \
+    G_STMT_START                                              \
+    {                                                         \
+        gboolean      _res;                                   \
+        gs_free char *_short = NULL;                          \
+                                                              \
+        _res = nm_utils_shorten_hostname((_host), &_short);   \
+        g_assert_cmpint((_res), ==, (_exp_res));              \
+        g_assert_cmpstr(_short, ==, (_exp_short));            \
+    }                                                         \
+    G_STMT_END
+
+    /* 'maxhost' is the longest allowed hostname according to
+     * system configuration (`getconf HOST_NAME_MAX`). On Linux
+     * it's typically 64 characters, but POSIX allows up to
+     * 255 characters.
+     */
+    maxhost = g_strnfill(HOST_NAME_MAX, 'a');
+
+    do_test_shorten_hostname("name1", TRUE, NULL);
+
+    do_test_shorten_hostname("name1.example.com", TRUE, NULL);
+
+    do_test_shorten_hostname(maxhost, TRUE, NULL);
+
+    hostname = g_strdup_printf("%sbbb", maxhost);
+    do_test_shorten_hostname(hostname, TRUE, maxhost);
+    nm_clear_g_free(&hostname);
+
+    hostname = g_strdup_printf("%s.com", maxhost);
+    do_test_shorten_hostname(hostname, TRUE, maxhost);
+    nm_clear_g_free(&hostname);
+
+    hostname = g_strdup_printf("name1.%s.com", maxhost);
+    do_test_shorten_hostname(hostname, TRUE, "name1");
+    nm_clear_g_free(&hostname);
+
+    do_test_shorten_hostname(".name1", FALSE, NULL);
+}
+
 /*****************************************************************************/
 
 NMTST_DEFINE();
@@ -223,6 +269,7 @@ main(int argc, char **argv)
 
     g_test_add_func("/utils/stable_privacy", test_stable_privacy);
     g_test_add_func("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth);
+    g_test_add_func("/utils/shorten-hostname", test_shorten_hostname);
 
     return g_test_run();
 }