NetworkManager/src/tests/test-route-manager.c
Thomas Haller cdd8c65799 platform: fix cache to use kernel's notion for equality of routes
Until now, NetworkManager's platform cache for routes used the quadruple
network/plen,metric,ifindex for equaliy. That is not kernel's
understanding of how routes behave. For example, with `ip route append`
you can add two IPv4 routes that only differ by their gateway. To
the previous form of platform cache, these two routes would wrongly
look identical, as the cache could not contain both routes. This also
easily leads to cache-inconsistencies.

Now that we have NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, fix the route's
compare operator to match kernel's.

Well, not entirely. Kernel understands more properties for routes then
NetworkManager. Some of these properties may also be part of the ID according
to kernel. To NetworkManager such routes would still look identical as
they only differ in a property that is not understood. This can still
cause cache-inconsistencies. The only fix here is to add support for
all these properties in NetworkManager as well. However, it's less serious,
because with this commit we support several of the more important properties.
See also the related bug rh#1337855 for kernel.

Another difficulty is that `ip route replace` and `ip route change`
changes an existing route. The replaced route has the same
NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID, but differ in the actual
NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:

    # ip -d -4 route show dev v
    # ip monitor route &
    # ip route add 192.168.5.0/24 dev v
    192.168.5.0/24 dev v scope link
    # ip route change 192.168.5.0/24 dev v scope 10
    192.168.5.0/24 dev v scope 10
    # ip -d -4 route show dev v
    unicast 192.168.5.0/24 proto boot scope 10

Note that we only got one RTM_NEWROUTE message, although from NMPCache's
point of view, a new route (with a particular ID) was added and another
route (with a different ID) was deleted. The cumbersome workaround is,
to keep an ordered list of the routes, and figure out which route was
replaced in response to an RTM_NEWROUTE. In absence of bugs, this should
work fine. However, as we only rely on events, we might wrongly
introduce a cache-inconsistancy as well. See the related bug rh#1337860.

Also drop nm_platform_ip4_route_get() and the like. The ID of routes
is complex, so it makes little sense to look up a route directly.
2017-08-12 16:04:28 +02:00

975 lines
33 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2015 Red Hat, Inc.
*
*/
#include "nm-default.h"
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include "platform/nm-platform.h"
#include "platform/nm-platform-utils.h"
#include "nm-route-manager.h"
#include "platform/tests/test-common.h"
typedef struct {
int ifindex0, ifindex1;
} test_fixture;
NMRouteManager *route_manager_get (void);
NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER);
/*****************************************************************************/
static void
setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_route)
{
GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
NMPlatformIP4Route route = { 0 };
route.ifindex = ifindex;
route.mss = 0;
route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
inet_pton (AF_INET, "6.6.6.0", &route.network);
route.plen = 24;
route.gateway = INADDR_ANY;
route.metric = 20;
route.mss = mss_of_first_route;
g_array_append_val (routes, route);
route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
inet_pton (AF_INET, "7.0.0.0", &route.network);
route.plen = 8;
inet_pton (AF_INET, "6.6.6.1", &route.gateway);
route.metric = metric_of_second_route;
route.mss = 0;
g_array_append_val (routes, route);
nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
g_array_free (routes, TRUE);
}
static void
setup_dev1_ip4 (int ifindex)
{
GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
NMPlatformIP4Route route = { 0 };
route.ifindex = ifindex;
route.mss = 0;
/* Add some route outside of route manager. The route manager
* should get rid of it upon sync. */
nmtstp_ip4_route_add (NM_PLATFORM_GET,
route.ifindex,
NM_IP_CONFIG_SOURCE_USER,
nmtst_inet4_from_string ("9.0.0.0"),
8,
INADDR_ANY,
0,
10,
route.mss);
route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
inet_pton (AF_INET, "6.6.6.0", &route.network);
route.plen = 24;
route.gateway = INADDR_ANY;
route.metric = 20;
g_array_append_val (routes, route);
route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
inet_pton (AF_INET, "7.0.0.0", &route.network);
route.plen = 8;
route.gateway = INADDR_ANY;
route.metric = 22;
g_array_append_val (routes, route);
route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
inet_pton (AF_INET, "8.0.0.0", &route.network);
route.plen = 8;
inet_pton (AF_INET, "6.6.6.2", &route.gateway);
route.metric = 22;
g_array_append_val (routes, route);
nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
g_array_free (routes, TRUE);
}
static void
update_dev0_ip4 (int ifindex)
{
GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
NMPlatformIP4Route route = { 0 };
route.ifindex = ifindex;
route.mss = 0;
route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
inet_pton (AF_INET, "6.6.6.0", &route.network);
route.plen = 24;
route.gateway = INADDR_ANY;
route.metric = 20;
g_array_append_val (routes, route);
route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
inet_pton (AF_INET, "7.0.0.0", &route.network);
route.plen = 8;
route.gateway = INADDR_ANY;
route.metric = 21;
g_array_append_val (routes, route);
nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
g_array_free (routes, TRUE);
}
static GArray *
ip_routes (test_fixture *fixture, NMPObjectType obj_type)
{
const NMPClass *klass;
GArray *routes;
const NMDedupMultiHeadEntry *pl_head_entry;
NMDedupMultiIter iter;
const NMPObject *plobj = NULL;
guint i;
g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
klass = nmp_class_from_type (obj_type);
routes = g_array_new (FALSE, FALSE, klass->sizeof_public);
for (i = 0; i < 2; i++) {
int ifindex;
if (i == 0)
ifindex = fixture->ifindex0;
else
ifindex = fixture->ifindex1;
pl_head_entry = nm_platform_lookup_route_visible (NM_PLATFORM_GET,
obj_type,
ifindex,
FALSE);
nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) {
const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (plobj);
if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r))
continue;
if (r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
continue;
g_assert (r->ifindex == ifindex);
g_assert (nmp_object_is_visible (plobj));
g_array_append_vals (routes, r, 1);
}
}
return routes;
}
static void
test_ip4 (test_fixture *fixture, gconstpointer user_data)
{
GArray *routes;
NMPlatformIP4Route state1[] = {
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("6.6.6.0"),
.plen = 24,
.ifindex = fixture->ifindex0,
.gateway = INADDR_ANY,
.metric = 20,
.mss = 1000,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("7.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex0,
.gateway = nmtst_inet4_from_string ("6.6.6.1"),
.metric = 21021,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("7.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex1,
.gateway = INADDR_ANY,
.metric = 22,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("6.6.6.0"),
.plen = 24,
.ifindex = fixture->ifindex1,
.gateway = INADDR_ANY,
.metric = 21,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("8.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex1,
.gateway = nmtst_inet4_from_string ("6.6.6.2"),
.metric = 22,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE),
},
};
NMPlatformIP4Route state2[] = {
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("6.6.6.0"),
.plen = 24,
.ifindex = fixture->ifindex0,
.gateway = INADDR_ANY,
.metric = 20,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("7.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex0,
.gateway = INADDR_ANY,
.metric = 21,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("7.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex1,
.gateway = INADDR_ANY,
.metric = 22,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("6.6.6.0"),
.plen = 24,
.ifindex = fixture->ifindex1,
.gateway = INADDR_ANY,
.metric = 21,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("8.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex1,
.gateway = nmtst_inet4_from_string ("6.6.6.2"),
.metric = 22,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE),
},
};
NMPlatformIP4Route state3[] = {
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("7.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex1,
.gateway = INADDR_ANY,
.metric = 22,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("6.6.6.0"),
.plen = 24,
.ifindex = fixture->ifindex1,
.gateway = INADDR_ANY,
.metric = 20,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("8.0.0.0"),
.plen = 8,
.ifindex = fixture->ifindex1,
.gateway = nmtst_inet4_from_string ("6.6.6.2"),
.metric = 22,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE),
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = nmtst_inet4_from_string ("6.6.6.0"),
.plen = 24,
.ifindex = fixture->ifindex1,
.gateway = INADDR_ANY,
/* this is a ghost entry because we synced ifindex0 and restore the route
* with metric 20 (above). But we don't remove the metric 21. */
.metric = 21,
.mss = 0,
.scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK),
},
};
setup_dev0_ip4 (fixture->ifindex0, 1000, 21021);
setup_dev1_ip4 (fixture->ifindex1);
g_test_assert_expected_messages ();
/* - 6.6.6.0/24 on dev0 won over 6.6.6.0/24 on dev1
* - 6.6.6.0/24 on dev1 has metric bumped.
* - 7.0.0.0/8 route, metric 21021 added
* - 7.0.0.0/8 route, metric 22 added
* - 8.0.0.0/8 could be added. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE);
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1));
nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE);
g_array_free (routes, TRUE);
setup_dev1_ip4 (fixture->ifindex1);
g_test_assert_expected_messages ();
setup_dev0_ip4 (fixture->ifindex0, 0, 21);
/* Ensure nothing changed. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE);
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1));
state1[0].mss = 0;
state1[1].metric = 21;
nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE);
g_array_free (routes, TRUE);
update_dev0_ip4 (fixture->ifindex0);
/* minor changes in the routes. Quite similar to state1. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE);
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2));
nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE);
g_array_free (routes, TRUE);
nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0);
/* 6.6.6.0/24 is now on dev1
* 6.6.6.0/24 is also still on dev1 with bumped metric 21.
* 7.0.0.0/8 gone from dev0, still present on dev1
* 8.0.0.0/8 is present on dev1
* No dev0 routes left. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE);
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3));
nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE);
g_array_free (routes, TRUE);
nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1);
/* No routes left. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE);
g_assert_cmpint (routes->len, ==, 0);
g_array_free (routes, TRUE);
}
static void
setup_dev0_ip6 (int ifindex)
{
GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
NMPlatformIP6Route *route;
/* Add an address so that a route to the gateway below gets added. */
nm_platform_ip6_address_add (NM_PLATFORM_GET,
ifindex,
*nmtst_inet6_from_string ("2001:db8:8086::666"),
64,
in6addr_any,
3600,
3600,
0);
route = nmtst_platform_ip6_route_full ("2001:db8:8086::",
48,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
20,
0);
g_array_append_val (routes, *route);
route = nmtst_platform_ip6_route_full ("2001:db8:1337::",
48,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
0,
0);
g_array_append_val (routes, *route);
route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::",
64,
"2001:db8:8086::1",
ifindex,
NM_IP_CONFIG_SOURCE_USER,
21,
0);
g_array_append_val (routes, *route);
nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
g_array_free (routes, TRUE);
}
static void
setup_dev1_ip6 (int ifindex)
{
GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
NMPlatformIP6Route *route;
/* Add some route outside of route manager. The route manager
* should get rid of it upon sync. */
nmtstp_ip6_route_add (NM_PLATFORM_GET,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
*nmtst_inet6_from_string ("2001:db8:8088::"),
48,
in6addr_any,
in6addr_any,
10,
0);
route = nmtst_platform_ip6_route_full ("2001:db8:8086::",
48,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
20,
0);
g_array_append_val (routes, *route);
route = nmtst_platform_ip6_route_full ("2001:db8:1337::",
48,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
1024,
0);
g_array_append_val (routes, *route);
route = nmtst_platform_ip6_route_full ("2001:db8:d34d::",
64,
"2001:db8:8086::2",
ifindex,
NM_IP_CONFIG_SOURCE_USER,
20,
0);
g_array_append_val (routes, *route);
route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::",
64,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
22,
0);
g_array_append_val (routes, *route);
nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
g_array_free (routes, TRUE);
}
static void
update_dev0_ip6 (int ifindex)
{
GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route));
NMPlatformIP6Route *route;
/* Add an address so that a route to the gateway below gets added. */
nm_platform_ip6_address_add (NM_PLATFORM_GET,
ifindex,
*nmtst_inet6_from_string ("2001:db8:8086::2"),
64,
in6addr_any,
3600,
3600,
0);
route = nmtst_platform_ip6_route_full ("2001:db8:8086::",
48,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
20,
0);
g_array_append_val (routes, *route);
route = nmtst_platform_ip6_route_full ("2001:db8:1337::",
48,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
0,
0);
g_array_append_val (routes, *route);
route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::",
64,
NULL,
ifindex,
NM_IP_CONFIG_SOURCE_USER,
21,
0);
g_array_append_val (routes, *route);
nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
g_array_free (routes, TRUE);
}
static void
test_ip6 (test_fixture *fixture, gconstpointer user_data)
{
GArray *routes;
int i;
NMPlatformIP6Route state1[] = {
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
.plen = 48,
.ifindex = fixture->ifindex0,
.gateway = in6addr_any,
.metric = 20,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
.plen = 48,
.ifindex = fixture->ifindex0,
.gateway = in6addr_any,
.metric = 1024,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
.plen = 64,
.ifindex = fixture->ifindex0,
.gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"),
.metric = 21,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
.plen = 64,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 22,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 1025,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 21,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:d34d::"),
.plen = 64,
.ifindex = fixture->ifindex1,
.gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"),
.metric = 20,
.mss = 0,
},
};
NMPlatformIP6Route state2[] = {
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
.plen = 48,
.ifindex = fixture->ifindex0,
.gateway = in6addr_any,
.metric = 20,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
.plen = 48,
.ifindex = fixture->ifindex0,
.gateway = in6addr_any,
.metric = 1024,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
.plen = 64,
.ifindex = fixture->ifindex0,
.gateway = in6addr_any,
.metric = 21,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
.plen = 64,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 22,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 1025,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 21,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:d34d::"),
.plen = 64,
.ifindex = fixture->ifindex1,
.gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"),
.metric = 20,
.mss = 0,
},
};
NMPlatformIP6Route state3[] = {
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
.plen = 64,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 22,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 20,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 1024,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:1337::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 1025,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:8086::"),
.plen = 48,
.ifindex = fixture->ifindex1,
.gateway = in6addr_any,
.metric = 21,
.mss = 0,
},
{
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:d34d::"),
.plen = 64,
.ifindex = fixture->ifindex1,
.gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"),
.metric = 20,
.mss = 0,
},
};
setup_dev0_ip6 (fixture->ifindex0);
setup_dev1_ip6 (fixture->ifindex1);
g_test_assert_expected_messages ();
/* 2001:db8:8086::/48 on dev0 won over 2001:db8:8086::/48 on dev1
* 2001:db8:d34d::/64 on dev1 could not be added
* 2001:db8:1337::/48 on dev0 won over 2001:db8:1337::/48 on dev1 and has metric 1024
* 2001:db8:abad:c0de::/64 routes did not clash */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE);
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1));
nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE);
g_array_free (routes, TRUE);
setup_dev1_ip6 (fixture->ifindex1);
g_test_assert_expected_messages ();
setup_dev0_ip6 (fixture->ifindex0);
/* Ensure nothing changed. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE);
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1));
nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE);
g_array_free (routes, TRUE);
update_dev0_ip6 (fixture->ifindex0);
/* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE);
if (routes->len != G_N_ELEMENTS (state2)) {
NMPlatformIP6Route rr;
/* hm, seems kernel may wrongly treat IPv6 gateway for `ip route replace`.
* See rh#1480427.
*
* We would expect that `ip route replace` replaces an existing route.
* However, kernel may not do so, and instead prepend it.
*
* Work around that, by checking if such a route exists and accept
* it. */
g_assert (nmtstp_is_root_test ());
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2) + 1);
rr = ((NMPlatformIP6Route) {
.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER),
.network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"),
.plen = 64,
.ifindex = fixture->ifindex0,
.gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"),
.metric = 21,
.mss = 0,
});
for (i = 0; i < routes->len; i++) {
if (nm_platform_ip6_route_cmp (&rr,
&g_array_index (routes, NMPlatformIP6Route, i),
NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0)
continue;
g_array_remove_index (routes, i);
break;
}
}
nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE);
g_array_free (routes, TRUE);
nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0);
/* 2001:db8:abad:c0de::/64 on dev1 is still there, went away from dev0
* 2001:db8:8086::/48 is now on dev1
* 2001:db8:1337::/48 is now on dev1, metric of 1024 still applies
* 2001:db8:d34d::/64 is present now that 2001:db8:8086::/48 is on dev1
* No dev0 routes left. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE);
g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3));
nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE);
g_array_free (routes, TRUE);
nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1);
/* No routes left. */
routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE);
g_assert_cmpint (routes->len, ==, 0);
g_array_free (routes, TRUE);
}
/*****************************************************************************/
static void
_assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NMPlatformIPXRoute *route)
{
const NMPlatformIPXRoute *r;
NMPlatformIPXRoute c;
g_assert (route);
if (vtable->is_ip4)
r = (const NMPlatformIPXRoute *) nmtstp_ip4_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric, route->r4.tos);
else
r = (const NMPlatformIPXRoute *) nmtstp_ip6_route_get (NM_PLATFORM_GET, route->rx.ifindex, &route->r6.network, route->rx.plen, route->rx.metric, &route->r6.src, route->r6.src_plen);
if (!has) {
g_assert (!r);
} else {
char buf[sizeof (_nm_utils_to_string_buffer)];
if (r) {
if (vtable->is_ip4)
c.r4 = route->r4;
else
c.r6 = route->r6;
c.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (c.rx.rt_source);
}
if (!r || vtable->route_cmp (r, &c, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) {
g_error ("Invalid route. Expect %s, has %s",
vtable->route_to_string (&c, NULL, 0),
vtable->route_to_string (r, buf, sizeof (buf)));
}
}
}
static void
test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data)
{
const NMPlatformVTableRoute *vtable = &nm_platform_vtable_route_v4;
gs_unref_array GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route));
NMPlatformIP4Route r01, r02, r03;
nm_log_dbg (LOGD_CORE, "TEST start test_ip4_full_sync(): start");
r01 = *nmtst_platform_ip4_route_full ("12.3.4.0", 24, NULL,
fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER,
100, 0, RT_SCOPE_LINK, NULL);
r02 = *nmtst_platform_ip4_route_full ("13.4.5.6", 32, "12.3.4.1",
fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER,
100, 0, RT_SCOPE_UNIVERSE, NULL);
r03 = *nmtst_platform_ip4_route_full ("14.5.6.7", 32, "12.3.4.1",
fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER,
110, 0, RT_SCOPE_UNIVERSE, NULL);
g_array_set_size (routes, 2);
g_array_index (routes, NMPlatformIP4Route, 0) = r01;
g_array_index (routes, NMPlatformIP4Route, 1) = r02;
nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02);
_assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03);
vtable->route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, (const NMPlatformIPXRoute *) &r03, 0, -1);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03);
nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03);
g_array_set_size (routes, 1);
nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01);
_assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03);
nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE);
_assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01);
_assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02);
_assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03);
nm_log_dbg (LOGD_CORE, "TEST test_ip4_full_sync(): done");
}
/*****************************************************************************/
static void
fixture_setup (test_fixture *fixture, gconstpointer user_data)
{
SignalData *link_added;
link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED,
NM_PLATFORM_SIGNAL_ADDED,
link_callback,
"nm-test-device0");
nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0"));
g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device0"));
g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device0", NULL) == NM_PLATFORM_ERROR_SUCCESS);
accept_signal (link_added);
free_signal (link_added);
fixture->ifindex0 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0");
g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL));
link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED,
NM_PLATFORM_SIGNAL_ADDED,
link_callback,
"nm-test-device1");
nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1"));
g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device1"));
g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device1", NULL) == NM_PLATFORM_ERROR_SUCCESS);
accept_signal (link_added);
free_signal (link_added);
fixture->ifindex1 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1");
g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL));
}
static void
fixture_teardown (test_fixture *fixture, gconstpointer user_data)
{
nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex0);
nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex1);
}
/*****************************************************************************/
NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
void
_nmtstp_init_tests (int *argc, char ***argv)
{
nmtst_init_assert_logging (argc, argv, "WARN", "ALL");
}
void
_nmtstp_setup_tests (void)
{
g_test_add ("/route-manager/ip4", test_fixture, NULL, fixture_setup, test_ip4, fixture_teardown);
g_test_add ("/route-manager/ip6", test_fixture, NULL, fixture_setup, test_ip6, fixture_teardown);
g_test_add ("/route-manager/ip4-full-sync", test_fixture, NULL, fixture_setup, test_ip4_full_sync, fixture_teardown);
}