summary refs log tree commit diff
path: root/src/core/dhcp
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
committerMichael Biebl <biebl@debian.org>2023-02-10 11:50:34 +0100
commit1372848511cb896b80b51ed1a3e9606bd9816631 (patch)
tree674792b9385bdef935988894b45f06b2af39f88c /src/core/dhcp
parent40ec077ea305994c1fc2130add6787ca0c73e2c6 (diff)
New upstream version 1.42.0 upstream/1.42.0
Diffstat (limited to 'src/core/dhcp')
-rw-r--r--src/core/dhcp/nm-dhcp-client.c229
-rw-r--r--src/core/dhcp/nm-dhcp-client.h23
-rw-r--r--src/core/dhcp/nm-dhcp-dhclient.c2
-rw-r--r--src/core/dhcp/nm-dhcp-dhcpcanon.c2
-rw-r--r--src/core/dhcp/nm-dhcp-dhcpcd.c3
-rw-r--r--src/core/dhcp/nm-dhcp-listener.h2
-rw-r--r--src/core/dhcp/nm-dhcp-manager.h2
-rw-r--r--src/core/dhcp/nm-dhcp-nettools.c60
-rw-r--r--src/core/dhcp/nm-dhcp-options.c4
-rw-r--r--src/core/dhcp/nm-dhcp-systemd.c12
-rw-r--r--src/core/dhcp/nm-dhcp-utils.c30
-rw-r--r--src/core/dhcp/tests/test-dhcp-utils.c41
12 files changed, 211 insertions, 199 deletions
diff --git a/src/core/dhcp/nm-dhcp-client.c b/src/core/dhcp/nm-dhcp-client.c
index 805b42d2..600cb930 100644
--- a/src/core/dhcp/nm-dhcp-client.c
+++ b/src/core/dhcp/nm-dhcp-client.c
@@ -84,6 +84,7 @@ typedef struct _NMDhcpClientPrivate {
      * and is set from l3cd_next. */
     const NML3ConfigData *l3cd_curr;
 
+    GSource *previous_lease_timeout_source;
     GSource *no_lease_timeout_source;
     GSource *watch_source;
     GBytes  *effective_client_id;
@@ -269,6 +270,12 @@ nm_dhcp_client_create_options_dict(NMDhcpClient *self, gboolean static_keys)
     return options;
 }
 
+const NML3ConfigData *
+nm_dhcp_client_get_lease(NMDhcpClient *self)
+{
+    return NM_DHCP_CLIENT_GET_PRIVATE(self)->l3cd_curr;
+}
+
 /*****************************************************************************/
 
 gboolean
@@ -301,11 +308,16 @@ nm_dhcp_client_set_effective_client_id(NMDhcpClient *self, GBytes *client_id)
 /*****************************************************************************/
 
 static void
-_emit_notify(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data)
+_emit_notify_data(NMDhcpClient *self, const NMDhcpClientNotifyData *notify_data)
 {
     g_signal_emit(G_OBJECT(self), signals[SIGNAL_NOTIFY], 0, notify_data);
 }
 
+#define _emit_notify(self, _notify_type, ...) \
+    _emit_notify_data(                        \
+        (self),                               \
+        &((const NMDhcpClientNotifyData){.notify_type = (_notify_type), __VA_ARGS__}))
+
 /*****************************************************************************/
 
 static void
@@ -392,12 +404,7 @@ _no_lease_timeout(gpointer user_data)
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
 
     nm_clear_g_source_inst(&priv->no_lease_timeout_source);
-
-    _emit_notify(self,
-                 &((NMDhcpClientNotifyData){
-                     .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT,
-                 }));
-
+    _emit_notify(self, NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT);
     return G_SOURCE_CONTINUE;
 }
 
@@ -473,7 +480,7 @@ _acd_complete_on_idle_cb(gpointer user_data)
 }
 
 #define _acd_reglist_data_get(priv, idx) \
-    nm_g_array_index_p((priv)->v4.acd.reglist, AcdRegListData, (idx))
+    (&nm_g_array_index((priv)->v4.acd.reglist, AcdRegListData, (idx)))
 
 static guint
 _acd_reglist_data_find(NMDhcpClientPrivate *priv, in_addr_t addr_needle)
@@ -503,10 +510,10 @@ _acd_reglist_data_remove(NMDhcpClient *self, guint idx, gboolean do_log)
     reglist_data = _acd_reglist_data_get(priv, idx);
 
     if (do_log) {
-        char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+        char sbuf_addr[NM_INET_ADDRSTRLEN];
 
         _LOGD("acd: drop check for address %s (l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")",
-              _nm_utils_inet4_ntop(reglist_data->addr, sbuf_addr),
+              nm_inet4_ntop(reglist_data->addr, sbuf_addr),
               NM_HASH_OBFUSCATE_PTR(reglist_data->l3cd));
     }
 
@@ -585,7 +592,7 @@ static void
 _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state)
 {
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
-    char                 sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+    char                 sbuf_addr[NM_INET_ADDRSTRLEN];
     in_addr_t            addr;
     gboolean             addr_changed = FALSE;
     guint                idx;
@@ -624,7 +631,7 @@ _acd_check_lease(NMDhcpClient *self, NMOptionBool *out_acd_state)
 
     _LOGD("acd: %s check for address %s (timeout %u msec, l3cd " NM_HASH_OBFUSCATE_PTR_FMT ")",
           addr_changed ? "add" : "update",
-          _nm_utils_inet4_ntop(addr, sbuf_addr),
+          nm_inet4_ntop(addr, sbuf_addr),
           priv->config.v4.acd_timeout_msec,
           NM_HASH_OBFUSCATE_PTR(priv->l3cd_next));
 
@@ -711,7 +718,7 @@ gboolean
 _nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr)
 {
     NMDhcpClientPrivate   *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
-    char                   sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+    char                   sbuf_addr[NM_INET_ADDRSTRLEN];
     NMIPAddr               yiaddr;
     const NML3AcdAddrInfo *acd_info;
 
@@ -744,7 +751,7 @@ _nm_dhcp_client_accept_offer(NMDhcpClient *self, gconstpointer p_yiaddr)
         return TRUE;
 
     _LOGD("offered lease rejected: address %s failed ACD check",
-          _nm_utils_inet4_ntop(yiaddr.addr4, sbuf_addr));
+          nm_inet4_ntop(yiaddr.addr4, sbuf_addr));
 
     return FALSE;
 }
@@ -863,6 +870,9 @@ _nm_dhcp_client_notify(NMDhcpClient         *self,
         return;
     }
 
+    if (priv->l3cd_next)
+        nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
+
     nm_l3_config_data_reset(&priv->l3cd_curr, priv->l3cd_next);
 
     if (client_event_type == NM_DHCP_CLIENT_EVENT_TYPE_BOUND && priv->l3cd_curr
@@ -883,18 +893,12 @@ _nm_dhcp_client_notify(NMDhcpClient         *self,
 
     l3_cfg_notify_check_connected(self);
 
-    {
-        const NMDhcpClientNotifyData notify_data = {
-            .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
-            .lease_update =
-                {
-                    .l3cd     = priv->l3cd_curr,
-                    .accepted = !priv->l3cfg_notify.wait_dhcp_commit,
-                },
-        };
-
-        _emit_notify(self, &notify_data);
-    }
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
+                 .lease_update = {
+                     .l3cd     = priv->l3cd_curr,
+                     .accepted = !priv->l3cfg_notify.wait_dhcp_commit,
+                 });
 }
 
 static void
@@ -1010,12 +1014,10 @@ ipv6_lladdr_timeout(gpointer user_data)
 
     nm_clear_g_source_inst(&priv->v6.lladdr_timeout_source);
 
-    _emit_notify(
-        self,
-        &((NMDhcpClientNotifyData){
-            .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-            .it_looks_bad.reason = "timeout reached while waiting for an IPv6 link-local address",
-        }));
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                 .it_looks_bad.reason =
+                     "timeout reached while waiting for an IPv6 link-local address");
     return G_SOURCE_CONTINUE;
 }
 
@@ -1027,12 +1029,9 @@ ipv6_dad_timeout(gpointer user_data)
 
     nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
 
-    _emit_notify(
-        self,
-        &((NMDhcpClientNotifyData){
-            .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-            .it_looks_bad.reason = "timeout reached while waiting for IPv6 DAD to complete",
-        }));
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                 .it_looks_bad.reason = "timeout reached while waiting for IPv6 DAD to complete");
     return G_SOURCE_CONTINUE;
 }
 
@@ -1068,35 +1067,33 @@ ipv6_lladdr_find(NMDhcpClient *self)
 static void
 ipv6_tentative_addr_check(NMDhcpClient                *self,
                           GPtrArray                  **tentative,
-                          GPtrArray                  **missing,
+                          GPtrArray                  **dadfailed,
                           const NMPlatformIP6Address **valid)
 {
     NMDhcpClientPrivate        *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
     NMDedupMultiIter            iter;
     const NMPlatformIP6Address *addr;
-    NML3Cfg                    *l3cfg = priv->config.l3cfg;
+    NML3Cfg                    *l3cfg    = priv->config.l3cfg;
+    NMPlatform                 *platform = nm_l3cfg_get_platform(l3cfg);
+    int                         ifindex  = nm_l3cfg_get_ifindex(l3cfg);
 
     /* For each address in the lease, check whether it's tentative
-     * in platform. */
+     * or dad-failed in platform. */
     nm_l3_config_data_iter_ip6_address_for_each (&iter, priv->l3cd_curr, &addr) {
         const NMPlatformIP6Address *pladdr;
-        NMPObject                   needle;
-
-        nmp_object_stackinit_id_ip6_address(&needle, nm_l3cfg_get_ifindex(l3cfg), &addr->address);
-        pladdr = NMP_OBJECT_CAST_IP6_ADDRESS(nm_platform_lookup_obj(nm_l3cfg_get_platform(l3cfg),
-                                                                    NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-                                                                    &needle));
-        if (!pladdr) {
-            /* address removed: we assume that's because DAD failed */
-            if (missing) {
-                if (!*missing)
-                    *missing = g_ptr_array_new();
-                g_ptr_array_add(*missing, (gpointer) addr);
+
+        pladdr = nm_platform_ip6_address_get(platform, ifindex, &addr->address);
+        if ((pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_DADFAILED))
+            || (!pladdr && nm_platform_ip6_dadfailed_check(platform, ifindex, &addr->address))) {
+            if (dadfailed) {
+                if (!*dadfailed)
+                    *dadfailed = g_ptr_array_new();
+                g_ptr_array_add(*dadfailed, (gpointer) addr);
             }
             continue;
         }
 
-        if (NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE)
+        if (pladdr && NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_TENTATIVE)
             && !NM_FLAGS_HAS(pladdr->n_ifa_flags, IFA_F_OPTIMISTIC)) {
             if (tentative) {
                 if (!*tentative)
@@ -1105,6 +1102,9 @@ ipv6_tentative_addr_check(NMDhcpClient                *self,
             }
         }
 
+        /* Here the address is non-tentative or it was removed externally by the user.
+         * In both cases it has completed DAD.
+         */
         NM_SET_OUT(valid, addr);
     }
 }
@@ -1113,7 +1113,7 @@ static void
 l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcpClient *self)
 {
     NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
-    char                 sbuf_addr[NM_UTILS_INET_ADDRSTRLEN];
+    char                 sbuf_addr[NM_INET_ADDRSTRLEN];
 
     nm_assert(l3cfg == priv->config.l3cfg);
 
@@ -1133,10 +1133,8 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
 
             if (!NM_DHCP_CLIENT_GET_CLASS(self)->ip6_start(self, &addr->address, &error)) {
                 _emit_notify(self,
-                             &((NMDhcpClientNotifyData){
-                                 .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-                                 .it_looks_bad.reason = error->message,
-                             }));
+                             NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                             .it_looks_bad.reason = error->message);
             }
         }
     }
@@ -1144,13 +1142,13 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
     if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE
         && priv->l3cfg_notify.wait_ipv6_dad) {
         gs_unref_ptrarray GPtrArray *tentative = NULL;
-        gs_unref_ptrarray GPtrArray *missing   = NULL;
+        gs_unref_ptrarray GPtrArray *dadfailed = NULL;
         const NMPlatformIP6Address  *valid     = NULL;
         char                         str[NM_UTILS_TO_STRING_BUFFER_SIZE];
         guint                        i;
         gs_free_error GError        *error = NULL;
 
-        ipv6_tentative_addr_check(self, &tentative, &missing, &valid);
+        ipv6_tentative_addr_check(self, &tentative, &dadfailed, &valid);
         if (tentative) {
             for (i = 0; i < tentative->len; i++) {
                 _LOGD("still waiting DAD for address: %s",
@@ -1163,10 +1161,10 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
             nm_clear_g_source_inst(&priv->v6.dad_timeout_source);
             l3_cfg_notify_check_connected(self);
 
-            if (missing) {
-                for (i = 0; i < missing->len; i++) {
+            if (dadfailed) {
+                for (i = 0; i < dadfailed->len; i++) {
                     _LOGE("DAD failed for address: %s",
-                          nm_platform_ip6_address_to_string(missing->pdata[i], str, sizeof(str)));
+                          nm_platform_ip6_address_to_string(dadfailed->pdata[i], str, sizeof(str)));
                 }
             }
 
@@ -1176,22 +1174,19 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
 
                 if (_dhcp_client_accept(self, priv->l3cd_curr, &error)) {
                     _emit_notify(self,
-                                 &((NMDhcpClientNotifyData){
-                                     .notify_type  = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
-                                     .lease_update = {
-                                         .l3cd     = priv->l3cd_curr,
-                                         .accepted = TRUE,
-                                     }}));
+                                 NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
+                                 .lease_update = {
+                                     .l3cd     = priv->l3cd_curr,
+                                     .accepted = TRUE,
+                                 });
                 } else {
                     gs_free char *reason =
                         g_strdup_printf("error accepting lease: %s", error->message);
 
                     _LOGD("accept failed: %s", error->message);
                     _emit_notify(self,
-                                 &((NMDhcpClientNotifyData){
-                                     .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-                                     .it_looks_bad.reason = reason,
-                                 }));
+                                 NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                                 .it_looks_bad.reason = reason);
                 }
             } else {
                 _LOGD("decline the lease");
@@ -1266,20 +1261,17 @@ l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, NMDhcp
                 _LOGD("accept failed: %s", error->message);
 
                 _emit_notify(self,
-                             &((NMDhcpClientNotifyData){
-                                 .notify_type         = NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
-                                 .it_looks_bad.reason = reason,
-                             }));
+                             NM_DHCP_CLIENT_NOTIFY_TYPE_IT_LOOKS_BAD,
+                             .it_looks_bad.reason = reason, );
                 goto wait_dhcp_commit_done;
             }
 
-            _emit_notify(
-                self,
-                &((NMDhcpClientNotifyData){.notify_type  = NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
-                                           .lease_update = {
-                                               .l3cd     = priv->l3cd_curr,
-                                               .accepted = TRUE,
-                                           }}));
+            _emit_notify(self,
+                         NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE,
+                         .lease_update = {
+                             .l3cd     = priv->l3cd_curr,
+                             .accepted = TRUE,
+                         });
         }
     }
 wait_dhcp_commit_done:
@@ -1318,7 +1310,7 @@ wait_dhcp_commit_done:
             if (acd_state != NM_OPTION_BOOL_DEFAULT) {
                 _LOGD("acd: acd %s for %s",
                       acd_state ? "ready" : "conflict",
-                      _nm_utils_inet4_ntop(priv->v4.acd.addr, sbuf_addr));
+                      nm_inet4_ntop(priv->v4.acd.addr, sbuf_addr));
                 nm_l3cfg_commit_type_clear(priv->config.l3cfg, &priv->v4.acd.l3cfg_commit_handle);
                 priv->v4.acd.state       = acd_state;
                 priv->v4.acd.done_source = nm_g_idle_add_source(_acd_complete_on_idle_cb, self);
@@ -1327,6 +1319,19 @@ wait_dhcp_commit_done:
     }
 }
 
+static gboolean
+_previous_lease_timeout_cb(gpointer user_data)
+{
+    NMDhcpClient        *self = user_data;
+    NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
+
+    nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
+
+    _nm_dhcp_client_notify(self, NM_DHCP_CLIENT_EVENT_TYPE_TIMEOUT, NULL);
+
+    return G_SOURCE_CONTINUE;
+}
+
 gboolean
 nm_dhcp_client_start(NMDhcpClient *self, GError **error)
 {
@@ -1358,6 +1363,23 @@ nm_dhcp_client_start(NMDhcpClient *self, GError **error)
 
     _no_lease_timeout_schedule(self);
 
+    if (priv->config.previous_lease) {
+        /* We got passed a previous lease (during a reapply). For a few seconds, we
+         * will pretend that this is current lease. */
+        priv->l3cd_curr = g_steal_pointer(&priv->config.previous_lease);
+
+        /* Schedule a timeout for when we give up using this lease. Note
+         * that then we will emit a NM_DHCP_CLIENT_NOTIFY_TYPE_LEASE_UPDATE event
+         * and the lease is gone. Note that NMDevice ignores that and will
+         * keep using the lease.
+         *
+         * At the same time, we have _no_lease_timeout_schedule() ticking, when
+         * that expires, we will emit a NM_DHCP_CLIENT_NOTIFY_TYPE_NO_LEASE_TIMEOUT
+         * signal, which causes NMDevice to clear the lease. */
+        priv->previous_lease_timeout_source =
+            nm_g_timeout_add_seconds_source(15, _previous_lease_timeout_cb, self);
+    }
+
     if (IS_IPv4)
         return NM_DHCP_CLIENT_GET_CLASS(self)->ip4_start(self, error);
 
@@ -1438,6 +1460,8 @@ nm_dhcp_client_stop(NMDhcpClient *self, gboolean release)
     if (priv->is_stopped)
         return;
 
+    nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
+
     priv->is_stopped = TRUE;
 
     if (priv->invocation) {
@@ -1597,15 +1621,11 @@ maybe_add_option(NMDhcpClient *self, GHashTable *hash, const char *key, GVariant
 void
 nm_dhcp_client_emit_ipv6_prefix_delegated(NMDhcpClient *self, const NMPlatformIP6Address *prefix)
 {
-    const NMDhcpClientNotifyData notify_data = {
-        .notify_type = NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED,
-        .prefix_delegated =
-            {
-                .prefix = prefix,
-            },
-    };
-
-    _emit_notify(self, &notify_data);
+    _emit_notify(self,
+                 NM_DHCP_CLIENT_NOTIFY_TYPE_PREFIX_DELEGATED,
+                 .prefix_delegated = {
+                     .prefix = prefix,
+                 });
 }
 
 gboolean
@@ -1621,7 +1641,7 @@ nm_dhcp_client_handle_event(gpointer               unused,
     nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL;
     NMDhcpClientEventType                   client_event_type;
     NMPlatformIP6Address                    prefix = {
-                           0,
+        0,
     };
 
     g_return_val_if_fail(NM_IS_DHCP_CLIENT(self), FALSE);
@@ -1751,14 +1771,14 @@ nm_dhcp_client_server_id_is_rejected(NMDhcpClient *self, gconstpointer addr)
             in_addr_t mask;
             int       r_prefix;
 
-            if (!nm_utils_parse_inaddr_prefix_bin(AF_INET,
-                                                  priv->config.reject_servers[i],
-                                                  NULL,
-                                                  &r_addr,
-                                                  &r_prefix))
+            if (!nm_inet_parse_with_prefix_bin(AF_INET,
+                                               priv->config.reject_servers[i],
+                                               NULL,
+                                               &r_addr,
+                                               &r_prefix))
                 nm_assert_not_reached();
 
-            mask = _nm_utils_ip4_prefix_to_netmask(r_prefix < 0 ? 32 : r_prefix);
+            mask = nm_ip4_addr_netmask_from_prefix(r_prefix < 0 ? 32 : r_prefix);
             if ((addr4 & mask) == (r_addr & mask))
                 return TRUE;
         }
@@ -1783,6 +1803,8 @@ config_init(NMDhcpClientConfig *config, const NMDhcpClientConfig *src)
 
     g_object_ref(config->l3cfg);
 
+    nm_l3_config_data_ref_and_seal(config->previous_lease);
+
     nm_g_bytes_ref(config->hwaddr);
     nm_g_bytes_ref(config->bcast_hwaddr);
     nm_g_bytes_ref(config->vendor_class_identifier);
@@ -1843,6 +1865,8 @@ config_clear(NMDhcpClientConfig *config)
 {
     g_object_unref(config->l3cfg);
 
+    nm_clear_l3cd(&config->previous_lease);
+
     nm_clear_pointer(&config->hwaddr, g_bytes_unref);
     nm_clear_pointer(&config->bcast_hwaddr, g_bytes_unref);
     nm_clear_pointer(&config->vendor_class_identifier, g_bytes_unref);
@@ -1919,6 +1943,7 @@ dispose(GObject *object)
 
     watch_cleanup(self);
 
+    nm_clear_g_source_inst(&priv->previous_lease_timeout_source);
     nm_clear_g_source_inst(&priv->no_lease_timeout_source);
 
     if (!NM_IS_IPv4(priv->config.addr_family)) {
diff --git a/src/core/dhcp/nm-dhcp-client.h b/src/core/dhcp/nm-dhcp-client.h
index 6f403b61..f43770a0 100644
--- a/src/core/dhcp/nm-dhcp-client.h
+++ b/src/core/dhcp/nm-dhcp-client.h
@@ -14,7 +14,8 @@
 #define NM_DHCP_TIMEOUT_INFINITY ((guint32) G_MAXINT32)
 
 #define NM_TYPE_DHCP_CLIENT (nm_dhcp_client_get_type())
-#define NM_DHCP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient))
+#define NM_DHCP_CLIENT(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient))
 #define NM_DHCP_CLIENT_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_CLIENT, NMDhcpClientClass))
 #define NM_IS_DHCP_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_CLIENT))
@@ -91,9 +92,8 @@ typedef struct {
      * NMDhcpClient is supposed to run. */
     NML3Cfg *l3cfg;
 
-    /* FIXME(l3cfg:dhcp:previous-lease): most parameters of NMDhcpClient are immutable,
-     * so to change them (during reapply), we need to create and start
-     * a new NMDhcpClient instance.
+    /* Most parameters of NMDhcpClient are immutable, so to change them (during
+     * reapply), we need to create and start a new NMDhcpClient instance.
      *
      * However, while the restart happens, we want to stick to the previous
      * lease (if any). Allow the caller to provide such a previous lease,
@@ -120,7 +120,7 @@ typedef struct {
     const char *uuid;
 
     /* Set to reduce the number of broadcast packets when the
-	 * anycast hardware address of the DHCP service is known. */
+     * anycast hardware address of the DHCP service is known. */
     const char *anycast_address;
 
     /* The hostname or FQDN to send. */
@@ -224,18 +224,7 @@ const NMDhcpClientConfig *nm_dhcp_client_get_config(NMDhcpClient *self);
 
 pid_t nm_dhcp_client_get_pid(NMDhcpClient *self);
 
-static inline const NML3ConfigData *
-nm_dhcp_client_get_lease(NMDhcpClient *self)
-{
-    /* FIXME(l3cfg:dhcp:previous-lease): this function returns the currently
-     * valid, exposed lease.
-     *
-     * Note that NMDhcpClient should accept as construct argument a *previous* lease,
-     * and (if that lease is still valid), pretend that it's good to use. The point is
-     * so that during reapply we keep using the current address, until a new lease
-     * was received. */
-    return NULL;
-}
+const NML3ConfigData *nm_dhcp_client_get_lease(NMDhcpClient *self);
 
 void nm_dhcp_client_stop(NMDhcpClient *self, gboolean release);
 
diff --git a/src/core/dhcp/nm-dhcp-dhclient.c b/src/core/dhcp/nm-dhcp-dhclient.c
index e4f40d7c..35b2fb2e 100644
--- a/src/core/dhcp/nm-dhcp-dhclient.c
+++ b/src/core/dhcp/nm-dhcp-dhclient.c
@@ -43,7 +43,7 @@ _addr_family_to_path_part(int addr_family)
 
 #define NM_TYPE_DHCP_DHCLIENT (nm_dhcp_dhclient_get_type())
 #define NM_DHCP_DHCLIENT(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclient))
 #define NM_DHCP_DHCLIENT_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientClass))
 #define NM_IS_DHCP_DHCLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCLIENT))
diff --git a/src/core/dhcp/nm-dhcp-dhcpcanon.c b/src/core/dhcp/nm-dhcp-dhcpcanon.c
index 87e43fa8..cd42b692 100644
--- a/src/core/dhcp/nm-dhcp-dhcpcanon.c
+++ b/src/core/dhcp/nm-dhcp-dhcpcanon.c
@@ -18,7 +18,7 @@
 
 #define NM_TYPE_DHCP_DHCPCANON (nm_dhcp_dhcpcanon_get_type())
 #define NM_DHCP_DHCPCANON(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanon))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanon))
 #define NM_DHCP_DHCPCANON_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCPCANON, NMDhcpDhcpcanonClass))
 #define NM_IS_DHCP_DHCPCANON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCPCANON))
diff --git a/src/core/dhcp/nm-dhcp-dhcpcd.c b/src/core/dhcp/nm-dhcp-dhcpcd.c
index 280d9ce6..8d2f928b 100644
--- a/src/core/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/core/dhcp/nm-dhcp-dhcpcd.c
@@ -23,7 +23,8 @@
 /*****************************************************************************/
 
 #define NM_TYPE_DHCP_DHCPCD (nm_dhcp_dhcpcd_get_type())
-#define NM_DHCP_DHCPCD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcd))
+#define NM_DHCP_DHCPCD(obj) \
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcd))
 #define NM_DHCP_DHCPCD_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_DHCPCD, NMDhcpDhcpcdClass))
 #define NM_IS_DHCP_DHCPCD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_DHCPCD))
diff --git a/src/core/dhcp/nm-dhcp-listener.h b/src/core/dhcp/nm-dhcp-listener.h
index 5f3c952c..0818b371 100644
--- a/src/core/dhcp/nm-dhcp-listener.h
+++ b/src/core/dhcp/nm-dhcp-listener.h
@@ -8,7 +8,7 @@
 
 #define NM_TYPE_DHCP_LISTENER (nm_dhcp_listener_get_type())
 #define NM_DHCP_LISTENER(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener))
 #define NM_IS_DHCP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_LISTENER))
 #define NM_DHCP_LISTENER_GET_CLASS(obj) \
     (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListenerClass))
diff --git a/src/core/dhcp/nm-dhcp-manager.h b/src/core/dhcp/nm-dhcp-manager.h
index bbd68236..980a26c9 100644
--- a/src/core/dhcp/nm-dhcp-manager.h
+++ b/src/core/dhcp/nm-dhcp-manager.h
@@ -12,7 +12,7 @@
 
 #define NM_TYPE_DHCP_MANAGER (nm_dhcp_manager_get_type())
 #define NM_DHCP_MANAGER(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_MANAGER, NMDhcpManager))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_MANAGER, NMDhcpManager))
 #define NM_DHCP_MANAGER_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_MANAGER, NMDhcpManagerClass))
 #define NM_IS_DHCP_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_MANAGER))
diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c
index df88362e..9cdfd9aa 100644
--- a/src/core/dhcp/nm-dhcp-nettools.c
+++ b/src/core/dhcp/nm-dhcp-nettools.c
@@ -36,7 +36,7 @@
 
 #define NM_TYPE_DHCP_NETTOOLS (nm_dhcp_nettools_get_type())
 #define NM_DHCP_NETTOOLS(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettools))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettools))
 #define NM_DHCP_NETTOOLS_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_NETTOOLS, NMDhcpNettoolsClass))
 #define NM_IS_DHCP_NETTOOLS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_NETTOOLS))
@@ -143,12 +143,12 @@ lease_option_consume_route(const uint8_t **datap,
         if (!nm_dhcp_lease_data_consume_in_addr(&data, &n_data, &dest))
             return FALSE;
 
-        plen = _nm_utils_ip4_get_default_prefix0(dest);
+        plen = nm_ip4_addr_get_default_prefix0(dest);
         if (plen == 0)
             return FALSE;
     }
 
-    dest = nm_utils_ip4_address_clear_host_address(dest, plen);
+    dest = nm_ip4_addr_clear_host_address(dest, plen);
 
     if (!nm_dhcp_lease_data_consume_in_addr(&data, &n_data, &gateway))
         return FALSE;
@@ -239,16 +239,16 @@ lease_parse_address(NMDhcpNettools    *self /* for logging context only */,
 
     r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_SUBNET_MASK, &l_data, &l_data_len);
     if (r == N_DHCP4_E_UNSET) {
-        char str1[NM_UTILS_INET_ADDRSTRLEN];
-        char str2[NM_UTILS_INET_ADDRSTRLEN];
+        char str1[NM_INET_ADDRSTRLEN];
+        char str2[NM_INET_ADDRSTRLEN];
 
         /* Some DHCP servers may not set the subnet-mask (issue#1037).
          * Do the same as the dhclient plugin and use a default. */
-        a_plen    = _nm_utils_ip4_get_default_prefix(a_address.s_addr);
-        a_netmask = _nm_utils_ip4_prefix_to_netmask(a_plen);
+        a_plen    = nm_ip4_addr_get_default_prefix(a_address.s_addr);
+        a_netmask = nm_ip4_addr_netmask_from_prefix(a_plen);
         _LOGT("missing subnet mask (option 1). Guess %s based on IP address %s",
-              _nm_utils_inet4_ntop(a_netmask, str1),
-              _nm_utils_inet4_ntop(a_address.s_addr, str2));
+              nm_inet4_ntop(a_netmask, str1),
+              nm_inet4_ntop(a_address.s_addr, str2));
     } else {
         if (r != 0
             || !nm_dhcp_lease_data_parse_in_addr(l_data,
@@ -261,8 +261,8 @@ lease_parse_address(NMDhcpNettools    *self /* for logging context only */,
                                        "could not get netmask from lease");
             return FALSE;
         }
-        a_plen    = _nm_utils_ip4_netmask_to_prefix(a_netmask);
-        a_netmask = _nm_utils_ip4_prefix_to_netmask(a_plen);
+        a_plen    = nm_ip4_addr_netmask_to_prefix(a_netmask);
+        a_netmask = nm_ip4_addr_netmask_from_prefix(a_plen);
     }
 
     nm_dhcp_option_add_option_in_addr(options,
@@ -335,25 +335,25 @@ lease_parse_address_list(NDhcp4ClientLease       *lease,
     nm_str_buf_reset(sbuf);
 
     for (; l_data_len > 0; l_data_len -= 4, l_data += 4) {
-        char            addr_str[NM_UTILS_INET_ADDRSTRLEN];
+        char            addr_str[NM_INET_ADDRSTRLEN];
         const in_addr_t addr = unaligned_read_ne32(l_data);
 
         nm_str_buf_append_required_delimiter(sbuf, ' ');
-        nm_str_buf_append(sbuf, _nm_utils_inet4_ntop(addr, addr_str));
+        nm_str_buf_append(sbuf, nm_inet4_ntop(addr, addr_str));
 
         switch (option) {
         case NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER:
-            if (addr == 0 || nm_utils_ip4_address_is_loopback(addr)) {
+            if (addr == 0 || nm_ip4_addr_is_loopback(addr)) {
                 /* Skip localhost addresses, like also networkd does.
                  * See https://github.com/systemd/systemd/issues/4524. */
                 nm_dhcp_lease_log_invalid_option(iface,
                                                  AF_INET,
                                                  option,
                                                  "address %s is ignored",
-                                                 _nm_utils_inet4_ntop(addr, addr_str));
+                                                 nm_inet4_ntop(addr, addr_str));
                 continue;
             }
-            nm_l3_config_data_add_nameserver(l3cd, AF_INET, &addr);
+            nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &addr, NULL);
             break;
         case NM_DHCP_OPTION_DHCP4_NIS_SERVERS:
             nm_l3_config_data_add_nis_server(l3cd, addr);
@@ -378,8 +378,8 @@ lease_parse_routes(NDhcp4ClientLease *lease,
                    GHashTable        *options,
                    NMStrBuf          *sbuf)
 {
-    char          dest_str[NM_UTILS_INET_ADDRSTRLEN];
-    char          gateway_str[NM_UTILS_INET_ADDRSTRLEN];
+    char          dest_str[NM_INET_ADDRSTRLEN];
+    char          gateway_str[NM_INET_ADDRSTRLEN];
     in_addr_t     dest;
     in_addr_t     gateway;
     uint8_t       plen;
@@ -412,8 +412,8 @@ lease_parse_routes(NDhcp4ClientLease *lease,
 
         nm_str_buf_reset(sbuf);
         while (lease_option_consume_route(&l_data, &l_data_len, TRUE, &dest, &plen, &gateway)) {
-            _nm_utils_inet4_ntop(dest, dest_str);
-            _nm_utils_inet4_ntop(gateway, gateway_str);
+            nm_inet4_ntop(dest, dest_str);
+            nm_inet4_ntop(gateway, gateway_str);
 
             nm_str_buf_append_required_delimiter(sbuf, ' ');
             nm_str_buf_append_printf(sbuf, "%s/%d %s", dest_str, (int) plen, gateway_str);
@@ -454,8 +454,8 @@ lease_parse_routes(NDhcp4ClientLease *lease,
         nm_str_buf_reset(sbuf);
 
         while (lease_option_consume_route(&l_data, &l_data_len, FALSE, &dest, &plen, &gateway)) {
-            _nm_utils_inet4_ntop(dest, dest_str);
-            _nm_utils_inet4_ntop(gateway, gateway_str);
+            nm_inet4_ntop(dest, dest_str);
+            nm_inet4_ntop(gateway, gateway_str);
 
             nm_str_buf_append_required_delimiter(sbuf, ' ');
             nm_str_buf_append_printf(sbuf, "%s/%d %s", dest_str, (int) plen, gateway_str);
@@ -501,7 +501,7 @@ lease_parse_routes(NDhcp4ClientLease *lease,
 
         while (nm_dhcp_lease_data_consume_in_addr(&l_data, &l_data_len, &gateway)) {
             nm_str_buf_append_required_delimiter(sbuf, ' ');
-            nm_str_buf_append(sbuf, _nm_utils_inet4_ntop(gateway, gateway_str));
+            nm_str_buf_append(sbuf, nm_inet4_ntop(gateway, gateway_str));
 
             if (gateway == 0) {
                 /* silently skip 0.0.0.0 */
@@ -868,7 +868,7 @@ lease_save(NMDhcpNettools *self, NDhcp4ClientLease *lease, const char *lease_fil
 {
     struct in_addr           a_address;
     nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE);
-    char                     addr_str[NM_UTILS_INET_ADDRSTRLEN];
+    char                     addr_str[NM_INET_ADDRSTRLEN];
     gs_free_error GError    *error = NULL;
 
     nm_assert(lease);
@@ -879,9 +879,7 @@ lease_save(NMDhcpNettools *self, NDhcp4ClientLease *lease, const char *lease_fil
         return;
 
     nm_str_buf_append(&sbuf, "# This is private data. Do not parse.\n");
-    nm_str_buf_append_printf(&sbuf,
-                             "ADDRESS=%s\n",
-                             _nm_utils_inet4_ntop(a_address.s_addr, addr_str));
+    nm_str_buf_append_printf(&sbuf, "ADDRESS=%s\n", nm_inet4_ntop(a_address.s_addr, addr_str));
 
     if (!g_file_set_contents(lease_file, nm_str_buf_get_str_unsafe(&sbuf), sbuf.len, &error))
         _LOGW("error saving lease to %s: %s", lease_file, error->message);
@@ -966,7 +964,7 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event)
 
         if (nm_dhcp_client_server_id_is_rejected(NM_DHCP_CLIENT(self), &server_id)) {
             _LOGD("server-id %s is in the reject-list, ignoring",
-                  nm_utils_inet_ntop(AF_INET, &server_id, addr_str));
+                  nm_inet_ntop(AF_INET, &server_id, addr_str));
             return;
         }
 
@@ -976,8 +974,8 @@ dhcp4_event_handle(NMDhcpNettools *self, NDhcp4ClientEvent *event)
         }
 
         _LOGT("selecting offered lease from %s for %s",
-              _nm_utils_inet4_ntop(server_id.s_addr, addr_str),
-              _nm_utils_inet4_ntop(yiaddr.s_addr, addr_str2));
+              nm_inet4_ntop(server_id.s_addr, addr_str),
+              nm_inet4_ntop(yiaddr.s_addr, addr_str2));
 
         r = n_dhcp4_client_lease_select(event->offer.lease);
 
@@ -1340,7 +1338,7 @@ ip4_start(NMDhcpClient *client, GError **error)
                                    NULL);
         nm_parse_env_file(contents, "ADDRESS", &s_addr);
         if (s_addr)
-            nm_utils_parse_inaddr_bin(AF_INET, s_addr, NULL, &last_addr);
+            nm_inet_parse_bin(AF_INET, s_addr, NULL, &last_addr);
     }
 
     if (last_addr.s_addr) {
diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c
index a5bb06d1..d95fe016 100644
--- a/src/core/dhcp/nm-dhcp-options.c
+++ b/src/core/dhcp/nm-dhcp-options.c
@@ -438,9 +438,9 @@ nm_dhcp_option_add_option_in_addr(GHashTable *options,
                                   guint       option,
                                   in_addr_t   value)
 {
-    char sbuf[NM_UTILS_INET_ADDRSTRLEN];
+    char sbuf[NM_INET_ADDRSTRLEN];
 
-    nm_dhcp_option_add_option(options, addr_family, option, _nm_utils_inet4_ntop(value, sbuf));
+    nm_dhcp_option_add_option(options, addr_family, option, nm_inet4_ntop(value, sbuf));
 }
 
 void
diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c
index 7ce15d30..10990822 100644
--- a/src/core/dhcp/nm-dhcp-systemd.c
+++ b/src/core/dhcp/nm-dhcp-systemd.c
@@ -30,7 +30,7 @@
 
 #define NM_TYPE_DHCP_SYSTEMD (nm_dhcp_systemd_get_type())
 #define NM_DHCP_SYSTEMD(obj) \
-    (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemd))
+    (_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemd))
 #define NM_DHCP_SYSTEMD_CLASS(klass) \
     (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_DHCP_SYSTEMD, NMDhcpSystemdClass))
 #define NM_IS_DHCP_SYSTEMD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_DHCP_SYSTEMD))
@@ -74,7 +74,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
     gs_unref_hashtable GHashTable          *options = NULL;
     struct in6_addr                         tmp_addr;
     const struct in6_addr                  *dns;
-    char                                    addr_str[NM_UTILS_INET_ADDRSTRLEN];
+    char                                    addr_str[NM_INET_ADDRSTRLEN];
     char                                  **domains;
     char                                  **ntp_fqdns;
     const struct in6_addr                  *ntp_addrs;
@@ -107,7 +107,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
 
             nm_l3_config_data_add_address_6(l3cd, &address);
 
-            _nm_utils_inet6_ntop(&tmp_addr, addr_str);
+            nm_inet6_ntop(&tmp_addr, addr_str);
             g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
 
             has_any_addresses = TRUE;
@@ -133,9 +133,9 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
     if (num > 0) {
         nm_gstring_prepare(&str);
         for (i = 0; i < num; i++) {
-            _nm_utils_inet6_ntop(&dns[i], addr_str);
+            nm_inet6_ntop(&dns[i], addr_str);
             g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
-            nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &dns[i]);
+            nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET6, &dns[i], NULL);
         }
         nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_DNS_SERVERS, str->str);
     }
@@ -170,7 +170,7 @@ lease_to_ip6_config(NMDhcpSystemd *self, sd_dhcp6_lease *lease, gint32 ts, GErro
     num = sd_dhcp6_lease_get_ntp_addrs(lease, &ntp_addrs);
     if (num > 0) {
         for (i = 0; i < num; i++) {
-            _nm_utils_inet6_ntop(&ntp_addrs[i], addr_str);
+            nm_inet6_ntop(&ntp_addrs[i], addr_str);
             g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
         }
     }
diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c
index 1bffb3c4..ca1c0482 100644
--- a/src/core/dhcp/nm-dhcp-utils.c
+++ b/src/core/dhcp/nm-dhcp-utils.c
@@ -94,7 +94,7 @@ ip4_process_dhcpcd_rfc3442_routes(const char     *iface,
                 l3cd,
                 &((const NMPlatformIP4Route){
                     .rt_source  = NM_IP_CONFIG_SOURCE_DHCP,
-                    .network    = nm_utils_ip4_address_clear_host_address(rt_addr, rt_cidr),
+                    .network    = nm_ip4_addr_clear_host_address(rt_addr, rt_cidr),
                     .plen       = rt_cidr,
                     .gateway    = rt_route,
                     .pref_src   = address,
@@ -139,7 +139,7 @@ process_dhclient_rfc3442_route(const char *const **p_octets, NMPlatformIP4Route
         str_addr = g_strjoin(".", addr[0], addr[1], addr[2], addr[3], NULL);
         if (inet_pton(AF_INET, str_addr, &tmp_addr) <= 0)
             return FALSE;
-        v_network = nm_utils_ip4_address_clear_host_address(tmp_addr, v_plen);
+        v_network = nm_ip4_addr_clear_host_address(tmp_addr, v_plen);
     }
 
     next_hop = g_strjoin(".", o[0], o[1], o[2], o[3], NULL);
@@ -203,9 +203,9 @@ ip4_process_dhclient_rfc3442_routes(const char     *iface,
             _LOG2I(LOGD_DHCP4,
                    iface,
                    "  classless static route %s/%d gw %s",
-                   _nm_utils_inet4_ntop(route.network, b1),
+                   nm_inet4_ntop(route.network, b1),
                    route.plen,
-                   _nm_utils_inet4_ntop(route.gateway, b2));
+                   nm_inet4_ntop(route.gateway, b2));
         }
     }
 
@@ -324,8 +324,8 @@ process_classful_routes(const char     *iface,
          * The Static Routes option (option 33) does not provide a subnet mask
          * for each route - it is assumed that the subnet mask is implicit in
          * whatever network number is specified in each route entry */
-        route.plen = _nm_utils_ip4_get_default_prefix(rt_addr);
-        if (rt_addr & ~_nm_utils_ip4_prefix_to_netmask(route.plen)) {
+        route.plen = nm_ip4_addr_get_default_prefix(rt_addr);
+        if (rt_addr & ~nm_ip4_addr_netmask_from_prefix(route.plen)) {
             /* RFC 943: target not "this network"; using host routing */
             route.plen = 32;
         }
@@ -337,7 +337,7 @@ process_classful_routes(const char     *iface,
         route.metric_any    = TRUE;
         route.metric        = 0;
 
-        route.network = nm_utils_ip4_address_clear_host_address(route.network, route.plen);
+        route.network = nm_ip4_addr_clear_host_address(route.network, route.plen);
 
         nm_l3_config_data_add_route_4(l3cd, &route);
 
@@ -400,7 +400,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
     gboolean                                gateway_has = FALSE;
     guint32                                 gateway     = 0;
     guint8                                  plen        = 0;
-    char                                    sbuf[NM_UTILS_INET_ADDRSTRLEN];
+    char                                    sbuf[NM_INET_ADDRSTRLEN];
     guint32                                 now;
 
     g_return_val_if_fail(options != NULL, NULL);
@@ -414,7 +414,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
     };
 
     str = g_hash_table_lookup(options, "ip_address");
-    if (!str || !nm_utils_parse_inaddr_bin(AF_INET, str, NULL, &addr))
+    if (!str || !nm_inet_parse_bin(AF_INET, str, NULL, &addr))
         return NULL;
     if (addr == INADDR_ANY)
         return NULL;
@@ -423,11 +423,11 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
 
     str = g_hash_table_lookup(options, "subnet_mask");
     if (str && (inet_pton(AF_INET, str, &tmp_addr) > 0)) {
-        plen = _nm_utils_ip4_netmask_to_prefix(tmp_addr);
+        plen = nm_ip4_addr_netmask_to_prefix(tmp_addr);
         _LOG2I(LOGD_DHCP4, iface, "  plen %d (%s)", plen, str);
     } else {
         /* Get default netmask for the IP according to appropriate class. */
-        plen = _nm_utils_ip4_get_default_prefix(addr);
+        plen = nm_ip4_addr_get_default_prefix(addr);
         _LOG2I(LOGD_DHCP4, iface, "  plen %d (default)", plen);
     }
 
@@ -440,7 +440,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
         process_classful_routes(iface, options, l3cd, address.address);
 
     if (gateway) {
-        _LOG2I(LOGD_DHCP4, iface, "  gateway %s", _nm_utils_inet4_ntop(gateway, sbuf));
+        _LOG2I(LOGD_DHCP4, iface, "  gateway %s", nm_inet4_ntop(gateway, sbuf));
         gateway_has = TRUE;
     } else {
         /* If the gateway wasn't provided as a classless static route with a
@@ -499,7 +499,7 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx,
         for (s = dns; dns && *s; s++) {
             if (inet_pton(AF_INET, *s, &tmp_addr) > 0) {
                 if (tmp_addr) {
-                    nm_l3_config_data_add_nameserver(l3cd, AF_INET, &tmp_addr);
+                    nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET, &tmp_addr, NULL);
                     _LOG2I(LOGD_DHCP4, iface, "  nameserver '%s'", *s);
                 }
             } else
@@ -593,7 +593,7 @@ nm_dhcp_utils_ip6_prefix_from_options(GHashTable *options)
 {
     gs_strfreev char   **split_addr = NULL;
     NMPlatformIP6Address address    = {
-           0,
+        0,
     };
     struct in6_addr tmp_addr;
     char           *str = NULL;
@@ -704,7 +704,7 @@ nm_dhcp_utils_ip6_config_from_options(NMDedupMultiIndex *multi_idx,
         for (s = dns; dns && *s; s++) {
             if (inet_pton(AF_INET6, *s, &tmp_addr) > 0) {
                 if (!IN6_IS_ADDR_UNSPECIFIED(&tmp_addr)) {
-                    nm_l3_config_data_add_nameserver(l3cd, AF_INET6, &tmp_addr);
+                    nm_l3_config_data_add_nameserver_detail(l3cd, AF_INET6, &tmp_addr, NULL);
                     _LOG2I(LOGD_DHCP6, iface, "  nameserver '%s'", *s);
                 }
             } else
diff --git a/src/core/dhcp/tests/test-dhcp-utils.c b/src/core/dhcp/tests/test-dhcp-utils.c
index 7597f516..1c6d6302 100644
--- a/src/core/dhcp/tests/test-dhcp-utils.c
+++ b/src/core/dhcp/tests/test-dhcp-utils.c
@@ -88,7 +88,6 @@ test_generic_options(void)
     const char                              *expected_route2_dest = "100.99.88.56";
     const char                              *expected_route2_gw   = "10.1.1.1";
     const char *const                       *strarr;
-    const in_addr_t                         *ia_arr;
     guint                                    u;
 
     options = fill_table(generic_options, NULL);
@@ -115,10 +114,10 @@ test_generic_options(void)
     g_assert_cmpstr(strarr[0], ==, expected_search1);
     g_assert_cmpstr(strarr[1], ==, expected_search2);
 
-    ia_arr = nm_l3_config_data_get_nameservers(l3cd, AF_INET, &u);
+    strarr = nm_l3_config_data_get_nameservers(l3cd, AF_INET, &u);
     g_assert_cmpint(u, ==, 2);
-    nmtst_assert_ip4_address(ia_arr[0], expected_dns1);
-    nmtst_assert_ip4_address(ia_arr[1], expected_dns2);
+    g_assert_cmpstr(strarr[0], ==, expected_dns1);
+    g_assert_cmpstr(strarr[1], ==, expected_dns2);
 
     g_assert_cmpint(nm_l3_config_data_get_num_routes(l3cd, AF_INET), ==, 3);
 
@@ -285,7 +284,7 @@ test_classless_static_routes_1(void)
     const char                              *expected_route2_dest = "10.0.0.0";
     const char                              *expected_route2_gw   = "10.17.66.41";
     static const Option                      data[]               = {
-                                           /* dhclient custom format */
+        /* dhclient custom format */
         {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41"},
         {NULL, NULL}};
 
@@ -310,7 +309,7 @@ test_classless_static_routes_2(void)
     const char                              *expected_route2_dest = "10.0.0.0";
     const char                              *expected_route2_gw   = "10.17.66.41";
     static const Option                      data[]               = {
-                                           /* dhcpcd format */
+        /* dhcpcd format */
         {"classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41"},
         {NULL, NULL}};
 
@@ -336,7 +335,7 @@ test_fedora_dhclient_classless_static_routes(void)
     const char                              *expected_route2_gw   = "10.34.255.6";
     const char                              *expected_gateway     = "192.168.0.113";
     static const Option                      data[]               = {
-                                           /* Fedora dhclient format */
+        /* Fedora dhclient format */
         {"classless_static_routes",
                                             "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6"},
         {NULL, NULL}};
@@ -362,7 +361,7 @@ test_dhclient_invalid_classless_routes_1(void)
     const char                              *expected_route1_dest = "192.168.10.0";
     const char                              *expected_route1_gw   = "192.168.1.1";
     static const Option                      data[]               = {
-                                           /* dhclient format */
+        /* dhclient format */
         {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41"},
         {NULL, NULL}};
 
@@ -389,7 +388,7 @@ test_dhcpcd_invalid_classless_routes_1(void)
     const char                              *expected_route2_dest = "100.99.88.56";
     const char                              *expected_route2_gw   = "10.1.1.1";
     static const Option                      data[]               = {
-                                           /* dhcpcd format */
+        /* dhcpcd format */
         {"classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41"},
         {NULL, NULL}};
 
@@ -419,8 +418,8 @@ test_dhclient_invalid_classless_routes_2(void)
     const char                              *expected_route2_dest = "100.99.88.56";
     const char                              *expected_route2_gw   = "10.1.1.1";
     static const Option                      data[]               = {
-                                           {"rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1"},
-                                           {NULL, NULL}};
+        {"rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1"},
+        {NULL, NULL}};
 
     options = fill_table(generic_options, NULL);
     options = fill_table(data, options);
@@ -448,8 +447,8 @@ test_dhcpcd_invalid_classless_routes_2(void)
     const char                              *expected_route2_dest = "100.99.88.56";
     const char                              *expected_route2_gw   = "10.1.1.1";
     static const Option                      data[]               = {
-                                           {"classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1"},
-                                           {NULL, NULL}};
+        {"classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1"},
+        {NULL, NULL}};
 
     options = fill_table(generic_options, NULL);
     options = fill_table(data, options);
@@ -477,8 +476,8 @@ test_dhclient_invalid_classless_routes_3(void)
     const char                              *expected_route1_dest = "192.168.10.0";
     const char                              *expected_route1_gw   = "192.168.1.1";
     static const Option                      data[]               = {
-                                           {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41"},
-                                           {NULL, NULL}};
+        {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41"},
+        {NULL, NULL}};
 
     options = fill_table(generic_options, NULL);
     options = fill_table(data, options);
@@ -501,8 +500,8 @@ test_dhcpcd_invalid_classless_routes_3(void)
     const char                              *expected_route1_dest = "192.168.10.0";
     const char                              *expected_route1_gw   = "192.168.1.1";
     static Option                            data[]               = {
-                                                 {"classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41"},
-                                                 {NULL, NULL}};
+        {"classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41"},
+        {NULL, NULL}};
 
     options = fill_table(generic_options, NULL);
     options = fill_table(data, options);
@@ -526,8 +525,8 @@ test_dhclient_gw_in_classless_routes(void)
     const char                              *expected_route1_gw   = "192.168.1.1";
     const char                              *expected_gateway     = "192.2.3.4";
     static Option                            data[]               = {
-                                                 {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4"},
-                                                 {NULL, NULL}};
+        {"rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4"},
+        {NULL, NULL}};
 
     options = fill_table(generic_options, NULL);
     options = fill_table(data, options);
@@ -550,8 +549,8 @@ test_dhcpcd_gw_in_classless_routes(void)
     const char                              *expected_route1_gw   = "192.168.1.1";
     const char                              *expected_gateway     = "192.2.3.4";
     static Option                            data[]               = {
-                                                 {"classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4"},
-                                                 {NULL, NULL}};
+        {"classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4"},
+        {NULL, NULL}};
 
     options = fill_table(generic_options, NULL);
     options = fill_table(data, options);