diff options
author | Thomas Haller <thaller@redhat.com> | 2020-07-21 14:27:48 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-07-21 18:32:36 +0200 |
commit | 4eabb731d652bfcd6e7acca199c42ff13e0d42f6 (patch) | |
tree | d39c7bf41185efda980385ebdae358e3430233b5 | |
parent | f3f4065f7c360e0d5a9ac464df8f414a6d1b07ad (diff) | |
download | NetworkManager-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.c | 452 | ||||
-rw-r--r-- | src/nm-l3cfg.h | 13 |
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)) |