summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2023-01-19 11:54:10 +0100
committerThomas Haller <thaller@redhat.com>2023-01-19 11:54:10 +0100
commitadad1d435814e6e195c97836073bce62bac40a47 (patch)
tree98c020c76704adbf4e14c9c04a916dc7f85011bd
parentdabfea2fc265e563af85cad62743c1c18738294c (diff)
parent87522ad3167d386d52c2188655c06bb6e898815d (diff)
platform: merge branch 'th/platform-cache-consistency-routes'
https://bugzilla.redhat.com/show_bug.cgi?id=2060684 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1494
-rw-r--r--src/core/NetworkManagerUtils.c4
-rw-r--r--src/core/platform/nm-fake-platform.c1
-rw-r--r--src/core/platform/tests/test-address.c2
-rw-r--r--src/core/platform/tests/test-common.c346
-rw-r--r--src/core/platform/tests/test-common.h133
-rw-r--r--src/core/platform/tests/test-link.c12
-rw-r--r--src/core/platform/tests/test-platform-general.c4
-rw-r--r--src/core/platform/tests/test-route.c224
-rw-r--r--src/core/platform/tests/test-tc.c8
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.c9
-rw-r--r--src/libnm-glib-aux/nm-shared-utils.h39
-rw-r--r--src/libnm-glib-aux/nm-test-utils.h23
-rw-r--r--src/libnm-platform/nm-linux-platform.c109
-rw-r--r--src/libnm-platform/nm-linux-platform.h7
-rw-r--r--src/libnm-platform/nm-platform.c76
-rw-r--r--src/libnm-platform/nm-platform.h5
-rw-r--r--src/libnm-platform/nmp-netns.c42
-rw-r--r--src/libnm-platform/nmp-object.c107
-rw-r--r--src/libnm-platform/nmp-object.h17
19 files changed, 929 insertions, 239 deletions
diff --git a/src/core/NetworkManagerUtils.c b/src/core/NetworkManagerUtils.c
index f3c032ce9f..15a3e5ddcf 100644
--- a/src/core/NetworkManagerUtils.c
+++ b/src/core/NetworkManagerUtils.c
@@ -1848,11 +1848,11 @@ nm_platform_get(void)
void
nm_linux_platform_setup(void)
{
- nm_platform_setup(nm_linux_platform_new(FALSE, FALSE, FALSE));
+ nm_platform_setup(nm_linux_platform_new(NULL, FALSE, FALSE, FALSE));
}
void
nm_linux_platform_setup_with_tc_cache(void)
{
- nm_platform_setup(nm_linux_platform_new(FALSE, FALSE, TRUE));
+ nm_platform_setup(nm_linux_platform_new(NULL, FALSE, FALSE, TRUE));
}
diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c
index f85a02e159..c92d9aef56 100644
--- a/src/core/platform/nm-fake-platform.c
+++ b/src/core/platform/nm-fake-platform.c
@@ -1232,6 +1232,7 @@ ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack)
obj,
FALSE,
nlmsgflags,
+ TRUE,
&obj_old,
&obj_new,
&obj_replace,
diff --git a/src/core/platform/tests/test-address.c b/src/core/platform/tests/test-address.c
index 8e5ad13b43..0bce00c421 100644
--- a/src/core/platform/tests/test-address.c
+++ b/src/core/platform/tests/test-address.c
@@ -453,7 +453,7 @@ _nmtstp_init_tests(int *argc, char ***argv)
void
_nmtstp_setup_tests(void)
{
-#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, FALSE)
+#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, 1, FALSE)
add_test_func("/address/ipv4/general", test_ip4_address_general);
add_test_func("/address/ipv6/general", test_ip6_address_general);
diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c
index e408113ad4..e9c36a52dd 100644
--- a/src/core/platform/tests/test-common.c
+++ b/src/core/platform/tests/test-common.c
@@ -12,6 +12,7 @@
#include <sys/wait.h>
#include <fcntl.h>
#include <linux/if_tun.h>
+#include <linux/rtnetlink.h>
#include "n-acd/src/n-acd.h"
@@ -21,8 +22,14 @@
(data)->ifname ? " ifname '" : "", (data)->ifname ?: "", (data)->ifname ? "'" : "", \
(data)->received_count
-int NMTSTP_ENV1_IFINDEX = -1;
-int NMTSTP_ENV1_EX = -1;
+int NMTSTP_ENV1_IFINDEXES[];
+
+const char *const NMTSTP_ENV1_DEVICE_NAME[] = {
+ "nm-test-device0",
+ "nm-test-device1",
+};
+
+int NMTSTP_ENV1_EX = -1;
/*****************************************************************************/
@@ -540,7 +547,7 @@ _ip4_route_get(NMPlatform *platform,
_init_platform(&platform, FALSE);
- nmp_lookup_init_ip4_route_by_weak_id(&lookup, network, plen, metric, tos);
+ nmp_lookup_init_ip4_route_by_weak_id(&lookup, RT_TABLE_MAIN, network, plen, metric, tos);
c = 0;
nmp_cache_iter_for_each (&iter, nm_platform_lookup(platform, &lookup), &o) {
@@ -633,7 +640,13 @@ _ip6_route_get(NMPlatform *platform,
_init_platform(&platform, FALSE);
- nmp_lookup_init_ip6_route_by_weak_id(&lookup, network, plen, metric, src, src_plen);
+ nmp_lookup_init_ip6_route_by_weak_id(&lookup,
+ RT_TABLE_MAIN,
+ network,
+ plen,
+ metric,
+ src,
+ src_plen);
c = 0;
nmp_cache_iter_for_each (&iter, nm_platform_lookup(platform, &lookup), &o) {
@@ -732,6 +745,331 @@ nmtstp_run_command(const char *format, ...)
/*****************************************************************************/
+static int
+_assert_platform_sort_objs(gconstpointer ptr_a, gconstpointer ptr_b)
+{
+ const NMPObject *a = *((const NMPObject *const *) ptr_a);
+ const NMPObject *b = *((const NMPObject *const *) ptr_b);
+
+ g_assert(NMP_OBJECT_IS_VALID(a));
+ g_assert(NMP_OBJECT_IS_VALID(b));
+ g_assert(NMP_OBJECT_GET_TYPE(a) == NMP_OBJECT_GET_TYPE(b));
+
+ NM_CMP_RETURN(nmp_object_id_cmp(a, b));
+ g_assert_not_reached();
+ return 0;
+}
+
+static void
+_assert_platform_printarr(NMPObjectType obj_type, GPtrArray *arr1, GPtrArray *arr2)
+{
+ char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
+ guint i;
+
+ _LOGT("compare arrays of %s. In cache %u entries, fetched %u entries",
+ NMP_OBJECT_TYPE_NAME(obj_type),
+ nm_g_ptr_array_len(arr1),
+ nm_g_ptr_array_len(arr2));
+
+ for (i = 0; i < nm_g_ptr_array_len(arr1); i++) {
+ _LOGT("cache[%u] %s",
+ i,
+ nmp_object_to_string(arr1->pdata[i], NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf)));
+ }
+ for (i = 0; i < nm_g_ptr_array_len(arr2); i++) {
+ _LOGT("fetch[%u] %s",
+ i,
+ nmp_object_to_string(arr2->pdata[i], NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf)));
+ }
+
+ switch (obj_type) {
+ case NMP_OBJECT_TYPE_LINK:
+ nmtstp_run_command("ip -d link");
+ break;
+ case NMP_OBJECT_TYPE_IP4_ADDRESS:
+ nmtstp_run_command("ip -d -4 address");
+ break;
+ case NMP_OBJECT_TYPE_IP6_ADDRESS:
+ nmtstp_run_command("ip -d -6 address");
+ break;
+ case NMP_OBJECT_TYPE_IP4_ROUTE:
+ nmtstp_run_command("ip -d -4 route show table all");
+ break;
+ case NMP_OBJECT_TYPE_IP6_ROUTE:
+ nmtstp_run_command("ip -d -6 route show table all");
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+static gboolean
+_assert_platform_normalize_all(GPtrArray *arr)
+{
+ guint i;
+ gboolean normalized = FALSE;
+
+ for (i = 0; i < nm_g_ptr_array_len(arr); i++) {
+ const NMPObject **ptr = (gpointer) &arr->pdata[i];
+ NMPObject *new;
+
+ switch (NMP_OBJECT_GET_TYPE(*ptr)) {
+ case NMP_OBJECT_TYPE_LINK:
+ new = nmp_object_clone(*ptr, FALSE);
+ new->link.rx_packets = 0;
+ new->link.rx_bytes = 0;
+ new->link.tx_packets = 0;
+ new->link.tx_bytes = 0;
+ nmp_object_ref_set(ptr, new);
+ nmp_object_unref(new);
+ normalized = TRUE;
+ default:
+ break;
+ }
+ }
+ return normalized;
+}
+
+static void
+_assert_platform_compare_arr(NMPObjectType obj_type,
+ const char *detail_type,
+ GPtrArray *arr1,
+ GPtrArray *arr2,
+ gboolean normalized,
+ gboolean share_multi_idx)
+{
+ const NMPClass *obj_class = nmp_class_from_type(obj_type);
+ char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE];
+ char sbuf2[NM_UTILS_TO_STRING_BUFFER_SIZE];
+ int idx;
+ int idx_pointer_comp = -1;
+
+ for (idx = 0; TRUE; idx++) {
+ if (nm_g_ptr_array_len(arr1) == idx && nm_g_ptr_array_len(arr2) == idx)
+ break;
+ if (idx >= nm_g_ptr_array_len(arr1)) {
+ _assert_platform_printarr(obj_type, arr1, arr2);
+ g_error("Comparing %s (%s) for platform fails. Platform now shows entry #%u which is "
+ "not in the cache but expected %s",
+ obj_class->obj_type_name,
+ detail_type,
+ idx,
+ nmp_object_to_string(arr2->pdata[idx],
+ NMP_OBJECT_TO_STRING_ALL,
+ sbuf1,
+ sizeof(sbuf1)));
+ }
+ if (idx >= nm_g_ptr_array_len(arr2)) {
+ _assert_platform_printarr(obj_type, arr1, arr2);
+ g_error("Comparing %s (%s) for platform fails. Platform has no more entry #%u which is "
+ "still in the cache as %s",
+ obj_class->obj_type_name,
+ detail_type,
+ idx,
+ nmp_object_to_string(arr1->pdata[idx],
+ NMP_OBJECT_TO_STRING_ALL,
+ sbuf1,
+ sizeof(sbuf1)));
+ }
+ if (!nmp_object_equal(arr1->pdata[idx], arr2->pdata[idx])) {
+ _assert_platform_printarr(obj_type, arr1, arr2);
+ g_error("Comparing %s (%s) for platform fails. Platform entry #%u is now %s but in "
+ "cache is %s",
+ obj_class->obj_type_name,
+ detail_type,
+ idx,
+ nmp_object_to_string(arr2->pdata[idx],
+ NMP_OBJECT_TO_STRING_ALL,
+ sbuf1,
+ sizeof(sbuf1)),
+ nmp_object_to_string(arr1->pdata[idx],
+ NMP_OBJECT_TO_STRING_ALL,
+ sbuf2,
+ sizeof(sbuf2)));
+ }
+
+ if (!normalized && (share_multi_idx != (arr1->pdata[idx] == arr2->pdata[idx]))
+ && idx_pointer_comp == -1)
+ idx_pointer_comp = idx;
+ }
+
+ if (idx_pointer_comp != -1) {
+ _assert_platform_printarr(obj_type, arr1, arr2);
+ g_error("Comparing %s (%s) for platform fails for pointer comparison. Platform entry "
+ "#%u is now %s but in cache is %s",
+ obj_class->obj_type_name,
+ detail_type,
+ idx_pointer_comp,
+ nmp_object_to_string(arr2->pdata[idx_pointer_comp],
+ NMP_OBJECT_TO_STRING_ALL,
+ sbuf1,
+ sizeof(sbuf1)),
+ nmp_object_to_string(arr1->pdata[idx_pointer_comp],
+ NMP_OBJECT_TO_STRING_ALL,
+ sbuf2,
+ sizeof(sbuf2)));
+ }
+}
+
+void
+nmtstp_assert_platform(NMPlatform *platform, guint32 obj_type_flags)
+{
+ static const NMPObjectType obj_types[] = {
+ NMP_OBJECT_TYPE_IP4_ADDRESS,
+ NMP_OBJECT_TYPE_IP6_ADDRESS,
+ NMP_OBJECT_TYPE_IP4_ROUTE,
+ NMP_OBJECT_TYPE_IP6_ROUTE,
+ NMP_OBJECT_TYPE_LINK,
+ };
+ gboolean obj_type_flags_all = (obj_type_flags == 0u);
+ gs_unref_object NMPlatform *platform2 = NULL;
+ int i_obj_types;
+ gboolean share_multi_idx = nmtst_get_rand_bool();
+
+ /* This test creates a new NMLinuxPlatform instance. This will fill
+ * the cache with a new dump.
+ *
+ * Then it compares the content with @platform and checks that they
+ * agree. This tests that @platform cache is consistent, as it was
+ * updated based on netlink events. */
+
+ g_assert(NM_IS_LINUX_PLATFORM(platform));
+
+ _LOGD("assert-platform: start");
+
+ nm_platform_process_events(platform);
+
+ platform2 = nm_linux_platform_new(share_multi_idx ? nm_platform_get_multi_idx(platform) : NULL,
+ TRUE,
+ nmtst_get_rand_bool(),
+ nmtst_get_rand_bool());
+ g_assert(NM_IS_LINUX_PLATFORM(platform2));
+
+ for (i_obj_types = 0; i_obj_types < (int) G_N_ELEMENTS(obj_types); i_obj_types++) {
+ const NMPObjectType obj_type = obj_types[i_obj_types];
+ const guint32 i_obj_type_flags = nmp_object_type_to_flags(obj_type);
+ gs_unref_ptrarray GPtrArray *arr1 = NULL;
+ gs_unref_ptrarray GPtrArray *arr2 = NULL;
+ NMPLookup lookup;
+ gboolean check_unordered = TRUE;
+ guint idx;
+ gboolean normalized;
+
+ if (!obj_type_flags_all) {
+ if (!NM_FLAGS_ANY(obj_type_flags, i_obj_type_flags))
+ continue;
+ obj_type_flags = NM_FLAGS_UNSET(obj_type_flags, i_obj_type_flags);
+ }
+
+ nmp_lookup_init_obj_type(&lookup, obj_type);
+
+ arr1 = nm_platform_lookup_clone(platform, &lookup, NULL, NULL) ?: g_ptr_array_new();
+ arr2 = nm_platform_lookup_clone(platform2, &lookup, NULL, NULL) ?: g_ptr_array_new();
+
+ normalized = _assert_platform_normalize_all(arr1);
+ normalized = _assert_platform_normalize_all(arr2);
+
+ if (check_unordered) {
+ /* We need to sort the two lists. */
+ g_ptr_array_sort(arr1, _assert_platform_sort_objs);
+ g_ptr_array_sort(arr2, _assert_platform_sort_objs);
+ }
+
+ _assert_platform_compare_arr(obj_type, "main", arr1, arr2, normalized, share_multi_idx);
+
+ if (NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) {
+ /* For routes, the WEAK_ID needs to be sorted and match the expected order. Check that. */
+ g_assert(!normalized);
+ for (idx = 0; idx < nm_g_ptr_array_len(arr1); idx++) {
+ const NMPObject *obj1 = arr1->pdata[idx];
+ const NMPObject *obj2 = arr2->pdata[idx];
+ gs_unref_ptrarray GPtrArray *arr1b = NULL;
+ gs_unref_ptrarray GPtrArray *arr2b = NULL;
+ gs_unref_ptrarray GPtrArray *arr1b_sorted = NULL;
+ gs_unref_ptrarray GPtrArray *arr2b_sorted = NULL;
+ guint found_obj1 = 0;
+ guint found_obj2 = 0;
+ guint i;
+
+ nmp_lookup_init_route_by_weak_id(&lookup, obj1);
+ arr1b =
+ nm_platform_lookup_clone(platform, &lookup, NULL, NULL) ?: g_ptr_array_new();
+ g_assert_cmpint(arr1b->len, >, 0u);
+
+ nmp_lookup_init_route_by_weak_id(&lookup, obj2);
+ arr2b =
+ nm_platform_lookup_clone(platform2, &lookup, NULL, NULL) ?: g_ptr_array_new();
+ g_assert_cmpint(arr2b->len, ==, arr1b->len);
+
+ /* First check that the lists agree, if we sort them. The list of
+ * weak-ids was supposed to honor the sort order from `ip route show`,
+ * but as that is not the case (see blow), first check whether at
+ * least the same routes are in the list (with wrong sort order). */
+ arr1b_sorted = nm_g_ptr_array_new_clone(arr1b, NULL, NULL, NULL);
+ arr2b_sorted = nm_g_ptr_array_new_clone(arr2b, NULL, NULL, NULL);
+ g_ptr_array_sort(arr1b_sorted, _assert_platform_sort_objs);
+ g_ptr_array_sort(arr2b_sorted, _assert_platform_sort_objs);
+ _assert_platform_compare_arr(obj_type,
+ "weak-id-sorted",
+ arr1b_sorted,
+ arr2b_sorted,
+ normalized,
+ share_multi_idx);
+
+ if (obj_type == NMP_OBJECT_TYPE_IP6_ROUTE) {
+ /* For IPv6, the weak-ids are actually not sorted correctly.
+ * This is because IPv6 multihop/ECMP routes get split into
+ * multiple objects, and we don't get this right.
+ *
+ * This may be a bug. But we probably don't rely on this
+ * anymore, because the weak-id were used to find which
+ * route got replaced with `NLM_F_REPLACE`, but that anyway
+ * doesn't work. We now always request a new dump. */
+ } else if (obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) {
+ /* For IPv4, it also does not reliably always work. This may
+ * be a bug we want to fix. For now, ignore the check.
+ *
+ * This is probably caused by kernel bug
+ * https://bugzilla.redhat.com/show_bug.cgi?id=2162315
+ * for which I think there is no workaround.
+ *
+ * Also, rhbz#2162315 means NMPlatform will merge two different
+ * routes together, if one of them were deleted, the RTM_DELROUTE
+ * message would wrongly delete single entry, leading to cache
+ * inconsistency. */
+ } else {
+ /* Assert that also the original, not-sorted lists agree. */
+ _assert_platform_compare_arr(obj_type,
+ "weak-id",
+ arr1b,
+ arr2b,
+ normalized,
+ share_multi_idx);
+ }
+
+ for (i = 0; i < arr1b->len; i++) {
+ if (arr1b->pdata[i] == obj1)
+ found_obj1++;
+ if (arr2b->pdata[i] == obj2)
+ found_obj2++;
+ }
+
+ g_assert_cmpint(found_obj1, ==, 1u);
+ g_assert_cmpint(found_obj2, ==, 1u);
+ }
+ }
+ }
+
+ g_clear_object(&platform2);
+
+ _LOGD("assert-platform: done");
+
+ g_assert_cmpint(obj_type_flags, ==, 0u);
+}
+
+/*****************************************************************************/
+
typedef struct {
GMainLoop *loop;
guint signal_counts;
diff --git a/src/core/platform/tests/test-common.h b/src/core/platform/tests/test-common.h
index 9319c79b8d..383a1fed47 100644
--- a/src/core/platform/tests/test-common.h
+++ b/src/core/platform/tests/test-common.h
@@ -18,8 +18,6 @@
#include "nm-test-utils-core.h"
-#define DEVICE_NAME "nm-test-device"
-
/*****************************************************************************/
#define nmtstp_normalize_jiffies_time(requested_value, kernel_value) \
@@ -141,6 +139,10 @@ int nmtstp_run_command(const char *format, ...) _nm_printf(1, 2);
/*****************************************************************************/
+void nmtstp_assert_platform(NMPlatform *platform, guint32 obj_type_flags);
+
+/*****************************************************************************/
+
guint nmtstp_wait_for_signal(NMPlatform *platform, gint64 timeout_msec);
guint nmtstp_wait_for_signal_until(NMPlatform *platform, gint64 until_ms);
const NMPlatformLink *nmtstp_wait_for_link(NMPlatform *platform,
@@ -535,36 +537,47 @@ void nmtstp_link_delete(NMPlatform *platform,
/*****************************************************************************/
-extern int NMTSTP_ENV1_IFINDEX;
+extern int NMTSTP_ENV1_IFINDEXES[2];
+extern const char *const NMTSTP_ENV1_DEVICE_NAME[G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)];
+
+#define DEVICE_NAME "nm-test-device0"
+#define NMTSTP_ENV1_IFINDEX (NMTSTP_ENV1_IFINDEXES[0])
+
extern int NMTSTP_ENV1_EX;
static inline void
_nmtstp_env1_wrapper_setup(const NmtstTestData *test_data)
{
- int *p_ifindex;
+ int *p_ifindexes;
+ gpointer p_n_ifaces;
gpointer p_ifup;
-
- nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, &p_ifup);
-
- g_assert(p_ifindex && *p_ifindex == -1);
+ int n_ifaces;
+ int i;
_LOGT("TEST[%s]: setup", test_data->testpath);
- nmtstp_link_delete(NM_PLATFORM_GET, -1, -1, DEVICE_NAME, FALSE);
+ nmtst_test_data_unpack(test_data, &p_ifindexes, &p_n_ifaces, NULL, NULL, NULL, &p_ifup);
- g_assert(NMTST_NM_ERR_SUCCESS(nm_platform_link_dummy_add(NM_PLATFORM_GET, DEVICE_NAME, NULL)));
+ n_ifaces = GPOINTER_TO_UINT(p_n_ifaces);
+ g_assert_cmpint(n_ifaces, >=, 1);
+ g_assert_cmpint(n_ifaces, <=, (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES));
- *p_ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME);
- g_assert_cmpint(*p_ifindex, >, 0);
- g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, -1);
+ for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++) {
+ g_assert_cmpint(NMTSTP_ENV1_IFINDEXES[i], ==, 0);
+ g_assert_cmpint(p_ifindexes[i], ==, 0);
+ }
- if (GPOINTER_TO_INT(p_ifup))
- g_assert(nm_platform_link_change_flags(NM_PLATFORM_GET, *p_ifindex, IFF_UP, TRUE) >= 0);
+ for (i = 0; i < n_ifaces; i++) {
+ p_ifindexes[i] = nmtstp_link_dummy_add(NULL, -1, NMTSTP_ENV1_DEVICE_NAME[i])->ifindex;
+ if (GPOINTER_TO_INT(p_ifup))
+ nmtstp_link_set_updown(NULL, -1, p_ifindexes[i], TRUE);
+ }
nm_platform_process_events(NM_PLATFORM_GET);
- NMTSTP_ENV1_IFINDEX = *p_ifindex;
- NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global();
+ for (i = 0; i < n_ifaces; i++)
+ NMTSTP_ENV1_IFINDEXES[i] = p_ifindexes[i];
+ NMTSTP_ENV1_EX = nmtstp_run_command_check_external_global();
}
static inline void
@@ -575,7 +588,7 @@ _nmtstp_env1_wrapper_run(gconstpointer user_data)
GTestFunc test_func;
gconstpointer d;
- nmtst_test_data_unpack(test_data, NULL, &test_func, &test_func_data, &d, NULL);
+ nmtst_test_data_unpack(test_data, NULL, NULL, &test_func, &test_func_data, &d, NULL);
_LOGT("TEST[%s]: run", test_data->testpath);
if (test_func)
@@ -587,55 +600,73 @@ _nmtstp_env1_wrapper_run(gconstpointer user_data)
static inline void
_nmtstp_env1_wrapper_teardown(const NmtstTestData *test_data)
{
- int *p_ifindex;
+ int *p_ifindexes;
+ gpointer p_n_ifaces;
+ int n_ifaces;
+ int i;
- nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, NULL);
+ _LOGT("TEST[%s]: teardown", test_data->testpath);
- g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, *p_ifindex);
- NMTSTP_ENV1_IFINDEX = -1;
+ nmtst_test_data_unpack(test_data, &p_ifindexes, &p_n_ifaces, NULL, NULL, NULL, NULL);
- _LOGT("TEST[%s]: teardown", test_data->testpath);
+ n_ifaces = GPOINTER_TO_UINT(p_n_ifaces);
+ g_assert_cmpint(n_ifaces, >=, 1);
+ g_assert_cmpint(n_ifaces, <=, (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES));
+
+ for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++) {
+ if (i < n_ifaces)
+ g_assert_cmpint(p_ifindexes[i], >, 0);
+ else
+ g_assert_cmpint(p_ifindexes[i], ==, 0);
+ g_assert_cmpint(NMTSTP_ENV1_IFINDEXES[i], ==, p_ifindexes[i]);
+ NMTSTP_ENV1_IFINDEXES[i] = 0;
+ }
- g_assert_cmpint(*p_ifindex, ==, nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME));
- g_assert(nm_platform_link_delete(NM_PLATFORM_GET, *p_ifindex));
+ for (i = 0; i < n_ifaces; i++)
+ nmtstp_link_delete(NULL, -1, p_ifindexes[i], NMTSTP_ENV1_DEVICE_NAME[i], TRUE);
nm_platform_process_events(NM_PLATFORM_GET);
_LOGT("TEST[%s]: finished", test_data->testpath);
- *p_ifindex = -1;
+ for (i = 0; i < (int) G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES); i++)
+ p_ifindexes[i] = 0;
}
/* add test function, that set's up a particular environment, consisting
* of a dummy device with ifindex NMTSTP_ENV1_IFINDEX. */
-#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, ifup) \
- nmtst_add_test_func_full(testpath, \
- _nmtstp_env1_wrapper_run, \
- _nmtstp_env1_wrapper_setup, \
- _nmtstp_env1_wrapper_teardown, \
- ({ \
- static int _ifindex = -1; \
- &_ifindex; \
- }), \
- ({ \
- GTestFunc _test_func = (test_func); \
- _test_func; \
- }), \
- ({ \
- GTestDataFunc _test_func = (test_data_func); \
- _test_func; \
- }), \
- (arg), \
- ({ \
- gboolean _ifup = (ifup); \
- GINT_TO_POINTER(_ifup); \
+#define _nmtstp_env1_add_test_func_full(testpath, test_func, test_data_func, arg, n_ifaces, ifup) \
+ nmtst_add_test_func_full(testpath, \
+ _nmtstp_env1_wrapper_run, \
+ _nmtstp_env1_wrapper_setup, \
+ _nmtstp_env1_wrapper_teardown, \
+ ({ \
+ static int _ifindexes[G_N_ELEMENTS(NMTSTP_ENV1_IFINDEXES)] = {0}; \
+ _ifindexes; \
+ }), \
+ ({ \
+ guint _n_ifaces = (n_ifaces); \
+ GUINT_TO_POINTER(_n_ifaces); \
+ }), \
+ ({ \
+ GTestFunc _test_func = (test_func); \
+ _test_func; \
+ }), \
+ ({ \
+ GTestDataFunc _test_func = (test_data_func); \
+ _test_func; \
+ }), \
+ (arg), \
+ ({ \
+ gboolean _ifup = (ifup); \
+ GINT_TO_POINTER(!!_ifup); \
}))
-#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, ifup) \
- _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, ifup)
+#define nmtstp_env1_add_test_func_data(testpath, test_func, arg, n_ifaces, ifup) \
+ _nmtstp_env1_add_test_func_full(testpath, NULL, test_func, arg, n_ifaces, ifup)
-#define nmtstp_env1_add_test_func(testpath, test_func, ifup) \
- _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, ifup)
+#define nmtstp_env1_add_test_func(testpath, test_func, n_ifaces, ifup) \
+ _nmtstp_env1_add_test_func_full(testpath, test_func, NULL, NULL, n_ifaces, ifup)
/*****************************************************************************/
diff --git a/src/core/platform/tests/test-link.c b/src/core/platform/tests/test-link.c
index c0321d3a3d..6c61bac2d2 100644
--- a/src/core/platform/tests/test-link.c
+++ b/src/core/platform/tests/test-link.c
@@ -2858,7 +2858,7 @@ _test_netns_create_platform(void)
netns = nmp_netns_new();
g_assert(NMP_IS_NETNS(netns));
- platform = nm_linux_platform_new(TRUE, TRUE, TRUE);
+ platform = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
g_assert(NM_IS_LINUX_PLATFORM(platform));
nmp_netns_pop(netns);
@@ -2947,7 +2947,7 @@ test_netns_general(gpointer fixture, gconstpointer test_data)
if (_check_sysctl_skip())
return;
- platform_1 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+ platform_1 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
platform_2 = _test_netns_create_platform();
/* add some dummy devices. The "other-*" devices are there to bump the ifindex */
@@ -3075,7 +3075,7 @@ test_netns_set_netns(gpointer fixture, gconstpointer test_data)
if (_test_netns_check_skip())
return;
- platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+ platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
platforms[1] = platform_1 = _test_netns_create_platform();
platforms[2] = platform_2 = _test_netns_create_platform();
@@ -3174,7 +3174,7 @@ test_netns_push(gpointer fixture, gconstpointer test_data)
if (_check_sysctl_skip())
return;
- pl[0].platform = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+ pl[0].platform = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
pl[1].platform = platform_1 = _test_netns_create_platform();
pl[2].platform = platform_2 = _test_netns_create_platform();
@@ -3321,7 +3321,7 @@ test_netns_bind_to_path(gpointer fixture, gconstpointer test_data)
if (_test_netns_check_skip())
return;
- platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+ platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
platforms[1] = platform_1 = _test_netns_create_platform();
platforms[2] = platform_2 = _test_netns_create_platform();
@@ -3486,7 +3486,7 @@ test_sysctl_netns_switch(void)
if (_test_netns_check_skip())
return;
- platforms[0] = platform_0 = nm_linux_platform_new(TRUE, TRUE, TRUE);
+ platforms[0] = platform_0 = nm_linux_platform_new(NULL, TRUE, TRUE, TRUE);
platforms[1] = platform_1 = _test_netns_create_platform();
platforms[2] = platform_2 = _test_netns_create_platform();
PL = platforms[nmtst_get_rand_uint32() % 3];
diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c
index db2a705435..cebd44f65b 100644
--- a/src/core/platform/tests/test-platform-general.c
+++ b/src/core/platform/tests/test-platform-general.c
@@ -31,7 +31,7 @@ test_init_linux_platform(void)
{
gs_unref_object NMPlatform *platform = NULL;
- platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
+ platform = nm_linux_platform_new(NULL, TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
}
/*****************************************************************************/
@@ -42,7 +42,7 @@ test_link_get_all(void)
gs_unref_object NMPlatform *platform = NULL;
gs_unref_ptrarray GPtrArray *links = NULL;
- platform = nm_linux_platform_new(TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
+ platform = nm_linux_platform_new(NULL, TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT, TRUE);
links = nm_platform_link_get_all(platform, TRUE);
}
diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c
index de34cfaef1..001ecbc70f 100644
--- a/src/core/platform/tests/test-route.c
+++ b/src/core/platform/tests/test-route.c
@@ -2160,6 +2160,213 @@ test_mptcp(gconstpointer test_data)
/*****************************************************************************/
+static void
+_ensure_onlink_routes(void)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS(NMTSTP_ENV1_DEVICE_NAME) && NMTSTP_ENV1_DEVICE_NAME[i]; i++) {
+ nmtstp_run_command("ip route append 7.7.7.0/24 dev %s", NMTSTP_ENV1_DEVICE_NAME[i]);
+ nmtstp_run_command("ip route append 7:7:7::/64 dev %s", NMTSTP_ENV1_DEVICE_NAME[i]);
+ }
+}
+
+static void
+test_cache_consistency_routes(gconstpointer test_data)
+{
+ const int TEST_IDX = GPOINTER_TO_INT(test_data);
+ NMPlatform *platform = NM_PLATFORM_GET;
+ gboolean is_test_quick = nmtst_test_quick();
+ const int N_RUN = is_test_quick ? 50 : 500;
+ int i_run;
+ gs_unref_ptrarray GPtrArray *keeper = g_ptr_array_new_with_free_func(g_free);
+
+ _ensure_onlink_routes();
+
+ for (i_run = 0; i_run < N_RUN; i_run++) {
+ const char *extra_options[100];
+ gsize n_extra_options = 0;
+ gs_free char *extra_options_str = NULL;
+ int i_if;
+ int ifindex;
+ const char *ifname;
+ int IS_IPv4;
+ const char *op;
+ const char *prefix;
+ const char *s;
+ const char *route_type;
+ int i;
+ int n;
+ char addr_family_char[2] = {'6', '4'};
+
+ g_ptr_array_set_size(keeper, 0);
+
+ switch (TEST_IDX) {
+ case 1:
+ IS_IPv4 = TRUE;
+ break;
+ case 2:
+ IS_IPv4 = FALSE;
+ break;
+ default:
+ IS_IPv4 = nmtst_get_rand_bool();
+ break;
+ }
+
+ i_if = nmtst_get_rand_uint32() % 2;
+ op = nmtst_rand_select_str("flush", "add", "change", "append", "prepend", "replace");
+
+ ifindex = NMTSTP_ENV1_IFINDEXES[i_if];
+ ifname = NMTSTP_ENV1_DEVICE_NAME[i_if];
+
+ g_assert_cmpint(ifindex, ==, nm_platform_link_get_ifindex(platform, ifname));
+
+ if (nm_streq(op, "flush")) {
+ if (!nmtst_get_rand_one_case_in(10)) {
+ /* flush more seldom. */
+ continue;
+ }
+ nmtstp_run_command("ip -%c route flush dev %s"
+ "%s" /* redirect */
+ "",
+ addr_family_char[IS_IPv4],
+ ifname,
+ nmtst_is_debug() ? "" : " &>/dev/null");
+ _ensure_onlink_routes();
+ goto done;
+ }
+
+ route_type = nmtst_get_rand_one_case_in(4)
+ ? nmtst_rand_select_str("unicast", "blackhole", "local", "broadcast")
+ : NULL;
+
+ if (NM_IN_STRSET(route_type, "blackhole")) {
+ ifindex = 0;
+ ifname = NULL;
+ }
+
+ if (IS_IPv4) {
+ prefix = nmtst_rand_select_str("192.168.4.0/24",
+ "192.168.5.0/24",
+ "192.168.5.5/32",
+ "default");
+ } else {
+ prefix =
+ nmtst_rand_select_str("a:b:c:d::/64", "a:b:c:e::/64", "a:b:c:f::/64", "default");
+ }
+
+ s = nmtst_rand_select_str(NULL, "kernel", "bird");
+ if (s) {
+ if (nmtst_get_rand_bool()) {
+ s = nm_streq(s, "kernel") ? nmtst_rand_select_str("boot", "static", "ra")
+ : nmtst_rand_select_str("babel", "bgp");
+ }
+ extra_options[n_extra_options++] = "proto";
+ extra_options[n_extra_options++] = s;
+ }
+
+ s = nmtst_rand_select_str(NULL, "10", "20");
+ if (s) {
+ extra_options[n_extra_options++] = "metric";
+ extra_options[n_extra_options++] = s;
+ }
+
+ s = nmtst_rand_select_str(NULL, "10222", "10223");
+ if (s) {
+ extra_options[n_extra_options++] = "table";
+ extra_options[n_extra_options++] = s;
+ }
+
+ if (!IS_IPv4 && NM_IN_STRSET(op, "add", "change", "append", "prepend", "replace")) {
+ /* kernel has a bug with append/prepend of IPv6 routes with next-hops.
+ * This leads to wrong notification messages, wrong merging of multi-hop
+ * routes and cache inconsistency in NMPlatform.
+ *
+ * https://bugzilla.redhat.com/show_bug.cgi?id=2161994
+ *
+ * For now, disable the test case to make the unit test not fail.
+ *
+ * While being a kernel bug, it leads to cache inconsistency in NMPlatform,
+ * which is a problem for NetworkManager. I don't see how we can detect
+ * this problem to trigger a refresh. */
+ } else if (ifname && nmtst_get_rand_one_case_in(3)) {
+ n = (nmtst_get_rand_uint32() % 4) + 1;
+ for (i = 0; i < n; i++) {
+ extra_options[n_extra_options++] = "nexthop";
+ extra_options[n_extra_options++] = "via";
+ if (IS_IPv4) {
+ extra_options[n_extra_options++] =
+ nmtst_keeper_printf(&keeper, "7.7.7.%d", i + 1);
+ } else {
+ extra_options[n_extra_options++] =
+ nmtst_keeper_printf(&keeper, "7:7:7:7::%d", i + 1);
+ }
+ extra_options[n_extra_options++] = "dev";
+ extra_options[n_extra_options++] = NMTSTP_ENV1_DEVICE_NAME[nmtst_get_rand_bool()];
+ if (nmtst_get_rand_one_case_in(3)) {
+ extra_options[n_extra_options++] = "weight";
+ extra_options[n_extra_options++] = "5";
+ }
+ }
+
+ ifname = NULL;
+ ifindex = 0;
+ }
+
+ g_assert_cmpint(n_extra_options, <, G_N_ELEMENTS(extra_options));
+ extra_options[n_extra_options] = NULL;
+
+ if (nmtst_is_debug())
+ nmtstp_run_command("ip -%c -d route show table all", addr_family_char[IS_IPv4]);
+
+ /* We ignore errors. The reason is that operations like "change" might fail if
+ * the route doesn't exist. That's fine for our test. We just do randomly things
+ * and some of them will stick. */
+ nmtstp_run_command(
+ "ip -%c route "
+ "%s" /* op */
+ "%s%s" /* route_type */
+ " %s" /* prefix */
+ "%s%s" /* ifname */
+ "%s%s" /* extra_options */
+ "%s" /* redirect */
+ "",
+ addr_family_char[IS_IPv4],
+ op,
+ NM_PRINT_FMT_QUOTED2(route_type, " ", route_type, ""),
+ prefix,
+ NM_PRINT_FMT_QUOTED2(ifname, " dev ", ifname, ""),
+ NM_PRINT_FMT_QUOTED2(extra_options[0],
+ " ",
+ (extra_options_str = g_strjoinv(" ", (char **) extra_options)),
+ ""),
+ nmtst_is_debug() ? "" : " &>/dev/null");
+
+ if (nmtst_is_debug())
+ nmtstp_run_command("ip -%c -d route show table all", addr_family_char[IS_IPv4]);
+done:
+ nm_platform_process_events(platform);
+
+ if (!is_test_quick || (i_run + 1 == N_RUN) || nmtst_get_rand_one_case_in(5)) {
+ nmtstp_assert_platform(
+ platform,
+ nmtst_get_rand_one_case_in(5)
+ ? 0u
+ : nmp_object_type_to_flags(NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4)));
+ }
+ }
+
+ if (is_test_quick) {
+ gs_free char *msg = NULL;
+
+ msg = g_strdup_printf("Ran a quick version of test %s (try NMTST_DEBUG=slow)",
+ nmtst_test_get_path());
+ g_test_skip(msg);
+ }
+}
+
+/*****************************************************************************/
+
NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
void
@@ -2171,9 +2378,11 @@ _nmtstp_init_tests(int *argc, char ***argv)
void
_nmtstp_setup_tests(void)
{
-#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE)
+#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, 1, TRUE)
#define add_test_func_data(testpath, test_func, arg) \
- nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE)
+ nmtstp_env1_add_test_func_data(testpath, test_func, arg, 1, TRUE)
+#define add_test_func_data_with_if2(testpath, test_func, arg) \
+ nmtstp_env1_add_test_func_data(testpath, test_func, arg, 2, TRUE)
add_test_func("/route/ip4", test_ip4_route);
add_test_func("/route/ip6", test_ip6_route);
@@ -2206,4 +2415,15 @@ _nmtstp_setup_tests(void)
add_test_func_data("/route/mptcp/1", test_mptcp, GINT_TO_POINTER(1));
add_test_func_data("/route/mptcp/2", test_mptcp, GINT_TO_POINTER(2));
}
+ if (nmtstp_is_root_test()) {
+ add_test_func_data_with_if2("/route/test_cache_consistency_routes/1",
+ test_cache_consistency_routes,
+ GINT_TO_POINTER(1));
+ add_test_func_data_with_if2("/route/test_cache_consistency_routes/2",
+ test_cache_consistency_routes,
+ GINT_TO_POINTER(2));
+ add_test_func_data_with_if2("/route/test_cache_consistency_routes/3",
+ test_cache_consistency_routes,
+ GINT_TO_POINTER(3));
+ }
}
diff --git a/src/core/platform/tests/test-tc.c b/src/core/platform/tests/test-tc.c
index 6a2019c84a..832fbea6bb 100644
--- a/src/core/platform/tests/test-tc.c
+++ b/src/core/platform/tests/test-tc.c
@@ -214,8 +214,8 @@ _nmtstp_init_tests(int *argc, char ***argv)
void
_nmtstp_setup_tests(void)
{
- nmtstp_env1_add_test_func("/link/qdisc/1", test_qdisc1, TRUE);
- nmtstp_env1_add_test_func("/link/qdisc/fq_codel", test_qdisc_fq_codel, TRUE);
- nmtstp_env1_add_test_func("/link/qdisc/sfq", test_qdisc_sfq, TRUE);
- nmtstp_env1_add_test_func("/link/qdisc/tbf", test_qdisc_tbf, TRUE);
+ nmtstp_env1_add_test_func("/link/qdisc/1", test_qdisc1, 1, TRUE);
+ nmtstp_env1_add_test_func("/link/qdisc/fq_codel", test_qdisc_fq_codel, 1, TRUE);
+ nmtstp_env1_add_test_func("/link/qdisc/sfq", test_qdisc_sfq, 1, TRUE);
+ nmtstp_env1_add_test_func("/link/qdisc/tbf", test_qdisc_tbf, 1, TRUE);
}
diff --git a/src/libnm-glib-aux/nm-shared-utils.c b/src/libnm-glib-aux/nm-shared-utils.c
index d2e989841f..702a63e9f6 100644
--- a/src/libnm-glib-aux/nm-shared-utils.c
+++ b/src/libnm-glib-aux/nm-shared-utils.c
@@ -2130,15 +2130,16 @@ nm_strv_cleanup(char **strv, gboolean strip_whitespace, gboolean skip_empty, gbo
/*****************************************************************************/
GPtrArray *
-_nm_g_ptr_array_copy(GPtrArray *array,
- GCopyFunc func,
- gpointer user_data,
- GDestroyNotify element_free_func)
+nm_g_ptr_array_new_clone(GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data,
+ GDestroyNotify element_free_func)
{
GPtrArray *new_array;
guint i;
g_return_val_if_fail(array, NULL);
+ nm_assert((!!func) == (!!element_free_func));
new_array = g_ptr_array_new_full(array->len, element_free_func);
for (i = 0; i < array->len; i++) {
diff --git a/src/libnm-glib-aux/nm-shared-utils.h b/src/libnm-glib-aux/nm-shared-utils.h
index 7ad2874244..083ed137ee 100644
--- a/src/libnm-glib-aux/nm-shared-utils.h
+++ b/src/libnm-glib-aux/nm-shared-utils.h
@@ -2068,40 +2068,29 @@ nm_g_ptr_array_pdata(const GPtrArray *arr)
return arr ? arr->pdata : NULL;
}
-GPtrArray *_nm_g_ptr_array_copy(GPtrArray *array,
- GCopyFunc func,
- gpointer user_data,
- GDestroyNotify element_free_func);
-
/**
- * nm_g_ptr_array_copy:
+ * nm_g_ptr_array_new_clone:
* @array: the #GPtrArray to clone.
* @func: the copy function.
* @user_data: the user data for the copy function
- * @element_free_func: the free function of the elements. @array MUST have
- * the same element_free_func. This argument is only used on older
- * glib, that doesn't support g_ptr_array_copy().
+ * @element_free_func: the free function of the elements. This function
+ * must agree with the owner-ship semantics of @func.
*
* This is a replacement for g_ptr_array_copy(), which is not available
* before glib 2.62. Since GPtrArray does not allow to access the internal
* element_free_func, we cannot add a compatibility implementation of g_ptr_array_copy()
- * and the user must provide a suitable destroy function.
+ * as the caller must provide the correct element_free_func.
*
- * Note that the @element_free_func MUST correspond to free function set in @array.
+ * So this is not the same as g_ptr_array_copy() (hence the different name) because
+ * g_ptr_array_copy() uses the free func of the source array, which we cannot access.
+ * With g_ptr_array_copy() the copy func must agree with the array's free func.
+ * Here, it must agree with the provided @element_free_func. This allows for example
+ * to do a shallow-copy without cloning the elements (which you cannot do with g_ptr_array_copy()).
*/
-#if GLIB_CHECK_VERSION(2, 62, 0)
-#define nm_g_ptr_array_copy(array, func, user_data, element_free_func) \
- ({ \
- _nm_unused GDestroyNotify const _element_free_func = (element_free_func); \
- \
- G_GNUC_BEGIN_IGNORE_DEPRECATIONS; \
- g_ptr_array_copy((array), (func), (user_data)); \
- G_GNUC_END_IGNORE_DEPRECATIONS; \
- })
-#else
-#define nm_g_ptr_array_copy(array, func, user_data, element_free_func) \
- _nm_g_ptr_array_copy((array), (func), (user_data), (element_free_func))
-#endif
+GPtrArray *nm_g_ptr_array_new_clone(GPtrArray *array,
+ GCopyFunc func,
+ gpointer user_data,
+ GDestroyNotify element_free_func);
/*****************************************************************************/
@@ -2510,7 +2499,7 @@ nm_strv_ptrarray_clone(const GPtrArray *src, gboolean null_if_empty)
{
if (!src || (null_if_empty && src->len == 0))
return NULL;
- return nm_g_ptr_array_copy((GPtrArray *) src, nm_copy_func_g_strdup, NULL, g_free);
+ return nm_g_ptr_array_new_clone((GPtrArray *) src, nm_copy_func_g_strdup, NULL, g_free);
}
static inline void
diff --git a/src/libnm-glib-aux/nm-test-utils.h b/src/libnm-glib-aux/nm-test-utils.h
index 5196c9a076..50280d92b4 100644
--- a/src/libnm-glib-aux/nm-test-utils.h
+++ b/src/libnm-glib-aux/nm-test-utils.h
@@ -652,7 +652,7 @@ __nmtst_init(int *argc,
if (!log_level && log_domains) {
/* if the log level is not specified (but the domain is), we assume
* the caller wants to set it depending on is_debug */
- log_level = is_debug ? "DEBUG" : "WARN";
+ log_level = is_debug ? "TRACE" : "WARN";
}
if (!__nmtst_internal.assert_logging) {
@@ -3069,6 +3069,27 @@ nmtst_ip_address_new(int addr_family, const char *str)
/*****************************************************************************/
+static inline gpointer
+nmtst_keeper_add(GPtrArray **p_arr, gpointer ptr)
+{
+ if (!p_arr) {
+ /* If not GPtrArray in/out argument is given, track the pointer
+ * via _nmtst_testdata_track_add(), which means it stays alive
+ * until the end of the test. */
+ _nmtst_testdata_track_add(ptr, g_free);
+ } else {
+ if (!*p_arr)
+ *p_arr = g_ptr_array_new_with_free_func(g_free);
+
+ g_ptr_array_add(*p_arr, ptr);
+ }
+ return ptr;
+}
+
+#define nmtst_keeper_printf(p_ptr, ...) nmtst_keeper_add((p_ptr), g_strdup_printf(__VA_ARGS__))
+
+/*****************************************************************************/
+
#define nmtst_gbytes_from_arr(...) \
({ \
const guint8 _arr[] = {__VA_ARGS__}; \
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index a4a6448e71..762b1645e1 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -590,25 +590,29 @@ NM_LINUX_PLATFORM_FROM_PRIVATE(NMLinuxPlatformPrivate *priv)
#define _NMLOG2(level, ...) _LOG(level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__)
#define _NMLOG2_err(errsv, level, ...) _LOG_err(errsv, level, _NMLOG2_DOMAIN, NULL, __VA_ARGS__)
-#define _LOG_print(__level, __domain, __errsv, self, ...) \
- G_STMT_START \
- { \
- char __prefix[32]; \
- const char *__p_prefix = _NMLOG_PREFIX_NAME; \
- NMPlatform *const __self = (self); \
- \
- if (__self && nm_platform_get_log_with_ptr(__self)) { \
- g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
- __p_prefix = __prefix; \
- } \
- _nm_log(__level, \
- __domain, \
- __errsv, \
- NULL, \
- NULL, \
- "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
- } \
+#define _LOG_print(__level, __domain, __errsv, self, ...) \
+ G_STMT_START \
+ { \
+ char __prefix[64]; \
+ const char *__p_prefix = _NMLOG_PREFIX_NAME; \
+ NMPlatform *const __self = (self); \
+ \
+ if (__self && nm_platform_get_log_with_ptr(__self)) { \
+ g_snprintf(__prefix, \
+ sizeof(__prefix), \
+ "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]", \
+ _NMLOG_PREFIX_NAME, \
+ NM_HASH_OBFUSCATE_PTR(__self)); \
+ __p_prefix = __prefix; \
+ } \
+ _nm_log(__level, \
+ __domain, \
+ __errsv, \
+ NULL, \
+ NULL, \
+ "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ __p_prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
G_STMT_END
#define _LOG(level, domain, self, ...) \
@@ -3783,15 +3787,6 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
else
return NULL;
- if (!NM_IN_SET(rtm->rtm_type,
- RTN_UNICAST,
- RTN_LOCAL,
- RTN_BLACKHOLE,
- RTN_UNREACHABLE,
- RTN_PROHIBIT,
- RTN_THROW))
- return NULL;
-
if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0)
return NULL;
@@ -5304,7 +5299,7 @@ ip_route_get_lock_flag(const NMPlatformIPRoute *route)
}
static gboolean
-ip_route_ignored_protocol(const NMPlatformIPRoute *route)
+ip_route_is_alive(const NMPlatformIPRoute *route)
{
guint8 prot;
@@ -5316,12 +5311,29 @@ ip_route_ignored_protocol(const NMPlatformIPRoute *route)
nm_assert(nmp_utils_ip_config_source_from_rtprot(prot) == route->rt_source);
- /* We ignore all routes outside a certain subest of rtm_protocol. NetworkManager
- * itself wouldn't configure those, so they are always configured by somebody
- * external. We thus ignore them to avoid the overhead that processing them brings.
- * For example, the BGP daemon "bird" might configure a huge number of RTPROT_BIRD routes. */
+ if (prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA)) {
+ /* We ignore certain rtm_protocol, because NetworkManager would only ever
+ * configure certain protocols. Other routes are not configured by NetworkManager
+ * and we don't track them in the platform cache.
+ *
+ * This is to help with the performance overhead of a huge number of
+ * routes, for example with the bird BGP software, that adds routes
+ * with RTPROT_BIRD protocol. */
+ return FALSE;
+ }
+
+ if (!NM_IN_SET(nm_platform_route_type_uncoerce(route->type_coerced),
+ RTN_UNICAST,
+ RTN_LOCAL,
+ RTN_BLACKHOLE,
+ RTN_UNREACHABLE,
+ RTN_PROHIBIT,
+ RTN_THROW)) {
+ /* Certain route types are ignored and not placed into the cache. */
+ return FALSE;
+ }
- return prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA);
+ return TRUE;
}
/* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */
@@ -7825,6 +7837,7 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg)
gboolean resync_required = FALSE;
gboolean only_dirty = FALSE;
gboolean is_ipv6;
+ gboolean route_is_alive;
/* IPv4 routes that are a response to RTM_GETROUTE must have
* the cloned flag while IPv6 routes don't have to. */
@@ -7854,24 +7867,13 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg)
}
}
- if (ip_route_ignored_protocol(NMP_OBJECT_CAST_IP_ROUTE(obj))) {
- /* We ignore certain rtm_protocol, because NetworkManager would only ever
- * configure certain protocols. Other routes were not added by NetworkManager
- * and we don't need to track them in the platform cache.
- *
- * This is to help with the performance overhead of a huge number of
- * routes, for example with the bird BGP software, that adds routes
- * with RTPROT_BIRD protocol.
- *
- * Even if this is a IPv6 multipath route, we abort (parse_nlmsg_iter). There
- * is nothing for us to do. */
- return;
- }
+ route_is_alive = ip_route_is_alive(NMP_OBJECT_CAST_IP_ROUTE(obj));
cache_op = nmp_cache_update_netlink_route(cache,
obj,
is_dump,
msghdr->nlmsg_flags,
+ route_is_alive,
&obj_old,
&obj_new,
&obj_replace,
@@ -7918,6 +7920,8 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg)
delayed_action_schedule(platform,
delayed_action_refresh_from_needle_object(obj),
NULL);
+ /* We are done here. */
+ return;
}
break;
}
@@ -10843,8 +10847,8 @@ constructed(GObject *_object)
: (!nmp_netns_get_current()
? "no netns support"
: nm_sprintf_bufa(100,
- "in netns[%p]%s",
- nmp_netns_get_current(),
+ "in netns[" NM_HASH_OBFUSCATE_PTR_FMT "]%s",
+ NM_HASH_OBFUSCATE_PTR(nmp_netns_get_current()),
nmp_netns_get_current() == nmp_netns_get_initial() ? "/main"
: "")),
nm_platform_get_use_udev(platform) ? "use" : "no",
@@ -10987,7 +10991,10 @@ path_is_read_only_fs(const char *path)
}
NMPlatform *
-nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean cache_tc)
+nm_linux_platform_new(NMDedupMultiIndex *multi_idx,
+ gboolean log_with_ptr,
+ gboolean netns_support,
+ gboolean cache_tc)
{
gboolean use_udev = FALSE;
@@ -10995,6 +11002,8 @@ nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean ca
use_udev = TRUE;
return g_object_new(NM_TYPE_LINUX_PLATFORM,
+ NM_PLATFORM_MULTI_IDX,
+ multi_idx,
NM_PLATFORM_LOG_WITH_PTR,
log_with_ptr,
NM_PLATFORM_USE_UDEV,
diff --git a/src/libnm-platform/nm-linux-platform.h b/src/libnm-platform/nm-linux-platform.h
index 546387a9d5..08135a4acb 100644
--- a/src/libnm-platform/nm-linux-platform.h
+++ b/src/libnm-platform/nm-linux-platform.h
@@ -23,6 +23,11 @@ typedef struct _NMLinuxPlatformClass NMLinuxPlatformClass;
GType nm_linux_platform_get_type(void);
-NMPlatform *nm_linux_platform_new(gboolean log_with_ptr, gboolean netns_support, gboolean cache_tc);
+struct _NMDedupMultiIndex;
+
+NMPlatform *nm_linux_platform_new(struct _NMDedupMultiIndex *multi_idx,
+ gboolean log_with_ptr,
+ gboolean netns_support,
+ gboolean cache_tc);
#endif /* __NETWORKMANAGER_LINUX_PLATFORM_H__ */
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index 85bd785ac8..6fad736cce 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -115,28 +115,32 @@ nmp_link_address_get_as_bytes(const NMPLinkAddress *addr)
#define _NMLOG_DOMAIN LOGD_PLATFORM
#define _NMLOG_PREFIX_NAME "platform"
-#define NMLOG_COMMON(level, name, ...) \
- G_STMT_START \
- { \
- char __prefix[32]; \
- const char *__p_prefix = _NMLOG_PREFIX_NAME; \
- const NMPlatform *const __self = (self); \
- const char *__name = name; \
- \
- if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \
- g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
- __p_prefix = __prefix; \
- } \
- _nm_log((level), \
- _NMLOG_DOMAIN, \
- 0, \
- __name, \
- NULL, \
- "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- __p_prefix, \
- NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") \
- _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
- } \
+#define NMLOG_COMMON(level, name, ...) \
+ G_STMT_START \
+ { \
+ char __prefix[64]; \
+ const char *__p_prefix = _NMLOG_PREFIX_NAME; \
+ const NMPlatform *const __self = (self); \
+ const char *__name = name; \
+ \
+ if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \
+ g_snprintf(__prefix, \
+ sizeof(__prefix), \
+ "%s[" NM_HASH_OBFUSCATE_PTR_FMT "]", \
+ _NMLOG_PREFIX_NAME, \
+ NM_HASH_OBFUSCATE_PTR(__self)); \
+ __p_prefix = __prefix; \
+ } \
+ _nm_log((level), \
+ _NMLOG_DOMAIN, \
+ 0, \
+ __name, \
+ NULL, \
+ "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ __p_prefix, \
+ NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") \
+ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
G_STMT_END
#define _NMLOG(level, ...) \
@@ -180,6 +184,7 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = {0};
enum {
PROP_0,
+ PROP_MULTI_IDX,
PROP_NETNS_SUPPORT,
PROP_USE_UDEV,
PROP_LOG_WITH_PTR,
@@ -9617,6 +9622,20 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self);
switch (prop_id) {
+ case PROP_MULTI_IDX:
+ /* construct-only */
+ {
+ NMDedupMultiIndex *multi_idx;
+
+ multi_idx = g_value_get_pointer(value);
+ if (!multi_idx)
+ multi_idx = nm_dedup_multi_index_new();
+ else
+ multi_idx = nm_dedup_multi_index_ref(multi_idx);
+
+ priv->multi_idx = multi_idx;
+ break;
+ }
case PROP_NETNS_SUPPORT:
/* construct-only */
if (g_value_get_boolean(value)) {
@@ -9663,8 +9682,9 @@ constructor(GType type, guint n_construct_params, GObjectConstructParam *constru
self = NM_PLATFORM(object);
priv = NM_PLATFORM_GET_PRIVATE(self);
- priv->multi_idx = nm_dedup_multi_index_new();
- priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev);
+ nm_assert(priv->multi_idx);
+
+ priv->cache = nmp_cache_new(priv->multi_idx, priv->use_udev);
c_list_init(&priv->ip6_dadfailed_lst_head);
return object;
@@ -9706,6 +9726,14 @@ nm_platform_class_init(NMPlatformClass *platform_class)
g_object_class_install_property(
object_class,
+ PROP_MULTI_IDX,
+ g_param_spec_pointer(NM_PLATFORM_MULTI_IDX,
+ "",
+ "",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property(
+ object_class,
PROP_NETNS_SUPPORT,
g_param_spec_boolean(NM_PLATFORM_NETNS_SUPPORT,
"",
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index ea46321534..dec38c1c93 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -23,10 +23,11 @@
/*****************************************************************************/
+#define NM_PLATFORM_CACHE_TC "cache-tc"
+#define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr"
+#define NM_PLATFORM_MULTI_IDX "multi-idx"
#define NM_PLATFORM_NETNS_SUPPORT "netns-support"
#define NM_PLATFORM_USE_UDEV "use-udev"
-#define NM_PLATFORM_LOG_WITH_PTR "log-with-ptr"
-#define NM_PLATFORM_CACHE_TC "cache-tc"
/*****************************************************************************/
diff --git a/src/libnm-platform/nmp-netns.c b/src/libnm-platform/nmp-netns.c
index 864e30e94f..c18f67ab1b 100644
--- a/src/libnm-platform/nmp-netns.c
+++ b/src/libnm-platform/nmp-netns.c
@@ -66,26 +66,28 @@ __ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len)
#define _NMLOG_DOMAIN LOGD_PLATFORM
#define _NMLOG_PREFIX_NAME "netns"
-#define _NMLOG(level, netns, ...) \
- G_STMT_START \
- { \
- NMLogLevel _level = (level); \
- \
- if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \
- NMPNetns *_netns = (netns); \
- char _sbuf[20]; \
- \
- _nm_log(_level, \
- _NMLOG_DOMAIN, \
- 0, \
- NULL, \
- NULL, \
- "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
- _NMLOG_PREFIX_NAME, \
- (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \
- : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
- } \
- } \
+#define _NMLOG(level, netns, ...) \
+ G_STMT_START \
+ { \
+ NMLogLevel _level = (level); \
+ \
+ if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \
+ NMPNetns *_netns = (netns); \
+ char _sbuf[32]; \
+ \
+ _nm_log(_level, \
+ _NMLOG_DOMAIN, \
+ 0, \
+ NULL, \
+ NULL, \
+ "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+ _NMLOG_PREFIX_NAME, \
+ (_netns ? nm_sprintf_buf(_sbuf, \
+ "[" NM_HASH_OBFUSCATE_PTR_FMT "]", \
+ NM_HASH_OBFUSCATE_PTR(_netns)) \
+ : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+ } \
+ } \
G_STMT_END
/*****************************************************************************/
diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c
index e510685b01..9ba027ba95 100644
--- a/src/libnm-platform/nmp-object.c
+++ b/src/libnm-platform/nmp-object.c
@@ -2336,19 +2336,23 @@ nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj)
switch (NMP_OBJECT_GET_TYPE(obj)) {
case NMP_OBJECT_TYPE_IP4_ROUTE:
r4 = NMP_OBJECT_CAST_IP4_ROUTE(obj);
- return nmp_lookup_init_ip4_route_by_weak_id(lookup,
- r4->network,
- r4->plen,
- r4->metric,
- r4->tos);
+ return nmp_lookup_init_ip4_route_by_weak_id(
+ lookup,
+ nm_platform_route_table_uncoerce(r4->table_coerced, TRUE),
+ r4->network,
+ r4->plen,
+ r4->metric,
+ r4->tos);
case NMP_OBJECT_TYPE_IP6_ROUTE:
r6 = NMP_OBJECT_CAST_IP6_ROUTE(obj);
- return nmp_lookup_init_ip6_route_by_weak_id(lookup,
- &r6->network,
- r6->plen,
- r6->metric,
- &r6->src,
- r6->src_plen);
+ return nmp_lookup_init_ip6_route_by_weak_id(
+ lookup,
+ nm_platform_route_table_uncoerce(r6->table_coerced, TRUE),
+ &r6->network,
+ r6->plen,
+ r6->metric,
+ &r6->src,
+ r6->src_plen);
default:
nm_assert_not_reached();
return NULL;
@@ -2357,6 +2361,7 @@ nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj)
const NMPLookup *
nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup,
+ guint32 route_table,
in_addr_t network,
guint plen,
guint32 metric,
@@ -2367,9 +2372,10 @@ nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup,
nm_assert(lookup);
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE);
- o->ip4_route.ifindex = 1;
- o->ip4_route.plen = plen;
- o->ip4_route.metric = metric;
+ o->ip4_route.ifindex = 1;
+ o->ip4_route.plen = plen;
+ o->ip4_route.table_coerced = nm_platform_route_table_coerce(route_table);
+ o->ip4_route.metric = metric;
if (network)
o->ip4_route.network = network;
o->ip4_route.tos = tos;
@@ -2379,6 +2385,7 @@ nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup,
const NMPLookup *
nmp_lookup_init_ip6_route_by_weak_id(NMPLookup *lookup,
+ guint32 route_table,
const struct in6_addr *network,
guint plen,
guint32 metric,
@@ -2390,9 +2397,10 @@ nmp_lookup_init_ip6_route_by_weak_id(NMPLookup *lookup,
nm_assert(lookup);
o = _nmp_object_stackinit_from_type(&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE);
- o->ip6_route.ifindex = 1;
- o->ip6_route.plen = plen;
- o->ip6_route.metric = metric;
+ o->ip6_route.ifindex = 1;
+ o->ip6_route.plen = plen;
+ o->ip6_route.table_coerced = nm_platform_route_table_coerce(route_table);
+ o->ip6_route.metric = metric;
if (network)
o->ip6_route.network = *network;
if (src)
@@ -2951,6 +2959,7 @@ nmp_cache_update_netlink_route(NMPCache *cache,
NMPObject *obj_hand_over,
gboolean is_dump,
guint16 nlmsgflags,
+ gboolean route_is_alive,
const NMPObject **out_obj_old,
const NMPObject **out_obj_new,
const NMPObject **out_obj_replace,
@@ -2969,30 +2978,60 @@ nmp_cache_update_netlink_route(NMPCache *cache,
nm_assert(cache);
nm_assert(NMP_OBJECT_IS_VALID(obj_hand_over));
nm_assert(!NMP_OBJECT_IS_STACKINIT(obj_hand_over));
- /* A link object from netlink must have the udev related fields unset.
- * We could implement to handle that, but there is no need to support such
- * a use-case */
nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_hand_over),
NMP_OBJECT_TYPE_IP4_ROUTE,
NMP_OBJECT_TYPE_IP6_ROUTE));
nm_assert(nm_dedup_multi_index_obj_find(cache->multi_idx, obj_hand_over) != obj_hand_over);
+ if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE)) {
+ /* This means, that the message indicates that another route was replaced.
+ * Since we don't cache all routes (see "route_is_alive"), we cannot know
+ * with certainty which route was replaced.
+ *
+ * Even if we would cache *all* routes (which we cannot, if kernel adds new
+ * routing features that modify the known nmp_object_id_equal()), it would
+ * be hard to find the right route that was replaced. Well, probably we
+ * would have to keep NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID sorted by order
+ * of notifications, which is hard. The code below actually makes an effort
+ * to do that, but it's not actually used, because we just resync.
+ *
+ * The only proper solution for this would be to improve kernel with [1]
+ * and [2].
+ *
+ * [1] https://bugzilla.redhat.com/show_bug.cgi?id=1337855
+ * [2] https://bugzilla.redhat.com/show_bug.cgi?id=1337860
+ *
+ * We need to resync.
+ */
+ if (NMP_OBJECT_GET_TYPE(obj_hand_over) == NMP_OBJECT_TYPE_IP4_ROUTE
+ && !nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
+ /* For IPv4, we can do a small optimization. We skip the resync, if we have
+ * no conflicting routes (by weak-id).
+ *
+ * This optimization does not work for IPv6 (maybe should be fixed).
+ */
+ } else {
+ entry_replace = NULL;
+ resync_required = TRUE;
+ goto out;
+ }
+ }
+
entry_old = _lookup_entry(cache, obj_hand_over);
entry_new = NULL;
NM_SET_OUT(out_obj_old, nmp_object_ref(nm_dedup_multi_entry_get_obj(entry_old)));
- if (!entry_old) {
- if (!nmp_object_is_alive(obj_hand_over))
- goto update_done;
+ is_alive = route_is_alive && nmp_object_is_alive(obj_hand_over);
- _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new);
- ops_type = NMP_CACHE_OPS_ADDED;
+ if (!entry_old) {
+ if (is_alive) {
+ _idxcache_update(cache, NULL, obj_hand_over, is_dump, &entry_new);
+ ops_type = NMP_CACHE_OPS_ADDED;
+ }
goto update_done;
}
- is_alive = nmp_object_is_alive(obj_hand_over);
-
if (!is_alive) {
/* the update would make @entry_old invalid. Remove it. */
_idxcache_update(cache, entry_old, NULL, FALSE, NULL);
@@ -3025,19 +3064,11 @@ update_done:
if (is_dump)
goto out;
- if (!entry_new) {
- if (NM_FLAGS_HAS(nlmsgflags, NLM_F_REPLACE)
- && nmp_cache_lookup_all(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, obj_hand_over)) {
- /* hm. @obj_hand_over was not added, meaning it was not alive.
- * However, we track some other objects with the same weak-id.
- * It's unclear what that means. To be sure, resync. */
- resync_required = TRUE;
- }
+ if (!entry_new)
goto out;
- }
- /* FIXME: for routes, we only maintain the order correctly for the BY_WEAK_ID
- * index. For all other indexes their order becomes messed up. */
+ /* For routes, we only maintain the order correctly for the BY_WEAK_ID
+ * index. For all other indexes, their order is not preserved. */
entry_cur =
_lookup_entry_with_idx_type(cache, NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, entry_new->obj);
if (!entry_cur) {
diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h
index dcf431255e..0d5f84b310 100644
--- a/src/libnm-platform/nmp-object.h
+++ b/src/libnm-platform/nmp-object.h
@@ -561,6 +561,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type)
return FALSE;
}
+#define NMP_OBJECT_TYPE_NAME(obj_type) (nmp_class_from_type(obj_type)->obj_type_name)
+
#define NMP_OBJECT_CAST_OBJECT(obj) \
({ \
typeof(obj) _obj = (obj); \
@@ -869,11 +871,13 @@ nmp_lookup_init_object_by_ifindex(NMPLookup *lookup, NMPObjectType obj_type, int
const NMPLookup *nmp_lookup_init_route_default(NMPLookup *lookup, NMPObjectType obj_type);
const NMPLookup *nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj);
const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup,
+ guint32 route_table,
in_addr_t network,
guint plen,
guint32 metric,
guint8 tos);
const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id(NMPLookup *lookup,
+ guint32 route_table,
const struct in6_addr *network,
guint plen,
guint32 metric,
@@ -971,6 +975,7 @@ NMPCacheOpsType nmp_cache_update_netlink_route(NMPCache *cache,
NMPObject *obj_hand_over,
gboolean is_dump,
guint16 nlmsgflags,
+ gboolean route_is_alive,
const NMPObject **out_obj_old,
const NMPObject **out_obj_new,
const NMPObject **out_obj_replace,
@@ -1132,6 +1137,7 @@ nm_platform_lookup_route_default_clone(NMPlatform *platform,
static inline const NMDedupMultiHeadEntry *
nm_platform_lookup_ip4_route_by_weak_id(NMPlatform *platform,
+ guint32 route_table,
in_addr_t network,
guint plen,
guint32 metric,
@@ -1139,12 +1145,13 @@ nm_platform_lookup_ip4_route_by_weak_id(NMPlatform *platform,
{
NMPLookup lookup;
- nmp_lookup_init_ip4_route_by_weak_id(&lookup, network, plen, metric, tos);
+ nmp_lookup_init_ip4_route_by_weak_id(&lookup, route_table, network, plen, metric, tos);
return nm_platform_lookup(platform, &lookup);
}
static inline const NMDedupMultiHeadEntry *
nm_platform_lookup_ip6_route_by_weak_id(NMPlatform *platform,
+ guint32 route_table,
const struct in6_addr *network,
guint plen,
guint32 metric,
@@ -1153,7 +1160,13 @@ nm_platform_lookup_ip6_route_by_weak_id(NMPlatform *platform,
{
NMPLookup lookup;
- nmp_lookup_init_ip6_route_by_weak_id(&lookup, network, plen, metric, src, src_plen);
+ nmp_lookup_init_ip6_route_by_weak_id(&lookup,
+ route_table,
+ network,
+ plen,
+ metric,
+ src,
+ src_plen);
return nm_platform_lookup(platform, &lookup);
}