summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2020-07-21 14:27:48 +0200
committerThomas Haller <thaller@redhat.com>2020-07-23 15:29:25 +0200
commita1dbaf5799420f91f54bdd629376b4d69f256eac (patch)
tree258307a565b3bfb941e4866054d7abc18030776d
parent6e8a98776340fd0005021655f07d15135b62695c (diff)
downloadNetworkManager-th/l3cfg-1.tar.gz
l3cfg: add NML3ConfigDatath/l3cfg-1
Currently NMIP4Config and NMIP6Config both track the data to be configured, they expose properties on D-Bus, and they have logic for capturing and applying settings to platform. We will split that. - NMIP4Config and NMIP6Config will expose data on D-Bus. - NML3Cfg will have the logic for handling IP configuration. - NML3ConfigData will track data to be configured. NML3ConfigData mirrors NMIP4Config/NMIP6Config in many aspects. For now, this duplicates a lot of code. More will be done later. Eventually, NMIP4Config/NMIP6Config will drop the duplicated functionality.
-rw-r--r--Makefile.am2
-rw-r--r--src/meson.build1
-rw-r--r--src/nm-l3-config-data.c868
-rw-r--r--src/nm-l3-config-data.h63
4 files changed, 934 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 20bc92cb3d..e8e72febaf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2170,6 +2170,8 @@ src_libNetworkManager_la_SOURCES = \
src/nm-checkpoint-manager.c \
src/nm-checkpoint-manager.h \
\
+ src/nm-l3-config-data.c \
+ src/nm-l3-config-data.h \
src/nm-l3cfg.c \
src/nm-l3cfg.h \
\
diff --git a/src/meson.build b/src/meson.build
index eaba2b4ea5..468f2159da 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -131,6 +131,7 @@ sources = files(
'nm-auth-manager.c',
'nm-auth-utils.c',
'nm-dbus-manager.c',
+ 'nm-l3-config-data.c',
'nm-l3cfg.c',
'nm-checkpoint.c',
'nm-checkpoint-manager.c',
diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c
new file mode 100644
index 0000000000..d20df9dc36
--- /dev/null
+++ b/src/nm-l3-config-data.c
@@ -0,0 +1,868 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include "nm-default.h"
+
+#include "nm-l3-config-data.h"
+
+#include "nm-core-internal.h"
+#include "platform/nm-platform.h"
+#include "platform/nmp-object.h"
+#include "NetworkManagerUtils.h"
+
+/*****************************************************************************/
+
+typedef struct {
+ NMDedupMultiIdxType parent;
+ NMPObjectType obj_type;
+} DedupMultiIdxType;
+
+struct _NML3ConfigData {
+ NMDedupMultiIndex *multi_idx;
+
+ union {
+ struct {
+ DedupMultiIdxType idx_addresses_6;
+ DedupMultiIdxType idx_addresses_4;
+ };
+ DedupMultiIdxType idx_addresses_x[2];
+ };
+
+ union {
+ struct {
+ DedupMultiIdxType idx_routes_6;
+ DedupMultiIdxType idx_routes_4;
+ };
+ DedupMultiIdxType idx_routes_x[2];
+ };
+
+ union {
+ struct {
+ const NMPObject *best_default_route_6;
+ const NMPObject *best_default_route_4;
+ };
+ const NMPObject *best_default_route_x[2];
+ };
+
+ union {
+ struct {
+ GArray *nameservers_6;
+ GArray *nameservers_4;
+ };
+ GArray *nameservers_x[2];
+ };
+
+ union {
+ struct {
+ GPtrArray *domains_6;
+ GPtrArray *domains_4;
+ };
+ GPtrArray *domains_x[2];
+ };
+
+ union {
+ struct {
+ GPtrArray *searches_6;
+ GPtrArray *searches_4;
+ };
+ GPtrArray *searches_x[2];
+ };
+
+ union {
+ struct {
+ GPtrArray *dns_options_6;
+ GPtrArray *dns_options_4;
+ };
+ GPtrArray *dns_options_x[2];
+ };
+
+ union {
+ struct {
+ int dns_priority_6;
+ int dns_priority_4;
+ };
+ int dns_priority_x[2];
+ };
+
+ NMSettingConnectionMdns mdns;
+ NMSettingConnectionLlmnr llmnr;
+
+ int ref_count;
+
+ bool is_sealed:1;
+};
+
+/*****************************************************************************/
+
+static gboolean
+_route_valid_4 (const NMPlatformIP4Route *r)
+{
+ return r
+ && r->plen <= 32
+ && r->network == nm_utils_ip4_address_clear_host_address (r->network, r->plen);
+}
+
+static gboolean
+_route_valid_6 (const NMPlatformIP6Route *r)
+{
+ struct in6_addr n;
+
+ return r
+ && r->plen <= 128
+ && (memcmp (&r->network,
+ nm_utils_ip6_address_clear_host_address (&n, &r->network, r->plen),
+ sizeof (n)) == 0);
+}
+
+static gboolean
+_route_valid (int addr_family, gconstpointer r)
+{
+ nm_assert_addr_family (addr_family);
+
+ return addr_family == AF_INET
+ ? _route_valid_4 (r)
+ : _route_valid_6 (r);
+}
+
+static gboolean
+NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self, gboolean allow_sealed)
+{
+ return self
+ && self->ref_count > 0
+ && ( allow_sealed
+ || !self->is_sealed);
+}
+
+static GArray *
+_garray_ensure_for_addrbin (GArray **p_arr,
+ int addr_family)
+{
+ nm_assert (p_arr);
+ nm_assert_addr_family (addr_family);
+
+ if (G_UNLIKELY (!*p_arr)) {
+ *p_arr = g_array_new (FALSE,
+ FALSE,
+ nm_utils_addr_family_to_size (addr_family));
+ }
+ return *p_arr;
+}
+
+static void
+_idx_obj_id_hash_update (const NMDedupMultiIdxType *idx_type,
+ const NMDedupMultiObj *obj,
+ NMHashState *h)
+{
+ nmp_object_id_hash_update ((NMPObject *) obj, h);
+}
+
+static gboolean
+_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type,
+ const NMDedupMultiObj *obj_a,
+ const NMDedupMultiObj *obj_b)
+{
+ return nmp_object_id_equal ((NMPObject *) obj_a, (NMPObject *) obj_b);
+}
+
+static void
+_idx_type_init (DedupMultiIdxType *idx_type,
+ NMPObjectType obj_type)
+{
+ static const NMDedupMultiIdxTypeClass idx_type_class = {
+ .idx_obj_id_hash_update = _idx_obj_id_hash_update,
+ .idx_obj_id_equal = _idx_obj_id_equal,
+ };
+
+ nm_dedup_multi_idx_type_init (&idx_type->parent,
+ &idx_type_class);
+ idx_type->obj_type = obj_type;
+}
+
+NML3ConfigData *
+nm_l3_config_data_new (NMDedupMultiIndex *multi_idx)
+{
+ NML3ConfigData *self;
+
+ nm_assert (multi_idx);
+
+ self = g_slice_new (NML3ConfigData);
+ *self = (NML3ConfigData) {
+ .ref_count = 1,
+ .multi_idx = nm_dedup_multi_index_ref (multi_idx),
+ .mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT,
+ .llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT,
+ };
+
+ _idx_type_init (&self->idx_addresses_4, NMP_OBJECT_TYPE_IP4_ADDRESS);
+ _idx_type_init (&self->idx_addresses_6, NMP_OBJECT_TYPE_IP4_ADDRESS);
+ _idx_type_init (&self->idx_routes_4, NMP_OBJECT_TYPE_IP4_ROUTE);
+ _idx_type_init (&self->idx_routes_6, NMP_OBJECT_TYPE_IP6_ROUTE);
+
+ return self;
+}
+
+NML3ConfigData *
+nm_l3_config_data_ref (NML3ConfigData *self)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ self->ref_count++;
+ return self;
+}
+
+NML3ConfigData *
+nm_l3_config_data_ref_and_seal (NML3ConfigData *self)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ self->is_sealed = TRUE;
+ self->ref_count++;
+ return self;
+}
+
+NML3ConfigData *
+nm_l3_config_data_seal (NML3ConfigData *self)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ self->is_sealed = TRUE;
+ return self;
+}
+
+gboolean
+nm_l3_config_data_is_sealed (NML3ConfigData *self)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ return self->is_sealed;
+}
+
+void
+nm_l3_config_data_unref (NML3ConfigData *self)
+{
+ if (!self)
+ return;
+
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ if (--self->ref_count > 0)
+ return;
+
+ nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_addresses_4.parent);
+ nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_addresses_6.parent);
+ nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_routes_4.parent);
+ nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_routes_6.parent);
+
+ nmp_object_unref (self->best_default_route_4);
+ nmp_object_unref (self->best_default_route_6);
+
+ nm_clear_pointer (&self->nameservers_4, g_array_unref);
+ nm_clear_pointer (&self->nameservers_6, g_array_unref);
+
+ nm_clear_pointer (&self->domains_4, g_ptr_array_unref);
+ nm_clear_pointer (&self->domains_6, g_ptr_array_unref);
+
+ nm_clear_pointer (&self->searches_4, g_ptr_array_unref);
+ nm_clear_pointer (&self->searches_6, g_ptr_array_unref);
+
+ nm_clear_pointer (&self->dns_options_4, g_ptr_array_unref);
+ nm_clear_pointer (&self->dns_options_6, g_ptr_array_unref);
+
+ nm_dedup_multi_index_unref (self->multi_idx);
+
+ nm_g_slice_free (self);
+}
+
+/*****************************************************************************/
+
+const NMDedupMultiHeadEntry *
+nm_l3_config_data_lookup_addresses (const NML3ConfigData *self,
+ int addr_family)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ nm_assert_addr_family (addr_family);
+
+ return nm_dedup_multi_index_lookup_head (self->multi_idx,
+ &self->idx_addresses_x[NM_IS_IPv4 (addr_family)].parent,
+ NULL);
+}
+
+const NMDedupMultiHeadEntry *
+nm_l3_config_data_lookup_routes (const NML3ConfigData *self,
+ int addr_family)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE));
+ nm_assert_addr_family (addr_family);
+
+ return nm_dedup_multi_index_lookup_head (self->multi_idx,
+ &self->idx_routes_x[NM_IS_IPv4 (addr_family)].parent,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static gboolean
+_l3_config_data_add_obj (NMDedupMultiIndex *multi_idx,
+ DedupMultiIdxType *idx_type,
+ int ifindex,
+ const NMPObject *obj_new,
+ const NMPlatformObject *pl_new,
+ gboolean merge,
+ gboolean append_force,
+ const NMPObject **out_obj_old /* returns a reference! */,
+ const NMPObject **out_obj_new /* does not return a reference */)
+{
+ NMPObject obj_new_stackinit;
+ const NMDedupMultiEntry *entry_old;
+ const NMDedupMultiEntry *entry_new;
+
+ nm_assert (multi_idx);
+ nm_assert (idx_type);
+ nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS,
+ NMP_OBJECT_TYPE_IP4_ROUTE,
+ NMP_OBJECT_TYPE_IP6_ADDRESS,
+ NMP_OBJECT_TYPE_IP6_ROUTE));
+ nm_assert (ifindex > 0);
+
+ /* we go through extra lengths to accept a full obj_new object. That one,
+ * can be reused by increasing the ref-count. */
+ if (!obj_new) {
+ nm_assert (pl_new);
+ obj_new = nmp_object_stackinit (&obj_new_stackinit, idx_type->obj_type, pl_new);
+ NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex;
+ } else {
+ nm_assert (!pl_new);
+ nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type);
+ if (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_new)->ifindex != ifindex) {
+ obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new);
+ NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex;
+ }
+ }
+ nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type);
+ nm_assert (nmp_object_is_alive (obj_new));
+
+ entry_old = nm_dedup_multi_index_lookup_obj (multi_idx, &idx_type->parent, obj_new);
+
+ if (entry_old) {
+ gboolean modified = FALSE;
+ const NMPObject *obj_old = entry_old->obj;
+
+ if (nmp_object_equal (obj_new, obj_old)) {
+ nm_dedup_multi_entry_set_dirty (entry_old, FALSE);
+ goto append_force_and_out;
+ }
+
+ /* if @merge, we merge the new object with the existing one.
+ * Otherwise, we replace it entirely. */
+ if (merge) {
+ switch (idx_type->obj_type) {
+ case NMP_OBJECT_TYPE_IP4_ADDRESS:
+ case NMP_OBJECT_TYPE_IP6_ADDRESS:
+ /* for addresses that we read from the kernel, we keep the timestamps as defined
+ * by the previous source (item_old). The reason is, that the other source configured the lifetimes
+ * with "what should be" and the kernel values are "what turned out after configuring it".
+ *
+ * For other sources, the longer lifetime wins. */
+ if ( ( obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL
+ && obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL)
+ || nm_platform_ip_address_cmp_expiry (NMP_OBJECT_CAST_IP_ADDRESS (obj_old), NMP_OBJECT_CAST_IP_ADDRESS(obj_new)) > 0) {
+ obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new);
+ obj_new_stackinit.ip_address.timestamp = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->timestamp;
+ obj_new_stackinit.ip_address.lifetime = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->lifetime;
+ obj_new_stackinit.ip_address.preferred = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->preferred;
+ modified = TRUE;
+ }
+
+ /* keep the maximum addr_source. */
+ if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) {
+ obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new);
+ obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source;
+ modified = TRUE;
+ }
+ break;
+ case NMP_OBJECT_TYPE_IP4_ROUTE:
+ case NMP_OBJECT_TYPE_IP6_ROUTE:
+ /* keep the maximum rt_source. */
+ if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) {
+ obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new);
+ obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source;
+ modified = TRUE;
+ }
+ break;
+ default:
+ nm_assert_not_reached ();
+ break;
+ }
+
+ if ( modified
+ && nmp_object_equal (obj_new, obj_old)) {
+ nm_dedup_multi_entry_set_dirty (entry_old, FALSE);
+ goto append_force_and_out;
+ }
+ }
+ }
+
+ if (!nm_dedup_multi_index_add_full (multi_idx,
+ &idx_type->parent,
+ obj_new,
+ append_force
+ ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE
+ : NM_DEDUP_MULTI_IDX_MODE_APPEND,
+ NULL,
+ entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING,
+ NULL,
+ &entry_new,
+ out_obj_old)) {
+ nm_assert_not_reached ();
+ NM_SET_OUT (out_obj_new, NULL);
+ return FALSE;
+ }
+
+ NM_SET_OUT (out_obj_new, entry_new->obj);
+ return TRUE;
+
+append_force_and_out:
+ NM_SET_OUT (out_obj_old, nmp_object_ref (entry_old->obj));
+ NM_SET_OUT (out_obj_new, entry_old->obj);
+ if (append_force) {
+ if (nm_dedup_multi_entry_reorder (entry_old, NULL, TRUE))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static const NMPObject *
+_l3_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp)
+{
+ nm_assert ( !obj_cur
+ || NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cur), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
+ nm_assert ( !obj_cmp
+ || ( !obj_cur
+ && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cmp), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE))
+ || NMP_OBJECT_GET_TYPE (obj_cur) == NMP_OBJECT_GET_TYPE (obj_cmp));
+ nm_assert ( !obj_cur
+ || nmp_object_ip_route_is_best_defaut_route (obj_cur));
+
+ /* assumes that @obj_cur is already the best default route (or NULL). It checks whether
+ * @obj_cmp is also a default route and returns the best of both. */
+ if ( obj_cmp
+ && nmp_object_ip_route_is_best_defaut_route (obj_cmp)) {
+ guint32 metric_cur, metric_cmp;
+
+ if (!obj_cur)
+ return obj_cmp;
+
+ if (obj_cur == obj_cmp)
+ return obj_cmp;
+
+ metric_cur = NMP_OBJECT_CAST_IP_ROUTE (obj_cur)->metric;
+ metric_cmp = NMP_OBJECT_CAST_IP_ROUTE (obj_cmp)->metric;
+
+ if (metric_cmp < metric_cur)
+ return obj_cmp;
+
+ if (metric_cmp == metric_cur) {
+ int c;
+
+ /* Routes have the same metric. We still want to deterministically
+ * prefer one or the other. It's important to consistently choose one
+ * or the other, so that the order doesn't matter how routes are added
+ * (and merged). */
+ c = nmp_object_cmp (obj_cur, obj_cmp);
+ if (c != 0)
+ return c < 0 ? obj_cur : obj_cmp;
+
+ /* as last resort, compare pointers. */
+ if (((uintptr_t) ((void *) (obj_cmp))) < ((uintptr_t) ((void *) (obj_cur))))
+ return obj_cmp;
+ }
+ }
+ return obj_cur;
+}
+
+static gboolean
+_l3_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate)
+{
+ new_candidate = _l3_config_best_default_route_find_better (*best_default_route,
+ new_candidate);
+ return nmp_object_ref_set (best_default_route, new_candidate);
+}
+
+gboolean
+_nm_l3_config_data_add_route (NML3ConfigData *self,
+ int addr_family,
+ int ifindex,
+ const NMPObject *obj_new,
+ const NMPlatformIPRoute *pl_new,
+ const NMPObject **out_obj_new,
+ gboolean *out_changed_best_default_route)
+{
+ const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family);
+ nm_auto_nmpobj const NMPObject *obj_old = NULL;
+ const NMPObject *obj_new_2;
+ gboolean changed = FALSE;
+ gboolean changed_best_default_route = FALSE;
+
+ nm_assert_addr_family (addr_family);
+ nm_assert (ifindex > 0);
+ nm_assert ((!pl_new) != (!obj_new));
+ nm_assert ( !pl_new
+ || _route_valid (addr_family, pl_new));
+ nm_assert ( !obj_new
+ || ( NMP_OBJECT_GET_ADDR_FAMILY (obj_new) == addr_family
+ && _route_valid (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_new))));
+
+ if (_l3_config_data_add_obj (self->multi_idx,
+ addr_family == AF_INET
+ ? &self->idx_routes_4
+ : &self->idx_routes_6,
+ ifindex,
+ obj_new,
+ (const NMPlatformObject *) pl_new,
+ TRUE,
+ FALSE,
+ &obj_old,
+ &obj_new_2)) {
+
+ if ( self->best_default_route_x[IS_IPv4] == obj_old
+ && obj_old != obj_new_2) {
+ changed_best_default_route = TRUE;
+ nm_clear_nmp_object (&self->best_default_route_x[IS_IPv4]);
+ }
+
+ if (_l3_config_best_default_route_merge (&self->best_default_route_x[IS_IPv4],
+ obj_new_2))
+ changed_best_default_route = TRUE;
+
+ changed = TRUE;
+ }
+
+ NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2));
+ NM_SET_OUT (out_changed_best_default_route, changed_best_default_route);
+ return changed;
+}
+
+gboolean
+_nm_l3_config_data_add_address (NML3ConfigData *self,
+ int addr_family,
+ int ifindex,
+ const NMPObject *obj_new,
+ const NMPlatformIPAddress *pl_new)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE));
+ nm_assert_addr_family (addr_family);
+ nm_assert (ifindex > 0);
+ nm_assert ((!pl_new) != (!obj_new));
+ nm_assert ( !obj_new
+ || NMP_OBJECT_GET_ADDR_FAMILY (obj_new) == addr_family);
+
+ return _l3_config_data_add_obj (self->multi_idx,
+ addr_family == AF_INET
+ ? &self->idx_addresses_4
+ : &self->idx_addresses_6,
+ ifindex,
+ obj_new,
+ (const NMPlatformObject *) pl_new,
+ TRUE,
+ FALSE,
+ NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static gboolean
+_check_and_add_domain (GPtrArray **p_arr, const char *domain)
+{
+ gs_free char *copy = NULL;
+ gsize len;
+
+ nm_assert (p_arr);
+ g_return_val_if_fail (domain, FALSE);
+
+ if (domain[0] == '\0')
+ g_return_val_if_reached (FALSE);
+
+ if (domain[0] == '.' || strstr (domain, ".."))
+ return FALSE;
+
+ len = strlen (domain);
+ if (domain[len - 1] == '.') {
+ copy = g_strndup (domain, len - 1);
+ domain = copy;
+ }
+
+ if (nm_strv_ptrarray_contains (*p_arr, domain))
+ return FALSE;
+
+ nm_strv_ptrarray_add_string_take (nm_strv_ptrarray_ensure (p_arr),
+ g_steal_pointer (&copy)
+ ?: g_strdup (domain));
+ return TRUE;
+}
+
+gboolean
+_nm_l3_config_data_add_domain (NML3ConfigData *self,
+ int addr_family,
+ const char *domain)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE));
+ nm_assert_addr_family (addr_family);
+
+ return _check_and_add_domain (&self->domains_x[NM_IS_IPv4 (addr_family)], domain);
+}
+
+gboolean
+_nm_l3_config_data_add_search (NML3ConfigData *self,
+ int addr_family,
+ const char *search)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE));
+ nm_assert_addr_family (addr_family);
+
+ return _check_and_add_domain (&self->searches_x[NM_IS_IPv4 (addr_family)], search);
+}
+
+gboolean
+_nm_l3_config_data_add_dns_option (NML3ConfigData *self,
+ int addr_family,
+ const char *dns_option)
+{
+ GPtrArray **p_arr;
+
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE));
+ nm_assert_addr_family (addr_family);
+
+ g_return_val_if_fail (dns_option, FALSE);
+
+ if (!dns_option[0])
+ g_return_val_if_reached (FALSE);
+
+ p_arr = &self->dns_options_x[NM_IS_IPv4 (addr_family)];
+
+ if (nm_strv_ptrarray_contains (*p_arr, dns_option))
+ return FALSE;
+
+ nm_strv_ptrarray_add_string_dup (nm_strv_ptrarray_ensure (p_arr),
+ dns_option);
+ return TRUE;
+}
+
+gboolean
+_nm_l3_config_data_set_dns_priority (NML3ConfigData *self,
+ int addr_family,
+ int dns_priority)
+{
+ int *p_val;
+
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE));
+ nm_assert_addr_family (addr_family);
+
+ p_val = &self->dns_priority_x[NM_IS_IPv4 (addr_family)];
+ if (*p_val == dns_priority)
+ return FALSE;
+
+ *p_val = dns_priority;
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+_init_from_connection_ip (NML3ConfigData *self,
+ int addr_family,
+ int ifindex,
+ NMConnection *connection,
+ guint32 route_table,
+ guint32 route_metric)
+{
+ const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family);
+ NMSettingIPConfig *s_ip;
+ guint naddresses;
+ guint nroutes;
+ guint nnameservers;
+ guint nsearches;
+ const char *gateway_str;
+ NMIPAddr gateway_bin;
+ guint i;
+ int idx;
+
+ nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE));
+ nm_assert (ifindex > 0);
+ nm_assert_addr_family (addr_family);
+ nm_assert (!connection || NM_IS_CONNECTION (connection));
+
+ if (!connection)
+ return;
+
+ s_ip = nm_connection_get_setting_ip_config (connection, addr_family);
+ if (!s_ip)
+ return;
+
+ if ( !nm_setting_ip_config_get_never_default (s_ip)
+ && (gateway_str = nm_setting_ip_config_get_gateway (s_ip))
+ && inet_pton (addr_family, gateway_str, &gateway_bin) == 1
+ && !nm_ip_addr_is_null (addr_family, &gateway_bin)) {
+ NMPlatformIPXRoute r;
+
+ if (IS_IPv4) {
+ r.r4 = (NMPlatformIP4Route) {
+ .rt_source = NM_IP_CONFIG_SOURCE_USER,
+ .gateway = gateway_bin.addr4,
+ .table_coerced = nm_platform_route_table_coerce (route_table),
+ .metric = route_metric,
+ };
+ } else {
+ r.r6 = (NMPlatformIP6Route) {
+ .rt_source = NM_IP_CONFIG_SOURCE_USER,
+ .gateway = gateway_bin.addr6,
+ .table_coerced = nm_platform_route_table_coerce (route_table),
+ .metric = route_metric,
+ };
+ }
+
+ _nm_l3_config_data_add_route (self, addr_family, ifindex, NULL, &r.rx, NULL, NULL);
+ }
+
+ naddresses = nm_setting_ip_config_get_num_addresses (s_ip);
+ for (i = 0; i < naddresses; i++) {
+ NMIPAddress *s_addr = nm_setting_ip_config_get_address (s_ip, i);
+ NMPlatformIPXAddress a;
+ NMIPAddr addr_bin;
+ GVariant *label;
+
+ nm_assert (nm_ip_address_get_family (s_addr) == addr_family);
+
+ nm_ip_address_get_address_binary (s_addr, &addr_bin);
+
+ if (IS_IPv4) {
+ a.a4 = (NMPlatformIP4Address) {
+ .address = addr_bin.addr4,
+ .peer_address = addr_bin.addr4,
+ .plen = nm_ip_address_get_prefix (s_addr),
+ .lifetime = NM_PLATFORM_LIFETIME_PERMANENT,
+ .preferred = NM_PLATFORM_LIFETIME_PERMANENT,
+ .addr_source = NM_IP_CONFIG_SOURCE_USER,
+ };
+ label = nm_ip_address_get_attribute (s_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+ if (label)
+ g_strlcpy (a.a4.label, g_variant_get_string (label, NULL), sizeof (a.a4.label));
+
+ nm_assert (a.a4.plen <= 32);
+ } else {
+ a.a6 = (NMPlatformIP6Address) {
+ .address = addr_bin.addr6,
+ .plen = nm_ip_address_get_prefix (s_addr),
+ .lifetime = NM_PLATFORM_LIFETIME_PERMANENT,
+ .preferred = NM_PLATFORM_LIFETIME_PERMANENT,
+ .addr_source = NM_IP_CONFIG_SOURCE_USER,
+ };
+
+ nm_assert (a.a6.plen <= 128);
+ }
+
+ _nm_l3_config_data_add_address (self, addr_family, ifindex, NULL, &a.ax);
+ }
+
+ nroutes = nm_setting_ip_config_get_num_routes (s_ip);
+ for (i = 0; i < nroutes; i++) {
+ NMIPRoute *s_route = nm_setting_ip_config_get_route (s_ip, i);
+ NMPlatformIPXRoute r;
+ NMIPAddr network_bin;
+ NMIPAddr next_hop_bin;
+ gint64 metric64;
+ guint32 metric;
+ guint plen;
+
+ nm_assert (nm_ip_route_get_family (s_route) == addr_family);
+
+ nm_ip_route_get_dest_binary (s_route, &network_bin);
+ nm_ip_route_get_next_hop_binary (s_route, &next_hop_bin);
+
+ metric64 = nm_ip_route_get_metric (s_route);
+ if (metric64 < 0)
+ metric = route_metric;
+ else
+ metric = metric64;
+ metric = nm_utils_ip_route_metric_normalize (addr_family, metric);
+
+ plen = nm_ip_route_get_prefix (s_route);
+
+ nm_utils_ipx_address_clear_host_address (addr_family, &network_bin, &network_bin, plen);
+
+ if (IS_IPv4) {
+ r.r4 = (NMPlatformIP4Route) {
+ .network = network_bin.addr4,
+ .plen = nm_ip_route_get_prefix (s_route),
+ .gateway = next_hop_bin.addr4,
+ .metric = metric,
+ .rt_source = NM_IP_CONFIG_SOURCE_USER,
+ };
+ nm_assert (r.r4.plen <= 32);
+ } else {
+ r.r6 = (NMPlatformIP6Route) {
+ .network = network_bin.addr6,
+ .plen = nm_ip_route_get_prefix (s_route),
+ .gateway = next_hop_bin.addr6,
+ .metric = metric,
+ .rt_source = NM_IP_CONFIG_SOURCE_USER,
+ };
+ nm_assert (r.r6.plen <= 128);
+ }
+
+ nm_utils_ip_route_attribute_to_platform (addr_family,
+ s_route,
+ &r.rx,
+ route_table);
+
+ _nm_l3_config_data_add_route (self, addr_family, ifindex, NULL, &r.rx, NULL, NULL);
+ }
+
+ nnameservers = nm_setting_ip_config_get_num_dns (s_ip);
+ for (i = 0; i < nnameservers; i++) {
+ const char *s;
+ NMIPAddr ip;
+
+ s = nm_setting_ip_config_get_dns (s_ip, i);
+ if (!nm_utils_parse_inaddr_bin (addr_family, s, NULL, &ip))
+ continue;
+ g_array_append_vals (_garray_ensure_for_addrbin (&self->nameservers_x[IS_IPv4], addr_family),
+ &ip,
+ 1);
+ }
+
+ nsearches = nm_setting_ip_config_get_num_dns_searches (s_ip);
+ for (i = 0; i < nsearches; i++) {
+ _nm_l3_config_data_add_search (self,
+ addr_family,
+ nm_setting_ip_config_get_dns_search (s_ip, i));
+ }
+
+ idx = 0;
+ while ((idx = nm_setting_ip_config_next_valid_dns_option (s_ip, i)) >= 0) {
+ _nm_l3_config_data_add_dns_option (self,
+ addr_family,
+ nm_setting_ip_config_get_dns_option (s_ip, i));
+ idx++;
+ }
+
+ _nm_l3_config_data_set_dns_priority (self,
+ addr_family,
+ nm_setting_ip_config_get_dns_priority (s_ip));
+}
+
+NML3ConfigData *
+nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_index,
+ int ifindex,
+ NMConnection *connection,
+ NMSettingConnectionMdns mdns,
+ NMSettingConnectionLlmnr llmnr,
+ guint32 route_table,
+ guint32 route_metric)
+{
+ NML3ConfigData *self;
+
+ self = nm_l3_config_data_new (multi_index);
+
+ _init_from_connection_ip (self, AF_INET, ifindex, connection, route_table, route_metric);
+ _init_from_connection_ip (self, AF_INET6, ifindex, connection, route_table, route_metric);
+
+ self->mdns = mdns;
+ self->llmnr = llmnr;
+
+ return self;
+}
diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h
new file mode 100644
index 0000000000..f7b87d7d16
--- /dev/null
+++ b/src/nm-l3-config-data.h
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#ifndef __NM_L3_CONFIG_DATA_H__
+#define __NM_L3_CONFIG_DATA_H__
+
+#include "nm-glib-aux/nm-dedup-multi.h"
+#include "nm-setting-connection.h"
+#include "platform/nm-platform.h"
+
+typedef struct _NML3ConfigData NML3ConfigData;
+
+NML3ConfigData *nm_l3_config_data_new (NMDedupMultiIndex *multi_idx);
+NML3ConfigData *nm_l3_config_data_ref (NML3ConfigData *self);
+NML3ConfigData *nm_l3_config_data_ref_and_seal (NML3ConfigData *self);
+NML3ConfigData *nm_l3_config_data_seal (NML3ConfigData *self);
+void nm_l3_config_data_unref (NML3ConfigData *self);
+
+gboolean nm_l3_config_data_is_sealed (NML3ConfigData *self);
+
+const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_addresses (const NML3ConfigData *self, int addr_family);
+const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_routes (const NML3ConfigData *self, int addr_family);
+
+NML3ConfigData *nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_index,
+ int ifindex,
+ NMConnection *connection,
+ NMSettingConnectionMdns mdns,
+ NMSettingConnectionLlmnr llmnr,
+ guint32 route_table,
+ guint32 route_metric);
+
+/*****************************************************************************/
+
+gboolean _nm_l3_config_data_add_address (NML3ConfigData *self,
+ int addr_family,
+ int ifindex,
+ const NMPObject *obj_new,
+ const NMPlatformIPAddress *pl_new);
+
+gboolean _nm_l3_config_data_add_route (NML3ConfigData *self,
+ int addr_family,
+ int ifindex,
+ const NMPObject *obj_new,
+ const NMPlatformIPRoute *pl_new,
+ const NMPObject **out_obj_new,
+ gboolean *out_changed_best_default_route);
+
+gboolean _nm_l3_config_data_add_domain (NML3ConfigData *self,
+ int addr_family,
+ const char *domain);
+
+gboolean _nm_l3_config_data_add_search (NML3ConfigData *self,
+ int addr_family,
+ const char *search);
+
+gboolean _nm_l3_config_data_add_dns_option (NML3ConfigData *self,
+ int addr_family,
+ const char *dns_option);
+
+gboolean _nm_l3_config_data_set_dns_priority (NML3ConfigData *self,
+ int addr_family,
+ int dns_priority);
+
+#endif /* __NM_L3_CONFIG_DATA_H__ */