mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-28 19:10:09 +01:00
platform/tests: add nmtstp_ensure_module() helper
This will make sure that the IP tunnel module is loaded. It does so by creating (and deleting) a tunnel interface. That is important, because those modules will create additional interfaces that show up in `ip link` (like "gre0"), and those interfaces can interfere with the tests. Also add nmtstp_link_is_iptunnel_special() to detect whether an interface is one of those special interfaces.
This commit is contained in:
parent
4966f9d784
commit
451cedf2bf
2 changed files with 270 additions and 0 deletions
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include "n-acd/src/n-acd.h"
|
||||
#include "libnm-platform/nm-platform-utils.h"
|
||||
|
||||
#define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)"
|
||||
#define SIGNAL_DATA_ARG(data) \
|
||||
|
|
@ -33,6 +34,52 @@ int NMTSTP_ENV1_EX = -1;
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
const char *module_name;
|
||||
|
||||
NMLinkType iftype;
|
||||
|
||||
/* These modules create additional interfaces, like
|
||||
* "gre0" for "ip_gre" module.
|
||||
*
|
||||
* - actually some modules create multiple interfaces, like "ip_gre"
|
||||
* creating "gre0", "gretap0", "erspan0".
|
||||
* - if already an interface with such name exist, kernel would create
|
||||
* names like "gre1" or "gretap1". We don't care for that, because
|
||||
* we run in our own namespace and we control which interfaces are there.
|
||||
* We wouldn't create an interface with such a conflicting name.
|
||||
*
|
||||
* Anyway. This is the name of *one* of the interfaces that the module would
|
||||
* create. */
|
||||
const char *ifname;
|
||||
|
||||
/* This only gets set, if iftype is NM_LINK_TYPE_UNKNOWN. It corresponds to
|
||||
* NMPlatformLink.kind. */
|
||||
const char *ifkind;
|
||||
|
||||
} IPTunnelModInfo;
|
||||
|
||||
#define INF(_module_name, _iftype, _ifname, ...) \
|
||||
{ \
|
||||
.module_name = ""_module_name, .iftype = _iftype, .ifname = ""_ifname, __VA_ARGS__ \
|
||||
}
|
||||
|
||||
static const IPTunnelModInfo ip_tunnel_mod_infos[] = {
|
||||
INF("ip_gre", NM_LINK_TYPE_GRE, "gre0"),
|
||||
INF("ip_gre", NM_LINK_TYPE_GRETAP, "gretap0"),
|
||||
INF("ip_gre", NM_LINK_TYPE_UNKNOWN, "erspan0", .ifkind = "erspan"),
|
||||
INF("ipip", NM_LINK_TYPE_IPIP, "tunl0"),
|
||||
INF("ip6_tunnel", NM_LINK_TYPE_IP6TNL, "ip6tnl0"),
|
||||
INF("ip6_gre", NM_LINK_TYPE_IP6GRE, "ip6gre0"),
|
||||
INF("sit", NM_LINK_TYPE_SIT, "sit0"),
|
||||
INF("ip_vti", NM_LINK_TYPE_VTI, "ip_vti0"),
|
||||
INF("ip6_vti", NM_LINK_TYPE_VTI6, "ip6_vti0"),
|
||||
};
|
||||
|
||||
#undef INF
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void
|
||||
nmtstp_setup_platform(void)
|
||||
{
|
||||
|
|
@ -912,6 +959,225 @@ _assert_platform_compare_arr(NMPObjectType obj_type,
|
|||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
nmtstp_link_is_iptunnel_special(const NMPlatformLink *link)
|
||||
{
|
||||
int i;
|
||||
|
||||
g_assert(link);
|
||||
|
||||
/* These interfaces are autogenerated when loading the ip tunnel
|
||||
* modules. For example, loading "ip_gre" results in interfaces
|
||||
* "gre0", "gretap0", "erspan0".
|
||||
*
|
||||
* Actually, if the interface names are already taken ("gre0" already
|
||||
* exists), it will create "gre1" and so on. We don't care about that,
|
||||
* because in our test's netns that is not happening. */
|
||||
|
||||
for (i = 0; i < (int) G_N_ELEMENTS(ip_tunnel_mod_infos); i++) {
|
||||
const IPTunnelModInfo *module_info = &ip_tunnel_mod_infos[i];
|
||||
|
||||
if (module_info->iftype != link->type)
|
||||
continue;
|
||||
if (!nm_streq(module_info->ifname, link->name))
|
||||
continue;
|
||||
if (module_info->ifkind && !nm_streq(module_info->ifkind, link->kind))
|
||||
continue;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
nmtstp_ensure_module(const char *module_name)
|
||||
{
|
||||
/* using iproute2 seems to fail sometimes? Force use of platform code. */
|
||||
const int EX = 0;
|
||||
gs_free char *test_ifname = NULL;
|
||||
const NMPlatformLink *link;
|
||||
static int module_state[G_N_ELEMENTS(ip_tunnel_mod_infos)] = {0};
|
||||
int i_module_info;
|
||||
const IPTunnelModInfo *module_info = NULL;
|
||||
int i;
|
||||
int ifindex;
|
||||
gboolean result;
|
||||
|
||||
if (!module_name) {
|
||||
result = TRUE;
|
||||
for (i = 0; i < (int) G_N_ELEMENTS(ip_tunnel_mod_infos); i++) {
|
||||
if (!nmtstp_ensure_module(ip_tunnel_mod_infos[i].module_name))
|
||||
result = FALSE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
for (i_module_info = 0; i_module_info < (int) G_N_ELEMENTS(ip_tunnel_mod_infos);
|
||||
i_module_info++) {
|
||||
if (nm_streq(module_name, ip_tunnel_mod_infos[i_module_info].module_name)) {
|
||||
module_info = &ip_tunnel_mod_infos[i_module_info];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!module_info)
|
||||
g_error("%s:%d: Module name \"%s\" not implemented!", __FILE__, __LINE__, module_name);
|
||||
|
||||
test_ifname = g_strdup_printf("nm-mod-%s", module_info->ifname);
|
||||
g_assert(nm_utils_ifname_valid_kernel(test_ifname, NULL));
|
||||
|
||||
again:
|
||||
i = g_atomic_int_get(&module_state[i_module_info]);
|
||||
if (i != 0)
|
||||
return i > 0;
|
||||
|
||||
/* When tunnel modules get loaded, then interfaces like "gre0", "gretap0"
|
||||
* and "erspan0" (for "ip_gre" module) appear. For other modules, the interfaces
|
||||
* are named differently. Of course, unless those interface name are already taken,
|
||||
* in which case it will create "gre1", etc). So ugly.
|
||||
*
|
||||
* Anyway. as we run unit tests in parallel (`make check -j`), another
|
||||
* test might just load the module just now, which results in the creation of
|
||||
* those interfaces in our current namespace. That can break the test.
|
||||
*/
|
||||
|
||||
link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, test_ifname, module_info->iftype);
|
||||
g_assert(!link);
|
||||
|
||||
link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype);
|
||||
if (link) {
|
||||
g_assert(nmtstp_link_is_iptunnel_special(link));
|
||||
/* An interface with this name exists. While technically this could not be the interface
|
||||
* generated by the kernel module, in our test netns we can assume that it is. This is
|
||||
* good enough. */
|
||||
result = TRUE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to load the module. It probably won't work, because we don't have permissions.
|
||||
* Ignore any failure. */
|
||||
nmp_utils_modprobe(NULL, TRUE, module_info->module_name, NULL);
|
||||
|
||||
if (nm_streq(module_name, "ip_gre")) {
|
||||
link = nmtstp_link_gre_add(NULL,
|
||||
EX,
|
||||
test_ifname,
|
||||
&((const NMPlatformLnkGre){
|
||||
.local = nmtst_inet4_from_string("192.168.233.204"),
|
||||
.remote = nmtst_inet4_from_string("172.168.10.25"),
|
||||
.parent_ifindex = 0,
|
||||
.ttl = 174,
|
||||
.tos = 37,
|
||||
.path_mtu_discovery = TRUE,
|
||||
}));
|
||||
} else if (nm_streq(module_name, "ipip")) {
|
||||
link = nmtstp_link_ipip_add(NULL,
|
||||
EX,
|
||||
test_ifname,
|
||||
&((const NMPlatformLnkIpIp){
|
||||
.local = nmtst_inet4_from_string("1.2.3.4"),
|
||||
.remote = nmtst_inet4_from_string("5.6.7.8"),
|
||||
.parent_ifindex = 0,
|
||||
.tos = 32,
|
||||
.path_mtu_discovery = FALSE,
|
||||
}));
|
||||
} else if (nm_streq(module_name, "ip6_tunnel")) {
|
||||
link = nmtstp_link_ip6tnl_add(NULL,
|
||||
EX,
|
||||
test_ifname,
|
||||
&((const NMPlatformLnkIp6Tnl){
|
||||
.local = nmtst_inet6_from_string("fd01::15"),
|
||||
.remote = nmtst_inet6_from_string("fd01::16"),
|
||||
.tclass = 20,
|
||||
.encap_limit = 6,
|
||||
.flow_label = 1337,
|
||||
.proto = IPPROTO_IPV6,
|
||||
}));
|
||||
} else if (nm_streq(module_name, "ip6_gre")) {
|
||||
link = nmtstp_link_ip6gre_add(NULL,
|
||||
EX,
|
||||
test_ifname,
|
||||
&((const NMPlatformLnkIp6Tnl){
|
||||
.local = nmtst_inet6_from_string("fd01::42"),
|
||||
.remote = nmtst_inet6_from_string("fd01::aaaa"),
|
||||
.tclass = 21,
|
||||
.flow_label = 1338,
|
||||
.is_gre = TRUE,
|
||||
}));
|
||||
} else if (nm_streq(module_name, "sit")) {
|
||||
link = nmtstp_link_sit_add(NULL,
|
||||
EX,
|
||||
test_ifname,
|
||||
&((const NMPlatformLnkSit){
|
||||
.local = nmtst_inet4_from_string("192.168.200.1"),
|
||||
.remote = nmtst_inet4_from_string("172.25.100.14"),
|
||||
.ttl = 0,
|
||||
.tos = 31,
|
||||
.path_mtu_discovery = FALSE,
|
||||
}));
|
||||
} else if (nm_streq(module_name, "ip_vti")) {
|
||||
link = nmtstp_link_vti_add(NULL,
|
||||
EX,
|
||||
test_ifname,
|
||||
&((const NMPlatformLnkVti){
|
||||
.local = nmtst_inet4_from_string("192.168.212.204"),
|
||||
.remote = nmtst_inet4_from_string("172.168.11.25"),
|
||||
.ikey = 12,
|
||||
.okey = 13,
|
||||
}));
|
||||
} else if (nm_streq(module_name, "ip6_vti")) {
|
||||
link = nmtstp_link_vti6_add(NULL,
|
||||
EX,
|
||||
test_ifname,
|
||||
&((const NMPlatformLnkVti6){
|
||||
.local = nmtst_inet6_from_string("fd01::1"),
|
||||
.remote = nmtst_inet6_from_string("fd02::2"),
|
||||
.ikey = 13,
|
||||
.okey = 14,
|
||||
}));
|
||||
} else
|
||||
g_error("%s:%d: Module name \"%s\" not implemented!", __FILE__, __LINE__, module_name);
|
||||
|
||||
if (!link) {
|
||||
/* We might be unable to add the interface, if the kernel module does not exist.
|
||||
* Be graceful about that. */
|
||||
ifindex = 0;
|
||||
g_assert(!nmtstp_link_get_typed(NM_PLATFORM_GET, 0, test_ifname, module_info->iftype));
|
||||
g_assert(
|
||||
!nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype));
|
||||
} else {
|
||||
ifindex = link->ifindex;
|
||||
|
||||
g_assert(link);
|
||||
g_assert(
|
||||
link
|
||||
== nmtstp_link_get_typed(NM_PLATFORM_GET, ifindex, test_ifname, module_info->iftype));
|
||||
|
||||
nmtstp_link_delete(NULL, -1, link->ifindex, test_ifname, TRUE);
|
||||
|
||||
link = nmtstp_link_get_typed(NM_PLATFORM_GET, 0, module_info->ifname, module_info->iftype);
|
||||
g_assert(nmtstp_link_is_iptunnel_special(link));
|
||||
}
|
||||
|
||||
result = ifindex > 0 ? 1 : -1;
|
||||
|
||||
out:
|
||||
if (!g_atomic_int_compare_and_exchange(&module_state[i_module_info], 0, result))
|
||||
goto again;
|
||||
|
||||
if (!result) {
|
||||
/* The function aims to be graceful about missing kernel modules. */
|
||||
|
||||
/* g_error("Failure to ensure module \"%s\"", module_name); */
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nmtstp_assert_platform(NMPlatform *platform, guint32 obj_type_flags)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -530,6 +530,10 @@ void nmtstp_link_delete(NMPlatform *platform,
|
|||
const char *name,
|
||||
gboolean require_exist);
|
||||
|
||||
gboolean nmtstp_link_is_iptunnel_special(const NMPlatformLink *link);
|
||||
|
||||
gboolean nmtstp_ensure_module(const char *module_name);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define nmtst_object_new_mptcp_addr(...) \
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue