about summary refs log tree commit diff
path: root/src/core/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2026-02-22 00:40:03 +0100
committerMichael Biebl <biebl@debian.org>2026-02-22 00:40:03 +0100
commitccdb9117cca7141ee709afca8b25c966ff4fa18e (patch)
tree3b7377c95e1d4049c13dd9101ae923fee70ab91d /src/core/devices
parentd0ea10125cc04f55c1864451198d87fb801d1457 (diff)
parent067fb576988f685e83ac8b0ae690334aff547c85 (diff)
Update upstream source from tag 'upstream/1.56.0'
Update to upstream version '1.56.0'
with Debian dir 15fab61a7abf1fd4e2ba46785f96d935e87d32bb
Diffstat (limited to 'src/core/devices')
-rw-r--r--src/core/devices/nm-device-bridge.c2
-rw-r--r--src/core/devices/nm-device-ethernet.c16
-rw-r--r--src/core/devices/nm-device-macsec.c13
-rw-r--r--src/core/devices/nm-device-private.h3
-rw-r--r--src/core/devices/nm-device-utils.c4
-rw-r--r--src/core/devices/nm-device-wireguard.c61
-rw-r--r--src/core/devices/nm-device.c122
-rw-r--r--src/core/devices/nm-device.h3
-rw-r--r--src/core/devices/ovs/nm-ovsdb.c105
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c38
-rw-r--r--src/core/devices/wifi/nm-iwd-manager.c2
-rw-r--r--src/core/devices/wwan/nm-modem-broadband.c12
-rw-r--r--src/core/devices/wwan/nm-modem.c41
-rw-r--r--src/core/devices/wwan/nm-modem.h2
14 files changed, 313 insertions, 111 deletions
diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c
index 45cdd83f..6ca3737f 100644
--- a/src/core/devices/nm-device-bridge.c
+++ b/src/core/devices/nm-device-bridge.c
@@ -1066,7 +1066,7 @@ attach_port(NMDevice                  *device,
 
             plat_vlans = setting_vlans_to_platform(vlans, &num_vlans);
 
-            /* Since the link was just enportd, there are no existing VLANs
+            /* Since the link was just attached, there are no existing VLANs
              * (except for the default one) and so there's no need to flush. */
 
             if (plat_vlans
diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c
index db1245b3..11f691de 100644
--- a/src/core/devices/nm-device-ethernet.c
+++ b/src/core/devices/nm-device-ethernet.c
@@ -15,7 +15,6 @@
 #include <linux/if_ether.h>
 
 #include "NetworkManagerUtils.h"
-#include "NetworkManagerUtils.h"
 #include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-glib-aux/nm-uuid.h"
@@ -708,6 +707,9 @@ supplicant_iface_start(NMDeviceEthernet *self)
     NMDeviceEthernetPrivate            *priv   = NM_DEVICE_ETHERNET_GET_PRIVATE(self);
     gs_unref_object NMSupplicantConfig *config = NULL;
     gs_free_error GError               *error  = NULL;
+    NMActRequest                       *request;
+    NMActiveConnection                 *controller_ac;
+    NMDevice                           *controller;
 
     config = build_supplicant_config(self, &error);
     if (!config) {
@@ -722,6 +724,16 @@ supplicant_iface_start(NMDeviceEthernet *self)
     }
 
     nm_supplicant_interface_disconnect(priv->supplicant.iface);
+
+    /* Tell the supplicant in which bridge the interface is */
+    if ((request = nm_device_get_act_request(NM_DEVICE(self)))
+        && (controller_ac = nm_active_connection_get_controller(NM_ACTIVE_CONNECTION(request)))
+        && (controller = nm_active_connection_get_device(controller_ac))
+        && nm_device_get_device_type(controller) == NM_DEVICE_TYPE_BRIDGE) {
+        nm_supplicant_interface_set_bridge(priv->supplicant.iface, nm_device_get_iface(controller));
+    } else
+        nm_supplicant_interface_set_bridge(priv->supplicant.iface, NULL);
+
     nm_supplicant_interface_assoc(priv->supplicant.iface, config, supplicant_iface_assoc_cb, self);
     return TRUE;
 }
@@ -1901,7 +1913,7 @@ get_ip_method_auto(NMDevice *device, int addr_family)
         /* We cannot do DHCPv4 on a PPP link, instead we get "auto" IP addresses
          * by pppd. Return "manual" here, which has the suitable effect to a
          * (zero) manual addresses in addition. */
-        return NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
+        return NM_SETTING_IP4_CONFIG_METHOD_MANUAL;
     }
 
     return NM_SETTING_IP6_CONFIG_METHOD_AUTO;
diff --git a/src/core/devices/nm-device-macsec.c b/src/core/devices/nm-device-macsec.c
index 1659ea05..eb39cb2a 100644
--- a/src/core/devices/nm-device-macsec.c
+++ b/src/core/devices/nm-device-macsec.c
@@ -440,6 +440,9 @@ supplicant_iface_start(NMDeviceMacsec *self)
     NMDeviceMacsecPrivate              *priv   = NM_DEVICE_MACSEC_GET_PRIVATE(self);
     gs_unref_object NMSupplicantConfig *config = NULL;
     gs_free_error GError               *error  = NULL;
+    NMActRequest                       *request;
+    NMActiveConnection                 *controller_ac;
+    NMDevice                           *controller;
 
     config = build_supplicant_config(self, &error);
     if (!config) {
@@ -452,6 +455,16 @@ supplicant_iface_start(NMDeviceMacsec *self)
     }
 
     nm_supplicant_interface_disconnect(priv->supplicant.iface);
+
+    /* Tell the supplicant in which bridge the interface is */
+    if ((request = nm_device_get_act_request(NM_DEVICE(self)))
+        && (controller_ac = nm_active_connection_get_controller(NM_ACTIVE_CONNECTION(request)))
+        && (controller = nm_active_connection_get_device(controller_ac))
+        && nm_device_get_device_type(controller) == NM_DEVICE_TYPE_BRIDGE) {
+        nm_supplicant_interface_set_bridge(priv->supplicant.iface, nm_device_get_iface(controller));
+    } else
+        nm_supplicant_interface_set_bridge(priv->supplicant.iface, NULL);
+
     nm_supplicant_interface_assoc(priv->supplicant.iface, config, supplicant_iface_assoc_cb, self);
     return TRUE;
 }
diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h
index 2568d9f1..6d828597 100644
--- a/src/core/devices/nm-device-private.h
+++ b/src/core/devices/nm-device-private.h
@@ -115,9 +115,6 @@ gboolean nm_device_sysctl_ip_conf_set(NMDevice   *self,
 
 NML3ConfigData *nm_device_create_l3_config_data(NMDevice *self, NMIPConfigSource source);
 
-NML3ConfigData *nm_device_create_l3_config_data_from_connection(NMDevice     *self,
-                                                                NMConnection *connection);
-
 void nm_device_ip_method_dhcp4_start(NMDevice *self);
 
 void nm_device_ip_method_autoconf6_start(NMDevice *self);
diff --git a/src/core/devices/nm-device-utils.c b/src/core/devices/nm-device-utils.c
index 3e86e2e8..be1de3ea 100644
--- a/src/core/devices/nm-device-utils.c
+++ b/src/core/devices/nm-device-utils.c
@@ -143,7 +143,9 @@ NM_UTILS_LOOKUP_STR_DEFINE(
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_SETTINGS,
                              "unmanaged-user-settings"),
     NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_UNMANAGED_USER_UDEV, "unmanaged-user-udev"),
-    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NETWORKING_OFF, "networking-off"), );
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_NETWORKING_OFF, "networking-off"),
+    NM_UTILS_LOOKUP_STR_ITEM(NM_DEVICE_STATE_REASON_MODEM_NO_OPERATOR_CODE,
+                             "modem-no-operator-code"), );
 
 NM_UTILS_LOOKUP_STR_DEFINE(nm_device_mtu_source_to_string,
                            NMDeviceMtuSource,
diff --git a/src/core/devices/nm-device-wireguard.c b/src/core/devices/nm-device-wireguard.c
index 299e3b30..d98059ba 100644
--- a/src/core/devices/nm-device-wireguard.c
+++ b/src/core/devices/nm-device-wireguard.c
@@ -1672,6 +1672,57 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
     return ret;
 }
 
+static gboolean
+skip_peer_route(const NMIPAddr    *peer_addr,
+                guint              peer_addr_prefix,
+                int                addr_family,
+                NMSettingIPConfig *s_ip)
+{
+    guint num_addresses;
+    guint i;
+
+    /*
+     * If the allowed-ip subnet is already reachable on the interface via the
+     * prefix route of a static IP address, skip adding the peer route.
+     * We don't want to override the prefix route with a new one because the
+     * prefix route also specifies the correct source IP address.
+     *
+     * wg-quick does something similar here:
+     * https://git.zx2c4.com/wireguard-tools/tree/src/wg-quick/linux.bash?h=v1.0.20250521#n177
+     * The condition in wg-quick is a bit different because it checks that no
+     * duplicate route exists on the interface. We can't do exactly the same
+     * because here we don't have visibility on all the platform routes.
+     */
+
+    if (!s_ip)
+        return FALSE;
+
+    num_addresses = nm_setting_ip_config_get_num_addresses(s_ip);
+    for (i = 0; i < num_addresses; i++) {
+        NMIPAddr     setting_addr;
+        NMIPAddr     peer_addr_tmp;
+        guint        setting_prefix;
+        NMIPAddress *a;
+
+        peer_addr_tmp = *peer_addr;
+
+        a = nm_setting_ip_config_get_address(s_ip, i);
+        nm_ip_address_get_address_binary(a, &setting_addr);
+        setting_prefix = nm_ip_address_get_prefix(a);
+
+        if (setting_prefix > peer_addr_prefix)
+            continue;
+
+        nm_ip_addr_clear_host_address(addr_family, &setting_addr, NULL, setting_prefix);
+        nm_ip_addr_clear_host_address(addr_family, &peer_addr_tmp, NULL, setting_prefix);
+
+        if (nm_ip_addr_equal(addr_family, &peer_addr_tmp, &setting_addr))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
 static const NML3ConfigData *
 _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family)
 {
@@ -1738,6 +1789,7 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family)
 
         n_aips = nm_wireguard_peer_get_allowed_ips_len(peer);
         for (j = 0; j < n_aips; j++) {
+            NMSettingIPConfig *s_ip;
             NMPlatformIPXRoute rt;
             NMIPAddr           addrbin;
             const char        *aip;
@@ -1745,7 +1797,8 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family)
             int                prefix;
             guint32            rtable_coerced;
 
-            aip = nm_wireguard_peer_get_allowed_ip(peer, j, &valid);
+            aip  = nm_wireguard_peer_get_allowed_ip(peer, j, &valid);
+            s_ip = nm_connection_get_setting_ip_config(connection, addr_family);
 
             if (!valid || !nm_inet_parse_with_prefix_bin(addr_family, aip, NULL, &addrbin, &prefix))
                 continue;
@@ -1754,9 +1807,6 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family)
                 prefix = (addr_family == AF_INET) ? 32 : 128;
 
             if (prefix == 0) {
-                NMSettingIPConfig *s_ip;
-
-                s_ip = nm_connection_get_setting_ip_config(connection, addr_family);
                 if (nm_setting_ip_config_get_never_default(s_ip))
                     continue;
             }
@@ -1769,6 +1819,9 @@ _get_dev2_ip_config(NMDeviceWireGuard *self, int addr_family)
 
             nm_ip_addr_clear_host_address(addr_family, &addrbin, NULL, prefix);
 
+            if (skip_peer_route(&addrbin, prefix, addr_family, s_ip))
+                continue;
+
             rtable_coerced = route_table_coerced;
 
             if (prefix == 0 && auto_default_route_enabled) {
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index b0a3f780..46fb2339 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -113,6 +113,19 @@ typedef enum {
     RELEASE_PORT_TYPE_CONFIG_FORCE,
 } ReleasePortType;
 
+/**
+ * CleanupType:
+ * @CLEANUP_TYPE_KEEP: Cleanup internally but keep the real device's config. This is
+ *   often used when moving a partially managed device to "unmanaged" (but not only).
+ * @CLEANUP_TYPE_REMOVED: The device suddently disappeared. Cleanup internally but don't
+ *   make any action on the real device at all, as it no longer exists.
+ * @CLEANUP_TYPE_DECONFIGURE: Also deconfigure the real device. This is the typical
+ *   action when a connection or device is set to "down", or fully managed devices
+ *   moved to "unmanaged".
+ * @CLEANUP_TYPE_KEEP_REAPPLY: Like %CLEANUP_TYPE_KEEP, but indicating that it's a
+ *   reapply. Some special actions can be done if we're doing a reapply, like keeping
+ *   the existing DHCP lease, for example.
+ */
 typedef enum {
     CLEANUP_TYPE_KEEP,
     CLEANUP_TYPE_REMOVED,
@@ -265,11 +278,11 @@ typedef struct {
     NMDeviceIPState state;
     union {
         struct {
-            NMDnsMasqManager      *dnsmasq_manager;
-            NMNetnsSharedIPHandle *shared_ip_handle;
-            NMFirewallConfig      *firewall_config;
-            gulong                 dnsmasq_state_id;
-            const NML3ConfigData  *l3cd;
+            NMDnsMasqManager     *dnsmasq_manager;
+            NMNetnsIPReservation *ip_reservation;
+            NMFirewallConfig     *firewall_config;
+            gulong                dnsmasq_state_id;
+            const NML3ConfigData *l3cd;
         } v4;
         struct {
         } v6;
@@ -1411,14 +1424,12 @@ _prop_get_ipvx_routed_dns(NMDevice *self, int addr_family)
 }
 
 static NMSettingConnectionMdns
-_prop_get_connection_mdns(NMDevice *self)
+_prop_get_connection_mdns(NMDevice *self, NMConnection *connection)
 {
-    NMConnection           *connection;
     NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), NM_SETTING_CONNECTION_MDNS_DEFAULT);
 
-    connection = nm_device_get_applied_connection(self);
     if (connection)
         mdns = nm_setting_connection_get_mdns(nm_connection_get_setting_connection(connection));
     if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT)
@@ -1453,14 +1464,12 @@ _prop_get_sriov_preserve_on_down(NMDevice *self, NMSettingSriov *s_sriov)
 }
 
 static NMSettingConnectionLlmnr
-_prop_get_connection_llmnr(NMDevice *self)
+_prop_get_connection_llmnr(NMDevice *self, NMConnection *connection)
 {
-    NMConnection            *connection;
     NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), NM_SETTING_CONNECTION_LLMNR_DEFAULT);
 
-    connection = nm_device_get_applied_connection(self);
     if (connection)
         llmnr = nm_setting_connection_get_llmnr(nm_connection_get_setting_connection(connection));
     if (llmnr != NM_SETTING_CONNECTION_LLMNR_DEFAULT)
@@ -1475,14 +1484,12 @@ _prop_get_connection_llmnr(NMDevice *self)
 }
 
 static NMSettingConnectionDnsOverTls
-_prop_get_connection_dns_over_tls(NMDevice *self)
+_prop_get_connection_dns_over_tls(NMDevice *self, NMConnection *connection)
 {
-    NMConnection                 *connection;
     NMSettingConnectionDnsOverTls dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT);
 
-    connection = nm_device_get_applied_connection(self);
     if (connection)
         dns_over_tls = nm_setting_connection_get_dns_over_tls(
             nm_connection_get_setting_connection(connection));
@@ -1497,15 +1504,33 @@ _prop_get_connection_dns_over_tls(NMDevice *self)
                                                        NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT);
 }
 
+static NMSettingConnectionDnssec
+_prop_get_connection_dnssec(NMDevice *self, NMConnection *connection)
+{
+    NMSettingConnectionDnssec dnssec = NM_SETTING_CONNECTION_DNSSEC_DEFAULT;
+
+    g_return_val_if_fail(NM_IS_DEVICE(self), NM_SETTING_CONNECTION_DNSSEC_DEFAULT);
+
+    if (connection)
+        dnssec = nm_setting_connection_get_dnssec(nm_connection_get_setting_connection(connection));
+    if (dnssec != NM_SETTING_CONNECTION_DNSSEC_DEFAULT)
+        return dnssec;
+
+    return nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
+                                                       NM_CON_DEFAULT("connection.dnssec"),
+                                                       self,
+                                                       NM_SETTING_CONNECTION_DNSSEC_NO,
+                                                       NM_SETTING_CONNECTION_DNSSEC_YES,
+                                                       NM_SETTING_CONNECTION_DNSSEC_DEFAULT);
+}
+
 static NMMptcpFlags
-_prop_get_connection_mptcp_flags(NMDevice *self)
+_prop_get_connection_mptcp_flags(NMDevice *self, NMConnection *connection)
 {
-    NMConnection *connection;
-    NMMptcpFlags  mptcp_flags = NM_MPTCP_FLAGS_NONE;
+    NMMptcpFlags mptcp_flags = NM_MPTCP_FLAGS_NONE;
 
     g_return_val_if_fail(NM_IS_DEVICE(self), NM_MPTCP_FLAGS_DISABLED);
 
-    connection = nm_device_get_applied_connection(self);
     if (connection) {
         mptcp_flags =
             nm_setting_connection_get_mptcp_flags(nm_connection_get_setting_connection(connection));
@@ -2471,16 +2496,14 @@ _prop_get_ipv4_dhcp_vendor_class_identifier(NMDevice *self, NMSettingIP4Config *
 }
 
 static NMSettingIP6ConfigPrivacy
-_prop_get_ipv6_ip6_privacy(NMDevice *self)
+_prop_get_ipv6_ip6_privacy(NMDevice *self, NMConnection *connection)
 {
     NMSettingIP6ConfigPrivacy ip6_privacy;
-    NMConnection             *connection;
 
     g_return_val_if_fail(self, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 
     /* 1.) First look at the per-connection setting. If it is not -1 (unknown),
      * use it. */
-    connection = nm_device_get_applied_connection(self);
     if (connection) {
         NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(connection);
 
@@ -3613,11 +3636,12 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co
 
     l3cd =
         nm_l3_config_data_new_from_connection(nm_device_get_multi_index(self), ifindex, connection);
-    nm_l3_config_data_set_mdns(l3cd, _prop_get_connection_mdns(self));
-    nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self));
-    nm_l3_config_data_set_dns_over_tls(l3cd, _prop_get_connection_dns_over_tls(self));
-    nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self));
-    nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self));
+    nm_l3_config_data_set_mdns(l3cd, _prop_get_connection_mdns(self, connection));
+    nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self, connection));
+    nm_l3_config_data_set_dns_over_tls(l3cd, _prop_get_connection_dns_over_tls(self, connection));
+    nm_l3_config_data_set_dnssec(l3cd, _prop_get_connection_dnssec(self, connection));
+    nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self, connection));
+    nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self, connection));
     return l3cd;
 }
 
@@ -12974,7 +12998,7 @@ _dev_ipac6_start(NMDevice *self)
             .router_solicitations         = router_solicitations,
             .router_solicitation_interval = router_solicitation_interval,
             .ra_timeout                   = ra_timeout,
-            .ip6_privacy                  = _prop_get_ipv6_ip6_privacy(self),
+            .ip6_privacy                  = _prop_get_ipv6_ip6_privacy(self, connection),
         };
 
         priv->ipac6_data.ndisc = nm_lndp_ndisc_new(&config);
@@ -13161,7 +13185,6 @@ _dev_addrgenmode6_set(NMDevice *self, guint8 addr_gen_mode)
     if (!priv->addrgenmode6_data.previous_mode_has) {
         priv->addrgenmode6_data.previous_mode_has = TRUE;
         priv->addrgenmode6_data.previous_mode_val = cur_addr_gen_mode;
-        nm_assert(priv->addrgenmode6_data.previous_mode_val == cur_addr_gen_mode);
     }
 
     _LOGD_ip(AF_INET6,
@@ -13650,7 +13673,7 @@ _dev_ipsharedx_cleanup(NMDevice *self, int addr_family)
             nm_clear_pointer(&priv->ipshared_data_4.v4.firewall_config, nm_firewall_config_free);
         }
 
-        nm_clear_pointer(&priv->ipshared_data_4.v4.shared_ip_handle, nm_netns_shared_ip_release);
+        nm_clear_pointer(&priv->ipshared_data_4.v4.ip_reservation, nm_netns_ip_reservation_release);
         nm_clear_l3cd(&priv->ipshared_data_4.v4.l3cd);
 
         _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_SHARED_4, NULL, FALSE);
@@ -13684,13 +13707,14 @@ _dev_ipshared4_new_l3cd(NMDevice *self, NMConnection *connection, NMPlatformIP4A
 
         nm_ip_address_get_address_binary(user, &a);
         nm_platform_ip4_address_set_addr(&address, a, nm_ip_address_get_prefix(user));
-        nm_clear_pointer(&priv->ipshared_data_4.v4.shared_ip_handle, nm_netns_shared_ip_release);
+        nm_clear_pointer(&priv->ipshared_data_4.v4.ip_reservation, nm_netns_ip_reservation_release);
     } else {
-        if (!priv->ipshared_data_4.v4.shared_ip_handle)
-            priv->ipshared_data_4.v4.shared_ip_handle =
-                nm_netns_shared_ip_reserve(nm_device_get_netns(self));
+        if (!priv->ipshared_data_4.v4.ip_reservation)
+            priv->ipshared_data_4.v4.ip_reservation =
+                nm_netns_ip_reservation_get(nm_device_get_netns(self),
+                                            NM_NETNS_IP_RESERVATION_TYPE_SHARED4);
         nm_platform_ip4_address_set_addr(&address,
-                                         priv->ipshared_data_4.v4.shared_ip_handle->addr,
+                                         priv->ipshared_data_4.v4.ip_reservation->addr,
                                          24);
     }
 
@@ -14283,6 +14307,7 @@ can_reapply_change(NMDevice   *self,
                                                  NM_SETTING_CONNECTION_MDNS,
                                                  NM_SETTING_CONNECTION_LLMNR,
                                                  NM_SETTING_CONNECTION_DNS_OVER_TLS,
+                                                 NM_SETTING_CONNECTION_DNSSEC,
                                                  NM_SETTING_CONNECTION_MPTCP_FLAGS,
                                                  NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY);
     }
@@ -14541,6 +14566,7 @@ check_and_reapply_connection(NMDevice            *self,
                 NM_SETTING_CONNECTION_MDNS,
                 NM_SETTING_CONNECTION_LLMNR,
                 NM_SETTING_CONNECTION_DNS_OVER_TLS,
+                NM_SETTING_CONNECTION_DNSSEC,
                 NM_SETTING_CONNECTION_MPTCP_FLAGS)) {
             priv->ip_data_4.do_reapply = TRUE;
             priv->ip_data_6.do_reapply = TRUE;
@@ -17285,6 +17311,25 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
         /* controller: release ports */
         nm_device_controller_release_ports_all(self);
 
+        /* port: detach from controller */
+        if (priv->controller) {
+            nm_device_controller_release_port(priv->controller,
+                                              self,
+                                              RELEASE_PORT_TYPE_CONFIG,
+                                              reason);
+        }
+    }
+
+    /* port: mark no longer attached */
+    if (priv->controller && priv->ifindex > 0
+        && nm_platform_link_get_controller(nm_device_get_platform(self), priv->ifindex) <= 0) {
+        nm_device_controller_release_port(priv->controller,
+                                          self,
+                                          RELEASE_PORT_TYPE_NO_CONFIG,
+                                          NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+    }
+
+    if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
         /* Take out any entries in the routing table and any IP address the device had. */
         if (ifindex > 0) {
             NMPlatform *platform = nm_device_get_platform(self);
@@ -17308,15 +17353,6 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu
     if (ifindex > 0)
         nm_platform_ip4_dev_route_blacklist_set(nm_device_get_platform(self), ifindex, NULL);
 
-    /* port: mark no longer attached */
-    if (priv->controller && priv->ifindex > 0
-        && nm_platform_link_get_controller(nm_device_get_platform(self), priv->ifindex) <= 0) {
-        nm_device_controller_release_port(priv->controller,
-                                          self,
-                                          RELEASE_PORT_TYPE_NO_CONFIG,
-                                          NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
-    }
-
     lldp_setup(self, NM_TERNARY_FALSE);
 
     nm_device_update_metered(self);
diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h
index 8632944a..2f287953 100644
--- a/src/core/devices/nm-device.h
+++ b/src/core/devices/nm-device.h
@@ -853,4 +853,7 @@ void nm_routing_rules_sync(NMConnection *applied_connection,
                            NMDevice *self,
                            NMNetns  *netns);
 
+NML3ConfigData *nm_device_create_l3_config_data_from_connection(NMDevice     *self,
+                                                                NMConnection *connection);
+
 #endif /* __NETWORKMANAGER_DEVICE_H__ */
diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c
index 1c9484c4..cb67848e 100644
--- a/src/core/devices/ovs/nm-ovsdb.c
+++ b/src/core/devices/ovs/nm-ovsdb.c
@@ -1890,7 +1890,7 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
         == -1) {
         /* This doesn't really have to be an error; the key might
          * be missing if there really are no bridges present. */
-        _LOGD("Bad update: %s", json_error.text);
+        _LOGD("monitor: bad update: %s", json_error.text);
     }
 
     if (ovs) {
@@ -1936,12 +1936,12 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                                              &unused))
                 continue;
 
-            _LOGT("obj[iface:%s]: removed an '%s' interface: %s%s%s",
-                  key,
-                  ovs_interface->type,
+            _LOGT("monitor: %s: interface removed: type=%s, obj[iface:%s]%s%s",
                   ovs_interface->name,
+                  ovs_interface->type,
+                  key,
                   NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid,
-                                       ", ",
+                                       ", connection=",
                                        ovs_interface->connection_uuid,
                                        ""));
             _signal_emit_device_removed(self,
@@ -1989,17 +1989,18 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                 gs_free char *strtmp1 = NULL;
                 gs_free char *strtmp2 = NULL;
 
-                _LOGT("obj[iface:%s]: changed an '%s' interface: %s%s%s, external-ids=%s, "
-                      "other-config=%s",
-                      key,
-                      type,
-                      ovs_interface->name,
-                      NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid,
-                                           ", ",
-                                           ovs_interface->connection_uuid,
-                                           ""),
-                      (strtmp1 = _strdict_to_string(ovs_interface->external_ids)),
-                      (strtmp2 = _strdict_to_string(ovs_interface->other_config)));
+                _LOGT(
+                    "monitor: %s: interface changed: type=%s, obj[iface:%s]%s%s, external-ids=%s, "
+                    "other-config=%s",
+                    ovs_interface->name,
+                    type,
+                    key,
+                    NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid,
+                                         ", connection=",
+                                         ovs_interface->connection_uuid,
+                                         ""),
+                    (strtmp1 = _strdict_to_string(ovs_interface->external_ids)),
+                    (strtmp2 = _strdict_to_string(ovs_interface->other_config)));
             }
         } else {
             gs_free char *strtmp1 = NULL;
@@ -2015,17 +2016,17 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                 .other_config    = g_steal_pointer(&other_config_arr),
             };
             g_hash_table_add(priv->interfaces, ovs_interface);
-            _LOGT(
-                "obj[iface:%s]: added an '%s' interface: %s%s%s, external-ids=%s, other-config=%s",
-                key,
-                ovs_interface->type,
-                ovs_interface->name,
-                NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid,
-                                     ", ",
-                                     ovs_interface->connection_uuid,
-                                     ""),
-                (strtmp1 = _strdict_to_string(ovs_interface->external_ids)),
-                (strtmp2 = _strdict_to_string(ovs_interface->other_config)));
+            _LOGT("monitor: %s: interface added: type=%s, obj[iface:%s]%s%s, external-ids=%s, "
+                  "other-config=%s",
+                  ovs_interface->name,
+                  ovs_interface->type,
+                  key,
+                  NM_PRINT_FMT_QUOTED2(ovs_interface->connection_uuid,
+                                       ", connection=",
+                                       ovs_interface->connection_uuid,
+                                       ""),
+                  (strtmp1 = _strdict_to_string(ovs_interface->external_ids)),
+                  (strtmp2 = _strdict_to_string(ovs_interface->other_config)));
             _signal_emit_device_added(self,
                                       ovs_interface->name,
                                       NM_DEVICE_TYPE_OVS_INTERFACE,
@@ -2071,11 +2072,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
             if (!g_hash_table_steal_extended(priv->ports, &key, (gpointer *) &ovs_port, &unused))
                 continue;
 
-            _LOGT("obj[port:%s]: removed a port: %s%s%s",
-                  key,
+            _LOGT("monitor: %s: port removed: obj[port:%s]%s%s",
                   ovs_port->name,
+                  key,
                   NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid,
-                                       ", ",
+                                       ", connection=",
                                        ovs_port->connection_uuid,
                                        ""));
             _signal_emit_device_removed(self, ovs_port->name, NM_DEVICE_TYPE_OVS_PORT, NULL);
@@ -2122,15 +2123,16 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                 gs_free char *strtmp1 = NULL;
                 gs_free char *strtmp2 = NULL;
 
-                _LOGT("obj[port:%s]: changed a port: %s%s%s, external-ids=%s, other-config=%s",
-                      key,
-                      ovs_port->name,
-                      NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid,
-                                           ", ",
-                                           ovs_port->connection_uuid,
-                                           ""),
-                      (strtmp1 = _strdict_to_string(ovs_port->external_ids)),
-                      (strtmp2 = _strdict_to_string(ovs_port->other_config)));
+                _LOGT(
+                    "monitor: %s: port changed: obj[port:%s]%s%s, external-ids=%s, other-config=%s",
+                    ovs_port->name,
+                    key,
+                    NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid,
+                                         ", connection=",
+                                         ovs_port->connection_uuid,
+                                         ""),
+                    (strtmp1 = _strdict_to_string(ovs_port->external_ids)),
+                    (strtmp2 = _strdict_to_string(ovs_port->other_config)));
             }
         } else {
             gs_free char *strtmp1 = NULL;
@@ -2146,11 +2148,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                 .other_config    = g_steal_pointer(&other_config_arr),
             };
             g_hash_table_add(priv->ports, ovs_port);
-            _LOGT("obj[port:%s]: added a port: %s%s%s, external-ids=%s, other-config=%s",
-                  key,
+            _LOGT("monitor: %s: port added: obj[port:%s]%s%s, external-ids=%s, other-config=%s",
                   ovs_port->name,
+                  key,
                   NM_PRINT_FMT_QUOTED2(ovs_port->connection_uuid,
-                                       ", ",
+                                       ", connection=",
                                        ovs_port->connection_uuid,
                                        ""),
                   (strtmp1 = _strdict_to_string(ovs_port->external_ids)),
@@ -2192,11 +2194,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                                              &unused))
                 continue;
 
-            _LOGT("obj[bridge:%s]: removed a bridge: %s%s%s",
-                  key,
+            _LOGT("monitor: %s: bridge removed: obj[bridge:%s]%s%s",
                   ovs_bridge->name,
+                  key,
                   NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid,
-                                       ", ",
+                                       ", connection=",
                                        ovs_bridge->connection_uuid,
                                        ""));
             _signal_emit_device_removed(self, ovs_bridge->name, NM_DEVICE_TYPE_OVS_BRIDGE, NULL);
@@ -2243,11 +2245,12 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                 gs_free char *strtmp1 = NULL;
                 gs_free char *strtmp2 = NULL;
 
-                _LOGT("obj[bridge:%s]: changed a bridge: %s%s%s, external-ids=%s, other-config=%s",
-                      key,
+                _LOGT("monitor: %s: bridge changed: obj[bridge:%s]%s%s, external-ids=%s, "
+                      "other-config=%s",
                       ovs_bridge->name,
+                      key,
                       NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid,
-                                           ", ",
+                                           ", connection=",
                                            ovs_bridge->connection_uuid,
                                            ""),
                       (strtmp1 = _strdict_to_string(ovs_bridge->external_ids)),
@@ -2267,11 +2270,11 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg)
                 .other_config    = g_steal_pointer(&other_config_arr),
             };
             g_hash_table_add(priv->bridges, ovs_bridge);
-            _LOGT("obj[bridge:%s]: added a bridge: %s%s%s, external-ids=%s, other-config=%s",
-                  key,
+            _LOGT("monitor: %s: bridge added: obj[bridge:%s]%s%s, external-ids=%s, other-config=%s",
                   ovs_bridge->name,
+                  key,
                   NM_PRINT_FMT_QUOTED2(ovs_bridge->connection_uuid,
-                                       ", ",
+                                       ", connection=",
                                        ovs_bridge->connection_uuid,
                                        ""),
                   (strtmp1 = _strdict_to_string(ovs_bridge->external_ids)),
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index 148caa11..b41ed5e1 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -191,6 +191,9 @@ static void supplicant_iface_notify_p2p_available(NMSupplicantInterface *iface,
                                                   GParamSpec            *pspec,
                                                   NMDeviceWifi          *self);
 
+static void supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface,
+                                                        NMDeviceWifi          *self);
+
 static void periodic_update(NMDeviceWifi *self);
 
 static void ap_add_remove(NMDeviceWifi *self,
@@ -624,6 +627,10 @@ supplicant_interface_acquire_cb(NMSupplicantManager         *supplicant_manager,
                      "notify::" NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE,
                      G_CALLBACK(supplicant_iface_notify_p2p_available),
                      self);
+    g_signal_connect(priv->sup_iface,
+                     NM_SUPPLICANT_INTERFACE_PSK_MISMATCH,
+                     G_CALLBACK(supplicant_iface_notify_wpa_psk_mismatch_cb),
+                     self);
 
     _scan_notify_is_scanning(self);
 
@@ -2398,6 +2405,9 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi              *self,
 
     g_return_val_if_fail(new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE);
 
+    if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG)
+        return FALSE;
+
     req = nm_device_get_act_request(NM_DEVICE(self));
     g_return_val_if_fail(req != NULL, FALSE);
 
@@ -2841,6 +2851,34 @@ handle_auth_or_fail(NMDeviceWifi *self, NMActRequest *req, gboolean new_secrets)
     return TRUE;
 }
 
+static void
+supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDeviceWifi *self)
+{
+    NMDevice     *device = NM_DEVICE(self);
+    NMActRequest *req;
+    const char   *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+
+    if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG)
+        return;
+
+    _LOGI(LOGD_DEVICE | LOGD_WIFI,
+          "Activation: (wifi) psk mismatch reported by supplicant, asking for new key");
+
+    req = nm_device_get_act_request(NM_DEVICE(self));
+    g_return_if_fail(req != NULL);
+
+    nm_act_request_clear_secrets(req);
+
+    cleanup_association_attempt(self, TRUE);
+    nm_device_state_changed(device,
+                            NM_DEVICE_STATE_NEED_AUTH,
+                            NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+    wifi_secrets_get_secrets(self,
+                             setting_name,
+                             NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
+                                 | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
+}
+
 /*
  * supplicant_connection_timeout_cb
  *
diff --git a/src/core/devices/wifi/nm-iwd-manager.c b/src/core/devices/wifi/nm-iwd-manager.c
index 76a342e2..bb2e056d 100644
--- a/src/core/devices/wifi/nm-iwd-manager.c
+++ b/src/core/devices/wifi/nm-iwd-manager.c
@@ -684,7 +684,7 @@ iwd_config_write(GKeyFile              *config,
      * in the last few filename characters -- it cannot end in .open, .psk
      * or .8021x.
      */
-    return nm_utils_file_set_contents(filepath, data, length, 0600, times, NULL, error);
+    return nm_utils_file_set_contents(filepath, data, length, 0600, times, NULL, NULL, error);
 }
 
 static const char *
diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c
index 018e5306..4bd0a45c 100644
--- a/src/core/devices/wwan/nm-modem-broadband.c
+++ b/src/core/devices/wwan/nm-modem-broadband.c
@@ -508,8 +508,9 @@ find_gsm_apn_cb(const char   *apn,
 static gboolean
 try_create_connect_properties(NMModemBroadband *self)
 {
-    NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE(self);
-    ConnectContext          *ctx  = priv->ctx;
+    NMModemBroadbandPrivate *priv        = NM_MODEM_BROADBAND_GET_PRIVATE(self);
+    ConnectContext          *ctx         = priv->ctx;
+    NMDeviceStateReason      fail_reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
 
     if (MODEM_CAPS_3GPP(ctx->caps)) {
         NMSettingGsm *s_gsm = nm_connection_get_setting_gsm(ctx->connection);
@@ -522,7 +523,7 @@ try_create_connect_properties(NMModemBroadband *self)
             if (s_gsm)
                 network_id = nm_setting_gsm_get_network_id(s_gsm);
             if (!network_id) {
-                if (mm_modem_get_state(self->_priv.modem_iface) < MM_MODEM_STATE_REGISTERED)
+                if (mm_modem_get_state(self->_priv.modem_iface) != MM_MODEM_STATE_REGISTERED)
                     return FALSE;
                 modem_3gpp = mm_object_get_modem_3gpp(priv->modem_object);
                 network_id = mm_modem_3gpp_get_operator_code(modem_3gpp);
@@ -530,6 +531,7 @@ try_create_connect_properties(NMModemBroadband *self)
             if (!network_id) {
                 _LOGW("failed to connect '%s': unable to determine the network id",
                       nm_connection_get_id(ctx->connection));
+                fail_reason = NM_DEVICE_STATE_REASON_MODEM_NO_OPERATOR_CODE;
                 goto out;
             }
 
@@ -558,7 +560,7 @@ try_create_connect_properties(NMModemBroadband *self)
     }
 
 out:
-    nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+    nm_modem_emit_prepare_result(NM_MODEM(self), FALSE, fail_reason);
     connect_context_clear(self);
     return TRUE;
 }
@@ -1649,6 +1651,8 @@ nm_modem_broadband_new(GObject *object, GError **error)
                         driver,
                         NM_MODEM_OPERATOR_CODE,
                         operator_code,
+                        NM_MODEM_DEVICE_UID,
+                        mm_modem_get_device(modem_iface),
                         NULL);
 }
 
diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c
index c4852ea2..9d8f61c5 100644
--- a/src/core/devices/wwan/nm-modem.c
+++ b/src/core/devices/wwan/nm-modem.c
@@ -39,7 +39,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMModem,
                              PROP_IP_TYPES,
                              PROP_SIM_OPERATOR_ID,
                              PROP_OPERATOR_CODE,
-                             PROP_APN, );
+                             PROP_APN,
+                             PROP_DEVICE_UID, );
 
 enum {
     PPP_STATS,
@@ -78,6 +79,7 @@ typedef struct _NMModemPrivate {
     char           *sim_operator_id;
     char           *operator_code;
     char           *apn;
+    char           *device_uid;
 
     NMPPPManager *ppp_manager;
     NMPppMgr     *ppp_mgr;
@@ -618,6 +620,12 @@ nm_modem_get_apn(NMModem *self)
     return NM_MODEM_GET_PRIVATE(self)->apn;
 }
 
+const char *
+nm_modem_get_device_uid(NMModem *self)
+{
+    return NM_MODEM_GET_PRIVATE(self)->device_uid;
+}
+
 /*****************************************************************************/
 
 static void
@@ -1121,6 +1129,22 @@ nm_modem_check_connection_compatible(NMModem *self, NMConnection *connection, GE
             }
         }
 
+        str = nm_setting_gsm_get_device_uid(s_gsm);
+        if (str) {
+            if (!priv->device_uid) {
+                nm_utils_error_set_literal(error,
+                                           NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+                                           "GSM profile has device-uid, device does not");
+                return FALSE;
+            }
+            if (!nm_streq(str, priv->device_uid)) {
+                nm_utils_error_set_literal(error,
+                                           NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+                                           "device has differing device-uid than GSM profile");
+                return FALSE;
+            }
+        }
+
         /* SIM properties may not be available before the SIM is unlocked, so
          * to ensure that autoconnect works, the connection's SIM properties
          * are only compared if present on the device.
@@ -1644,6 +1668,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_APN:
         g_value_set_string(value, priv->apn);
         break;
+    case PROP_DEVICE_UID:
+        g_value_set_string(value, priv->device_uid);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -1699,6 +1726,10 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         /* construct-only */
         priv->operator_code = g_value_dup_string(value);
         break;
+    case PROP_DEVICE_UID:
+        /* construct-only */
+        priv->device_uid = g_value_dup_string(value);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
         break;
@@ -1758,6 +1789,7 @@ finalize(GObject *object)
     g_free(priv->sim_operator_id);
     g_free(priv->operator_code);
     g_free(priv->apn);
+    g_free(priv->device_uid);
 
     G_OBJECT_CLASS(nm_modem_parent_class)->finalize(object);
 }
@@ -1863,6 +1895,13 @@ nm_modem_class_init(NMModemClass *klass)
     obj_properties[PROP_APN] =
         g_param_spec_string(NM_MODEM_APN, "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
+    obj_properties[PROP_DEVICE_UID] =
+        g_param_spec_string(NM_MODEM_DEVICE_UID,
+                            "",
+                            "",
+                            NULL,
+                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
     signals[PPP_STATS] = g_signal_new(NM_MODEM_PPP_STATS,
diff --git a/src/core/devices/wwan/nm-modem.h b/src/core/devices/wwan/nm-modem.h
index 021d77b2..1f54b0b6 100644
--- a/src/core/devices/wwan/nm-modem.h
+++ b/src/core/devices/wwan/nm-modem.h
@@ -30,6 +30,7 @@
 #define NM_MODEM_SIM_OPERATOR_ID "sim-operator-id"
 #define NM_MODEM_OPERATOR_CODE   "operator-code"
 #define NM_MODEM_APN             "apn"
+#define NM_MODEM_DEVICE_UID      "device-uid"
 
 /* Signals */
 #define NM_MODEM_PPP_STATS      "ppp-stats"
@@ -154,6 +155,7 @@ const char *nm_modem_get_sim_id(NMModem *modem);
 const char *nm_modem_get_sim_operator_id(NMModem *modem);
 const char *nm_modem_get_operator_code(NMModem *modem);
 const char *nm_modem_get_apn(NMModem *modem);
+const char *nm_modem_get_device_uid(NMModem *modem);
 
 gboolean nm_modem_set_data_port(NMModem        *self,
                                 NMPlatform     *platform,