Thomas Haller 2023-01-19 11:54:10 +01:00
commit adad1d4358
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
19 changed files with 930 additions and 240 deletions

View file

@ -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));
}

View file

@ -1232,6 +1232,7 @@ ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack)
obj,
FALSE,
nlmsgflags,
TRUE,
&obj_old,
&obj_new,
&obj_replace,

View file

@ -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);

View file

@ -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;

View file

@ -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;
nmtst_test_data_unpack(test_data, &p_ifindex, NULL, NULL, NULL, NULL);
g_assert_cmpint(NMTSTP_ENV1_IFINDEX, ==, *p_ifindex);
NMTSTP_ENV1_IFINDEX = -1;
int *p_ifindexes;
gpointer p_n_ifaces;
int n_ifaces;
int i;
_LOGT("TEST[%s]: teardown", test_data->testpath);
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));
nmtst_test_data_unpack(test_data, &p_ifindexes, &p_n_ifaces, NULL, NULL, NULL, 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));
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;
}
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)
/*****************************************************************************/

View file

@ -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];

View file

@ -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);
}

View file

@ -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));
}
}

View file

@ -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);
}

View file

@ -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++) {

View file

@ -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

View file

@ -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__}; \

View file

@ -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;
}
return prot > RTPROT_STATIC && !NM_IN_SET(prot, RTPROT_DHCP, RTPROT_RA);
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 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,

View file

@ -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__ */

View file

@ -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;
@ -9704,6 +9724,14 @@ nm_platform_class_init(NMPlatformClass *platform_class)
platform_class->wifi_set_powersave = wifi_set_powersave;
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,

View file

@ -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"
/*****************************************************************************/

View file

@ -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
/*****************************************************************************/

View file

@ -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) {

View file

@ -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);
}