about summary refs log tree commit diff
path: root/src/core/dns
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2022-02-14 19:23:28 +0100
committerMichael Biebl <biebl@debian.org>2022-02-14 19:23:28 +0100
commit2f94dba7385fd0e0ef19a06eb4a2fcf6c43d7946 (patch)
treee2222f5577115985dd52044d2991253403cdd952 /src/core/dns
parent88c227d90a6b7b388c5c85d72802a0ca8f05ed5c (diff)
New upstream version 1.35.91 upstream/1.35.91
Diffstat (limited to 'src/core/dns')
-rw-r--r--src/core/dns/nm-dns-dnsmasq.c109
-rw-r--r--src/core/dns/nm-dns-manager.c784
-rw-r--r--src/core/dns/nm-dns-manager.h20
-rw-r--r--src/core/dns/nm-dns-plugin.c8
-rw-r--r--src/core/dns/nm-dns-plugin.h16
-rw-r--r--src/core/dns/nm-dns-systemd-resolved.c193
-rw-r--r--src/core/dns/nm-dns-systemd-resolved.h10
-rw-r--r--src/core/dns/nm-dns-unbound.c12
8 files changed, 622 insertions, 530 deletions
diff --git a/src/core/dns/nm-dns-dnsmasq.c b/src/core/dns/nm-dns-dnsmasq.c
index 126e0a21..f0abf564 100644
--- a/src/core/dns/nm-dns-dnsmasq.c
+++ b/src/core/dns/nm-dns-dnsmasq.c
@@ -19,10 +19,9 @@
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
-#include "nm-ip4-config.h"
-#include "nm-ip6-config.h"
 #include "nm-dbus-manager.h"
 #include "NetworkManagerUtils.h"
+#include "nm-l3-config-data.h"
 
 #define PIDFILE NMRUNDIR "/dnsmasq.pid"
 #define CONFDIR NMCONFDIR "/dnsmasq.d"
@@ -47,8 +46,8 @@ G_STATIC_ASSERT(WAIT_MSEC_AFTER_SIGKILL + 100 <= NM_SHUTDOWN_TIMEOUT_MS_WATCHDOG
 
 typedef void (*GlPidSpawnAsyncNotify)(GCancellable *cancellable,
                                       GPid          pid,
-                                      const int *   p_exit_code,
-                                      GError *      error,
+                                      const int    *p_exit_code,
+                                      GError       *error,
                                       gpointer      notify_user_data);
 
 typedef struct {
@@ -60,10 +59,10 @@ typedef struct {
 } GlPidKillExternalData;
 
 typedef struct {
-    const char *          dm_binary;
+    const char           *dm_binary;
     GlPidSpawnAsyncNotify notify;
     gpointer              notify_user_data;
-    GCancellable *        cancellable;
+    GCancellable         *cancellable;
 } GlPidSpawnAsyncData;
 
 static struct {
@@ -167,9 +166,9 @@ process_gone:
 static gboolean
 _gl_pid_kill_external(void)
 {
-    gs_free char *contents         = NULL;
-    gs_free char *cmdline_contents = NULL;
-    gs_free_error GError *error    = NULL;
+    gs_free char         *contents         = NULL;
+    gs_free char         *cmdline_contents = NULL;
+    gs_free_error GError *error            = NULL;
     gint64                pid64;
     GPid                  pid          = 0;
     guint64               p_start_time = 0;
@@ -482,7 +481,7 @@ static void
 _gl_pid_spawn_next_step(void)
 {
     gs_free_error GError *error = NULL;
-    const char *          argv[15];
+    const char           *argv[15];
     GPid                  pid = 0;
     guint                 argv_idx;
 
@@ -611,8 +610,8 @@ _gl_pid_spawn_next_step(void)
  *   asynchronously.
  */
 static void
-_gl_pid_spawn(const char *          dm_binary,
-              GCancellable *        cancellable,
+_gl_pid_spawn(const char           *dm_binary,
+              GCancellable         *cancellable,
               GlPidSpawnAsyncNotify notify,
               gpointer              notify_user_data)
 {
@@ -719,10 +718,10 @@ static gboolean start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError *
 /*****************************************************************************/
 
 static void
-add_dnsmasq_nameserver(NMDnsDnsmasq *   self,
+add_dnsmasq_nameserver(NMDnsDnsmasq    *self,
                        GVariantBuilder *servers,
-                       const char *     ip,
-                       const char *     domain)
+                       const char      *ip,
+                       const char      *domain)
 {
     g_return_if_fail(ip);
 
@@ -779,8 +778,8 @@ ip_addr_to_string(int addr_family, gconstpointer addr, const char *iface, char *
 }
 
 static void
-add_global_config(NMDnsDnsmasq *           self,
-                  GVariantBuilder *        dnsmasq_servers,
+add_global_config(NMDnsDnsmasq            *self,
+                  GVariantBuilder         *dnsmasq_servers,
                   const NMGlobalDnsConfig *config)
 {
     guint i, j;
@@ -790,7 +789,7 @@ add_global_config(NMDnsDnsmasq *           self,
     for (i = 0; i < nm_global_dns_config_get_num_domains(config); i++) {
         NMGlobalDnsDomain *domain  = nm_global_dns_config_get_domain(config, i);
         const char *const *servers = nm_global_dns_domain_get_servers(domain);
-        const char *       name    = nm_global_dns_domain_get_name(domain);
+        const char        *name    = nm_global_dns_domain_get_name(domain);
 
         g_return_if_fail(name);
 
@@ -806,20 +805,22 @@ add_global_config(NMDnsDnsmasq *           self,
 static void
 add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsConfigIPData *ip_data)
 {
-    NMIPConfig *  ip_config = ip_data->ip_config;
-    gconstpointer addr;
-    const char *  iface, *domain;
+    const char   *iface;
+    const char   *domain;
     char          ip_addr_to_string_buf[IP_ADDR_TO_STRING_BUFLEN];
-    int           addr_family;
-    guint         i, j, num;
+    gconstpointer nameservers;
+    guint         num;
+    guint         i;
+    guint         j;
 
-    iface       = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex);
-    addr_family = nm_ip_config_get_addr_family(ip_config);
+    iface = nm_platform_link_get_name(NM_PLATFORM_GET, ip_data->data->ifindex);
 
-    num = nm_ip_config_get_num_nameservers(ip_config);
+    nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num);
     for (i = 0; i < num; i++) {
-        addr = nm_ip_config_get_nameserver(ip_config, i);
-        ip_addr_to_string(addr_family, addr, iface, ip_addr_to_string_buf);
+        gconstpointer addr;
+
+        addr = nm_ip_addr_from_packed_array(ip_data->addr_family, nameservers, i);
+        ip_addr_to_string(ip_data->addr_family, addr, iface, ip_addr_to_string_buf);
 
         if (!ip_data->domains.has_default_route_explicit && ip_data->domains.has_default_route)
             add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, NULL);
@@ -844,10 +845,10 @@ add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsConfigIPD
 }
 
 static GVariant *
-create_update_args(NMDnsDnsmasq *           self,
+create_update_args(NMDnsDnsmasq            *self,
                    const NMGlobalDnsConfig *global_config,
-                   const CList *            ip_config_lst_head,
-                   const char *             hostname)
+                   const CList             *ip_data_lst_head,
+                   const char              *hostname)
 {
     GVariantBuilder          servers;
     const NMDnsConfigIPData *ip_data;
@@ -857,7 +858,7 @@ create_update_args(NMDnsDnsmasq *           self,
     if (global_config)
         add_global_config(self, &servers, global_config);
     else {
-        c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst)
+        c_list_for_each_entry (ip_data, ip_data_lst_head, ip_data_lst)
             add_ip_config(self, &servers, ip_data);
     }
 
@@ -869,8 +870,8 @@ create_update_args(NMDnsDnsmasq *           self,
 static void
 dnsmasq_update_done(GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-    NMDnsDnsmasq *self;
-    gs_free_error GError *error         = NULL;
+    NMDnsDnsmasq              *self;
+    gs_free_error GError      *error    = NULL;
     gs_unref_variant GVariant *response = NULL;
 
     response = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source_object), res, &error);
@@ -966,15 +967,15 @@ name_owner_changed(NMDnsDnsmasq *self, const char *name_owner)
 
 static void
 name_owner_changed_cb(GDBusConnection *connection,
-                      const char *     sender_name,
-                      const char *     object_path,
-                      const char *     interface_name,
-                      const char *     signal_name,
-                      GVariant *       parameters,
+                      const char      *sender_name,
+                      const char      *object_path,
+                      const char      *interface_name,
+                      const char      *signal_name,
+                      GVariant        *parameters,
                       gpointer         user_data)
 {
     NMDnsDnsmasq *self = user_data;
-    const char *  new_owner;
+    const char   *new_owner;
 
     if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)")))
         return;
@@ -1006,11 +1007,11 @@ spawn_timeout_cb(gpointer user_data)
 static void
 spawn_notify(GCancellable *cancellable,
              GPid          pid,
-             const int *   p_exit_code,
-             GError *      error,
+             const int    *p_exit_code,
+             GError       *error,
              gpointer      notify_user_data)
 {
-    NMDnsDnsmasq *       self;
+    NMDnsDnsmasq        *self;
     NMDnsDnsmasqPrivate *priv;
 
     if (nm_utils_error_is_cancelled(error))
@@ -1043,7 +1044,7 @@ spawn_notify(GCancellable *cancellable,
 static gboolean
 _burst_retry_timeout_cb(gpointer user_data)
 {
-    NMDnsDnsmasq *       self = user_data;
+    NMDnsDnsmasq        *self = user_data;
     NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
 
     priv->burst_retry_timeout_id = 0;
@@ -1057,7 +1058,7 @@ static gboolean
 start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError **error)
 {
     NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
-    const char *         dm_binary;
+    const char          *dm_binary;
     gint64               now;
 
     if (G_LIKELY(priv->main_cancellable)) {
@@ -1120,13 +1121,13 @@ start_dnsmasq(NMDnsDnsmasq *self, gboolean force_start, GError **error)
 }
 
 static gboolean
-update(NMDnsPlugin *            plugin,
+update(NMDnsPlugin             *plugin,
        const NMGlobalDnsConfig *global_config,
-       const CList *            ip_config_lst_head,
-       const char *             hostname,
-       GError **                error)
+       const CList             *ip_data_lst_head,
+       const char              *hostname,
+       GError                 **error)
 {
-    NMDnsDnsmasq *       self = NM_DNS_DNSMASQ(plugin);
+    NMDnsDnsmasq        *self = NM_DNS_DNSMASQ(plugin);
     NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
 
     if (!start_dnsmasq(self, TRUE, error))
@@ -1134,7 +1135,7 @@ update(NMDnsPlugin *            plugin,
 
     nm_clear_pointer(&priv->set_server_ex_args, g_variant_unref);
     priv->set_server_ex_args =
-        g_variant_ref_sink(create_update_args(self, global_config, ip_config_lst_head, hostname));
+        g_variant_ref_sink(create_update_args(self, global_config, ip_data_lst_head, hostname));
 
     send_dnsmasq_update(self);
     return TRUE;
@@ -1145,7 +1146,7 @@ update(NMDnsPlugin *            plugin,
 static void
 stop(NMDnsPlugin *plugin)
 {
-    NMDnsDnsmasq *       self = NM_DNS_DNSMASQ(plugin);
+    NMDnsDnsmasq        *self = NM_DNS_DNSMASQ(plugin);
     NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
 
     priv->is_stopped     = TRUE;
@@ -1172,7 +1173,7 @@ nm_dns_dnsmasq_new(void)
 static void
 dispose(GObject *object)
 {
-    NMDnsDnsmasq *       self = NM_DNS_DNSMASQ(object);
+    NMDnsDnsmasq        *self = NM_DNS_DNSMASQ(object);
     NMDnsDnsmasqPrivate *priv = NM_DNS_DNSMASQ_GET_PRIVATE(self);
 
     priv->is_stopped = TRUE;
@@ -1192,7 +1193,7 @@ static void
 nm_dns_dnsmasq_class_init(NMDnsDnsmasqClass *dns_class)
 {
     NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS(dns_class);
-    GObjectClass *    object_class = G_OBJECT_CLASS(dns_class);
+    GObjectClass     *object_class = G_OBJECT_CLASS(dns_class);
 
     object_class->dispose = dispose;
 
diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c
index f0ec0c89..efbf847b 100644
--- a/src/core/dns/nm-dns-manager.c
+++ b/src/core/dns/nm-dns-manager.c
@@ -7,6 +7,8 @@
 
 #include "src/core/nm-default-daemon.h"
 
+#include "nm-dns-manager.h"
+
 #include <fcntl.h>
 #include <resolv.h>
 #include <stdlib.h>
@@ -22,22 +24,21 @@
 #include <libpsl.h>
 #endif
 
-#include "libnm-glib-aux/nm-str-buf.h"
-#include "nm-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
-#include "nm-dns-manager.h"
-#include "nm-ip4-config.h"
-#include "nm-ip6-config.h"
+#include "libnm-glib-aux/nm-str-buf.h"
+
 #include "NetworkManagerUtils.h"
+#include "devices/nm-device.h"
 #include "nm-config.h"
 #include "nm-dbus-object.h"
-#include "devices/nm-device.h"
-#include "nm-manager.h"
-
-#include "nm-dns-plugin.h"
 #include "nm-dns-dnsmasq.h"
+#include "nm-dns-plugin.h"
 #include "nm-dns-systemd-resolved.h"
 #include "nm-dns-unbound.h"
+#include "nm-ip-config.h"
+#include "nm-l3-config-data.h"
+#include "nm-manager.h"
+#include "nm-utils.h"
 
 #define HASH_LEN NM_UTILS_CHECKSUM_LENGTH_SHA1
 
@@ -60,11 +61,11 @@
 typedef enum { SR_SUCCESS, SR_NOTFOUND, SR_ERROR } SpawnResult;
 
 typedef struct {
-    GPtrArray * nameservers;
-    GPtrArray * searches;
-    GPtrArray * options;
+    GPtrArray  *nameservers;
+    GPtrArray  *searches;
+    GPtrArray  *options;
     const char *nis_domain;
-    GPtrArray * nis_servers;
+    GPtrArray  *nis_servers;
     NMTernary   has_trust_ad;
 } NMResolvConfData;
 
@@ -84,16 +85,18 @@ typedef struct {
     GHashTable *configs_dict;
     CList       configs_lst_head;
 
-    CList     ip_config_lst_head;
+    CList     ip_data_lst_head;
     GVariant *config_variant;
 
-    bool ip_config_lst_need_sort : 1;
+    bool ip_data_lst_need_sort : 1;
 
     bool configs_lst_need_sort : 1;
 
     bool dns_touched : 1;
     bool is_stopped : 1;
 
+    bool config_changed : 1;
+
     char *hostname;
     guint updates_queue;
 
@@ -101,9 +104,9 @@ typedef struct {
     guint8 prev_hash[HASH_LEN]; /* Hash when begin_updates() was called */
 
     NMDnsManagerResolvConfManager rc_manager;
-    char *                        mode;
-    NMDnsPlugin *                 sd_resolve_plugin;
-    NMDnsPlugin *                 plugin;
+    char                         *mode;
+    NMDnsPlugin                  *sd_resolve_plugin;
+    NMDnsPlugin                  *plugin;
 
     NMConfig *config;
 
@@ -159,11 +162,6 @@ NM_DEFINE_SINGLETON_GETTER(NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER
 
 /*****************************************************************************/
 
-static void
-_ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsConfigIPData *ip_data);
-
-/*****************************************************************************/
-
 static gboolean
 domain_is_valid(const char *domain, gboolean check_public_suffix)
 {
@@ -208,6 +206,24 @@ static NM_UTILS_LOOKUP_STR_DEFINE(
 
 /*****************************************************************************/
 
+static int
+_dns_config_ip_data_get_dns_priority1(const NML3ConfigData *l3cd, int addr_family)
+{
+    int prio;
+
+    if (!nm_l3_config_data_get_dns_priority(l3cd, addr_family, &prio))
+        return 0;
+
+    nm_assert(prio != 0);
+    return prio;
+}
+
+static int
+_dns_config_ip_data_get_dns_priority(const NMDnsConfigIPData *ip_data)
+{
+    return _dns_config_ip_data_get_dns_priority1(ip_data->l3cd, ip_data->addr_family);
+}
+
 static void
 _ASSERT_dns_config_data(const NMDnsConfigData *data)
 {
@@ -221,9 +237,9 @@ _ASSERT_dns_config_ip_data(const NMDnsConfigIPData *ip_data)
 {
     nm_assert(ip_data);
     _ASSERT_dns_config_data(ip_data->data);
-    nm_assert(NM_IS_IP_CONFIG(ip_data->ip_config));
+    nm_assert(NM_IS_L3_CONFIG_DATA(ip_data->l3cd));
     nm_assert(c_list_contains(&ip_data->data->data_lst_head, &ip_data->data_lst));
-    nm_assert(ip_data->data->ifindex == nm_ip_config_get_ifindex(ip_data->ip_config));
+    nm_assert(ip_data->data->ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd));
 #if NM_MORE_ASSERTS > 5
     {
         gboolean has_default = FALSE;
@@ -243,37 +259,36 @@ _ASSERT_dns_config_ip_data(const NMDnsConfigIPData *ip_data)
         if (ip_data->domains.has_default_route_exclusive)
             nm_assert(ip_data->domains.has_default_route);
     }
+    nm_assert(_dns_config_ip_data_get_dns_priority(ip_data) != 0);
 #endif
 }
 
 static NMDnsConfigIPData *
-_dns_config_ip_data_new(NMDnsConfigData * data,
-                        NMIPConfig *      ip_config,
-                        NMDnsIPConfigType ip_config_type)
+_dns_config_ip_data_new(NMDnsConfigData      *data,
+                        int                   addr_family,
+                        gconstpointer         source_tag,
+                        const NML3ConfigData *l3cd,
+                        NMDnsIPConfigType     ip_config_type)
 {
     NMDnsConfigIPData *ip_data;
 
     _ASSERT_dns_config_data(data);
-    nm_assert(NM_IS_IP_CONFIG(ip_config));
+    nm_assert(NM_IS_L3_CONFIG_DATA(l3cd));
     nm_assert(ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED);
 
     ip_data  = g_slice_new(NMDnsConfigIPData);
     *ip_data = (NMDnsConfigIPData){
         .data           = data,
-        .ip_config      = g_object_ref(ip_config),
+        .source_tag     = source_tag,
+        .l3cd           = nm_l3_config_data_ref_and_seal(l3cd),
         .ip_config_type = ip_config_type,
+        .addr_family    = addr_family,
     };
     c_list_link_tail(&data->data_lst_head, &ip_data->data_lst);
-    c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_config_lst_head,
-                     &ip_data->ip_config_lst);
-
-    /* We also need to set priv->ip_config_lst_need_sort, but the caller will do that! */
+    c_list_link_tail(&NM_DNS_MANAGER_GET_PRIVATE(data->self)->ip_data_lst_head,
+                     &ip_data->ip_data_lst);
 
-    g_signal_connect(ip_config,
-                     NM_IS_IP4_CONFIG(ip_config) ? "notify::" NM_IP4_CONFIG_DNS_PRIORITY
-                                                 : "notify::" NM_IP6_CONFIG_DNS_PRIORITY,
-                     G_CALLBACK(_ip_config_dns_priority_changed),
-                     ip_data);
+    /* We also need to set priv->ip_data_lst_need_sort, but the caller will do that! */
 
     _ASSERT_dns_config_ip_data(ip_data);
     return ip_data;
@@ -285,35 +300,15 @@ _dns_config_ip_data_free(NMDnsConfigIPData *ip_data)
     _ASSERT_dns_config_ip_data(ip_data);
 
     c_list_unlink_stale(&ip_data->data_lst);
-    c_list_unlink_stale(&ip_data->ip_config_lst);
+    c_list_unlink_stale(&ip_data->ip_data_lst);
 
     g_free(ip_data->domains.search);
     g_strfreev(ip_data->domains.reverse);
 
-    g_signal_handlers_disconnect_by_func(ip_data->ip_config,
-                                         _ip_config_dns_priority_changed,
-                                         ip_data);
-
-    g_object_unref(ip_data->ip_config);
+    nm_l3_config_data_unref(ip_data->l3cd);
     nm_g_slice_free(ip_data);
 }
 
-static NMDnsConfigIPData *
-_dns_config_data_find_ip_config(NMDnsConfigData *data, NMIPConfig *ip_config)
-{
-    NMDnsConfigIPData *ip_data;
-
-    _ASSERT_dns_config_data(data);
-
-    c_list_for_each_entry (ip_data, &data->data_lst_head, data_lst) {
-        _ASSERT_dns_config_ip_data(ip_data);
-
-        if (ip_data->ip_config == ip_config)
-            return ip_data;
-    }
-    return NULL;
-}
-
 static void
 _dns_config_data_free(NMDnsConfigData *data)
 {
@@ -325,14 +320,13 @@ _dns_config_data_free(NMDnsConfigData *data)
 }
 
 static int
-_mgr_get_ip_config_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data)
+_mgr_get_ip_data_lst_cmp(const CList *a_lst, const CList *b_lst, const void *user_data)
 {
-    const NMDnsConfigIPData *a = c_list_entry(a_lst, NMDnsConfigIPData, ip_config_lst);
-    const NMDnsConfigIPData *b = c_list_entry(b_lst, NMDnsConfigIPData, ip_config_lst);
+    const NMDnsConfigIPData *a = c_list_entry(a_lst, NMDnsConfigIPData, ip_data_lst);
+    const NMDnsConfigIPData *b = c_list_entry(b_lst, NMDnsConfigIPData, ip_data_lst);
 
     /* Configurations with lower priority value first */
-    NM_CMP_DIRECT(nm_ip_config_get_dns_priority(a->ip_config),
-                  nm_ip_config_get_dns_priority(b->ip_config));
+    NM_CMP_DIRECT(_dns_config_ip_data_get_dns_priority(a), _dns_config_ip_data_get_dns_priority(b));
 
     /* Sort according to type (descendingly) */
     NM_CMP_FIELD(b, a, ip_config_type);
@@ -341,16 +335,16 @@ _mgr_get_ip_config_lst_cmp(const CList *a_lst, const CList *b_lst, const void *u
 }
 
 static CList *
-_mgr_get_ip_config_lst_head(NMDnsManager *self)
+_mgr_get_ip_data_lst_head(NMDnsManager *self)
 {
     NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
-    if (G_UNLIKELY(priv->ip_config_lst_need_sort)) {
-        priv->ip_config_lst_need_sort = FALSE;
-        c_list_sort(&priv->ip_config_lst_head, _mgr_get_ip_config_lst_cmp, NULL);
+    if (G_UNLIKELY(priv->ip_data_lst_need_sort)) {
+        priv->ip_data_lst_need_sort = FALSE;
+        c_list_sort(&priv->ip_data_lst_head, _mgr_get_ip_data_lst_cmp, NULL);
     }
 
-    return &priv->ip_config_lst_head;
+    return &priv->ip_data_lst_head;
 }
 
 static int
@@ -382,7 +376,7 @@ NMDnsPlugin *
 nm_dns_manager_get_systemd_resolved(NMDnsManager *self)
 {
     NMDnsManagerPrivate *priv;
-    NMDnsPlugin *        plugin = NULL;
+    NMDnsPlugin         *plugin = NULL;
 
     g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
 
@@ -430,28 +424,33 @@ add_dns_option_item(GPtrArray *array, const char *str)
 }
 
 static void
-add_dns_domains(GPtrArray *       array,
-                const NMIPConfig *ip_config,
-                gboolean          include_routing,
-                gboolean          dup)
+add_dns_domains(GPtrArray            *array,
+                int                   addr_family,
+                const NML3ConfigData *l3cd,
+                gboolean              include_routing,
+                gboolean              dup)
 {
-    guint       num_domains, num_searches, i;
-    const char *str;
+    const char *const *domains;
+    const char *const *searches;
+    guint              num_domains;
+    guint              num_searches;
+    guint              i;
+    const char        *str;
 
-    num_domains  = nm_ip_config_get_num_domains(ip_config);
-    num_searches = nm_ip_config_get_num_searches(ip_config);
+    domains  = nm_l3_config_data_get_domains(l3cd, addr_family, &num_domains);
+    searches = nm_l3_config_data_get_searches(l3cd, addr_family, &num_searches);
 
     for (i = 0; i < num_searches; i++) {
-        str = nm_ip_config_get_search(ip_config, i);
+        str = searches[i];
         if (!include_routing && domain_is_routing(str))
             continue;
         if (!domain_is_valid(nm_utils_parse_dns_domain(str, NULL), FALSE))
             continue;
         add_string_item(array, str, dup);
     }
-    if (num_domains > 1 || !num_searches) {
+    if (num_domains > 1 || num_searches == 0) {
         for (i = 0; i < num_domains; i++) {
-            str = nm_ip_config_get_domain(ip_config, i);
+            str = domains[i];
             if (!include_routing && domain_is_routing(str))
                 continue;
             if (!domain_is_valid(nm_utils_parse_dns_domain(str, NULL), FALSE))
@@ -462,26 +461,24 @@ add_dns_domains(GPtrArray *       array,
 }
 
 static void
-merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_config)
+merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3ConfigData *l3cd)
 {
-    int      addr_family;
-    char     buf[NM_UTILS_INET_ADDRSTRLEN + 50];
-    gboolean has_trust_ad;
-    guint    num_nameservers;
-    guint    num;
-    guint    i;
+    char               buf[NM_UTILS_INET_ADDRSTRLEN + 50];
+    gboolean           has_trust_ad;
+    guint              num_nameservers;
+    guint              num;
+    guint              i;
+    gconstpointer      nameservers;
+    const char *const *strv;
 
-    addr_family = nm_ip_config_get_addr_family(ip_config);
+    nm_assert(ifindex == nm_l3_config_data_get_ifindex(l3cd));
 
-    nm_assert_addr_family(addr_family);
-    nm_assert(ifindex > 0);
-    nm_assert(ifindex == nm_ip_config_get_ifindex(ip_config));
-
-    num_nameservers = nm_ip_config_get_num_nameservers(ip_config);
+    nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num_nameservers);
     for (i = 0; i < num_nameservers; i++) {
         const NMIPAddr *addr;
 
-        addr = nm_ip_config_get_nameserver(ip_config, i);
+        addr = nm_ip_addr_from_packed_array(addr_family, nameservers, i);
+
         if (addr_family == AF_INET)
             nm_utils_inet_ntop(addr_family, addr, buf);
         else if (IN6_IS_ADDR_V4MAPPED(addr))
@@ -502,22 +499,23 @@ merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_conf
         add_string_item(rc->nameservers, buf, TRUE);
     }
 
-    add_dns_domains(rc->searches, ip_config, FALSE, TRUE);
+    add_dns_domains(rc->searches, addr_family, l3cd, FALSE, TRUE);
 
     has_trust_ad = FALSE;
-    num          = nm_ip_config_get_num_dns_options(ip_config);
+    strv         = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num);
     for (i = 0; i < num; i++) {
-        const char *option = nm_ip_config_get_dns_option(ip_config, i);
+        const char *option = strv[i];
 
         if (nm_streq(option, NM_SETTING_DNS_OPTION_TRUST_AD)) {
             has_trust_ad = TRUE;
             continue;
         }
-        add_dns_option_item(rc->options, nm_ip_config_get_dns_option(ip_config, i));
+        add_dns_option_item(rc->options, option);
     }
+
     if (num_nameservers == 0) {
-        /* If the @ip_config contributes no DNS servers, ignore whether trust-ad is set or unset
-         * for this @ip_config. */
+        /* If the @l3cd contributes no DNS servers, ignore whether trust-ad is set or unset
+         * for this @l3cd. */
     } else if (has_trust_ad) {
         /* We only set has_trust_ad to TRUE, if all IP configs agree (or don't contribute).
          * Once set to FALSE, it doesn't get reset. */
@@ -527,20 +525,17 @@ merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_conf
         rc->has_trust_ad = NM_TERNARY_FALSE;
 
     if (addr_family == AF_INET) {
-        const NMIP4Config *ip4_config = (const NMIP4Config *) ip_config;
+        const in_addr_t *nis_servers;
+        const char      *nis_domain;
 
-        /* NIS stuff */
-        num = nm_ip4_config_get_num_nis_servers(ip4_config);
-        for (i = 0; i < num; i++) {
-            add_string_item(rc->nis_servers,
-                            _nm_utils_inet4_ntop(nm_ip4_config_get_nis_server(ip4_config, i), buf),
-                            TRUE);
-        }
+        nis_servers = nm_l3_config_data_get_nis_servers(l3cd, &num);
+        for (i = 0; i < num; i++)
+            add_string_item(rc->nis_servers, _nm_utils_inet4_ntop(nis_servers[i], buf), TRUE);
 
-        if (nm_ip4_config_get_nis_domain(ip4_config)) {
+        if ((nis_domain = nm_l3_config_data_get_nis_domain(l3cd))) {
             /* FIXME: handle multiple domains */
             if (!rc->nis_domain)
-                rc->nis_domain = nm_ip4_config_get_nis_domain(ip4_config);
+                rc->nis_domain = nis_domain;
         }
     }
 }
@@ -548,7 +543,7 @@ merge_one_ip_config(NMResolvConfData *rc, int ifindex, const NMIPConfig *ip_conf
 static GPid
 run_netconfig(NMDnsManager *self, GError **error, int *stdin_fd)
 {
-    char *        argv[5];
+    char         *argv[5];
     gs_free char *tmp = NULL;
     GPid          pid = -1;
 
@@ -586,9 +581,9 @@ netconfig_construct_str(NMDnsManager *self, GString *str, const char *key, const
 }
 
 static void
-netconfig_construct_strv(NMDnsManager *     self,
-                         GString *          str,
-                         const char *       key,
+netconfig_construct_strv(NMDnsManager      *self,
+                         GString           *str,
+                         const char        *key,
                          const char *const *values)
 {
     if (values) {
@@ -600,18 +595,18 @@ netconfig_construct_strv(NMDnsManager *     self,
 }
 
 static SpawnResult
-dispatch_netconfig(NMDnsManager *     self,
+dispatch_netconfig(NMDnsManager      *self,
                    const char *const *searches,
                    const char *const *nameservers,
-                   const char *       nis_domain,
+                   const char        *nis_domain,
                    const char *const *nis_servers,
-                   GError **          error)
+                   GError           **error)
 {
-    GPid                 pid;
-    int                  fd;
-    int                  errsv;
-    int                  status;
-    gssize               l;
+    GPid                          pid;
+    int                           fd;
+    int                           errsv;
+    int                           status;
+    gssize                        l;
     nm_auto_free_gstring GString *str = NULL;
 
     pid = run_netconfig(self, error, &fd);
@@ -768,11 +763,11 @@ write_resolv_conf_contents(FILE *f, const char *content, GError **error)
 }
 
 static gboolean
-write_resolv_conf(FILE *             f,
+write_resolv_conf(FILE              *f,
                   const char *const *searches,
                   const char *const *nameservers,
                   const char *const *options,
-                  GError **          error)
+                  GError           **error)
 {
     gs_free char *content = NULL;
 
@@ -782,17 +777,17 @@ write_resolv_conf(FILE *             f,
 
 static SpawnResult
 dispatch_resolvconf(NMDnsManager *self,
-                    char **       searches,
-                    char **       nameservers,
-                    char **       options,
-                    GError **     error)
+                    char        **searches,
+                    char        **nameservers,
+                    char        **options,
+                    GError      **error)
 {
     gs_free char *cmd = NULL;
-    FILE *        f;
+    FILE         *f;
     gboolean      success = FALSE;
     int           errsv;
     int           err;
-    char *        argv[] = {RESOLVCONF_PATH, "-d", "NetworkManager", NULL};
+    char         *argv[] = {RESOLVCONF_PATH, "-d", "NetworkManager", NULL};
     int           status;
 
     if (!g_file_test(RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE)) {
@@ -883,13 +878,13 @@ _read_link_cached(const char *path, gboolean *is_cached, char **cached)
 #define NO_STUB_RESOLV_CONF NMRUNDIR "/no-stub-resolv.conf"
 
 static void
-update_resolv_conf_no_stub(NMDnsManager *     self,
+update_resolv_conf_no_stub(NMDnsManager      *self,
                            const char *const *searches,
                            const char *const *nameservers,
                            const char *const *options)
 {
     gs_free char *content = NULL;
-    GError *      local   = NULL;
+    GError       *local   = NULL;
 
     content = create_resolv_conf(searches, nameservers, options);
 
@@ -903,14 +898,14 @@ update_resolv_conf_no_stub(NMDnsManager *     self,
 }
 
 static SpawnResult
-update_resolv_conf(NMDnsManager *                self,
-                   const char *const *           searches,
-                   const char *const *           nameservers,
-                   const char *const *           options,
-                   GError **                     error,
+update_resolv_conf(NMDnsManager                 *self,
+                   const char *const            *searches,
+                   const char *const            *nameservers,
+                   const char *const            *options,
+                   GError                      **error,
                    NMDnsManagerResolvConfManager rc_manager)
 {
-    FILE *        f;
+    FILE         *f;
     gboolean      success;
     gs_free char *content           = NULL;
     SpawnResult   write_file_result = SR_SUCCESS;
@@ -923,10 +918,10 @@ update_resolv_conf(NMDnsManager *                self,
     if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE
         || (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK
             && !_read_link_cached(_PATH_RESCONF, &resconf_link_cached, &resconf_link))) {
-        gs_free char *     rc_path_syml = NULL;
+        gs_free char      *rc_path_syml = NULL;
         nm_auto_free char *rc_path_real = NULL;
-        const char *       rc_path      = _PATH_RESCONF;
-        GError *           local        = NULL;
+        const char        *rc_path      = _PATH_RESCONF;
+        GError            *local        = NULL;
 
         if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
             rc_path_real = realpath(_PATH_RESCONF, NULL);
@@ -1110,7 +1105,7 @@ static void
 compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[static HASH_LEN])
 {
     nm_auto_free_checksum GChecksum *sum = NULL;
-    NMDnsConfigIPData *              ip_data;
+    NMDnsConfigIPData               *ip_data;
 
     sum = g_checksum_new(G_CHECKSUM_SHA1);
     nm_assert(HASH_LEN == g_checksum_type_get_length(G_CHECKSUM_SHA1));
@@ -1122,9 +1117,9 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[
 
         /* FIXME(ip-config-checksum): this relies on the fact that an IP
          * configuration without DNS parameters gives a zero checksum. */
-        head = _mgr_get_ip_config_lst_head(self);
-        c_list_for_each_entry (ip_data, head, ip_config_lst)
-            nm_ip_config_hash(ip_data->ip_config, sum, TRUE);
+        head = _mgr_get_ip_data_lst_head(self);
+        c_list_for_each_entry (ip_data, head, ip_data_lst)
+            nm_ip_config_dns_hash(ip_data->l3cd, sum, ip_data->addr_family);
     }
 
     nm_utils_checksum_get_digest_len(sum, buffer, HASH_LEN);
@@ -1172,19 +1167,21 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
 }
 
 static const char *
-get_nameserver_list(const NMIPConfig *config, NMStrBuf *tmp_strbuf)
+get_nameserver_list(int addr_family, const NML3ConfigData *l3cd, NMStrBuf *tmp_strbuf)
 {
-    char  buf[NM_UTILS_INET_ADDRSTRLEN];
-    int   addr_family;
-    guint num;
-    guint i;
+    char          buf[NM_UTILS_INET_ADDRSTRLEN];
+    guint         num;
+    guint         i;
+    gconstpointer nameservers;
 
     nm_str_buf_reset(tmp_strbuf);
 
-    addr_family = nm_ip_config_get_addr_family(config);
-    num         = nm_ip_config_get_num_nameservers(config);
+    nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num);
     for (i = 0; i < num; i++) {
-        nm_utils_inet_ntop(addr_family, nm_ip_config_get_nameserver(config, i), buf);
+        const NMIPAddr *addr;
+
+        addr = nm_ip_addr_from_packed_array(addr_family, nameservers, i);
+        nm_utils_inet_ntop(addr_family, addr->addr_ptr, buf);
         if (i > 0)
             nm_str_buf_append_c(tmp_strbuf, ' ');
         nm_str_buf_append(tmp_strbuf, buf);
@@ -1203,22 +1200,22 @@ _ptrarray_to_strv(GPtrArray *parray)
 }
 
 static void
-_collect_resolv_conf_data(NMDnsManager *     self,
+_collect_resolv_conf_data(NMDnsManager      *self,
                           NMGlobalDnsConfig *global_config,
-                          char ***           out_searches,
-                          char ***           out_options,
-                          char ***           out_nameservers,
-                          char ***           out_nis_servers,
-                          const char **      out_nis_domain)
+                          char            ***out_searches,
+                          char            ***out_options,
+                          char            ***out_nameservers,
+                          char            ***out_nis_servers,
+                          const char       **out_nis_domain)
 {
     NMDnsManagerPrivate *priv;
     NMResolvConfData     rc = {
-        .nameservers  = g_ptr_array_new(),
-        .searches     = g_ptr_array_new(),
-        .options      = g_ptr_array_new(),
-        .nis_domain   = NULL,
-        .nis_servers  = g_ptr_array_new(),
-        .has_trust_ad = NM_TERNARY_DEFAULT,
+            .nameservers  = g_ptr_array_new(),
+            .searches     = g_ptr_array_new(),
+            .options      = g_ptr_array_new(),
+            .nis_domain   = NULL,
+            .nis_servers  = g_ptr_array_new(),
+            .has_trust_ad = NM_TERNARY_DEFAULT,
     };
 
     priv = NM_DNS_MANAGER_GET_PRIVATE(self);
@@ -1227,36 +1224,39 @@ _collect_resolv_conf_data(NMDnsManager *     self,
         merge_global_dns_config(&rc, global_config);
     else {
         nm_auto_str_buf NMStrBuf tmp_strbuf = NM_STR_BUF_INIT(0, FALSE);
-        int                      prio;
         int                      first_prio = 0;
         const NMDnsConfigIPData *ip_data;
-        const CList *            head;
+        const CList             *head;
         gboolean                 is_first = TRUE;
 
-        head = _mgr_get_ip_config_lst_head(self);
-        c_list_for_each_entry (ip_data, head, ip_config_lst) {
+        head = _mgr_get_ip_data_lst_head(self);
+        c_list_for_each_entry (ip_data, head, ip_data_lst) {
             gboolean skip = FALSE;
+            int      dns_priority;
 
             _ASSERT_dns_config_ip_data(ip_data);
 
-            prio = nm_ip_config_get_dns_priority(ip_data->ip_config);
+            if (!nm_l3_config_data_get_dns_priority(ip_data->l3cd,
+                                                    ip_data->addr_family,
+                                                    &dns_priority))
+                nm_assert_not_reached();
 
             if (is_first) {
                 is_first   = FALSE;
-                first_prio = prio;
-            } else if (first_prio < 0 && first_prio != prio)
+                first_prio = dns_priority;
+            } else if (first_prio < 0 && first_prio != dns_priority)
                 skip = TRUE;
 
             _LOGT("config: %8d %-7s v%c %-5d %s: %s",
-                  prio,
+                  dns_priority,
                   _config_type_to_string(ip_data->ip_config_type),
-                  nm_utils_addr_family_to_char(nm_ip_config_get_addr_family(ip_data->ip_config)),
+                  nm_utils_addr_family_to_char(ip_data->addr_family),
                   ip_data->data->ifindex,
                   skip ? "<SKIP>" : "",
-                  get_nameserver_list(ip_data->ip_config, &tmp_strbuf));
+                  get_nameserver_list(ip_data->addr_family, ip_data->l3cd, &tmp_strbuf));
 
             if (!skip)
-                merge_one_ip_config(&rc, ip_data->data->ifindex, ip_data->ip_config);
+                merge_one_l3cd(&rc, ip_data->addr_family, ip_data->data->ifindex, ip_data->l3cd);
         }
     }
 
@@ -1292,27 +1292,32 @@ _collect_resolv_conf_data(NMDnsManager *     self,
 /*****************************************************************************/
 
 static char **
-get_ip_rdns_domains(NMIPConfig *ip_config)
+get_ip_rdns_domains(int addr_family, const NML3ConfigData *l3cd)
 {
-    int                        addr_family = nm_ip_config_get_addr_family(ip_config);
-    char **                    strv;
-    GPtrArray *                domains;
-    NMDedupMultiIter           ipconf_iter;
-    const NMPlatformIPAddress *address;
-    const NMPlatformIPRoute *  route;
-
-    nm_assert_addr_family(addr_family);
+    const int        IS_IPv4 = NM_IS_IPv4(addr_family);
+    char           **strv;
+    GPtrArray       *domains;
+    NMDedupMultiIter ipconf_iter;
+    const NMPObject *obj;
 
     domains = g_ptr_array_sized_new(5);
 
-    nm_ip_config_iter_ip_address_for_each (&ipconf_iter, ip_config, &address) {
+    nm_l3_config_data_iter_obj_for_each (&ipconf_iter,
+                                         l3cd,
+                                         &obj,
+                                         NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)) {
         nm_utils_get_reverse_dns_domains_ip(addr_family,
-                                            address->address_ptr,
-                                            address->plen,
+                                            NMP_OBJECT_CAST_IP_ADDRESS(obj)->address_ptr,
+                                            NMP_OBJECT_CAST_IP_ADDRESS(obj)->plen,
                                             domains);
     }
 
-    nm_ip_config_iter_ip_route_for_each (&ipconf_iter, ip_config, &route) {
+    nm_l3_config_data_iter_obj_for_each (&ipconf_iter,
+                                         l3cd,
+                                         &obj,
+                                         NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)) {
+        const NMPlatformIPRoute *route = NMP_OBJECT_CAST_IP_ROUTE(obj);
+
         if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route)) {
             nm_utils_get_reverse_dns_domains_ip(addr_family,
                                                 route->network_ptr,
@@ -1345,11 +1350,11 @@ _domain_track_get_priority(GHashTable *ht, const char *domain, int *out_priority
 
 /* Check if the domain is shadowed by a parent domain with more negative priority */
 static gboolean
-_domain_track_is_shadowed(GHashTable * ht,
-                          const char * domain,
+_domain_track_is_shadowed(GHashTable  *ht,
+                          const char  *domain,
                           int          priority,
                           const char **out_parent,
-                          int *        out_parent_priority)
+                          int         *out_parent_priority)
 {
     char *parent;
     int   parent_priority;
@@ -1388,18 +1393,18 @@ _domain_track_is_shadowed(GHashTable * ht,
 static void
 _mgr_configs_data_construct(NMDnsManager *self)
 {
-    NMDnsConfigIPData *ip_data;
+    NMDnsConfigIPData             *ip_data;
     gs_unref_hashtable GHashTable *ht               = NULL;
     gs_unref_hashtable GHashTable *wildcard_entries = NULL;
-    CList *                        head;
+    CList                         *head;
     int                            prev_priority = G_MININT;
 
-    head = _mgr_get_ip_config_lst_head(self);
+    head = _mgr_get_ip_data_lst_head(self);
 
 #if NM_MORE_ASSERTS
     /* we call _mgr_configs_data_clear() at the end of update. We
      * don't expect any domain settings here. */
-    c_list_for_each_entry (ip_data, head, ip_config_lst) {
+    c_list_for_each_entry (ip_data, head, ip_data_lst) {
         nm_assert(!ip_data->domains.search);
         nm_assert(!ip_data->domains.reverse);
         nm_assert(!ip_data->domains.has_default_route_explicit);
@@ -1408,15 +1413,17 @@ _mgr_configs_data_construct(NMDnsManager *self)
     }
 #endif
 
-    c_list_for_each_entry (ip_data, head, ip_config_lst) {
-        NMIPConfig *ip_config    = ip_data->ip_config;
-        gboolean    add_wildcard = FALSE;
+    c_list_for_each_entry (ip_data, head, ip_data_lst) {
+        gboolean add_wildcard = FALSE;
+        guint    num;
 
-        if (!nm_ip_config_get_num_nameservers(ip_config))
+        nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num);
+        if (num == 0)
             continue;
-        if (nm_ip_config_best_default_route_get(ip_config))
+        if (nm_l3_config_data_get_best_default_route(ip_data->l3cd, ip_data->addr_family)) {
+            /* FIXME(l3cfg): the best-default route of a l3cd is not significant! */
             add_wildcard = TRUE;
-        else {
+        } else {
             /* If a VPN has never-default=no but doesn't get a default
              * route (this can happen for example when the server
              * pushes routes with openconnect), and there are no
@@ -1424,9 +1431,10 @@ _mgr_configs_data_construct(NMDnsManager *self)
              * by the server would be unused. It is preferable in this
              * case to use the VPN DNS server for all queries. */
             if (ip_data->ip_config_type == NM_DNS_IP_CONFIG_TYPE_VPN
-                && !nm_ip_config_get_never_default(ip_data->ip_config)
-                && nm_ip_config_get_num_searches(ip_data->ip_config) == 0
-                && nm_ip_config_get_num_domains(ip_data->ip_config) == 0)
+                && nm_l3_config_data_get_never_default(ip_data->l3cd, ip_data->addr_family)
+                       == NM_TERNARY_FALSE
+                && !nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, &num)
+                && !nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, &num))
                 add_wildcard = TRUE;
         }
 
@@ -1437,29 +1445,33 @@ _mgr_configs_data_construct(NMDnsManager *self)
         }
     }
 
-    c_list_for_each_entry (ip_data, head, ip_config_lst) {
-        NMIPConfig * ip_config = ip_data->ip_config;
-        int          priority;
-        const char **domains;
-        guint        n_searches;
-        guint        n_domains;
-        guint        num_dom1;
-        guint        num_dom2;
-        guint        n_domains_allocated;
-        guint        i;
-        gboolean     has_default_route_maybe    = FALSE;
-        gboolean     has_default_route_explicit = FALSE;
-        gboolean     has_default_route_auto     = FALSE;
-
-        if (!nm_ip_config_get_num_nameservers(ip_config))
+    c_list_for_each_entry (ip_data, head, ip_data_lst) {
+        int                priority;
+        const char       **domains;
+        const char *const *strv_searches;
+        const char *const *strv_domains;
+        guint              n_searches;
+        guint              n_domains;
+        guint              num_dom1;
+        guint              num_dom2;
+        guint              n_domains_allocated;
+        guint              i;
+        gboolean           has_default_route_maybe    = FALSE;
+        gboolean           has_default_route_explicit = FALSE;
+        gboolean           has_default_route_auto     = FALSE;
+        guint              num;
+
+        nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num);
+        if (num == 0)
             continue;
 
-        n_searches = nm_ip_config_get_num_searches(ip_config);
-        n_domains  = nm_ip_config_get_num_domains(ip_config);
+        strv_searches =
+            nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, &n_searches);
+        strv_domains =
+            nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, &n_domains);
 
-        priority = nm_ip_config_get_dns_priority(ip_config);
+        priority = _dns_config_ip_data_get_dns_priority(ip_data);
 
-        nm_assert(priority != 0);
         nm_assert(prev_priority <= priority);
         prev_priority = priority;
 
@@ -1487,10 +1499,10 @@ _mgr_configs_data_construct(NMDnsManager *self)
         /* searches are preferred over domains */
         if (n_searches > 0) {
             for (i = 0; i < n_searches; i++)
-                domains[num_dom1++] = nm_ip_config_get_search(ip_config, i);
+                domains[num_dom1++] = strv_searches[i];
         } else {
             for (i = 0; i < n_domains; i++)
-                domains[num_dom1++] = nm_ip_config_get_domain(ip_config, i);
+                domains[num_dom1++] = strv_domains[i];
         }
 
         nm_assert(num_dom1 < n_domains_allocated);
@@ -1578,8 +1590,8 @@ _mgr_configs_data_construct(NMDnsManager *self)
 
         nm_assert(!ip_data->domains.search);
         nm_assert(!ip_data->domains.reverse);
-        ip_data->domains.search                     = domains;
-        ip_data->domains.reverse                    = get_ip_rdns_domains(ip_config);
+        ip_data->domains.search  = domains;
+        ip_data->domains.reverse = get_ip_rdns_domains(ip_data->addr_family, ip_data->l3cd);
         ip_data->domains.has_default_route_explicit = has_default_route_explicit;
         ip_data->domains.has_default_route_exclusive =
             has_default_route_explicit || (priority < 0 && has_default_route_auto);
@@ -1609,10 +1621,10 @@ static void
 _mgr_configs_data_clear(NMDnsManager *self)
 {
     NMDnsConfigIPData *ip_data;
-    CList *            head;
+    CList             *head;
 
-    head = _mgr_get_ip_config_lst_head(self);
-    c_list_for_each_entry (ip_data, head, ip_config_lst) {
+    head = _mgr_get_ip_data_lst_head(self);
+    c_list_for_each_entry (ip_data, head, ip_data_lst) {
         nm_clear_g_free(&ip_data->domains.search);
         nm_clear_pointer(&ip_data->domains.reverse, g_strfreev);
         ip_data->domains.has_default_route_explicit  = FALSE;
@@ -1626,23 +1638,25 @@ _mgr_configs_data_clear(NMDnsManager *self)
 static gboolean
 update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError **error)
 {
-    NMDnsManagerPrivate *priv                = NM_DNS_MANAGER_GET_PRIVATE(self);
-    const char *         nis_domain          = NULL;
-    gs_strfreev char **  searches            = NULL;
-    gs_strfreev char **  options             = NULL;
-    gs_strfreev char **  nameservers         = NULL;
-    gs_strfreev char **  nis_servers         = NULL;
-    gboolean             caching             = FALSE;
-    gboolean             do_update           = TRUE;
-    gboolean             resolv_conf_updated = FALSE;
-    SpawnResult          result              = SR_SUCCESS;
-    NMConfigData *       data;
-    NMGlobalDnsConfig *  global_config;
+    NMDnsManagerPrivate  *priv                = NM_DNS_MANAGER_GET_PRIVATE(self);
+    const char           *nis_domain          = NULL;
+    gs_strfreev char    **searches            = NULL;
+    gs_strfreev char    **options             = NULL;
+    gs_strfreev char    **nameservers         = NULL;
+    gs_strfreev char    **nis_servers         = NULL;
+    gboolean              caching             = FALSE;
+    gboolean              do_update           = TRUE;
+    gboolean              resolv_conf_updated = FALSE;
+    SpawnResult           result              = SR_SUCCESS;
+    NMConfigData         *data;
+    NMGlobalDnsConfig    *global_config;
     gs_free_error GError *local_error   = NULL;
     GError **const        p_local_error = error ? &local_error : NULL;
 
     nm_assert(!error || !*error);
 
+    priv->config_changed = FALSE;
+
     if (priv->is_stopped) {
         _LOGD("update-dns: not updating resolv.conf (is stopped)");
         return TRUE;
@@ -1680,15 +1694,15 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError
     if (priv->sd_resolve_plugin) {
         nm_dns_plugin_update(priv->sd_resolve_plugin,
                              global_config,
-                             _mgr_get_ip_config_lst_head(self),
+                             _mgr_get_ip_data_lst_head(self),
                              priv->hostname,
                              NULL);
     }
 
     /* Let any plugins do their thing first */
     if (priv->plugin) {
-        NMDnsPlugin * plugin               = priv->plugin;
-        const char *  plugin_name          = nm_dns_plugin_get_name(plugin);
+        NMDnsPlugin          *plugin       = priv->plugin;
+        const char           *plugin_name  = nm_dns_plugin_get_name(plugin);
         gs_free_error GError *plugin_error = NULL;
 
         if (nm_dns_plugin_is_caching(plugin)) {
@@ -1702,7 +1716,7 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError
         _LOGD("update-dns: updating plugin %s", plugin_name);
         if (!nm_dns_plugin_update(plugin,
                                   global_config,
-                                  _mgr_get_ip_config_lst_head(self),
+                                  _mgr_get_ip_data_lst_head(self),
                                   priv->hostname,
                                   &plugin_error)) {
             _LOGW("update-dns: plugin %s update failed: %s", plugin_name, plugin_error->message);
@@ -1834,51 +1848,107 @@ plugin_skip:;
 
 /*****************************************************************************/
 
-static void
-_ip_config_dns_priority_changed(gpointer config, GParamSpec *pspec, NMDnsConfigIPData *ip_data)
-{
-    _ASSERT_dns_config_ip_data(ip_data);
-
-    NM_DNS_MANAGER_GET_PRIVATE(ip_data->data->self)->ip_config_lst_need_sort = TRUE;
-}
-
 gboolean
-nm_dns_manager_set_ip_config(NMDnsManager *    self,
-                             NMIPConfig *      ip_config,
-                             NMDnsIPConfigType ip_config_type)
+nm_dns_manager_set_ip_config(NMDnsManager         *self,
+                             int                   addr_family,
+                             gconstpointer         source_tag,
+                             const NML3ConfigData *l3cd,
+                             NMDnsIPConfigType     ip_config_type,
+                             gboolean              replace_all)
 {
     NMDnsManagerPrivate *priv;
-    NMDnsConfigIPData *  ip_data;
-    NMDnsConfigData *    data;
+    NMDnsConfigData     *data;
     int                  ifindex;
+    gboolean             changed = FALSE;
+    NMDnsConfigIPData   *ip_data = NULL;
+    int                  dns_priority;
+    gboolean             any_removed = FALSE;
 
     g_return_val_if_fail(NM_IS_DNS_MANAGER(self), FALSE);
-    g_return_val_if_fail(NM_IS_IP_CONFIG(ip_config), FALSE);
+    g_return_val_if_fail(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd), FALSE);
+    g_return_val_if_fail(source_tag, FALSE);
+
+    if (addr_family == AF_UNSPEC) {
+        /* Setting AF_UNSPEC is a shortcut for calling this function twice for AF_INET and
+         * AF_INET6. */
+        if (nm_dns_manager_set_ip_config(self,
+                                         AF_INET,
+                                         source_tag,
+                                         l3cd,
+                                         ip_config_type,
+                                         replace_all))
+            changed = TRUE;
+        if (nm_dns_manager_set_ip_config(self,
+                                         AF_INET6,
+                                         source_tag,
+                                         l3cd,
+                                         ip_config_type,
+                                         replace_all))
+            changed = TRUE;
+        return changed;
+    }
 
-    ifindex = nm_ip_config_get_ifindex(ip_config);
-    g_return_val_if_fail(ifindex > 0, FALSE);
+    nm_assert_addr_family(addr_family);
 
     priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
-    data = g_hash_table_lookup(priv->configs_dict, &ifindex);
-    if (!data)
-        ip_data = NULL;
-    else
-        ip_data = _dns_config_data_find_ip_config(data, ip_config);
+    data = NULL;
+    if (l3cd) {
+        ifindex = nm_l3_config_data_get_ifindex(l3cd);
+        nm_assert(ifindex > 0);
+        data = g_hash_table_lookup(priv->configs_dict, &ifindex);
+    }
+
+    if (data) {
+        NMDnsConfigIPData *ip_data_iter;
+        NMDnsConfigIPData *ip_data_safe;
 
-    if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED) {
-        if (!ip_data)
-            return FALSE;
-        /* deleting a config doesn't invalidate the configs' sort order. */
-        _dns_config_ip_data_free(ip_data);
-        if (c_list_is_empty(&data->data_lst_head))
-            g_hash_table_remove(priv->configs_dict, &ifindex);
-        goto changed;
+        c_list_for_each_entry_safe (ip_data_iter, ip_data_safe, &data->data_lst_head, data_lst) {
+            _ASSERT_dns_config_ip_data(ip_data_iter);
+
+            if (ip_data_iter->source_tag != source_tag)
+                continue;
+            if (ip_data_iter->addr_family != addr_family)
+                continue;
+
+            if (ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED && ip_data_iter->l3cd == l3cd) {
+                nm_assert(!ip_data);
+                ip_data = ip_data_iter;
+                continue;
+            }
+
+            if (!replace_all && l3cd && ip_data_iter->l3cd != l3cd)
+                continue;
+
+            if (!l3cd || ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED
+                || nm_l3_config_data_cmp_full(l3cd,
+                                              ip_data_iter->l3cd,
+                                              NM_L3_CONFIG_CMP_FLAGS_DNS
+                                                  | NM_L3_CONFIG_CMP_FLAGS_ROUTES_ID)
+                       != 0) {
+                changed = TRUE;
+            }
+
+            any_removed = TRUE;
+            _dns_config_ip_data_free(ip_data_iter);
+        }
     }
 
+    if (ip_config_type == NM_DNS_IP_CONFIG_TYPE_REMOVED)
+        goto done;
+
+    if (!l3cd)
+        goto done;
+
     if (ip_data && ip_data->ip_config_type == ip_config_type) {
         /* nothing to do. */
-        return FALSE;
+        goto done;
+    }
+
+    dns_priority = _dns_config_ip_data_get_dns_priority1(l3cd, addr_family);
+    if (dns_priority == 0) {
+        /* no DNS priority for this address family. Skip it! */
+        goto done;
     }
 
     if (!data) {
@@ -1894,14 +1964,34 @@ nm_dns_manager_set_ip_config(NMDnsManager *    self,
         priv->configs_lst_need_sort = TRUE;
     }
 
-    if (!ip_data)
-        ip_data = _dns_config_ip_data_new(data, ip_config, ip_config_type);
-    else
+    if (!ip_data) {
+        ip_data = _dns_config_ip_data_new(data, addr_family, source_tag, l3cd, ip_config_type);
+        if (!any_removed)
+            changed = TRUE;
+    } else {
         ip_data->ip_config_type = ip_config_type;
+        changed                 = TRUE;
+    }
+
+    if (changed)
+        priv->ip_data_lst_need_sort = TRUE;
+
+    nm_assert(l3cd);
+    nm_assert(ip_config_type != NM_DNS_IP_CONFIG_TYPE_REMOVED);
+    nm_assert(ip_data->addr_family == addr_family);
+    nm_assert(ip_data->source_tag == source_tag);
+    nm_assert(ip_data->l3cd == l3cd);
+    nm_assert(ip_data->ip_config_type == ip_config_type);
+
+done:
+    if (!changed)
+        return FALSE;
+
+    priv->config_changed = TRUE;
 
-    priv->ip_config_lst_need_sort = TRUE;
+    if (data && c_list_is_empty(&data->data_lst_head))
+        g_hash_table_remove(priv->configs_dict, data);
 
-changed:
     if (!priv->updates_queue) {
         gs_free_error GError *error = NULL;
 
@@ -1924,22 +2014,18 @@ nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname)
 void
 nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update)
 {
-    NMDnsManagerPrivate *priv     = NM_DNS_MANAGER_GET_PRIVATE(self);
-    const char *         filtered = NULL;
+    NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
     /* Certain hostnames we don't want to include in resolv.conf 'searches' */
     if (hostname && nm_utils_is_specific_hostname(hostname) && !strstr(hostname, ".in-addr.arpa")
         && strchr(hostname, '.')) {
-        filtered = hostname;
-    }
+        /* pass */
+    } else
+        hostname = NULL;
 
-    if ((!priv->hostname && !filtered)
-        || (priv->hostname && filtered && !strcmp(priv->hostname, filtered)))
+    if (!nm_strdup_reset(&priv->hostname, hostname))
         return;
 
-    g_free(priv->hostname);
-    priv->hostname = g_strdup(filtered);
-
     if (skip_update)
         return;
 
@@ -1957,6 +2043,7 @@ nm_dns_manager_begin_updates(NMDnsManager *self, const char *func)
     NMDnsManagerPrivate *priv;
 
     g_return_if_fail(self != NULL);
+
     priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
     /* Save current hash when starting a new batch */
@@ -1971,9 +2058,8 @@ nm_dns_manager_begin_updates(NMDnsManager *self, const char *func)
 void
 nm_dns_manager_end_updates(NMDnsManager *self, const char *func)
 {
-    NMDnsManagerPrivate *priv;
+    NMDnsManagerPrivate  *priv;
     gs_free_error GError *error = NULL;
-    gboolean              changed;
     guint8 new[HASH_LEN];
 
     g_return_if_fail(self != NULL);
@@ -1982,11 +2068,11 @@ nm_dns_manager_end_updates(NMDnsManager *self, const char *func)
     g_return_if_fail(priv->updates_queue > 0);
 
     compute_hash(self, nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)), new);
-    changed = (memcmp(new, priv->prev_hash, sizeof(new)) != 0) ? TRUE : FALSE;
-    _LOGD("(%s): DNS configuration %s", func, changed ? "changed" : "did not change");
+    priv->config_changed = (memcmp(new, priv->prev_hash, sizeof(new)) != 0) ? TRUE : FALSE;
+    _LOGD("(%s): DNS configuration %s", func, priv->config_changed ? "changed" : "did not change");
 
     priv->updates_queue--;
-    if ((priv->updates_queue > 0) || (changed == FALSE)) {
+    if ((priv->updates_queue > 0) || !priv->config_changed) {
         _LOGD("(%s): no DNS changes to commit (%d)", func, priv->updates_queue);
         return;
     }
@@ -1995,8 +2081,6 @@ nm_dns_manager_end_updates(NMDnsManager *self, const char *func)
     _LOGD("(%s): committing DNS changes (%d)", func, priv->updates_queue);
     if (!update_dns(self, FALSE, FALSE, &error))
         _LOGW("could not commit DNS changes: %s", error->message);
-
-    memset(priv->prev_hash, 0, sizeof(priv->prev_hash));
 }
 
 void
@@ -2116,7 +2200,7 @@ _resolvconf_resolved_managed(void)
         return FALSE;
 
     if (S_ISLNK(st.st_mode)) {
-        gs_free char *     full_path = NULL;
+        gs_free char      *full_path = NULL;
         nm_auto_free char *real_path = NULL;
 
         /* see if resolv.conf is a symlink with a target that is
@@ -2168,9 +2252,9 @@ _resolvconf_resolved_managed(void)
 static void
 init_resolv_conf_mode(NMDnsManager *self, gboolean force_reload_plugin)
 {
-    NMDnsManagerPrivate *         priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+    NMDnsManagerPrivate          *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
     NMDnsManagerResolvConfManager rc_manager;
-    const char *                  mode;
+    const char                   *mode;
     gboolean                      systemd_resolved;
     gboolean                      param_changed            = FALSE;
     gboolean                      plugin_changed           = FALSE;
@@ -2313,12 +2397,14 @@ again:
 }
 
 static void
-config_changed_cb(NMConfig *          config,
-                  NMConfigData *      config_data,
+config_changed_cb(NMConfig           *config,
+                  NMConfigData       *config_data,
                   NMConfigChangeFlags changes,
-                  NMConfigData *      old_data,
-                  NMDnsManager *      self)
+                  NMConfigData       *old_data,
+                  NMDnsManager       *self)
 {
+    NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+
     if (NM_FLAGS_ANY(changes,
                      NM_CONFIG_CHANGE_DNS_MODE | NM_CONFIG_CHANGE_RC_MANAGER
                          | NM_CONFIG_CHANGE_CAUSE_SIGHUP | NM_CONFIG_CHANGE_CAUSE_DNS_FULL)) {
@@ -2338,6 +2424,7 @@ config_changed_cb(NMConfig *          config,
                          | NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) {
         gs_free_error GError *error = NULL;
 
+        priv->config_changed = TRUE;
         if (!update_dns(self, FALSE, TRUE, &error))
             _LOGW("could not commit DNS changes: %s", error->message);
     }
@@ -2355,7 +2442,7 @@ _get_global_config_variant(NMGlobalDnsConfig *global)
     for (i = 0; i < num; i++) {
         GVariantBuilder    conf_builder;
         GVariantBuilder    item_builder;
-        const char *       domain_name;
+        const char        *domain_name;
         const char *const *servers;
 
         g_variant_builder_init(&conf_builder, G_VARIANT_TYPE("a{sv}"));
@@ -2395,12 +2482,12 @@ _get_global_config_variant(NMGlobalDnsConfig *global)
 static GVariant *
 _get_config_variant(NMDnsManager *self)
 {
-    NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
-    NMGlobalDnsConfig *  global_config;
-    gs_free char *       str = NULL;
-    GVariantBuilder      builder;
-    NMDnsConfigIPData *  ip_data;
-    const CList *        head;
+    NMDnsManagerPrivate         *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
+    NMGlobalDnsConfig           *global_config;
+    gs_free char                *str = NULL;
+    GVariantBuilder              builder;
+    NMDnsConfigIPData           *ip_data;
+    const CList                 *head;
     gs_unref_ptrarray GPtrArray *array_domains = NULL;
 
     if (priv->config_variant)
@@ -2415,51 +2502,54 @@ _get_config_variant(NMDnsManager *self)
 
     g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
 
-    head = _mgr_get_ip_config_lst_head(self);
-    c_list_for_each_entry (ip_data, head, ip_config_lst) {
-        const NMIPConfig *ip_config = ip_data->ip_config;
-        GVariantBuilder   entry_builder;
-        GVariantBuilder   strv_builder;
-        guint             i, num;
-        const int         addr_family = nm_ip_config_get_addr_family(ip_config);
-        char              buf[NM_UTILS_INET_ADDRSTRLEN];
-        const NMIPAddr *  addr;
-        const char *      ifname;
-
-        num = nm_ip_config_get_num_nameservers(ip_config);
-        if (!num)
+    head = _mgr_get_ip_data_lst_head(self);
+    c_list_for_each_entry (ip_data, head, ip_data_lst) {
+        GVariantBuilder entry_builder;
+        GVariantBuilder strv_builder;
+        guint           num;
+        guint           num_domains;
+        guint           num_searches;
+        guint           i;
+        char            buf[NM_UTILS_INET_ADDRSTRLEN];
+        const char     *ifname;
+        gconstpointer   nameservers;
+
+        nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &num);
+        if (num == 0)
             continue;
 
         g_variant_builder_init(&entry_builder, G_VARIANT_TYPE("a{sv}"));
 
         g_variant_builder_init(&strv_builder, G_VARIANT_TYPE("as"));
         for (i = 0; i < num; i++) {
-            addr = nm_ip_config_get_nameserver(ip_config, i);
-            g_variant_builder_add(&strv_builder, "s", nm_utils_inet_ntop(addr_family, addr, buf));
+            const NMIPAddr *addr;
+
+            addr = nm_ip_addr_from_packed_array(ip_data->addr_family, nameservers, i);
+            g_variant_builder_add(&strv_builder,
+                                  "s",
+                                  nm_utils_inet_ntop(ip_data->addr_family, addr, buf));
         }
         g_variant_builder_add(&entry_builder,
                               "{sv}",
                               "nameservers",
                               g_variant_builder_end(&strv_builder));
 
-        num = nm_ip_config_get_num_domains(ip_config);
-        num += nm_ip_config_get_num_searches(ip_config);
+        nm_l3_config_data_get_domains(ip_data->l3cd, ip_data->addr_family, &num_domains);
+        nm_l3_config_data_get_searches(ip_data->l3cd, ip_data->addr_family, &num_searches);
+        num = num_domains + num_searches;
         if (num > 0) {
             if (!array_domains)
                 array_domains = g_ptr_array_sized_new(num);
             else
                 g_ptr_array_set_size(array_domains, 0);
 
-            add_dns_domains(array_domains, ip_config, TRUE, FALSE);
+            add_dns_domains(array_domains, ip_data->addr_family, ip_data->l3cd, TRUE, FALSE);
             if (array_domains->len) {
-                g_variant_builder_init(&strv_builder, G_VARIANT_TYPE("as"));
-                for (i = 0; i < array_domains->len; i++) {
-                    g_variant_builder_add(&strv_builder, "s", array_domains->pdata[i]);
-                }
                 g_variant_builder_add(&entry_builder,
                                       "{sv}",
                                       "domains",
-                                      g_variant_builder_end(&strv_builder));
+                                      g_variant_new_strv((const char *const *) array_domains->pdata,
+                                                         array_domains->len));
             }
         }
 
@@ -2474,7 +2564,7 @@ _get_config_variant(NMDnsManager *self)
         g_variant_builder_add(&entry_builder,
                               "{sv}",
                               "priority",
-                              g_variant_new_int32(nm_ip_config_get_dns_priority(ip_config)));
+                              g_variant_new_int32(_dns_config_ip_data_get_dns_priority(ip_data)));
 
         g_variant_builder_add(
             &entry_builder,
@@ -2494,7 +2584,7 @@ _get_config_variant(NMDnsManager *self)
 static void
 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
-    NMDnsManager *       self = NM_DNS_MANAGER(object);
+    NMDnsManager        *self = NM_DNS_MANAGER(object);
     NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
     switch (prop_id) {
@@ -2521,7 +2611,7 @@ nm_dns_manager_init(NMDnsManager *self)
     _LOGT("creating...");
 
     c_list_init(&priv->configs_lst_head);
-    c_list_init(&priv->ip_config_lst_head);
+    c_list_init(&priv->ip_data_lst_head);
 
     priv->config = g_object_ref(nm_config_get());
 
@@ -2531,9 +2621,7 @@ nm_dns_manager_init(NMDnsManager *self)
                                                (GDestroyNotify) _dns_config_data_free,
                                                NULL);
 
-    /* Set the initial hash */
     compute_hash(self, NULL, NM_DNS_MANAGER_GET_PRIVATE(self)->hash);
-
     g_signal_connect(G_OBJECT(priv->config),
                      NM_CONFIG_SIGNAL_CONFIG_CHANGED,
                      G_CALLBACK(config_changed_cb),
@@ -2544,9 +2632,9 @@ nm_dns_manager_init(NMDnsManager *self)
 static void
 dispose(GObject *object)
 {
-    NMDnsManager *       self = NM_DNS_MANAGER(object);
+    NMDnsManager        *self = NM_DNS_MANAGER(object);
     NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
-    NMDnsConfigIPData *  ip_data, *ip_data_safe;
+    NMDnsConfigIPData   *ip_data, *ip_data_safe;
 
     _LOGT("disposing");
 
@@ -2559,7 +2647,7 @@ dispose(GObject *object)
     g_clear_object(&priv->sd_resolve_plugin);
     _clear_plugin(self);
 
-    c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_config_lst_head, ip_config_lst)
+    c_list_for_each_entry_safe (ip_data, ip_data_safe, &priv->ip_data_lst_head, ip_data_lst)
         _dns_config_ip_data_free(ip_data);
 
     nm_clear_pointer(&priv->configs_dict, g_hash_table_destroy);
@@ -2577,7 +2665,7 @@ dispose(GObject *object)
 static void
 finalize(GObject *object)
 {
-    NMDnsManager *       self = NM_DNS_MANAGER(object);
+    NMDnsManager        *self = NM_DNS_MANAGER(object);
     NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE(self);
 
     g_free(priv->hostname);
@@ -2602,7 +2690,7 @@ static const NMDBusInterfaceInfoExtended interface_info_dns_manager = {
 static void
 nm_dns_manager_class_init(NMDnsManagerClass *klass)
 {
-    GObjectClass *     object_class      = G_OBJECT_CLASS(klass);
+    GObjectClass      *object_class      = G_OBJECT_CLASS(klass);
     NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS(klass);
 
     object_class->dispose      = dispose;
diff --git a/src/core/dns/nm-dns-manager.h b/src/core/dns/nm-dns-manager.h
index 1972a5dd..84821ece 100644
--- a/src/core/dns/nm-dns-manager.h
+++ b/src/core/dns/nm-dns-manager.h
@@ -8,8 +8,7 @@
 #ifndef __NETWORKMANAGER_DNS_MANAGER_H__
 #define __NETWORKMANAGER_DNS_MANAGER_H__
 
-#include "nm-ip4-config.h"
-#include "nm-ip6-config.h"
+#include "c-list/src/c-list.h"
 #include "nm-setting-connection.h"
 #include "nm-dns-plugin.h"
 
@@ -28,13 +27,15 @@ struct _NMDnsManager;
 
 typedef struct {
     struct _NMDnsConfigData *data;
-    NMIPConfig *             ip_config;
+    gconstpointer            source_tag;
+    const NML3ConfigData    *l3cd;
     CList                    data_lst;
-    CList                    ip_config_lst;
+    CList                    ip_data_lst;
     NMDnsIPConfigType        ip_config_type;
+    int                      addr_family;
     struct {
         const char **search;
-        char **      reverse;
+        char       **reverse;
 
         /* Whether "search" explicitly contains a default route "~"
          * or "". It is redundant information, but for faster lookup. */
@@ -97,9 +98,12 @@ NMDnsManager *nm_dns_manager_get(void);
 void nm_dns_manager_begin_updates(NMDnsManager *self, const char *func);
 void nm_dns_manager_end_updates(NMDnsManager *self, const char *func);
 
-gboolean nm_dns_manager_set_ip_config(NMDnsManager *    self,
-                                      NMIPConfig *      ip_config,
-                                      NMDnsIPConfigType ip_config_type);
+gboolean nm_dns_manager_set_ip_config(NMDnsManager         *self,
+                                      int                   addr_family,
+                                      gconstpointer         source_tag,
+                                      const NML3ConfigData *l3cd,
+                                      NMDnsIPConfigType     ip_config_type,
+                                      gboolean              replace_all);
 
 void nm_dns_manager_set_initial_hostname(NMDnsManager *self, const char *hostname);
 void nm_dns_manager_set_hostname(NMDnsManager *self, const char *hostname, gboolean skip_update);
diff --git a/src/core/dns/nm-dns-plugin.c b/src/core/dns/nm-dns-plugin.c
index e5035f06..2126da77 100644
--- a/src/core/dns/nm-dns-plugin.c
+++ b/src/core/dns/nm-dns-plugin.c
@@ -57,11 +57,11 @@ G_DEFINE_ABSTRACT_TYPE(NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT)
 /*****************************************************************************/
 
 gboolean
-nm_dns_plugin_update(NMDnsPlugin *            self,
+nm_dns_plugin_update(NMDnsPlugin             *self,
                      const NMGlobalDnsConfig *global_config,
-                     const CList *            ip_config_lst_head,
-                     const char *             hostname,
-                     GError **                error)
+                     const CList             *ip_config_lst_head,
+                     const char              *hostname,
+                     GError                 **error)
 {
     g_return_val_if_fail(NM_DNS_PLUGIN_GET_CLASS(self)->update != NULL, FALSE);
 
diff --git a/src/core/dns/nm-dns-plugin.h b/src/core/dns/nm-dns-plugin.h
index 82ca7d6b..bc854cce 100644
--- a/src/core/dns/nm-dns-plugin.h
+++ b/src/core/dns/nm-dns-plugin.h
@@ -31,11 +31,11 @@ typedef struct {
      * 'global_config' is the optional global DNS
      * configuration.
      */
-    gboolean (*update)(NMDnsPlugin *            self,
+    gboolean (*update)(NMDnsPlugin             *self,
                        const NMGlobalDnsConfig *global_config,
-                       const CList *            ip_config_lst_head,
-                       const char *             hostname,
-                       GError **                error);
+                       const CList             *ip_config_lst_head,
+                       const char              *hostname,
+                       GError                 **error);
 
     void (*stop)(NMDnsPlugin *self);
 
@@ -55,11 +55,11 @@ gboolean nm_dns_plugin_is_caching(NMDnsPlugin *self);
 
 const char *nm_dns_plugin_get_name(NMDnsPlugin *self);
 
-gboolean nm_dns_plugin_update(NMDnsPlugin *            self,
+gboolean nm_dns_plugin_update(NMDnsPlugin             *self,
                               const NMGlobalDnsConfig *global_config,
-                              const CList *            ip_config_lst_head,
-                              const char *             hostname,
-                              GError **                error);
+                              const CList             *ip_config_lst_head,
+                              const char              *hostname,
+                              GError                 **error);
 
 void nm_dns_plugin_stop(NMDnsPlugin *self);
 
diff --git a/src/core/dns/nm-dns-systemd-resolved.c b/src/core/dns/nm-dns-systemd-resolved.c
index 445f8498..a043e855 100644
--- a/src/core/dns/nm-dns-systemd-resolved.c
+++ b/src/core/dns/nm-dns-systemd-resolved.c
@@ -21,10 +21,9 @@
 #include "libnm-core-intern/nm-core-internal.h"
 #include "libnm-platform/nm-platform.h"
 #include "nm-utils.h"
-#include "nm-ip4-config.h"
-#include "nm-ip6-config.h"
 #include "nm-dbus-manager.h"
 #include "nm-manager.h"
+#include "nm-l3-config-data.h"
 #include "nm-setting-connection.h"
 #include "devices/nm-device.h"
 #include "NetworkManagerUtils.h"
@@ -47,8 +46,8 @@ typedef struct {
 
 typedef struct {
     CList                 request_queue_lst;
-    const char *          operation;
-    GVariant *            argument;
+    const char           *operation;
+    GVariant             *argument;
     NMDnsSystemdResolved *self;
     int                   ifindex;
 } RequestItem;
@@ -56,8 +55,8 @@ typedef struct {
 struct _NMDnsSystemdResolvedResolveHandle {
     CList                 handle_lst;
     NMDnsSystemdResolved *self;
-    GSource *             timeout_source;
-    GCancellable *        handle_cancellable;
+    GSource              *timeout_source;
+    GCancellable         *handle_cancellable;
     gpointer              callback_user_data;
     guint                 timeout_msec;
     bool                  is_failing_on_idle;
@@ -76,11 +75,11 @@ struct _NMDnsSystemdResolvedResolveHandle {
 
 typedef struct {
     GDBusConnection *dbus_connection;
-    GHashTable *     dirty_interfaces;
-    GCancellable *   cancellable;
-    GSource *        try_start_timeout_source;
+    GHashTable      *dirty_interfaces;
+    GCancellable    *cancellable;
+    GSource         *try_start_timeout_source;
     CList            request_queue_lst_head;
-    char *           dbus_owner;
+    char            *dbus_owner;
     CList            handle_lst_head;
     guint            name_owner_changed_id;
     bool             send_updates_warn_ratelimited : 1;
@@ -157,12 +156,12 @@ _request_item_free(RequestItem *request_item)
 
 static void
 _request_item_append(NMDnsSystemdResolved *self,
-                     const char *          operation,
+                     const char           *operation,
                      int                   ifindex,
-                     GVariant *            argument)
+                     GVariant             *argument)
 {
     NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
-    RequestItem *                request_item;
+    RequestItem                 *request_item;
 
     request_item  = g_slice_new(RequestItem);
     *request_item = (RequestItem){
@@ -186,11 +185,11 @@ _interface_config_free(InterfaceConfig *config)
 static void
 call_done(GObject *source, GAsyncResult *r, gpointer user_data)
 {
-    gs_unref_variant GVariant *v       = NULL;
-    gs_free_error GError *       error = NULL;
-    NMDnsSystemdResolved *       self;
+    gs_unref_variant GVariant   *v     = NULL;
+    gs_free_error GError        *error = NULL;
+    NMDnsSystemdResolved        *self;
     NMDnsSystemdResolvedPrivate *priv;
-    RequestItem *                request_item;
+    RequestItem                 *request_item;
     NMLogLevel                   log_level;
 
     v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), r, &error);
@@ -244,42 +243,44 @@ call_done(GObject *source, GAsyncResult *r, gpointer user_data)
 
 static gboolean
 update_add_ip_config(NMDnsSystemdResolved *self,
-                     GVariantBuilder *     dns,
-                     GVariantBuilder *     domains,
-                     NMDnsConfigIPData *   data)
+                     GVariantBuilder      *dns,
+                     GVariantBuilder      *domains,
+                     NMDnsConfigIPData    *ip_data)
 {
-    int         addr_family;
-    gsize       addr_size;
-    guint       i, n;
-    gboolean    is_routing;
-    const char *domain;
-    gboolean    has_config = FALSE;
-
-    addr_family = nm_ip_config_get_addr_family(data->ip_config);
-    addr_size   = nm_utils_addr_family_to_size(addr_family);
-
-    if ((!data->domains.search || !data->domains.search[0])
-        && !data->domains.has_default_route_exclusive && !data->domains.has_default_route)
+    gsize         addr_size;
+    guint         n;
+    guint         i;
+    gboolean      is_routing;
+    const char   *domain;
+    gboolean      has_config = FALSE;
+    gconstpointer nameservers;
+
+    addr_size = nm_utils_addr_family_to_size(ip_data->addr_family);
+
+    if ((!ip_data->domains.search || !ip_data->domains.search[0])
+        && !ip_data->domains.has_default_route_exclusive && !ip_data->domains.has_default_route)
         return FALSE;
 
-    n = nm_ip_config_get_num_nameservers(data->ip_config);
+    nameservers = nm_l3_config_data_get_nameservers(ip_data->l3cd, ip_data->addr_family, &n);
     for (i = 0; i < n; i++) {
         g_variant_builder_open(dns, G_VARIANT_TYPE("(iay)"));
-        g_variant_builder_add(dns, "i", addr_family);
+        g_variant_builder_add(dns, "i", ip_data->addr_family);
         g_variant_builder_add_value(
             dns,
-            nm_g_variant_new_ay(nm_ip_config_get_nameserver(data->ip_config, i), addr_size));
+            nm_g_variant_new_ay(nm_ip_addr_from_packed_array(ip_data->addr_family, nameservers, i),
+                                addr_size));
         g_variant_builder_close(dns);
         has_config = TRUE;
     }
 
-    if (!data->domains.has_default_route_explicit && data->domains.has_default_route_exclusive) {
+    if (!ip_data->domains.has_default_route_explicit
+        && ip_data->domains.has_default_route_exclusive) {
         g_variant_builder_add(domains, "(sb)", ".", TRUE);
         has_config = TRUE;
     }
-    if (data->domains.search) {
-        for (i = 0; data->domains.search[i]; i++) {
-            domain = nm_utils_parse_dns_domain(data->domains.search[i], &is_routing);
+    if (ip_data->domains.search) {
+        for (i = 0; ip_data->domains.search[i]; i++) {
+            domain = nm_utils_parse_dns_domain(ip_data->domains.search[i], &is_routing);
             g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing);
             has_config = TRUE;
         }
@@ -292,7 +293,7 @@ static void
 free_pending_updates(NMDnsSystemdResolved *self)
 {
     NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
-    RequestItem *                request_item;
+    RequestItem                 *request_item;
 
     while ((request_item =
                 c_list_first_entry(&priv->request_queue_lst_head, RequestItem, request_queue_lst)))
@@ -304,11 +305,11 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic)
 {
     GVariantBuilder               dns;
     GVariantBuilder               domains;
-    NMCListElem *                 elem;
+    NMCListElem                  *elem;
     NMSettingConnectionMdns       mdns         = NM_SETTING_CONNECTION_MDNS_DEFAULT;
     NMSettingConnectionLlmnr      llmnr        = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
     NMSettingConnectionDnsOverTls dns_over_tls = NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT;
-    const char *                  mdns_arg = NULL, *llmnr_arg = NULL, *dns_over_tls_arg = NULL;
+    const char                   *mdns_arg = NULL, *llmnr_arg = NULL, *dns_over_tls_arg = NULL;
     gboolean                      has_config        = FALSE;
     gboolean                      has_default_route = FALSE;
 
@@ -321,19 +322,17 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic)
     g_variant_builder_open(&domains, G_VARIANT_TYPE("a(sb)"));
 
     c_list_for_each_entry (elem, &ic->configs_lst_head, lst) {
-        NMDnsConfigIPData *data      = elem->data;
-        NMIPConfig *       ip_config = data->ip_config;
+        NMDnsConfigIPData *ip_data = elem->data;
 
-        has_config |= update_add_ip_config(self, &dns, &domains, data);
+        has_config |= update_add_ip_config(self, &dns, &domains, ip_data);
 
-        if (data->domains.has_default_route)
+        if (ip_data->domains.has_default_route)
             has_default_route = TRUE;
 
-        if (NM_IS_IP4_CONFIG(ip_config)) {
-            mdns  = NM_MAX(mdns, nm_ip4_config_mdns_get(NM_IP4_CONFIG(ip_config)));
-            llmnr = NM_MAX(llmnr, nm_ip4_config_llmnr_get(NM_IP4_CONFIG(ip_config)));
-            dns_over_tls =
-                NM_MAX(dns_over_tls, nm_ip4_config_dns_over_tls_get(NM_IP4_CONFIG(ip_config)));
+        if (NM_IS_IPv4(ip_data->addr_family)) {
+            mdns         = NM_MAX(mdns, nm_l3_config_data_get_mdns(ip_data->l3cd));
+            llmnr        = NM_MAX(llmnr, nm_l3_config_data_get_llmnr(ip_data->l3cd));
+            dns_over_tls = NM_MAX(dns_over_tls, nm_l3_config_data_get_dns_over_tls(ip_data->l3cd));
         }
     }
 
@@ -417,8 +416,8 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic)
 static gboolean
 _ensure_resolved_running_timeout(gpointer user_data)
 {
-    NMDnsSystemdResolved *             self = user_data;
-    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolved              *self = user_data;
+    NMDnsSystemdResolvedPrivate       *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
     NMDnsSystemdResolvedResolveHandle *handle;
 
     nm_clear_g_source_inst(&priv->try_start_timeout_source);
@@ -479,8 +478,8 @@ ensure_resolved_running(NMDnsSystemdResolved *self)
 static void
 send_updates(NMDnsSystemdResolved *self)
 {
-    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
-    RequestItem *                      request_item;
+    NMDnsSystemdResolvedPrivate       *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    RequestItem                       *request_item;
     NMDnsSystemdResolvedResolveHandle *handle;
 
     if (!priv->send_updates_waiting) {
@@ -549,31 +548,31 @@ start_resolve:
 }
 
 static gboolean
-update(NMDnsPlugin *            plugin,
+update(NMDnsPlugin             *plugin,
        const NMGlobalDnsConfig *global_config,
-       const CList *            ip_config_lst_head,
-       const char *             hostname,
-       GError **                error)
+       const CList             *ip_data_lst_head,
+       const char              *hostname,
+       GError                 **error)
 {
-    NMDnsSystemdResolved *       self         = NM_DNS_SYSTEMD_RESOLVED(plugin);
-    NMDnsSystemdResolvedPrivate *priv         = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
-    gs_unref_hashtable GHashTable *interfaces = NULL;
-    gs_free gpointer * interfaces_keys        = NULL;
-    guint              interfaces_len;
-    int                ifindex;
-    gpointer           pointer;
-    NMDnsConfigIPData *ip_data;
-    GHashTableIter     iter;
-    guint              i;
+    NMDnsSystemdResolved          *self            = NM_DNS_SYSTEMD_RESOLVED(plugin);
+    NMDnsSystemdResolvedPrivate   *priv            = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    gs_unref_hashtable GHashTable *interfaces      = NULL;
+    gs_free gpointer              *interfaces_keys = NULL;
+    guint                          interfaces_len;
+    int                            ifindex;
+    gpointer                       pointer;
+    NMDnsConfigIPData             *ip_data;
+    GHashTableIter                 iter;
+    guint                          i;
 
     interfaces =
         g_hash_table_new_full(nm_direct_hash, NULL, NULL, (GDestroyNotify) _interface_config_free);
 
-    c_list_for_each_entry (ip_data, ip_config_lst_head, ip_config_lst) {
+    c_list_for_each_entry (ip_data, ip_data_lst_head, ip_data_lst) {
         InterfaceConfig *ic = NULL;
 
         ifindex = ip_data->data->ifindex;
-        nm_assert(ifindex == nm_ip_config_get_ifindex(ip_data->ip_config));
+        nm_assert(ifindex == nm_l3_config_data_get_ifindex(ip_data->l3cd));
 
         ic = g_hash_table_lookup(interfaces, GINT_TO_POINTER(ifindex));
         if (!ic) {
@@ -654,16 +653,16 @@ name_owner_changed(NMDnsSystemdResolved *self, const char *owner)
 
 static void
 name_owner_changed_cb(GDBusConnection *connection,
-                      const char *     sender_name,
-                      const char *     object_path,
-                      const char *     interface_name,
-                      const char *     signal_name,
-                      GVariant *       parameters,
+                      const char      *sender_name,
+                      const char      *object_path,
+                      const char      *interface_name,
+                      const char      *signal_name,
+                      GVariant        *parameters,
                       gpointer         user_data)
 {
-    NMDnsSystemdResolved *       self = user_data;
+    NMDnsSystemdResolved        *self = user_data;
     NMDnsSystemdResolvedPrivate *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
-    const char *                 new_owner;
+    const char                  *new_owner;
 
     if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)")))
         return;
@@ -684,7 +683,7 @@ name_owner_changed_cb(GDBusConnection *connection,
 static void
 get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data)
 {
-    NMDnsSystemdResolved *       self;
+    NMDnsSystemdResolved        *self;
     NMDnsSystemdResolvedPrivate *priv;
 
     if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
@@ -718,13 +717,13 @@ nm_dns_systemd_resolved_is_running(NMDnsSystemdResolved *self)
 /*****************************************************************************/
 
 static void
-_resolve_complete(NMDnsSystemdResolvedResolveHandle *      handle,
+_resolve_complete(NMDnsSystemdResolvedResolveHandle       *handle,
                   const NMDnsSystemdResolvedAddressResult *names,
                   guint                                    names_len,
                   guint64                                  flags,
-                  GError *                                 error)
+                  GError                                  *error)
 {
-    NMDnsSystemdResolved *       self;
+    NMDnsSystemdResolved        *self;
     NMDnsSystemdResolvedPrivate *priv;
 
     g_return_if_fail(handle && NM_IS_DNS_SYSTEMD_RESOLVED(handle->self));
@@ -760,16 +759,16 @@ _resolve_complete_error(NMDnsSystemdResolvedResolveHandle *handle, GError *error
 static void
 _resolve_handle_call_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
-    gs_unref_variant GVariant *v             = NULL;
-    gs_free_error GError *             error = NULL;
+    gs_unref_variant GVariant         *v     = NULL;
+    gs_free_error GError              *error = NULL;
     NMDnsSystemdResolvedResolveHandle *handle;
-    NMDnsSystemdResolved *             self;
-    GVariantIter *                     v_names_iter;
+    NMDnsSystemdResolved              *self;
+    GVariantIter                      *v_names_iter;
     guint64                            v_flags;
     int                                v_ifindex;
-    char *                             v_name;
-    gs_unref_array GArray *v_names = NULL;
-    gs_free char *         ss      = NULL;
+    char                              *v_name;
+    gs_unref_array GArray             *v_names = NULL;
+    gs_free char                      *ss      = NULL;
 
     v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
     if (nm_utils_error_is_cancelled(error))
@@ -824,7 +823,7 @@ static gboolean
 _resolve_failing_on_idle(gpointer user_data)
 {
     NMDnsSystemdResolvedResolveHandle *handle = user_data;
-    gs_free_error GError *error               = NULL;
+    gs_free_error GError              *error  = NULL;
 
     nm_utils_error_set_literal(&error,
                                NM_UTILS_ERROR_NOT_READY,
@@ -837,7 +836,7 @@ static gboolean
 _resolve_handle_timeout(gpointer user_data)
 {
     NMDnsSystemdResolvedResolveHandle *handle = user_data;
-    gs_free_error GError *error               = NULL;
+    gs_free_error GError              *error  = NULL;
 
     nm_utils_error_set_literal(&error, NM_UTILS_ERROR_UNKNOWN, "timeout for request");
     _resolve_complete_error(handle, error);
@@ -901,16 +900,16 @@ _resolve_start(NMDnsSystemdResolved *self, NMDnsSystemdResolvedResolveHandle *ha
 }
 
 NMDnsSystemdResolvedResolveHandle *
-nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved *                     self,
+nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved                      *self,
                                         int                                        ifindex,
                                         int                                        addr_family,
-                                        const NMIPAddr *                           addr,
+                                        const NMIPAddr                            *addr,
                                         guint64                                    flags,
                                         guint                                      timeout_msec,
                                         NMDnsSystemdResolvedResolveAddressCallback callback,
                                         gpointer                                   user_data)
 {
-    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolvedPrivate       *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
     NMDnsSystemdResolvedResolveHandle *handle;
     char                               addr_str[NM_UTILS_INET_ADDRSTRLEN];
 
@@ -999,8 +998,8 @@ nm_dns_systemd_resolved_new(void)
 static void
 dispose(GObject *object)
 {
-    NMDnsSystemdResolved *             self = NM_DNS_SYSTEMD_RESOLVED(object);
-    NMDnsSystemdResolvedPrivate *      priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
+    NMDnsSystemdResolved              *self = NM_DNS_SYSTEMD_RESOLVED(object);
+    NMDnsSystemdResolvedPrivate       *priv = NM_DNS_SYSTEMD_RESOLVED_GET_PRIVATE(self);
     NMDnsSystemdResolvedResolveHandle *handle;
 
     while ((handle = c_list_first_entry(&priv->handle_lst_head,
@@ -1032,7 +1031,7 @@ static void
 nm_dns_systemd_resolved_class_init(NMDnsSystemdResolvedClass *dns_class)
 {
     NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS(dns_class);
-    GObjectClass *    object_class = G_OBJECT_CLASS(dns_class);
+    GObjectClass     *object_class = G_OBJECT_CLASS(dns_class);
 
     object_class->dispose = dispose;
 
diff --git a/src/core/dns/nm-dns-systemd-resolved.h b/src/core/dns/nm-dns-systemd-resolved.h
index 9f973318..02bc524c 100644
--- a/src/core/dns/nm-dns-systemd-resolved.h
+++ b/src/core/dns/nm-dns-systemd-resolved.h
@@ -41,19 +41,19 @@ typedef struct {
 } NMDnsSystemdResolvedAddressResult;
 
 typedef void (*NMDnsSystemdResolvedResolveAddressCallback)(
-    NMDnsSystemdResolved *                   self,
-    NMDnsSystemdResolvedResolveHandle *      handle,
+    NMDnsSystemdResolved                    *self,
+    NMDnsSystemdResolvedResolveHandle       *handle,
     const NMDnsSystemdResolvedAddressResult *names,
     guint                                    names_len,
     guint64                                  flags,
-    GError *                                 error,
+    GError                                  *error,
     gpointer                                 user_data);
 
 NMDnsSystemdResolvedResolveHandle *
-nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved *                     self,
+nm_dns_systemd_resolved_resolve_address(NMDnsSystemdResolved                      *self,
                                         int                                        ifindex,
                                         int                                        addr_family,
-                                        const NMIPAddr *                           addr,
+                                        const NMIPAddr                            *addr,
                                         guint64                                    flags,
                                         guint                                      timeout_msec,
                                         NMDnsSystemdResolvedResolveAddressCallback callback,
diff --git a/src/core/dns/nm-dns-unbound.c b/src/core/dns/nm-dns-unbound.c
index 15df3acf..f5a53332 100644
--- a/src/core/dns/nm-dns-unbound.c
+++ b/src/core/dns/nm-dns-unbound.c
@@ -25,14 +25,14 @@ G_DEFINE_TYPE(NMDnsUnbound, nm_dns_unbound, NM_TYPE_DNS_PLUGIN)
 /*****************************************************************************/
 
 static gboolean
-update(NMDnsPlugin *            plugin,
+update(NMDnsPlugin             *plugin,
        const NMGlobalDnsConfig *global_config,
-       const CList *            ip_config_lst_head,
-       const char *             hostname,
-       GError **                error)
+       const CList             *ip_config_lst_head,
+       const char              *hostname,
+       GError                 **error)
 {
-    char *        argv[]        = {DNSSEC_TRIGGER_PATH, "--async", "--update", NULL};
-    gs_free_error GError *local = NULL;
+    char                 *argv[] = {DNSSEC_TRIGGER_PATH, "--async", "--update", NULL};
+    gs_free_error GError *local  = NULL;
     int                   status;
 
     /* TODO: We currently call a script installed with the dnssec-trigger