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-21 18:32:36 +0200
commit4eabb731d652bfcd6e7acca199c42ff13e0d42f6 (patch)
treed39c7bf41185efda980385ebdae358e3430233b5
parentf3f4065f7c360e0d5a9ac464df8f414a6d1b07ad (diff)
downloadNetworkManager-4eabb731d652bfcd6e7acca199c42ff13e0d42f6.tar.gz
l3cfg: add NML3ConfigDatath/l3cfg
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.
-rw-r--r--src/nm-l3cfg.c452
-rw-r--r--src/nm-l3cfg.h13
2 files changed, 465 insertions, 0 deletions
diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c
index aefaef1cd8..0df464cc2e 100644
--- a/src/nm-l3cfg.c
+++ b/src/nm-l3cfg.c
@@ -4,12 +4,34 @@
#include "nm-l3cfg.h"
+#include "nm-core-internal.h"
#include "platform/nm-platform.h"
#include "platform/nmp-object.h"
#include "nm-netns.h"
/*****************************************************************************/
+typedef struct {
+ NMDedupMultiIdxType parent;
+ NMPObjectType obj_type;
+} DedupMultiIdxType;
+
+struct _NML3ConfigData {
+ NMDedupMultiIndex *multi_idx;
+ DedupMultiIdxType idx_ip4_routes;
+ DedupMultiIdxType idx_ip6_routes;
+
+ union {
+ struct {
+ const NMPObject *best_default_route_6;
+ const NMPObject *best_default_route_4;
+ };
+ const NMPObject *best_default_route_x[2];
+ };
+
+ int ref_count;
+};
+
NM_GOBJECT_PROPERTIES_DEFINE (NML3Cfg,
PROP_NETNS,
PROP_IFINDEX,
@@ -44,6 +66,436 @@ static void _property_emit_notify (NML3Cfg *self, NML3CfgPropertyEmitType emit_t
/*****************************************************************************/
+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 (NML3ConfigData *cfg_data)
+{
+ return cfg_data
+ && cfg_data->ref_count > 0;
+}
+
+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 *cfg_data;
+
+ nm_assert (multi_idx);
+
+ cfg_data = g_slice_new (NML3ConfigData);
+ *cfg_data = (NML3ConfigData) {
+ .ref_count = 1,
+ .multi_idx = nm_dedup_multi_index_ref (multi_idx),
+ };
+
+ _idx_type_init (&cfg_data->idx_ip4_routes, NMP_OBJECT_TYPE_IP4_ROUTE);
+ _idx_type_init (&cfg_data->idx_ip6_routes, NMP_OBJECT_TYPE_IP6_ROUTE);
+
+ return cfg_data;
+}
+
+NML3ConfigData *
+nm_l3_config_data_ref (NML3ConfigData *cfg_data)
+{
+ nm_assert (NM_IS_L3_CONFIG_DATA (cfg_data));
+ cfg_data->ref_count++;
+ return cfg_data;
+}
+
+void
+nm_l3_config_data_unref (NML3ConfigData *cfg_data)
+{
+ if (!cfg_data)
+ return;
+
+ nm_assert (NM_IS_L3_CONFIG_DATA (cfg_data));
+ if (--cfg_data->ref_count > 0)
+ return;
+
+ nm_dedup_multi_index_remove_idx (cfg_data->multi_idx, &cfg_data->idx_ip4_routes.parent);
+ nm_dedup_multi_index_remove_idx (cfg_data->multi_idx, &cfg_data->idx_ip6_routes.parent);
+
+ nmp_object_unref (cfg_data->best_default_route_4);
+ nmp_object_unref (cfg_data->best_default_route_6);
+
+ nm_dedup_multi_index_unref (cfg_data->multi_idx);
+
+ nm_g_slice_free (cfg_data);
+}
+
+/*****************************************************************************/
+
+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
+ || nm_ip_config_best_default_route_is (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
+ && nm_ip_config_best_default_route_is (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 = _nm_ip_config_best_default_route_find_better (*best_default_route,
+ new_candidate);
+ return nmp_object_ref_set (best_default_route, new_candidate);
+}
+
+static gboolean
+_l3_config_data_add_route (NML3ConfigData *cfg_data,
+ int addr_family,
+ int ifindex,
+ const NMPObject *obj_new,
+ const NMPlatformIPRoute *new,
+ const NMPObject **out_obj_new,
+ gboolean *out_changed_best_default_route)
+{
+ const gboolean IS_IPv4 = (addr_family == AF_INET);
+ 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 ((!new) != (!obj_new));
+ nm_assert ( !new
+ || _route_valid (addr_family, 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 (cfg_data->multi_idx,
+ addr_family == AF_INET
+ ? &cfg_data->idx_ip4_routes
+ : &cfg_data->idx_ip6_routes,
+ ifindex,
+ obj_new,
+ (const NMPlatformObject *) new,
+ TRUE,
+ FALSE,
+ &obj_old,
+ &obj_new_2)) {
+
+ if ( cfg_data->best_default_route_x[IS_IPv4] == obj_old
+ && obj_old != obj_new_2) {
+ changed_best_default_route = TRUE;
+ nm_clear_nmp_object (&cfg_data->best_default_route_x[IS_IPv4]);
+ }
+
+ NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2));
+
+ if (_l3_config_best_default_route_merge (&cfg_data->best_default_route_x[IS_IPv4],
+ obj_new_2))
+ changed_best_default_route = TRUE;
+
+ changed = TRUE;
+ } else
+ 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;
+}
+
+#if 0
+static void
+_l3_config_data_add_route_6 (NML3ConfigData *self,
+ const NMPObject *obj_new,
+ const NMPlatformIP6Route *new,
+ const NMPObject **out_obj_new)
+{
+ NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
+ nm_auto_nmpobj const NMPObject *obj_old = NULL;
+ const NMPObject *obj_new_2;
+
+ nm_assert ((!new) != (!obj_new));
+ nm_assert (!new || _route_valid (new));
+ nm_assert (!obj_new || _route_valid (NMP_OBJECT_CAST_IP6_ROUTE (obj_new)));
+
+ if (_nm_ip_config_add_obj (priv->multi_idx,
+ &priv->idx_ip6_routes_,
+ priv->ifindex,
+ obj_new,
+ (const NMPlatformObject *) new,
+ TRUE,
+ FALSE,
+ &obj_old,
+ &obj_new_2)) {
+ gboolean changed_default_route = FALSE;
+
+ if ( priv->best_default_route == obj_old
+ && obj_old != obj_new_2) {
+ changed_default_route = TRUE;
+ nm_clear_nmp_object (&priv->best_default_route);
+ }
+ NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2));
+ if (_nm_ip_config_best_default_route_merge (&priv->best_default_route, obj_new_2))
+ changed_default_route = TRUE;
+
+ if (changed_default_route)
+ _notify (self, PROP_GATEWAY);
+ _notify_routes (self);
+ } else
+ NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2));
+}
+#endif
+
+/*****************************************************************************/
+
+NML3ConfigData *
+nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_index,
+ NMConnection *connection)
+{
+ NML3ConfigData *cfg_data;
+ int IS_IPv4;
+
+ cfg_data = nm_l3_config_data_new (multi_index);
+
+ for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
+ const int addr_family = IS_IPv4 ? AF_INET : AF_INET6;
+ NMSettingIPConfig *s_ip;
+
+ if (!connection)
+ continue;
+
+ s_ip = nm_connection_get_setting_ip_config (connection, addr_family);
+ if (!s_ip)
+ continue;
+ }
+
+ return cfg_data;
+}
+
+/*****************************************************************************/
+
static void
_load_link (NML3Cfg *self, gboolean initial)
{
diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h
index 7fc4b4b7d2..fdf613d649 100644
--- a/src/nm-l3cfg.h
+++ b/src/nm-l3cfg.h
@@ -5,6 +5,19 @@
#include "platform/nmp-object.h"
+/*****************************************************************************/
+
+typedef struct _NML3ConfigData NML3ConfigData;
+
+NML3ConfigData *nm_l3_config_data_new (NMDedupMultiIndex *multi_idx);
+NML3ConfigData *nm_l3_config_data_ref (NML3ConfigData *cfg_data);
+void nm_l3_config_data_unref (NML3ConfigData *cfg_data);
+
+NML3ConfigData *nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_index,
+ NMConnection *connection);
+
+/*****************************************************************************/
+
#define NM_TYPE_L3CFG (nm_l3cfg_get_type ())
#define NM_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_L3CFG, NML3Cfg))
#define NM_L3CFG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_L3CFG, NML3CfgClass))