diff options
author | Thomas Haller <thaller@redhat.com> | 2020-07-21 14:27:48 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-07-23 15:29:25 +0200 |
commit | a1dbaf5799420f91f54bdd629376b4d69f256eac (patch) | |
tree | 258307a565b3bfb941e4866054d7abc18030776d | |
parent | 6e8a98776340fd0005021655f07d15135b62695c (diff) | |
download | NetworkManager-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.am | 2 | ||||
-rw-r--r-- | src/meson.build | 1 | ||||
-rw-r--r-- | src/nm-l3-config-data.c | 868 | ||||
-rw-r--r-- | src/nm-l3-config-data.h | 63 |
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 (©) + ?: 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__ */ |