summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2023-11-29 12:01:08 +0100
committerThomas Haller <thaller@redhat.com>2023-11-29 12:18:59 +0100
commit0e30415575ccd611602c9d2858cf25125740c357 (patch)
tree4ec5d413a49bfea7f860527850333e57cc93e034
parentca7cbf6feabd475d5f4b8032f4650a47ef9174ce (diff)
core: assert that tracked objects in NML3Cfg don't have metric_anyth/tmp8
It wouldn't work otherwise. The object state is used to track routes and compare them to what we find in platform. A "metric_any" is useful at higher layers, to track a route where the metric is decided by somebody else. But at the point when we add such an object to the object-state, a fixed metric must be chosen. Assert for that. Also add some code comment to nm_platofmr_ip_route_normalize() related to this.
-rw-r--r--src/core/nm-l3cfg.c8
-rw-r--r--src/libnm-platform/nm-platform.c27
2 files changed, 35 insertions, 0 deletions
diff --git a/src/core/nm-l3cfg.c b/src/core/nm-l3cfg.c
index 1c4a7774d0..0b4ad62b84 100644
--- a/src/core/nm-l3cfg.c
+++ b/src/core/nm-l3cfg.c
@@ -811,6 +811,14 @@ _nm_n_acd_data_probe_new(NML3Cfg *self, in_addr_t addr, guint32 timeout_msec, gp
nm_assert(_self->priv.p->combined_l3cd_commited); \
\
if (NM_MORE_ASSERTS > 5) { \
+ /* metric-any must be resolved before adding the object. Otherwise,
+ * their real metric is not known, and they cannot be compared to objects
+ * from NMPlatform cache. */ \
+ nm_assert(!NM_IN_SET(NMP_OBJECT_GET_TYPE(_obj_state->obj), \
+ NMP_OBJECT_TYPE_IP4_ROUTE, \
+ NMP_OBJECT_TYPE_IP6_ROUTE) \
+ || !NMP_OBJECT_CAST_IP_ROUTE(_obj_state->obj)->metric_any); \
+ \
nm_assert(c_list_contains(&_self->priv.p->obj_state_lst_head, \
&_obj_state->os_lst)); \
nm_assert(_obj_state->os_plobj \
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index 5fa191b958..a797cb034c 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -5336,6 +5336,33 @@ nm_platform_ip_route_normalize(int addr_family,
route->rt_source = nmp_utils_ip_config_source_round_trip_rtprot(route->rt_source);
+ /* For the most part, nm_platform_ip_route_normalize() tries to normalize some fields
+ * as it happens when they go through kernel.
+ *
+ * In most cases, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID comparison performs the same
+ * relaxed comparison. For example, normalize() will normalize "scope_inv", and also
+ * the NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID comparison will do that on-the-fly. Optimally,
+ * looking into a hash table gives you the same result, whether you normalize the
+ * needle first (or whether the entries in the hash table are normalized).
+ *
+ * Unfortunately, that's not always the case. Examples:
+ *
+ * - "metric": we have a "metric_any" field. This is used by higher layers
+ * to indicate that the metric is dynamically chosen (e.g. by the default
+ * metric of the default route). As such, as far as NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID
+ * is concerned, the "metric_any" and "metric" values are treated as distinguishing
+ * properties. But when we add a route in kernel, "metric_any" no longer exist.
+ * It becomes a fixed metric. Normalize will fix the metric.
+ * - "weight": for IPv4 single-hop routes, the weight does not exist in kernel. We however
+ * use the field to track ECMP information in higher layers. Consequently,
+ * NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID treats the weight as-is, while normalization
+ * (and adding it to kernel) will mangle it.
+ *
+ * You thus must be careful when you track NMPlatformIP4Route that make use of such
+ * higher-level features, which cannot be represented in kernel or the NMPlatform
+ * cache.
+ */
+
switch (addr_family) {
case AF_INET:
r4 = (NMPlatformIP4Route *) route;