diff options
| author | Michael Biebl <biebl@debian.org> | 2016-01-20 16:26:51 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2016-01-20 16:26:51 +0100 |
| commit | 494f296a3baab08522617b24b1f126d8f9a17502 (patch) | |
| tree | c8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /src/platform/tests | |
| parent | 54f6333410ffd570e62717d9e77c5c987175e397 (diff) | |
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'src/platform/tests')
| -rw-r--r-- | src/platform/tests/Makefile.am | 6 | ||||
| -rw-r--r-- | src/platform/tests/Makefile.in | 24 | ||||
| -rw-r--r-- | src/platform/tests/monitor.c | 44 | ||||
| -rw-r--r-- | src/platform/tests/test-address.c | 278 | ||||
| -rw-r--r-- | src/platform/tests/test-cleanup.c | 6 | ||||
| -rw-r--r-- | src/platform/tests/test-common.c | 1222 | ||||
| -rw-r--r-- | src/platform/tests/test-common.h | 131 | ||||
| -rw-r--r-- | src/platform/tests/test-general.c | 3 | ||||
| -rw-r--r-- | src/platform/tests/test-link.c | 1351 | ||||
| -rw-r--r-- | src/platform/tests/test-nmp-object.c | 6 | ||||
| -rw-r--r-- | src/platform/tests/test-route.c | 38 |
11 files changed, 2935 insertions, 174 deletions
diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index 085f56ca..b097fcf6 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -1,14 +1,14 @@ AM_CPPFLAGS = \ -I${top_srcdir} \ - -I${top_srcdir}/include \ - -I${top_builddir}/include \ + -I${top_srcdir}/shared \ + -I${top_builddir}/shared \ -I${top_srcdir}/src \ -I${top_builddir}/src \ -I${top_srcdir}/libnm-core \ -I${top_builddir}/libnm-core \ -I${srcdir}/.. \ -DG_LOG_DOMAIN=\""NetworkManager"\" \ - -DNETWORKMANAGER_COMPILATION \ + -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ $(GLIB_CFLAGS) \ $(GUDEV_CFLAGS) \ diff --git a/src/platform/tests/Makefile.in b/src/platform/tests/Makefile.in index e21a3bb6..c04d6a1b 100644 --- a/src/platform/tests/Makefile.in +++ b/src/platform/tests/Makefile.in @@ -503,7 +503,6 @@ BLUEZ5_LIBS = @BLUEZ5_LIBS@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ -CKDB_PATH = @CKDB_PATH@ CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@ CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ @@ -515,8 +514,6 @@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_CFLAGS = @DBUS_CFLAGS@ -DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@ -DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@ DBUS_LIBS = @DBUS_LIBS@ DBUS_SYS_DIR = @DBUS_SYS_DIR@ DEFS = @DEFS@ @@ -526,6 +523,7 @@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ DNSMASQ_PATH = @DNSMASQ_PATH@ +DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -580,16 +578,13 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ -IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@ -IWMX_SDK_LIBS = @IWMX_SDK_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ +LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ +LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ LIBDL = @LIBDL@ -LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ -LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ -LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -626,6 +621,8 @@ NEWT_LIBS = @NEWT_LIBS@ NM = @NM@ NMEDIT = @NMEDIT@ NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@ +NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT = @NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@ +NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT = @NM_CONFIG_LOGGING_BACKEND_DEFAULT_TEXT@ NM_MAJOR_VERSION = @NM_MAJOR_VERSION@ NM_MICRO_VERSION = @NM_MICRO_VERSION@ NM_MINOR_VERSION = @NM_MINOR_VERSION@ @@ -654,7 +651,6 @@ POLKIT_LIBS = @POLKIT_LIBS@ POSUB = @POSUB@ PPPD_PATH = @PPPD_PATH@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@ -PPPOE_PATH = @PPPOE_PATH@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ @@ -669,6 +665,8 @@ SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@ SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@ SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@ SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@ +SYSTEMD_JOURNAL_CFLAGS = @SYSTEMD_JOURNAL_CFLAGS@ +SYSTEMD_JOURNAL_LIBS = @SYSTEMD_JOURNAL_LIBS@ SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@ SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@ SYSTEM_CA_PATH = @SYSTEM_CA_PATH@ @@ -729,6 +727,7 @@ mkdir_p = @mkdir_p@ nmbinary = @nmbinary@ nmconfdir = @nmconfdir@ nmdatadir = @nmdatadir@ +nmlibdir = @nmlibdir@ nmrundir = @nmrundir@ nmstatedir = @nmstatedir@ oldincludedir = @oldincludedir@ @@ -736,6 +735,7 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -751,12 +751,12 @@ with_dhcpcd = @with_dhcpcd@ with_netconfig = @with_netconfig@ with_resolvconf = @with_resolvconf@ with_valgrind = @with_valgrind@ -AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/include \ - -I${top_builddir}/include -I${top_srcdir}/src \ +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/shared \ + -I${top_builddir}/shared -I${top_srcdir}/src \ -I${top_builddir}/src -I${top_srcdir}/libnm-core \ -I${top_builddir}/libnm-core -I${srcdir}/.. \ -DG_LOG_DOMAIN=\""NetworkManager"\" \ - -DNETWORKMANAGER_COMPILATION \ + -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_INSIDE_DAEMON \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE $(GLIB_CFLAGS) \ $(GUDEV_CFLAGS) $(LIBNL_CFLAGS) $(am__append_1) PLATFORM_SOURCES = \ diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index f1058db8..5cc3114c 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -3,34 +3,66 @@ #include <stdlib.h> #include <syslog.h> +#include "nm-default.h" #include "nm-linux-platform.h" -#include "nm-logging.h" #include "nm-test-utils.h" NMTST_DEFINE (); +static struct { + gboolean persist; +} global_opt = { + .persist = TRUE, +}; + +static gboolean +read_argv (int *argc, char ***argv) +{ + GOptionContext *context; + GOptionEntry options[] = { + { "no-persist", 'P', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &global_opt.persist, "Exit after processing netlink messages", NULL }, + { 0 }, + }; + gs_free_error GError *error = NULL; + + context = g_option_context_new (NULL); + g_option_context_set_summary (context, "Monitor netlink events in NMPlatform."); + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, argc, argv, &error)) { + g_warning ("Error parsing command line arguments: %s", error->message); + g_option_context_free (context); + return FALSE; + } + + g_option_context_free (context); + return TRUE; +} + int main (int argc, char **argv) { GMainLoop *loop; -#if !GLIB_CHECK_VERSION (2, 35, 0) - g_type_init (); -#endif - if (!g_getenv ("G_MESSAGES_DEBUG")) g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); nmtst_init_with_logging (&argc, &argv, "DEBUG", "ALL"); + if (!read_argv (&argc, &argv)) + return 2; + nm_log_info (LOGD_PLATFORM, "platform monitor start"); loop = g_main_loop_new (NULL, FALSE); nm_linux_platform_setup (); - g_main_loop_run (loop); + if (global_opt.persist) + g_main_loop_run (loop); + + g_main_loop_unref (loop); return EXIT_SUCCESS; } diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index d6f346b3..566bb479 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -4,12 +4,19 @@ #define DEVICE_NAME "nm-test-device" #define IP4_ADDRESS "192.0.2.1" +#define IP4_ADDRESS_PEER "192.0.2.2" +#define IP4_ADDRESS_PEER2 "192.0.3.1" #define IP4_PLEN 24 #define IP6_ADDRESS "2001:db8:a:b:1:2:3:4" #define IP6_PLEN 64 +static int DEVICE_IFINDEX = -1; +static int EX = -1; + +/*****************************************************************************/ + static void -ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *received, NMPlatformSignalChangeType change_type, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -25,11 +32,11 @@ ip4_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *received, NMPlatformSignalChangeType change_type, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -45,13 +52,15 @@ ip6_address_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } +/*****************************************************************************/ + static void -test_ip4_address (void) +test_ip4_address_general (void) { - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + const int ifindex = DEVICE_IFINDEX; SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback, ifindex); SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip4_address_callback, ifindex); SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback, ifindex); @@ -64,13 +73,13 @@ test_ip4_address (void) inet_pton (AF_INET, IP4_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); - g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, NULL); accept_signals (address_changed, 0, 1); /* Test address listing */ @@ -80,16 +89,17 @@ test_ip4_address (void) address = &g_array_index (addresses, NMPlatformIP4Address, 0); g_assert_cmpint (address->ifindex, ==, ifindex); g_assert_cmphex (address->address, ==, addr); + g_assert_cmphex (address->peer_address, ==, addr); g_assert_cmpint (address->plen, ==, IP4_PLEN); g_array_unref (addresses); /* Remove address */ - g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_removed); /* Remove address again */ - g_assert (nm_platform_ip4_address_delete (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); free_signal (address_added); free_signal (address_changed); @@ -97,9 +107,9 @@ test_ip4_address (void) } static void -test_ip6_address (void) +test_ip6_address_general (void) { - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + const int ifindex = DEVICE_IFINDEX; SignalData *address_added = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback, ifindex); SignalData *address_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, ip6_address_callback, ifindex); SignalData *address_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback, ifindex); @@ -114,12 +124,12 @@ test_ip6_address (void) /* Add address */ g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_added); /* Add address again (aka update) */ - g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); accept_signals (address_changed, 0, 1); /* Test address listing */ @@ -133,12 +143,15 @@ test_ip6_address (void) g_array_unref (addresses); /* Remove address */ - g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + nmtstp_ip6_address_del (EX, ifindex, addr, IP6_PLEN); g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); accept_signal (address_removed); /* Remove address again */ - g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + nmtstp_ip6_address_del (EX, ifindex, addr, IP6_PLEN); + + /* ensure not pending signal. */ + accept_signals (address_changed, 0, 1); free_signal (address_added); free_signal (address_changed); @@ -146,11 +159,11 @@ test_ip6_address (void) } static void -test_ip4_address_external (void) +test_ip4_address_general_2 (void) { + const int ifindex = DEVICE_IFINDEX; SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback); SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback); - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); in_addr_t addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -161,38 +174,31 @@ test_ip4_address_external (void) /* Looks like addresses are not announced by kerenl when the interface * is down. Link-local IPv6 address is automatically added. */ - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, DEVICE_IFINDEX, NULL)); /* Add/delete notification */ - run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", - IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); - wait_signal (address_added); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); - run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); - wait_signal (address_removed); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + accept_signal (address_added); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); + accept_signal (address_removed); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); /* Add/delete conflict */ - run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", - IP4_ADDRESS, IP4_PLEN, DEVICE_NAME, lifetime, preferred); - g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr, 0, IP4_PLEN, lifetime, preferred, NULL)); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_added); - /*run_command ("ip address delete %s/%d dev %s", IP4_ADDRESS, IP4_PLEN, DEVICE_NAME); - g_assert (nm_platform_ip4_address_delete (ifindex, addr, IP4_PLEN, 0)); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN)); - accept_signal (address_removed);*/ free_signal (address_added); free_signal (address_removed); } static void -test_ip6_address_external (void) +test_ip6_address_general_2 (void) { + const int ifindex = DEVICE_IFINDEX; SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip6_address_callback); SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip6_address_callback); - int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); struct in6_addr addr; guint32 lifetime = 2000; guint32 preferred = 1000; @@ -201,51 +207,197 @@ test_ip6_address_external (void) inet_pton (AF_INET6, IP6_ADDRESS, &addr); /* Add/delete notification */ - run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", - IP6_ADDRESS, IP6_PLEN, DEVICE_NAME, lifetime, preferred); - wait_signal (address_added); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); + accept_signal (address_added); g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); - wait_signal (address_removed); + + nmtstp_ip6_address_del (EX, ifindex, addr, IP6_PLEN); + accept_signal (address_removed); g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); /* Add/delete conflict */ - run_command ("ip address add %s/%d dev %s valid_lft %d preferred_lft %d", - IP6_ADDRESS, IP6_PLEN, DEVICE_NAME, lifetime, preferred); - g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr, in6addr_any, IP6_PLEN, lifetime, preferred, flags)); + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, 0); + accept_signal (address_added); g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + + nmtstp_ip6_address_add (EX, ifindex, addr, IP6_PLEN, in6addr_any, lifetime, preferred, flags); + ensure_no_signal (address_added); + g_assert (nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); + + free_signal (address_added); + free_signal (address_removed); +} + +/*****************************************************************************/ + +static void +test_ip4_address_peer (void) +{ + const int ifindex = DEVICE_IFINDEX; + SignalData *address_added = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_ADDED, ip4_address_callback); + SignalData *address_removed = add_signal (NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, ip4_address_callback); + in_addr_t addr, addr_peer, addr_peer2; + guint32 lifetime = 2000; + guint32 preferred = 1000; + const NMPlatformIP4Address *a; + + inet_pton (AF_INET, IP4_ADDRESS, &addr); + inet_pton (AF_INET, IP4_ADDRESS_PEER, &addr_peer); + inet_pton (AF_INET, IP4_ADDRESS_PEER2, &addr_peer2); + g_assert (ifindex > 0); + + g_assert (addr != addr_peer); + + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + accept_signals (address_removed, 0, G_MAXINT); + accept_signals (address_added, 0, G_MAXINT); + + /* Add/delete notification */ + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer, lifetime, preferred, NULL); accept_signal (address_added); - /*run_command ("ip address delete %s/%d dev %s", IP6_ADDRESS, IP6_PLEN, DEVICE_NAME); - g_assert (nm_platform_ip6_address_delete (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - g_assert (!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, addr, IP6_PLEN)); - wait_signal (address_removed);*/ + g_assert ((a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer))); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2)); + + nmtstp_ip_address_assert_lifetime ((NMPlatformIPAddress *) a, -1, lifetime, preferred); + + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr_peer2, lifetime, preferred, NULL); + accept_signal (address_added); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer)); + g_assert ((a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2))); + + nmtstp_ip_address_assert_lifetime ((NMPlatformIPAddress *) a, -1, lifetime, preferred); + + g_assert (addr != addr_peer); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr_peer); + accept_signal (address_removed); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer)); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr_peer2)); free_signal (address_added); free_signal (address_removed); } +/*****************************************************************************/ + +static void +test_ip4_address_peer_zero (void) +{ + const int ifindex = DEVICE_IFINDEX; + in_addr_t addr, addr_peer; + guint32 lifetime = 2000; + guint32 preferred = 1000; + const int plen = 24; + const char *label = NULL; + in_addr_t peers[3], r_peers[3]; + int i; + GArray *addrs; + + g_assert (ifindex > 0); + + inet_pton (AF_INET, "192.168.5.2", &addr); + inet_pton (AF_INET, "192.168.6.2", &addr_peer); + peers[0] = addr; + peers[1] = addr_peer; + peers[2] = 0; + + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + + nmtst_rand_perm (NULL, r_peers, peers, sizeof (peers[0]), G_N_ELEMENTS (peers)); + for (i = 0; i < G_N_ELEMENTS (peers); i++) { + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); + + nmtstp_ip4_address_add (EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, label); + + addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + g_assert (addrs); + g_assert_cmpint (addrs->len, ==, i + 1); + g_array_unref (addrs); + } + + if (nmtst_is_debug () && nmtstp_is_root_test ()) + nmtstp_run_command_check ("ip address show dev %s", DEVICE_NAME); + + nmtst_rand_perm (NULL, r_peers, peers, sizeof (peers[0]), G_N_ELEMENTS (peers)); + for (i = 0; i < G_N_ELEMENTS (peers); i++) { + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); + + nmtstp_ip4_address_del (EX, ifindex, addr, plen, r_peers[i]); + + addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + g_assert (addrs); + g_assert_cmpint (addrs->len, ==, G_N_ELEMENTS (peers) - i - 1); + g_array_unref (addrs); + } +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { nmtst_init_with_logging (argc, argv, NULL, "ALL"); } -void -setup_tests (void) +/***************************************************************************** + * SETUP TESTS + *****************************************************************************/ + +typedef struct { + const char *testpath; + GTestFunc test_func; +} TestSetup; + +static void +_g_test_run (gconstpointer user_data) { - SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); + const TestSetup *s = user_data; + int ifindex; + + _LOGT ("TEST: start %s", s->testpath); nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); + g_assert_cmpint (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL), ==, NM_PLATFORM_ERROR_SUCCESS); - g_test_add_func ("/address/internal/ip4", test_ip4_address); - g_test_add_func ("/address/internal/ip6", test_ip6_address); + ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + g_assert_cmpint (ifindex, >, 0); + g_assert_cmpint (DEVICE_IFINDEX, ==, -1); - if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform")) { - g_test_add_func ("/address/external/ip4", test_ip4_address_external); - g_test_add_func ("/address/external/ip6", test_ip6_address_external); - } + DEVICE_IFINDEX = ifindex; + EX = nmtstp_run_command_check_external_global (); + + s->test_func (); + + g_assert_cmpint (DEVICE_IFINDEX, ==, ifindex); + DEVICE_IFINDEX = -1; + + g_assert_cmpint (ifindex, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + _LOGT ("TEST: finished %s", s->testpath); +} + +static void +_g_test_add_func (const char *testpath, + GTestFunc test_func) +{ + TestSetup *s; + + s = g_new0 (TestSetup, 1); + s->testpath = testpath; + s->test_func = test_func; + + g_test_add_data_func_full (testpath, s, _g_test_run, g_free); +} + +void +setup_tests (void) +{ + _g_test_add_func ("/address/ipv4/general", test_ip4_address_general); + _g_test_add_func ("/address/ipv6/general", test_ip6_address_general); + + _g_test_add_func ("/address/ipv4/general-2", test_ip4_address_general_2); + _g_test_add_func ("/address/ipv6/general-2", test_ip6_address_general_2); + + _g_test_add_func ("/address/ipv4/peer", test_ip4_address_peer); + _g_test_add_func ("/address/ipv4/peer/zero", test_ip4_address_peer_zero); } diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 0b825114..9e624a02 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -35,7 +35,7 @@ test_cleanup_internal (void) inet_pton (AF_INET6, "2001:db8:e:f:1:2:3:4", &gateway6); /* Create and set up device */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME), NULL)); @@ -43,8 +43,8 @@ test_cleanup_internal (void) g_assert (ifindex > 0); /* Add routes and addresses */ - g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, 0, plen4, lifetime, preferred, NULL)); - g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, in6addr_any, plen6, lifetime, preferred, flags)); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, NULL)); + g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, lifetime, preferred, flags)); g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss)); g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss)); g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss)); diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index b0eae63b..09a57f87 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -2,6 +2,8 @@ #include <sys/mount.h> #include <sched.h> +#include <sys/wait.h> +#include <fcntl.h> #include "test-common.h" @@ -10,9 +12,8 @@ #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" #define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count - gboolean -nmtst_platform_is_root_test (void) +nmtstp_is_root_test (void) { NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") return (SETUP == nm_linux_platform_setup); @@ -20,9 +21,9 @@ nmtst_platform_is_root_test (void) } gboolean -nmtst_platform_is_sysfs_writable (void) +nmtstp_is_sysfs_writable (void) { - return !nmtst_platform_is_root_test () + return !nmtstp_is_root_test () || (access ("/sys/devices", W_OK) == 0); } @@ -34,7 +35,7 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall data->name = name; data->change_type = change_type; data->received_count = 0; - data->handler_id = g_signal_connect (nm_platform_get (), name, callback, data); + data->handler_id = g_signal_connect (NM_PLATFORM_GET, name, callback, data); data->ifindex = ifindex; data->ifname = ifname; @@ -46,7 +47,7 @@ add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCall void _accept_signal (const char *file, int line, const char *func, SignalData *data) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + _LOGD ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); if (data->received_count != 1) g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal one time: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); data->received_count = 0; @@ -55,7 +56,7 @@ _accept_signal (const char *file, int line, const char *func, SignalData *data) void _accept_signals (const char *file, int line, const char *func, SignalData *data, int min, int max) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); + _LOGD ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); if (data->received_count < min || data->received_count > max) g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal [%d,%d] times: "SIGNAL_DATA_FMT, file, line, func, min, max, SIGNAL_DATA_ARG (data)); data->received_count = 0; @@ -64,15 +65,28 @@ _accept_signals (const char *file, int line, const char *func, SignalData *data, void _ensure_no_signal (const char *file, int line, const char *func, SignalData *data) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal 0 times: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + _LOGD ("NMPlatformSignalAssert: %s:%d, %s(): Accepting signal 0 times: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); if (data->received_count > 0) g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to accept signal 0 times: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); } void +_accept_or_wait_signal (const char *file, int line, const char *func, SignalData *data) +{ + _LOGD ("NMPlatformSignalAssert: %s:%d, %s(): accept-or-wait signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + if (data->received_count == 0) { + data->loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (data->loop); + g_clear_pointer (&data->loop, g_main_loop_unref); + } + + _accept_signal (file, line, func, data); +} + +void _wait_signal (const char *file, int line, const char *func, SignalData *data) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): wait signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + _LOGD ("NMPlatformSignalAssert: %s:%d, %s(): wait signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); if (data->received_count) g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to wait for signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); @@ -86,16 +100,16 @@ _wait_signal (const char *file, int line, const char *func, SignalData *data) void _free_signal (const char *file, int line, const char *func, SignalData *data) { - debug ("NMPlatformSignalAssert: %s:%d, %s(): free signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); + _LOGD ("NMPlatformSignalAssert: %s:%d, %s(): free signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); if (data->received_count != 0) g_error ("NMPlatformSignalAssert: %s:%d, %s(): failure to free non-accepted signal: "SIGNAL_DATA_FMT, file, line, func, SIGNAL_DATA_ARG (data)); - g_signal_handler_disconnect (nm_platform_get (), data->handler_id); + g_signal_handler_disconnect (NM_PLATFORM_GET, data->handler_id); g_free (data); } void -link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, SignalData *data) { GArray *links; NMPlatformLink *cached; @@ -114,12 +128,12 @@ link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlat return; if (data->loop) { - debug ("Quitting main loop."); + _LOGD ("Quitting main loop."); g_main_loop_quit (data->loop); } data->received_count++; - debug ("Received signal '%s-%s' ifindex %d ifname '%s' %dth time.", data->name, nm_platform_signal_change_type_to_string (data->change_type), ifindex, received->name, data->received_count); + _LOGD ("Received signal '%s-%s' ifindex %d ifname '%s' %dth time.", data->name, nm_platform_signal_change_type_to_string (data->change_type), ifindex, received->name, data->received_count); if (change_type == NM_PLATFORM_SIGNAL_REMOVED) g_assert (!nm_platform_link_get_name (NM_PLATFORM_GET, ifindex)); @@ -253,21 +267,1176 @@ _assert_ip4_route_exists (const char *file, guint line, const char *func, gboole } } -void -run_command (const char *format, ...) +int +nmtstp_run_command (const char *format, ...) { - char *command; + int result; + gs_free char *command = NULL; va_list ap; va_start (ap, format); command = g_strdup_vprintf (format, ap); va_end (ap); - debug ("Running command: %s", command); - g_assert (!system (command)); - debug ("Command finished."); - g_free (command); + + _LOGD ("Running command: %s", command); + result = system (command); + _LOGD ("Command finished: result=%d", result); + + return result; +} + +/*****************************************************************************/ + +typedef struct { + GMainLoop *loop; + gboolean timeout; + guint id; +} WaitForSignalData; + +static void +_wait_for_signal_cb (NMPlatform *platform, + NMPObjectType obj_type, + int ifindex, + NMPlatformLink *plink, + NMPlatformSignalChangeType change_type, + gpointer user_data) +{ + WaitForSignalData *data = user_data; + + g_main_loop_quit (data->loop); +} + +static gboolean +_wait_for_signal_timeout (gpointer user_data) +{ + WaitForSignalData *data = user_data; + + data->timeout = TRUE; + data->id = 0; + g_main_loop_quit (data->loop); + return G_SOURCE_REMOVE; +} + +gboolean +nmtstp_wait_for_signal (guint timeout_ms) +{ + WaitForSignalData data = { 0 }; + + gulong id_link, id_ip4_address, id_ip6_address, id_ip4_route, id_ip6_route; + + data.loop = g_main_loop_new (NULL, FALSE); + + id_link = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip4_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip6_address = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip4_route = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + id_ip6_route = g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_wait_for_signal_cb), &data); + + if (timeout_ms != 0) + data.id = g_timeout_add (timeout_ms, _wait_for_signal_timeout, &data); + + g_main_loop_run (data.loop); + + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_link)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_address)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_address)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip4_route)); + g_assert (nm_clear_g_signal_handler (NM_PLATFORM_GET, &id_ip6_route)); + + if (nm_clear_g_source (&data.id)) + g_assert (timeout_ms != 0 && !data.timeout); + + g_clear_pointer (&data.loop, g_main_loop_unref); + + return !data.timeout; +} + +gboolean +nmtstp_wait_for_signal_until (gint64 until_ms) +{ + gint64 now; + + while (TRUE) { + now = nm_utils_get_monotonic_timestamp_ms (); + + if (until_ms < now) + return FALSE; + + if (nmtstp_wait_for_signal (MAX (1, until_ms - now))) + return TRUE; + } +} + +const NMPlatformLink * +nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms) +{ + return nmtstp_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); +} + +const NMPlatformLink * +nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms) +{ + const NMPlatformLink *plink; + gint64 now; + + while (TRUE) { + now = nm_utils_get_monotonic_timestamp_ms (); + + plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname); + if ( plink + && (expected_link_type == NM_LINK_TYPE_NONE || plink->type == expected_link_type)) + return plink; + + if (until_ms < now) + return NULL; + + nmtstp_wait_for_signal (MAX (1, until_ms - now)); + } +} + +const NMPlatformLink * +nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms) +{ + return nmtstp_assert_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); +} + +const NMPlatformLink * +nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms) +{ + const NMPlatformLink *plink; + + plink = nmtstp_wait_for_link_until (ifname, expected_link_type, until_ms); + g_assert (plink); + return plink; +} + +int +nmtstp_run_command_check_external_global (void) +{ + if (!nmtstp_is_root_test ()) + return FALSE; + switch (nmtst_get_rand_int () % 3) { + case 0: + return -1; + case 1: + return FALSE; + default: + return TRUE; + } +} + +gboolean +nmtstp_run_command_check_external (int external_command) +{ + if (external_command != -1) { + g_assert (NM_IN_SET (external_command, FALSE, TRUE)); + g_assert (!external_command || nmtstp_is_root_test ()); + return !!external_command; + } + if (!nmtstp_is_root_test ()) + return FALSE; + return (nmtst_get_rand_int () % 2) == 0; +} + +#define CHECK_LIFETIME_MAX_DIFF 2 + +gboolean +nmtstp_ip_address_check_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred) +{ + gint64 offset; + int i; + + g_assert (addr); + + if (now == -1) + now = nm_utils_get_monotonic_timestamp_s (); + g_assert (now > 0); + + g_assert (expected_preferred <= expected_lifetime); + + if ( expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT) { + return addr->timestamp == 0 + && addr->lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && addr->preferred == NM_PLATFORM_LIFETIME_PERMANENT; + } + + if (addr->timestamp == 0) + return FALSE; + + offset = (gint64) now - addr->timestamp; + + for (i = 0; i < 2; i++) { + guint32 lft = i ? expected_lifetime : expected_preferred; + guint32 adr = i ? addr->lifetime : addr->preferred; + + if (lft == NM_PLATFORM_LIFETIME_PERMANENT) { + if (adr != NM_PLATFORM_LIFETIME_PERMANENT) + return FALSE; + } else { + if ( adr - offset <= lft - CHECK_LIFETIME_MAX_DIFF + || adr - offset >= lft + CHECK_LIFETIME_MAX_DIFF) + return FALSE; + } + } + return TRUE; +} + +void +nmtstp_ip_address_assert_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred) +{ + gint64 n = now; + gint64 offset; + int i; + + g_assert (addr); + + if (now == -1) + now = nm_utils_get_monotonic_timestamp_s (); + g_assert (now > 0); + + g_assert (expected_preferred <= expected_lifetime); + + if ( expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && expected_lifetime == NM_PLATFORM_LIFETIME_PERMANENT) { + g_assert_cmpint (addr->timestamp, ==, 0); + g_assert_cmpint (addr->lifetime, ==, NM_PLATFORM_LIFETIME_PERMANENT); + g_assert_cmpint (addr->preferred, ==, NM_PLATFORM_LIFETIME_PERMANENT); + return; + } + + g_assert_cmpint (addr->timestamp, >, 0); + g_assert_cmpint (addr->timestamp, <=, now); + + offset = (gint64) now - addr->timestamp; + g_assert_cmpint (offset, >=, 0); + + for (i = 0; i < 2; i++) { + guint32 lft = i ? expected_lifetime : expected_preferred; + guint32 adr = i ? addr->lifetime : addr->preferred; + + if (lft == NM_PLATFORM_LIFETIME_PERMANENT) + g_assert_cmpint (adr, ==, NM_PLATFORM_LIFETIME_PERMANENT); + else { + g_assert_cmpint (adr, <=, lft); + g_assert_cmpint (offset, <=, adr); + g_assert_cmpint (adr - offset, <=, lft + CHECK_LIFETIME_MAX_DIFF); + g_assert_cmpint (adr - offset, >=, lft - CHECK_LIFETIME_MAX_DIFF); + } + } + + g_assert (nmtstp_ip_address_check_lifetime (addr, n, expected_lifetime, expected_preferred)); +} + +static void +_ip_address_add (gboolean external_command, + gboolean is_v4, + int ifindex, + const NMIPAddr *address, + int plen, + const NMIPAddr *peer_address, + guint32 lifetime, + guint32 preferred, + const char *label, + guint flags) +{ + gint64 end_time; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *ifname; + gs_free char *s_valid = NULL; + gs_free char *s_preferred = NULL; + gs_free char *s_label = NULL; + char b1[NM_UTILS_INET_ADDRSTRLEN], b2[NM_UTILS_INET_ADDRSTRLEN]; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + g_assert (ifname); + + if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + s_valid = g_strdup_printf (" valid_lft %d", lifetime); + if (preferred != NM_PLATFORM_LIFETIME_PERMANENT) + s_preferred = g_strdup_printf (" preferred_lft %d", preferred); + if (label) + s_label = g_strdup_printf ("%s:%s", ifname, label); + + if (is_v4) { + char s_peer[100]; + + g_assert (flags == 0); + + if ( peer_address->addr4 != address->addr4 + || nmtst_get_rand_int () % 2) { + /* If the peer is the same as the local address, we can omit it. The result should be identical */ + g_snprintf (s_peer, sizeof (s_peer), " peer %s", nm_utils_inet4_ntop (peer_address->addr4, b2)); + } else + s_peer[0] = '\0'; + + nmtstp_run_command_check ("ip address change %s%s/%d dev %s%s%s%s", + nm_utils_inet4_ntop (address->addr4, b1), + s_peer, + plen, + ifname, + s_valid ?: "", + s_preferred ?: "", + s_label ?: ""); + } else { + g_assert (label == NULL); + + /* flags not implemented (yet) */ + g_assert (flags == 0); + nmtstp_run_command_check ("ip address change %s%s%s/%d dev %s%s%s%s", + nm_utils_inet6_ntop (&address->addr6, b1), + !IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) ? " peer " : "", + !IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) ? nm_utils_inet6_ntop (&peer_address->addr6, b2) : "", + plen, + ifname, + s_valid ?: "", + s_preferred ?: "", + s_label ?: ""); + } + } else { + gboolean success; + + if (is_v4) { + g_assert (flags == 0); + success = nm_platform_ip4_address_add (NM_PLATFORM_GET, + ifindex, + address->addr4, + plen, + peer_address->addr4, + lifetime, + preferred, + label); + } else { + g_assert (label == NULL); + success = nm_platform_ip6_address_add (NM_PLATFORM_GET, + ifindex, + address->addr6, + plen, + peer_address->addr6, + lifetime, + preferred, + flags); + } + g_assert (success); + } + + /* Let's wait until we see the address. */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + /* let's wait until we see the address as we added it. */ + if (is_v4) { + const NMPlatformIP4Address *a; + + g_assert (flags == 0); + a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); + if ( a + && a->peer_address == peer_address->addr4 + && nmtstp_ip_address_check_lifetime ((NMPlatformIPAddress*) a, -1, lifetime, preferred) + && strcmp (a->label, label ?: "") == 0) + break; + } else { + const NMPlatformIP6Address *a; + + g_assert (label == NULL); + g_assert (flags == 0); + + a = nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, address->addr6, plen); + if ( a + && !memcmp (nm_platform_ip6_address_get_peer (a), + (IN6_IS_ADDR_UNSPECIFIED (&peer_address->addr6) || IN6_ARE_ADDR_EQUAL (&address->addr6, &peer_address->addr6)) + ? &address->addr6 : &peer_address->addr6, + sizeof (struct in6_addr)) + && nmtstp_ip_address_check_lifetime ((NMPlatformIPAddress*) a, -1, lifetime, preferred)) + break; + } + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); +} + +#define _assert_pllink(success, pllink, name, type) \ + G_STMT_START { \ + const NMPlatformLink *_pllink = (pllink); \ + \ + if ((success)) { \ + g_assert (_pllink); \ + g_assert (_pllink == nmtstp_link_get_typed (_pllink->ifindex, (name), (type))); \ + } else { \ + g_assert (!_pllink); \ + g_assert (!nmtstp_link_get (0, (name))); \ + } \ + } G_STMT_END + +const NMPlatformLink * +nmtstp_link_dummy_add (gboolean external_command, + const char *name) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + success = !nmtstp_run_command ("ip link add %s type dummy", + name); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_DUMMY, 100); + } else + success = nm_platform_link_dummy_add (NM_PLATFORM_GET, name, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + g_assert (success); + _assert_pllink (success, pllink, name, NM_LINK_TYPE_DUMMY); + return pllink; +} + +const NMPlatformLink * +nmtstp_link_gre_add (gboolean external_command, + const char *name, + const NMPlatformLnkGre *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + success = !nmtstp_run_command ("ip tunnel add %s mode gre %s local %s remote %s ttl %u tos %02x %s", + name, + dev ? dev : "", + nm_utils_inet4_ntop (lnk->local, NULL), + nm_utils_inet4_ntop (lnk->remote, buffer), + lnk->ttl, + lnk->tos, + lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_GRE, 100); + } else + success = nm_platform_link_gre_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_GRE); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_ip6tnl_add (gboolean external_command, + const char *name, + const NMPlatformLnkIp6Tnl *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET6_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + const char *mode; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + switch (lnk->proto) { + case IPPROTO_IPIP: + mode = "ipip6"; + break; + case IPPROTO_IPV6: + mode = "ip6ip6"; + break; + default: + g_assert_not_reached (); + } + + success = !nmtstp_run_command ("ip -6 tunnel add %s mode %s %s local %s remote %s ttl %u tclass %02x encaplimit %u flowlabel %x", + name, + mode, + dev, + nm_utils_inet6_ntop (&lnk->local, NULL), + nm_utils_inet6_ntop (&lnk->remote, buffer), + lnk->ttl, + lnk->tclass, + lnk->encap_limit, + lnk->flow_label); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IP6TNL, 100); + } else + success = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_IP6TNL); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_ipip_add (gboolean external_command, + const char *name, + const NMPlatformLnkIpIp *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + success = !nmtstp_run_command ("ip tunnel add %s mode ipip %s local %s remote %s ttl %u tos %02x %s", + name, + dev, + nm_utils_inet4_ntop (lnk->local, NULL), + nm_utils_inet4_ntop (lnk->remote, buffer), + lnk->ttl, + lnk->tos, + lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_IPIP, 100); + } else + success = nm_platform_link_ipip_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_IPIP); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_macvlan_add (gboolean external_command, + const char *name, + int parent, + const NMPlatformLnkMacvlan *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + NMLinkType link_type; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + link_type = lnk->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN; + + if (external_command) { + const char *dev; + char *modes[] = { + [MACVLAN_MODE_BRIDGE] = "bridge", + [MACVLAN_MODE_VEPA] = "vepa", + [MACVLAN_MODE_PRIVATE] = "private", + [MACVLAN_MODE_PASSTHRU] = "passthru", + }; + + dev = nm_platform_link_get_name (NM_PLATFORM_GET, parent); + g_assert (dev); + g_assert_cmpint (lnk->mode, <, G_N_ELEMENTS (modes)); + + success = !nmtstp_run_command ("ip link add name %s link %s type %s mode %s %s", + name, + dev, + lnk->tap ? "macvtap" : "macvlan", + modes[lnk->mode], + lnk->no_promisc ? "nopromisc" : ""); + if (success) + pllink = nmtstp_assert_wait_for_link (name, link_type, 100); + } else + success = nm_platform_link_macvlan_add (NM_PLATFORM_GET, name, parent, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, link_type); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_sit_add (gboolean external_command, + const char *name, + const NMPlatformLnkSit *lnk) +{ + const NMPlatformLink *pllink = NULL; + gboolean success; + char buffer[INET_ADDRSTRLEN]; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *dev = ""; + + if (lnk->parent_ifindex) { + const char *parent_name; + + parent_name = nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex); + g_assert (parent_name); + dev = nm_sprintf_bufa (100, " dev %s", parent_name); + } + + success = !nmtstp_run_command ("ip tunnel add %s mode sit%s local %s remote %s ttl %u tos %02x %s", + name, + dev, + nm_utils_inet4_ntop (lnk->local, NULL), + nm_utils_inet4_ntop (lnk->remote, buffer), + lnk->ttl, + lnk->tos, + lnk->path_mtu_discovery ? "pmtudisc" : "nopmtudisc"); + if (success) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_SIT, 100); + } else + success = nm_platform_link_sit_add (NM_PLATFORM_GET, name, lnk, &pllink) == NM_PLATFORM_ERROR_SUCCESS; + + _assert_pllink (success, pllink, name, NM_LINK_TYPE_SIT); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_vxlan_add (gboolean external_command, + const char *name, + const NMPlatformLnkVxlan *lnk) +{ + const NMPlatformLink *pllink = NULL; + NMPlatformError plerr; + int err; + + g_assert (nm_utils_iface_valid_name (name)); + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + gs_free char *dev = NULL; + gs_free char *local = NULL, *remote = NULL; + + if (lnk->parent_ifindex) + dev = g_strdup_printf ("dev %s", nm_platform_link_get_name (NM_PLATFORM_GET, lnk->parent_ifindex)); + + if (lnk->local) + local = g_strdup_printf ("%s", nm_utils_inet4_ntop (lnk->local, NULL)); + else if (memcmp (&lnk->local6, &in6addr_any, sizeof (in6addr_any))) + local = g_strdup_printf ("%s", nm_utils_inet6_ntop (&lnk->local6, NULL)); + + if (lnk->group) + remote = g_strdup_printf ("%s", nm_utils_inet4_ntop (lnk->group, NULL)); + else if (memcmp (&lnk->group6, &in6addr_any, sizeof (in6addr_any))) + remote = g_strdup_printf ("%s", nm_utils_inet6_ntop (&lnk->group6, NULL)); + + err = nmtstp_run_command ("ip link add %s type vxlan id %u %s local %s group %s ttl %u tos %02x dstport %u srcport %u %u ageing %u", + name, + lnk->id, + dev ? dev : "", + local, + remote, + lnk->ttl, + lnk->tos, + lnk->dst_port, + lnk->src_port_min, lnk->src_port_max, + lnk->ageing); + /* Older versions of iproute2 don't support adding vxlan devices. + * On failure, fallback to using platform code. */ + if (err == 0) + pllink = nmtstp_assert_wait_for_link (name, NM_LINK_TYPE_VXLAN, 100); + else + _LOGI ("Adding vxlan device via iproute2 failed. Assume iproute2 is not up to the task."); + } + if (!pllink) { + plerr = nm_platform_link_vxlan_add (NM_PLATFORM_GET, name, lnk, &pllink); + g_assert_cmpint (plerr, ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert (pllink); + } + + g_assert_cmpint (pllink->type, ==, NM_LINK_TYPE_VXLAN); + g_assert_cmpstr (pllink->name, ==, name); + return pllink; +} + +void +nmtstp_ip4_address_add (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address, + guint32 lifetime, + guint32 preferred, + const char *label) +{ + _ip_address_add (external_command, + TRUE, + ifindex, + (NMIPAddr *) &address, + plen, + (NMIPAddr *) &peer_address, + lifetime, + preferred, + label, + 0); +} + +void +nmtstp_ip6_address_add (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen, + struct in6_addr peer_address, + guint32 lifetime, + guint32 preferred, + guint flags) +{ + _ip_address_add (external_command, + FALSE, + ifindex, + (NMIPAddr *) &address, + plen, + (NMIPAddr *) &peer_address, + lifetime, + preferred, + NULL, + flags); +} + +static void +_ip_address_del (gboolean external_command, + gboolean is_v4, + int ifindex, + const NMIPAddr *address, + int plen, + const NMIPAddr *peer_address) +{ + gint64 end_time; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *ifname; + char b1[NM_UTILS_INET_ADDRSTRLEN], b2[NM_UTILS_INET_ADDRSTRLEN]; + int success; + gboolean had_address; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + g_assert (ifname); + + /* let's wait until we see the address as we added it. */ + if (is_v4) + had_address = !!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); + else + had_address = !!nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, address->addr6, plen); + + if (is_v4) { + success = nmtstp_run_command ("ip address delete %s%s%s/%d dev %s", + nm_utils_inet4_ntop (address->addr4, b1), + peer_address->addr4 != address->addr4 ? " peer " : "", + peer_address->addr4 != address->addr4 ? nm_utils_inet4_ntop (peer_address->addr4, b2) : "", + plen, + ifname); + } else { + g_assert (!peer_address); + success = nmtstp_run_command ("ip address delete %s/%d dev %s", + nm_utils_inet6_ntop (&address->addr6, b1), + plen, + ifname); + } + g_assert (success == 0 || !had_address); + } else { + gboolean success; + + if (is_v4) { + success = nm_platform_ip4_address_delete (NM_PLATFORM_GET, + ifindex, + address->addr4, + plen, + peer_address->addr4); + } else { + g_assert (!peer_address); + success = nm_platform_ip6_address_delete (NM_PLATFORM_GET, + ifindex, + address->addr6, + plen); + } + g_assert (success); + } + + /* Let's wait until we get the result */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + /* let's wait until we see the address as we added it. */ + if (is_v4) { + const NMPlatformIP4Address *a; + + a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); + if (!a) + break; + } else { + const NMPlatformIP6Address *a; + + a = nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, address->addr6, plen); + if (!a) + break; + } + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); +} + +void +nmtstp_ip4_address_del (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address) +{ + _ip_address_del (external_command, + TRUE, + ifindex, + (NMIPAddr *) &address, + plen, + (NMIPAddr *) &peer_address); +} + +void +nmtstp_ip6_address_del (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen) +{ + _ip_address_del (external_command, + FALSE, + ifindex, + (NMIPAddr *) &address, + plen, + NULL); +} + +const NMPlatformLink * +nmtstp_link_get_typed (int ifindex, + const char *name, + NMLinkType link_type) +{ + const NMPlatformLink *pllink = NULL; + + if (ifindex > 0) { + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + + if (pllink) { + g_assert_cmpint (pllink->ifindex, ==, ifindex); + if (name) + g_assert_cmpstr (name, ==, pllink->name); + } else { + if (name) + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name)); + } + } else { + g_assert (name); + + pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name); + + if (pllink) + g_assert_cmpstr (name, ==, pllink->name); + } + + g_assert (!name || nm_utils_iface_valid_name (name)); + + if (pllink && link_type != NM_LINK_TYPE_NONE) + g_assert_cmpint (pllink->type, ==, link_type); + + return pllink; +} + +const NMPlatformLink * +nmtstp_link_get (int ifindex, + const char *name) +{ + return nmtstp_link_get_typed (ifindex, name, NM_LINK_TYPE_NONE); +} + +void +nmtstp_link_del (gboolean external_command, + int ifindex, + const char *name) +{ + gint64 end_time; + const NMPlatformLink *pllink; + gboolean success; + gs_free char *name_copy = NULL; + + pllink = nmtstp_link_get (ifindex, name); + + g_assert (pllink); + + name = name_copy = g_strdup (pllink->name); + ifindex = pllink->ifindex; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + nmtstp_run_command_check ("ip link delete %s", name); + } else { + success = nm_platform_link_delete (NM_PLATFORM_GET, ifindex); + g_assert (success); + } + + /* Let's wait until we get the result */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + if (!nm_platform_link_get (NM_PLATFORM_GET, ifindex)) { + g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name)); + break; + } + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); } +void +nmtstp_link_set_updown (gboolean external_command, + int ifindex, + gboolean up) +{ + const NMPlatformLink *plink; + gint64 end_time; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *ifname; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + g_assert (ifname); + + nmtstp_run_command_check ("ip link set %s %s", + ifname, + up ? "up" : "down"); + } else { + if (up) + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + else + g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); + } + + /* Let's wait until we get the result */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + /* let's wait until we see the address as we added it. */ + plink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + g_assert (plink); + + if (NM_FLAGS_HAS (plink->flags, IFF_UP) == !!up) + break; + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); +} + +/*****************************************************************************/ + +struct _NMTstpNamespaceHandle { + pid_t pid; + int pipe_fd; +}; + +NMTstpNamespaceHandle * +nmtstp_namespace_create (int unshare_flags, GError **error) +{ + NMTstpNamespaceHandle *ns_handle; + int e; + int errsv; + pid_t pid, pid2; + int pipefd_c2p[2]; + int pipefd_p2c[2]; + ssize_t r; + + e = pipe (pipefd_c2p); + if (e != 0) { + errsv = errno; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "pipe() failed with %d (%s)", errsv, strerror (errsv)); + return FALSE; + } + + e = pipe (pipefd_p2c); + if (e != 0) { + errsv = errno; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "pipe() failed with %d (%s)", errsv, strerror (errsv)); + close (pipefd_c2p[0]); + close (pipefd_c2p[1]); + return FALSE; + } + + pid = fork (); + if (pid < 0) { + errsv = errno; + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "fork() failed with %d (%s)", errsv, strerror (errsv)); + close (pipefd_c2p[0]); + close (pipefd_c2p[1]); + close (pipefd_p2c[0]); + close (pipefd_p2c[1]); + return FALSE; + } + + if (pid == 0) { + char read_buf[1]; + + close (pipefd_c2p[0]); /* close read-end */ + close (pipefd_p2c[1]); /* close write-end */ + + if (unshare (unshare_flags) != 0) { + errsv = errno; + if (errsv == 0) + errsv = -1; + } else + errsv = 0; + + /* sync with parent process and send result. */ + do { + r = write (pipefd_c2p[1], &errsv, sizeof (errsv)); + } while (r < 0 && errno == EINTR); + if (r != sizeof (errsv)) { + errsv = errno; + if (errsv == 0) + errsv = -2; + } + close (pipefd_c2p[1]); + + /* wait until parent process terminates (or kills us). */ + if (errsv == 0) { + do { + r = read (pipefd_p2c[0], read_buf, sizeof (read_buf)); + } while (r < 0 && errno == EINTR); + } + close (pipefd_p2c[0]); + _exit (0); + } + + close (pipefd_c2p[1]); /* close write-end */ + close (pipefd_p2c[0]); /* close read-end */ + + /* sync with child process. */ + do { + r = read (pipefd_c2p[0], &errsv, sizeof (errsv)); + } while (r < 0 && errno == EINTR); + + close (pipefd_c2p[0]); + + if ( r != sizeof (errsv) + || errsv != 0) { + int status; + + if (r != sizeof (errsv)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "child process failed for unknown reason"); + } else { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "child process signaled failure %d (%s)", errsv, strerror (errsv)); + } + close (pipefd_p2c[1]); + kill (pid, SIGKILL); + do { + pid2 = waitpid (pid, &status, 0); + } while (pid2 == -1 && errno == EINTR); + return FALSE; + } + + ns_handle = g_new0 (NMTstpNamespaceHandle, 1); + ns_handle->pid = pid; + ns_handle->pipe_fd = pipefd_p2c[1]; + return ns_handle; +} + +pid_t +nmtstp_namespace_handle_get_pid (NMTstpNamespaceHandle *ns_handle) +{ + g_return_val_if_fail (ns_handle, 0); + g_return_val_if_fail (ns_handle->pid > 0, 0); + + return ns_handle->pid; +} + +void +nmtstp_namespace_handle_release (NMTstpNamespaceHandle *ns_handle) +{ + pid_t pid; + int status; + + if (!ns_handle) + return; + + g_return_if_fail (ns_handle->pid > 0); + + close (ns_handle->pipe_fd); + ns_handle->pipe_fd = 0; + + kill (ns_handle->pid, SIGKILL); + + do { + pid = waitpid (ns_handle->pid, &status, 0); + } while (pid == -1 && errno == EINTR); + ns_handle->pid = 0; + + g_free (ns_handle); +} + +int +nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name) +{ + char p[1000]; + + g_return_val_if_fail (pid > 0, 0); + g_return_val_if_fail (ns_name && ns_name[0] && strlen (ns_name) < 50, 0); + + nm_sprintf_buf (p, "/proc/%lu/ns/%s", (long unsigned) pid, ns_name); + + return open(p, O_RDONLY); +} + +/*****************************************************************************/ + NMTST_DEFINE(); static gboolean @@ -318,7 +1487,7 @@ main (int argc, char **argv) init_tests (&argc, &argv); - if ( nmtst_platform_is_root_test () + if ( nmtstp_is_root_test () && (geteuid () != 0 || getegid () != 0)) { if ( g_getenv ("NMTST_FORCE_REAL_ROOT") || !unshare_user ()) { @@ -335,7 +1504,7 @@ main (int argc, char **argv) } } - if (nmtst_platform_is_root_test () && !g_getenv ("NMTST_NO_UNSHARE")) { + if (nmtstp_is_root_test () && !g_getenv ("NMTST_NO_UNSHARE")) { int errsv; if (unshare (CLONE_NEWNET | CLONE_NEWNS) != 0) { @@ -345,10 +1514,7 @@ main (int argc, char **argv) /* Mount our /sys instance, so that gudev sees only our devices. * Needs to be read-only, because we don't run udev. */ - if (mount (NULL, "/sys", "sysfs", MS_SLAVE, NULL) != 0) { - errsv = errno; - g_error ("mount(\"/\", MS_SLAVE) failed with %s (%d)", strerror (errsv), errsv); - } + mount (NULL, "/sys", "sysfs", MS_SLAVE, NULL); if (mount ("sys", "/sys", "sysfs", MS_RDONLY, NULL) != 0) { errsv = errno; g_error ("mount(\"/sys\") failed with %s (%d)", strerror (errsv), errsv); @@ -383,6 +1549,6 @@ main (int argc, char **argv) nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); - g_object_unref (nm_platform_get ()); + g_object_unref (NM_PLATFORM_GET); return result; } diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 3d647543..0281d3c5 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -4,7 +4,7 @@ #include <string.h> #include <arpa/inet.h> -#include "nm-logging.h" +#include "nm-default.h" #include "nm-platform.h" #include "nm-fake-platform.h" #include "nm-linux-platform.h" @@ -13,10 +13,33 @@ #define DEVICE_NAME "nm-test-device" -#define debug(...) nm_log_dbg (LOGD_PLATFORM, __VA_ARGS__) +/*********************************************************************************************/ + +#define _NMLOG_PREFIX_NAME "platform-test" +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG(level, ...) _LOG(level, _NMLOG_DOMAIN, __VA_ARGS__) + +#define _LOG(level, domain, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + const NMLogDomain __domain = (domain); \ + \ + if (nm_logging_enabled (__level, __domain)) { \ + gint64 _ts = nm_utils_get_monotonic_timestamp_ns (); \ + \ + _nm_log (__level, __domain, 0, \ + "%s[%ld.%09ld]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + (long) (_ts / NM_UTILS_NS_PER_SECOND), \ + (long) (_ts % NM_UTILS_NS_PER_SECOND) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*********************************************************************************************/ typedef struct { - int handler_id; + gulong handler_id; const char *name; NMPlatformSignalChangeType change_type; gint received_count; @@ -25,8 +48,21 @@ typedef struct { const char *ifname; } SignalData; -gboolean nmtst_platform_is_root_test (void); -gboolean nmtst_platform_is_sysfs_writable (void); +gboolean nmtstp_is_root_test (void); +gboolean nmtstp_is_sysfs_writable (void); + +/******************************************************************************/ + +typedef struct _NMTstpNamespaceHandle NMTstpNamespaceHandle; + +NMTstpNamespaceHandle *nmtstp_namespace_create (int flags, GError **error); + +void nmtstp_namespace_handle_release (NMTstpNamespaceHandle *handle); +pid_t nmtstp_namespace_handle_get_pid (NMTstpNamespaceHandle *handle); + +int nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name); + +/******************************************************************************/ SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change_type, GCallback callback, int ifindex, const char *ifname); #define add_signal(name, change_type, callback) add_signal_full (name, change_type, (GCallback) callback, 0, NULL) @@ -35,11 +71,13 @@ SignalData *add_signal_full (const char *name, NMPlatformSignalChangeType change void _accept_signal (const char *file, int line, const char *func, SignalData *data); void _accept_signals (const char *file, int line, const char *func, SignalData *data, int min, int max); void _wait_signal (const char *file, int line, const char *func, SignalData *data); +void _accept_or_wait_signal (const char *file, int line, const char *func, SignalData *data); void _ensure_no_signal (const char *file, int line, const char *func, SignalData *data); void _free_signal (const char *file, int line, const char *func, SignalData *data); #define accept_signal(data) _accept_signal(__FILE__, __LINE__, G_STRFUNC, data) #define accept_signals(data, min, max) _accept_signals(__FILE__, __LINE__, G_STRFUNC, data, min, max) #define wait_signal(data) _wait_signal(__FILE__, __LINE__, G_STRFUNC, data) +#define accept_or_wait_signal(data) _accept_or_wait_signal(__FILE__, __LINE__, G_STRFUNC, data) #define ensure_no_signal(data) _ensure_no_signal(__FILE__, __LINE__, G_STRFUNC, data) #define free_signal(data) _free_signal(__FILE__, __LINE__, G_STRFUNC, data) @@ -48,9 +86,88 @@ gboolean ip4_route_exists (const char *ifname, guint32 network, int plen, guint3 void _assert_ip4_route_exists (const char *file, guint line, const char *func, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric); #define assert_ip4_route_exists(exists, ifname, network, plen, metric) _assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, exists, ifname, network, plen, metric) -void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data); +void link_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, NMPlatformLink *received, NMPlatformSignalChangeType change_type, SignalData *data); + +int nmtstp_run_command (const char *format, ...) __attribute__((__format__ (__printf__, 1, 2))); +#define nmtstp_run_command_check(...) do { g_assert_cmpint (nmtstp_run_command (__VA_ARGS__), ==, 0); } while (0) + +gboolean nmtstp_wait_for_signal (guint timeout_ms); +gboolean nmtstp_wait_for_signal_until (gint64 until_ms); +const NMPlatformLink *nmtstp_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms); +const NMPlatformLink *nmtstp_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms); + +const NMPlatformLink *nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms); +const NMPlatformLink *nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms); + +int nmtstp_run_command_check_external_global (void); +gboolean nmtstp_run_command_check_external (int external_command); + +gboolean nmtstp_ip_address_check_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred); +void nmtstp_ip_address_assert_lifetime (const NMPlatformIPAddress *addr, + gint64 now, + guint32 expected_lifetime, + guint32 expected_preferred); +void nmtstp_ip4_address_add (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address, + guint32 lifetime, + guint32 preferred, + const char *label); +void nmtstp_ip6_address_add (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen, + struct in6_addr peer_address, + guint32 lifetime, + guint32 preferred, + guint flags); +void nmtstp_ip4_address_del (gboolean external_command, + int ifindex, + in_addr_t address, + int plen, + in_addr_t peer_address); +void nmtstp_ip6_address_del (gboolean external_command, + int ifindex, + struct in6_addr address, + int plen); + +const NMPlatformLink *nmtstp_link_get_typed (int ifindex, const char *name, NMLinkType link_type); +const NMPlatformLink *nmtstp_link_get (int ifindex, const char *name); + +void nmtstp_link_set_updown (gboolean external_command, + int ifindex, + gboolean up); + +const NMPlatformLink *nmtstp_link_dummy_add (gboolean external_command, + const char *name); +const NMPlatformLink *nmtstp_link_gre_add (gboolean external_command, + const char *name, + const NMPlatformLnkGre *lnk); +const NMPlatformLink *nmtstp_link_ip6tnl_add (gboolean external_command, + const char *name, + const NMPlatformLnkIp6Tnl *lnk); +const NMPlatformLink *nmtstp_link_ipip_add (gboolean external_command, + const char *name, + const NMPlatformLnkIpIp *lnk); +const NMPlatformLink *nmtstp_link_macvlan_add (gboolean external_command, + const char *name, + int parent, + const NMPlatformLnkMacvlan *lnk); +const NMPlatformLink *nmtstp_link_sit_add (gboolean external_command, + const char *name, + const NMPlatformLnkSit *lnk); +const NMPlatformLink *nmtstp_link_vxlan_add (gboolean external_command, + const char *name, + const NMPlatformLnkVxlan *lnk); -void run_command (const char *format, ...); +void nmtstp_link_del (gboolean external_command, + int ifindex, + const char *name); void init_tests (int *argc, char ***argv); void setup_tests (void); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index 0a397b37..f342e565 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -23,7 +23,7 @@ #include <linux/rtnetlink.h> #include "nm-linux-platform.h" -#include "nm-logging.h" +#include "nm-default.h" #include "nm-test-utils.h" @@ -65,3 +65,4 @@ main (int argc, char **argv) return g_test_run (); } + diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 47cbcec1..8b496262 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -1,5 +1,9 @@ #include "config.h" +#include <sched.h> + +#include "nmp-object.h" + #include "test-common.h" #include "nm-test-utils.h" @@ -28,10 +32,18 @@ test_bogus(void) g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX)); + g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, BOGUS_IFINDEX)); @@ -39,15 +51,18 @@ test_bogus(void) g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, &addrlen)); g_assert (!addrlen); g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + + g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *"); g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU)); + g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, BOGUS_IFINDEX)); - g_assert (!nm_platform_vlan_get_info (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL, NULL)); - g_assert (!nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); - g_assert (!nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); + g_assert (!nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); + g_assert (!nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); + g_assert (!nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); } static void @@ -68,15 +83,15 @@ software_add (NMLinkType link_type, const char *name) { switch (link_type) { case NM_LINK_TYPE_DUMMY: - return nm_platform_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_dummy_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BRIDGE: - return nm_platform_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_bridge_add (NM_PLATFORM_GET, name, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_BOND: { gboolean bond0_exists = !!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "bond0"); NMPlatformError plerr; - plerr = nm_platform_bond_add (NM_PLATFORM_GET, name, NULL); + plerr = nm_platform_link_bond_add (NM_PLATFORM_GET, name, NULL); /* Check that bond0 is *not* automatically created. */ if (!bond0_exists) @@ -84,14 +99,14 @@ software_add (NMLinkType link_type, const char *name) return plerr == NM_PLATFORM_ERROR_SUCCESS; } case NM_LINK_TYPE_TEAM: - return nm_platform_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_team_add (NM_PLATFORM_GET, name, NULL) == NM_PLATFORM_ERROR_SUCCESS; case NM_LINK_TYPE_VLAN: { SignalData *parent_added; SignalData *parent_changed; /* Don't call link_callback for the bridge interface */ parent_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, PARENT_NAME); - if (nm_platform_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) + if (nm_platform_link_bridge_add (NM_PLATFORM_GET, PARENT_NAME, NULL, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS) accept_signal (parent_added); free_signal (parent_added); @@ -108,7 +123,7 @@ software_add (NMLinkType link_type, const char *name) accept_signal (parent_changed); free_signal (parent_changed); - return nm_platform_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; + return nm_platform_link_vlan_add (NM_PLATFORM_GET, name, parent_ifindex, VLAN_ID, 0, NULL) == NM_PLATFORM_ERROR_SUCCESS; } } default: @@ -123,7 +138,6 @@ test_link_changed_signal_cb (NMPlatform *platform, int ifindex, const NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, - NMPlatformReason reason, gboolean *p_test_link_changed_signal_arg) { /* test invocation of platform signals with multiple listeners @@ -146,9 +160,6 @@ test_link_changed_signal_cb (NMPlatform *platform, g_assert_cmpint ((gint64) change_type, !=, (gint64) 0); g_assert_cmpint (change_type, !=, NM_PLATFORM_SIGNAL_NONE); - g_assert_cmpint ((gint64) reason, !=, (gint64) 0); - g_assert_cmpint (reason, !=, NM_PLATFORM_REASON_NONE); - *p_test_link_changed_signal_arg = TRUE; } @@ -262,9 +273,9 @@ test_slave (int master, int type, SignalData *master_changed) /* Set slave option */ switch (type) { case NM_LINK_TYPE_BRIDGE: - if (nmtst_platform_is_sysfs_writable ()) { - g_assert (nm_platform_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); - value = nm_platform_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); + if (nmtstp_is_sysfs_writable ()) { + g_assert (nm_platform_sysctl_slave_set_option (NM_PLATFORM_GET, ifindex, "priority", "789")); + value = nm_platform_sysctl_slave_get_option (NM_PLATFORM_GET, ifindex, "priority"); g_assert_cmpstr (value, ==, "789"); g_free (value); } @@ -296,7 +307,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_added); ensure_no_signal (link_changed); ensure_no_signal (link_removed); - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + nmtstp_link_del (-1, ifindex, NULL); accept_signals (master_changed, 0, 1); accept_signals (link_changed, 0, 1); accept_signal (link_removed); @@ -311,7 +322,7 @@ test_software (NMLinkType link_type, const char *link_typename) { int ifindex; char *value; - int vlan_parent, vlan_id; + int vlan_parent = -1, vlan_id; SignalData *link_added, *link_changed, *link_removed; @@ -327,7 +338,15 @@ test_software (NMLinkType link_type, const char *link_typename) link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); if (link_type == NM_LINK_TYPE_VLAN) { - g_assert (nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &vlan_parent, &vlan_id)); + const NMPlatformLink *plink; + const NMPlatformLnkVlan *plnk; + + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink); + g_assert (plnk); + g_assert (plink); + + vlan_parent = plink->parent; + vlan_id = plnk->id; g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); g_assert_cmpint (vlan_id, ==, VLAN_ID); } @@ -347,17 +366,17 @@ test_software (NMLinkType link_type, const char *link_typename) /* Set master option */ switch (link_type) { case NM_LINK_TYPE_BRIDGE: - if (nmtst_platform_is_sysfs_writable ()) { - g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); - value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); + if (nmtstp_is_sysfs_writable ()) { + g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "forward_delay", "789")); + value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "forward_delay"); g_assert_cmpstr (value, ==, "789"); g_free (value); } break; case NM_LINK_TYPE_BOND: - if (nmtst_platform_is_sysfs_writable ()) { - g_assert (nm_platform_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); - value = nm_platform_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); + if (nmtstp_is_sysfs_writable ()) { + g_assert (nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, "mode", "active-backup")); + value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, "mode"); /* When reading back, the output looks slightly different. */ g_assert (g_str_has_prefix (value, "active-backup")); g_free (value); @@ -382,20 +401,18 @@ test_software (NMLinkType link_type, const char *link_typename) free_signal (link_changed); /* Delete */ - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert_cmpint (nm_platform_link_get_type (NM_PLATFORM_GET, ifindex), ==, NM_LINK_TYPE_NONE); - g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, ifindex)); + nmtstp_link_del (-1, ifindex, DEVICE_NAME); accept_signal (link_removed); /* Delete again */ g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME))); + g_assert (!nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); /* VLAN: Delete parent */ if (link_type == NM_LINK_TYPE_VLAN) { SignalData *link_removed_parent = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, vlan_parent); - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, vlan_parent)); + nmtstp_link_del (-1, vlan_parent, NULL); accept_signal (link_removed_parent); free_signal (link_removed_parent); } @@ -414,7 +431,7 @@ test_bridge (void) static void test_bond (void) { - if (nmtst_platform_is_root_test () && + if (nmtstp_is_root_test () && !g_file_test ("/proc/1/net/bonding", G_FILE_TEST_IS_DIR) && system("modprobe --show bonding") != 0) { g_test_skip ("Skipping test for bonding: bonding module not available"); @@ -436,6 +453,53 @@ test_vlan (void) test_software (NM_LINK_TYPE_VLAN, "vlan"); } +/*****************************************************************************/ + +static void +test_bridge_addr (void) +{ + char addr[ETH_ALEN]; + NMPlatformLink link; + const NMPlatformLink *plink = NULL; + + nm_utils_hwaddr_aton ("de:ad:be:ef:00:11", addr, sizeof (addr)); + + g_assert_cmpint (nm_platform_link_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &plink), ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert (plink); + link = *plink; + g_assert_cmpstr (link.name, ==, DEVICE_NAME); + + g_assert_cmpint (link.addr.len, ==, sizeof (addr)); + g_assert (!memcmp (link.addr.data, addr, sizeof (addr))); + + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + + if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { + g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); + + g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE)); + g_assert (nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE); + + g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE)); + g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); + } + + g_assert_cmpint (plink->addr.len, ==, sizeof (addr)); + g_assert (!memcmp (plink->addr.data, addr, sizeof (addr))); + + nmtstp_link_del (-1, link.ifindex, link.name); +} + +/*****************************************************************************/ + static void test_internal (void) { @@ -451,11 +515,11 @@ test_internal (void) g_assert (!nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); /* Add device */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); /* Try to add again */ - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_EXISTS); /* Check device index, name and type */ ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); @@ -506,7 +570,7 @@ test_internal (void) accept_signal (link_changed); /* Delete device */ - g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + nmtstp_link_del (-1, ifindex, DEVICE_NAME); accept_signal (link_removed); /* Try to delete again */ @@ -517,15 +581,18 @@ test_internal (void) free_signal (link_removed); } +/*****************************************************************************/ + static void test_external (void) { const NMPlatformLink *pllink; - SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - SignalData *link_changed, *link_removed; + SignalData *link_added, *link_changed, *link_removed; int ifindex; - run_command ("ip link add %s type %s", DEVICE_NAME, "dummy"); + link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); + + nmtstp_run_command_check ("ip link add %s type %s", DEVICE_NAME, "dummy"); wait_signal (link_added); g_assert (nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); @@ -549,24 +616,24 @@ test_external (void) g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); - run_command ("ip link set %s up", DEVICE_NAME); + nmtstp_run_command_check ("ip link set %s up", DEVICE_NAME); wait_signal (link_changed); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - run_command ("ip link set %s down", DEVICE_NAME); + nmtstp_run_command_check ("ip link set %s down", DEVICE_NAME); wait_signal (link_changed); g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, ifindex)); g_assert (!nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex)); - run_command ("ip link set %s arp on", DEVICE_NAME); + nmtstp_run_command_check ("ip link set %s arp on", DEVICE_NAME); wait_signal (link_changed); g_assert (nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); - run_command ("ip link set %s arp off", DEVICE_NAME); + nmtstp_run_command_check ("ip link set %s arp off", DEVICE_NAME); wait_signal (link_changed); g_assert (!nm_platform_link_uses_arp (NM_PLATFORM_GET, ifindex)); - run_command ("ip link del %s", DEVICE_NAME); + nmtstp_run_command_check ("ip link del %s", DEVICE_NAME); wait_signal (link_removed); accept_signals (link_changed, 0, 1); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); @@ -576,6 +643,1183 @@ test_external (void) free_signal (link_removed); } +/*****************************************************************************/ + +typedef struct { + NMLinkType link_type; + int test_mode; + gboolean external_command; +} TestAddSoftwareDetectData; + +static void +test_software_detect (gconstpointer user_data) +{ + const TestAddSoftwareDetectData *test_data = user_data; + int ifindex, ifindex_parent; + const NMPlatformLink *plink; + const NMPObject *lnk; + guint i_step; + const gboolean ext = test_data->external_command; + + nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); + ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + switch (test_data->link_type) { + case NM_LINK_TYPE_GRE: { + NMPlatformLnkGre lnk_gre = { }; + gboolean gracefully_skip = FALSE; + + lnk_gre.local = nmtst_inet4_from_string ("192.168.233.204"); + lnk_gre.remote = nmtst_inet4_from_string ("172.168.10.25"); + lnk_gre.parent_ifindex = ifindex_parent; + lnk_gre.ttl = 174; + lnk_gre.tos = 37; + lnk_gre.path_mtu_discovery = TRUE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "gre0")) { + /* Seems that the ip_gre module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip_gre", NULL) != 0; + } + + if (!nmtstp_link_gre_add (ext, DEVICE_NAME, &lnk_gre)) { + if (gracefully_skip) { + g_test_skip ("Cannot create gre tunnel because of missing ip_gre module (modprobe ip_gre)"); + goto out_delete_parent; + } + g_error ("Failed adding GRE tunnel"); + } + break; + } + case NM_LINK_TYPE_IPIP: { + NMPlatformLnkIpIp lnk_ipip = { }; + gboolean gracefully_skip = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "tunl0")) { + /* Seems that the ipip module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ipip", NULL) != 0; + } + + lnk_ipip.local = nmtst_inet4_from_string ("1.2.3.4"); + lnk_ipip.remote = nmtst_inet4_from_string ("5.6.7.8"); + lnk_ipip.parent_ifindex = ifindex_parent; + lnk_ipip.tos = 32; + lnk_ipip.path_mtu_discovery = FALSE; + + if (!nmtstp_link_ipip_add (ext, DEVICE_NAME, &lnk_ipip)) { + if (gracefully_skip) { + g_test_skip ("Cannot create ipip tunnel because of missing ipip module (modprobe ipip)"); + goto out_delete_parent; + } + g_error ("Failed adding IPIP tunnel"); + } + break; + } + case NM_LINK_TYPE_IP6TNL: { + NMPlatformLnkIp6Tnl lnk_ip6tnl = { }; + gboolean gracefully_skip = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "ip6tnl0")) { + /* Seems that the ip6_tunnel module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip6_tunnel", NULL) != 0; + } + + lnk_ip6tnl.local = *nmtst_inet6_from_string ("fd01::15"); + lnk_ip6tnl.remote = *nmtst_inet6_from_string ("fd01::16"); + lnk_ip6tnl.parent_ifindex = ifindex_parent; + lnk_ip6tnl.tclass = 20; + lnk_ip6tnl.encap_limit = 6; + lnk_ip6tnl.flow_label = 1337; + lnk_ip6tnl.proto = IPPROTO_IPV6; + + if (!nmtstp_link_ip6tnl_add (ext, DEVICE_NAME, &lnk_ip6tnl)) { + if (gracefully_skip) { + g_test_skip ("Cannot create ip6tnl tunnel because of missing ip6_tunnel module (modprobe ip6_tunnel)"); + goto out_delete_parent; + } + g_error ("Failed adding IP6TNL tunnel"); + } + break; + } + case NM_LINK_TYPE_MACVLAN: { + NMPlatformLnkMacvlan lnk_macvlan = { }; + + lnk_macvlan.mode = MACVLAN_MODE_BRIDGE; + lnk_macvlan.no_promisc = FALSE; + lnk_macvlan.tap = FALSE; + + if (!nmtstp_link_macvlan_add (ext, DEVICE_NAME, ifindex_parent, &lnk_macvlan)) + g_error ("Failed adding MACVLAN interface"); + break; + } + case NM_LINK_TYPE_MACVTAP: { + NMPlatformLnkMacvtap lnk_macvtap = { }; + + lnk_macvtap.mode = MACVLAN_MODE_PRIVATE; + lnk_macvtap.no_promisc = FALSE; + lnk_macvtap.tap = TRUE; + + if (!nmtstp_link_macvlan_add (ext, DEVICE_NAME, ifindex_parent, &lnk_macvtap)) + g_error ("Failed adding MACVTAP interface"); + break; + } + case NM_LINK_TYPE_SIT: { + NMPlatformLnkSit lnk_sit = { }; + gboolean gracefully_skip = FALSE; + + lnk_sit.local = nmtst_inet4_from_string ("192.168.200.1"); + lnk_sit.remote = nmtst_inet4_from_string ("172.25.100.14"); + lnk_sit.parent_ifindex = ifindex_parent; + lnk_sit.ttl = 0; + lnk_sit.tos = 31; + lnk_sit.path_mtu_discovery = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "sit0")) { + /* Seems that the sit module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "sit", NULL) != 0; + } + + if (!nmtstp_link_sit_add (ext, DEVICE_NAME, &lnk_sit)) { + if (gracefully_skip) { + g_test_skip ("Cannot create sit tunnel because of missing sit module (modprobe sit)"); + goto out_delete_parent; + } + g_error ("Failed adding SIT tunnel"); + } + break; + } + case NM_LINK_TYPE_VLAN: + nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1242", DEVICE_NAME, PARENT_NAME); + break; + case NM_LINK_TYPE_VXLAN: { + NMPlatformLnkVxlan lnk_vxlan = { }; + + switch (test_data->test_mode) { + case 0: + lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME); + lnk_vxlan.id = 42; + lnk_vxlan.local = nmtst_inet4_from_string ("23.1.2.164"); + lnk_vxlan.group = nmtst_inet4_from_string ("239.1.2.134"); + lnk_vxlan.dst_port = 4789; + lnk_vxlan.learning = TRUE; + lnk_vxlan.ageing = 1245; + break; + case 1: + lnk_vxlan.parent_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME); + lnk_vxlan.id = 11214423; + lnk_vxlan.local6 = *nmtst_inet6_from_string ("1:2:3:4:334:23::23"); + lnk_vxlan.group6 = *nmtst_inet6_from_string ("ff0e::115"); + lnk_vxlan.ttl = 32; + lnk_vxlan.dst_port = 57412; + lnk_vxlan.src_port_min = 1000; + lnk_vxlan.src_port_max = 1003; + lnk_vxlan.learning = TRUE; + lnk_vxlan.ageing = 3245; + break; + } + + g_assert (nmtstp_link_vxlan_add (ext, DEVICE_NAME, &lnk_vxlan)); + break; + } + default: + g_assert_not_reached (); + } + + ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, test_data->link_type, 100)->ifindex; + + nmtstp_link_set_updown (-1, ifindex_parent, TRUE); + + for (i_step = 0; i_step < 5; i_step++) { + + _LOGD ("test-software-detect: step %u", i_step); + if (nmtst_is_debug ()) + nmtstp_run_command_check ("ip -d link show %s", DEVICE_NAME); + + if (i_step > 0) { + gboolean set_up = (i_step % 2) == 1; + + if ( test_data->link_type == NM_LINK_TYPE_VXLAN + && set_up) { + /* On RHEL-7, we need to add a tiny sleep here, otherwise, + * upping the vxlan device fails with EADDRINUSE. + * https://bugzilla.redhat.com/show_bug.cgi?id=1277131 */ + g_usleep (1); + } + nmtstp_link_set_updown (-1, ifindex, set_up); + } + + lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, test_data->link_type, &plink); + g_assert (plink); + g_assert_cmpint (plink->ifindex, ==, ifindex); + g_assert (lnk); + + switch (test_data->link_type) { + case NM_LINK_TYPE_GRE: { + const NMPlatformLnkGre *plnk = &lnk->lnk_gre; + + g_assert (plnk == nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + g_assert_cmpint (plnk->input_flags, ==, 0); + g_assert_cmpint (plnk->output_flags, ==, 0); + g_assert_cmpint (plnk->input_key, ==, 0); + g_assert_cmpint (plnk->output_key, ==, 0); + nmtst_assert_ip4_address (plnk->local, "192.168.233.204"); + nmtst_assert_ip4_address (plnk->remote, "172.168.10.25"); + g_assert_cmpint (plnk->ttl, ==, 174); + g_assert_cmpint (plnk->tos, ==, 37); + g_assert_cmpint (plnk->path_mtu_discovery, ==, TRUE); + break; + } + case NM_LINK_TYPE_IP6TNL: { + const NMPlatformLnkIp6Tnl *plnk = &lnk->lnk_ip6tnl; + + g_assert (plnk == nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip6_address (&plnk->local, "fd01::15"); + nmtst_assert_ip6_address (&plnk->remote, "fd01::16"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->tclass, ==, 20); + g_assert_cmpint (plnk->encap_limit, ==, 6); + g_assert_cmpint (plnk->flow_label, ==, 1337); + g_assert_cmpint (plnk->proto, ==, IPPROTO_IPV6); + break; + } + case NM_LINK_TYPE_IPIP: { + const NMPlatformLnkIpIp *plnk = &lnk->lnk_ipip; + + g_assert (plnk == nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip4_address (plnk->local, "1.2.3.4"); + nmtst_assert_ip4_address (plnk->remote, "5.6.7.8"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->tos, ==, 32); + g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE); + break; + } + case NM_LINK_TYPE_MACVLAN: { + const NMPlatformLnkMacvlan *plnk = &lnk->lnk_macvlan; + + g_assert (plnk == nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->no_promisc, ==, FALSE); + g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_BRIDGE); + break; + } + case NM_LINK_TYPE_MACVTAP: { + const NMPlatformLnkMacvtap *plnk = &lnk->lnk_macvlan; + + g_assert (plnk == nm_platform_link_get_lnk_macvtap (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->no_promisc, ==, FALSE); + g_assert_cmpint (plnk->mode, ==, MACVLAN_MODE_PRIVATE); + break; + } + case NM_LINK_TYPE_SIT: { + const NMPlatformLnkSit *plnk = &lnk->lnk_sit; + + g_assert (plnk == nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, ifindex_parent); + nmtst_assert_ip4_address (plnk->local, "192.168.200.1"); + nmtst_assert_ip4_address (plnk->remote, "172.25.100.14"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->tos, ==, 31); + g_assert_cmpint (plnk->path_mtu_discovery, ==, FALSE); + break; + } + case NM_LINK_TYPE_VLAN: { + const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; + + g_assert (plnk == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->id, ==, 1242); + break; + } + case NM_LINK_TYPE_VXLAN: { + const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan; + + g_assert (plnk == nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, !=, 0); + g_assert_cmpint (plnk->tos, ==, 0); + g_assert_cmpint (plnk->learning, ==, TRUE); + g_assert_cmpint (plnk->limit, ==, 0); + g_assert_cmpint (plnk->proxy, ==, FALSE); + g_assert_cmpint (plnk->rsc, ==, FALSE); + g_assert_cmpint (plnk->l2miss, ==, FALSE); + g_assert_cmpint (plnk->l3miss, ==, FALSE); + + switch (test_data->test_mode) { + case 0: + g_assert_cmpint (plnk->id, ==, 42); + nmtst_assert_ip4_address (plnk->local, "23.1.2.164"); + nmtst_assert_ip4_address (plnk->group, "239.1.2.134"); + nmtst_assert_ip6_address (&plnk->group6, "::"); + nmtst_assert_ip6_address (&plnk->local6, "::"); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->ageing, ==, 1245); + g_assert_cmpint (plnk->dst_port, ==, 4789); + if ( plnk->src_port_min != 0 + || plnk->src_port_max != 0) { + /* on some kernels, omiting the port range results in setting + * following default port range. */ + g_assert_cmpint (plnk->src_port_min, ==, 32768); + g_assert_cmpint (plnk->src_port_max, ==, 61000); + } + break; + case 1: + g_assert_cmpint (plnk->id, ==, 11214423); + nmtst_assert_ip4_address (plnk->local, "0.0.0.0"); + nmtst_assert_ip4_address (plnk->group, "0.0.0.0"); + nmtst_assert_ip6_address (&plnk->group6, "ff0e::115"); + nmtst_assert_ip6_address (&plnk->local6, "1:2:3:4:334:23::23"); + g_assert_cmpint (plnk->ageing, ==, 3245); + g_assert_cmpint (plnk->dst_port, ==, 57412); + g_assert_cmpint (plnk->ttl, ==, 32); + g_assert_cmpint (plnk->src_port_min, ==, 1000); + g_assert_cmpint (plnk->src_port_max, ==, 1003); + break; + } + break; + } + default: + g_assert_not_reached (); + } + } + + nmtstp_link_del (-1, ifindex, DEVICE_NAME); +out_delete_parent: + nmtstp_link_del (-1, ifindex_parent, PARENT_NAME); +} + +static void +test_software_detect_add (const char *testpath, + NMLinkType link_type, + int test_mode) +{ + TestAddSoftwareDetectData *test_data; + char *path; + + test_data = g_new0 (TestAddSoftwareDetectData, 1); + test_data->link_type = link_type; + test_data->test_mode = test_mode; + test_data->external_command = TRUE; + + path = g_strdup_printf ("%s/external", testpath); + g_test_add_data_func_full (path, test_data, test_software_detect, g_free); + g_free (path); + + test_data = g_new0 (TestAddSoftwareDetectData, 1); + test_data->link_type = link_type; + test_data->test_mode = test_mode; + test_data->external_command = FALSE; + + path = g_strdup_printf ("%s/platform", testpath); + g_test_add_data_func_full (path, test_data, test_software_detect, g_free); + g_free (path); + + test_data = g_new0 (TestAddSoftwareDetectData, 1); + test_data->link_type = link_type; + test_data->test_mode = test_mode; + test_data->external_command = -1; + + path = g_strdup_printf ("%s/random", testpath); + g_test_add_data_func_full (path, test_data, test_software_detect, g_free); + g_free (path); +} + +/*****************************************************************************/ + +static void +_assert_xgress_qos_mappings_impl (int ifindex, + gboolean is_ingress_map , + int n_entries, + int n, + ...) +{ + const NMPlatformLink *plink; + const NMPObject *lnk; + guint n_map; + const NMVlanQosMapping *map; + va_list ap; + guint i; + + lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink); + + g_assert (plink); + g_assert_cmpint (plink->ifindex, ==, ifindex); + g_assert (lnk); + g_assert (&lnk->lnk_vlan == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL)); + + if (nmtst_is_debug ()) + nmtstp_run_command_check ("ip -d link show %s", plink->name); + + if (is_ingress_map) { + map = lnk->_lnk_vlan.ingress_qos_map; + n_map = lnk->_lnk_vlan.n_ingress_qos_map; + } else { + map = lnk->_lnk_vlan.egress_qos_map; + n_map = lnk->_lnk_vlan.n_egress_qos_map; + } + + if (n_entries != -1) + g_assert_cmpint (n_map, ==, n_entries); + + for (i = 0; i < n_map; i++) { + if (is_ingress_map) { + g_assert_cmpint (map[i].from, >=, 0); + g_assert_cmpint (map[i].from, <=, 7); + } + if (i > 0) + g_assert_cmpint (map[i - 1].from, <, map[i].from); + } + + va_start (ap, n); + for (; n > 0; n--) { + gboolean found = FALSE; + guint from = va_arg (ap, guint); + guint to = va_arg (ap, guint); + + for (i = 0; i < n_map; i++) { + if (map[i].from == from) { + g_assert (!found); + found = TRUE; + + g_assert (map[i].to == to); + } + } + g_assert (found); + } + va_end (ap); +} +#define _assert_xgress_qos_mappings(ifindex, is_ingress_map, n_entries, ...) \ + _assert_xgress_qos_mappings_impl ((ifindex), (is_ingress_map), (n_entries), \ + (G_STATIC_ASSERT_EXPR ((NM_NARG (__VA_ARGS__) % 2) == 0), NM_NARG (__VA_ARGS__) / 2), \ + __VA_ARGS__) +#define _assert_ingress_qos_mappings(ifindex, n_entries, ...) _assert_xgress_qos_mappings (ifindex, TRUE, n_entries, __VA_ARGS__) +#define _assert_egress_qos_mappings(ifindex, n_entries, ...) _assert_xgress_qos_mappings (ifindex, FALSE, n_entries, __VA_ARGS__) + +static void +_assert_vlan_flags (int ifindex, NMVlanFlags flags) +{ + const NMPlatformLnkVlan *plnk; + + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL); + g_assert (plnk); + g_assert_cmpint (plnk->flags, ==, flags); +} + +static void +test_vlan_set_xgress (void) +{ + int ifindex, ifindex_parent; + + nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); + ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1245", DEVICE_NAME, PARENT_NAME); + ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex; + + /* ingress-qos-map */ + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 4, 5)); + _assert_ingress_qos_mappings (ifindex, 1, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 7)); + _assert_ingress_qos_mappings (ifindex, 2, + 3, 7, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 8)); + _assert_ingress_qos_mappings (ifindex, 2, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 4)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 4, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, G_MAXUINT32, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32 - 1)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, G_MAXUINT32 - 1, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + /* Set invalid values: */ + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 8, 3)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + g_assert (nm_platform_link_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 9, 4)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + /* egress-qos-map */ + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 7, 3)); + _assert_egress_qos_mappings (ifindex, 1, + 7, 3); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 4)); + _assert_egress_qos_mappings (ifindex, 2, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 4)); + _assert_egress_qos_mappings (ifindex, 3, + 0, 4, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 4)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 4, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 5)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 5, + 7, 3, + 8, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 9, 5)); + _assert_egress_qos_mappings (ifindex, 5, + 0, 4, + 1, 5, + 7, 3, + 8, 4, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 5)); + _assert_egress_qos_mappings (ifindex, 5, + 0, 4, + 1, 5, + 7, 3, + 8, 5, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 0)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 0)); + _assert_egress_qos_mappings (ifindex, 3, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 4)); + _assert_egress_qos_mappings (ifindex, 4, + 1, 5, + 7, 3, + 9, 5, + 100, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 4)); + _assert_egress_qos_mappings (ifindex, 5, + 1, 5, + 7, 3, + 9, 5, + 100, 4, + G_MAXUINT32, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 8)); + _assert_egress_qos_mappings (ifindex, 5, + 1, 5, + 7, 3, + 9, 5, + 100, 4, + G_MAXUINT32, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 0)); + _assert_egress_qos_mappings (ifindex, 4, + 1, 5, + 7, 3, + 9, 5, + 100, 4); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 0)); + _assert_egress_qos_mappings (ifindex, 3, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_link_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 0)); + _assert_egress_qos_mappings (ifindex, 2, + 7, 3, + 9, 5); + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 5 }, + { .from = 7, .to = 1655 }, + { .from = 7, .to = 17655 }, + { .from = 5, .to = 754 }, + { .from = 4, .to = 12 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 4, + 3, 5, + 4, 12, + 7, 17655, + 5, 754); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 18 }, + { .from = 6, .to = 121 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 5, + 3, 18, + 4, 12, + 6, 121, + 7, 17655, + 5, 754); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 0 }, + { .from = 6, .to = 7 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 6, 7); + } + + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 5, .to = 1 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 5, 1); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 5, .to = 3 }, + { .from = 1655, .to = 5 }, + { .from = 1655, .to = 7 }, + { .from = G_MAXUINT32, .to = 6 }, + { .from = G_MAXUINT32, .to = 8 }, + { .from = 754, .to = 4 }, + { .from = 3, .to = 2 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 5, + 3, 2, + 5, 3, + 754, 4, + 1655, 7, + G_MAXUINT32, 6); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 754, .to = 3 }, + { .from = 755, .to = 8 }, + { .from = 1655, .to = 0 }, + { .from = 6, .to = 1 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + FALSE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 5, + 3, 2, + 5, 3, + 6, 1, + 754, 3, + G_MAXUINT32, 6); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 6, .to = 0 }, + { .from = 3, .to = 4 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 3, 4); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 6, .to = 145 }, + { .from = 4, .to = 1 }, + { .from = 6, .to = 12 }, + }; + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 5 }, + { .from = 3232, .to = 7 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP, + NM_VLAN_FLAG_REORDER_HEADERS, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_ingress_qos_mappings (ifindex, 2, + 4, 1, + 6, 12); + _assert_egress_qos_mappings (ifindex, 2, + 1, 5, + 3232, 7); + _assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 6, .to = 145 }, + { .from = 4, .to = 1 }, + { .from = 6, .to = 12 }, + }; + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 7 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 3232, .to = 0 }, + { .from = 64, .to = 4 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + NM_VLAN_FLAG_GVRP, + NM_VLAN_FLAG_GVRP, + FALSE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_ingress_qos_mappings (ifindex, 2, + 4, 1, + 6, 12); + _assert_egress_qos_mappings (ifindex, 2, + 1, 7, + 64, 4); + _assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP); + } + + nmtstp_link_del (-1, ifindex, DEVICE_NAME); + nmtstp_link_del (-1, ifindex_parent, PARENT_NAME); +} + +/*****************************************************************************/ + +static void +test_create_many_links_do (guint n_devices) +{ + gint64 time, start_time = nm_utils_get_monotonic_timestamp_ns (); + guint i; + char name[64]; + const NMPlatformLink *pllink; + gs_unref_array GArray *ifindexes = g_array_sized_new (FALSE, FALSE, sizeof (int), n_devices); + const gint EX = ((int) (nmtst_get_rand_int () % 4)) - 1; + + g_assert (EX >= -1 && EX <= 2); + + _LOGI (">>> create devices (EX=%d)...", EX); + + for (i = 0; i < n_devices; i++) { + nm_sprintf_buf (name, "t-%05u", i); + if (EX == 2) { + /* This mode is different from letting nmtstp_link_dummy_add() + * because in this case we don't process any platform events + * while adding all the links. */ + nmtstp_run_command_check ("ip link add %s type dummy", name); + } else + nmtstp_link_dummy_add (EX, name); + } + + _LOGI (">>> process events after creating devices..."); + + nm_platform_process_events (NM_PLATFORM_GET); + + _LOGI (">>> check devices..."); + + for (i = 0; i < n_devices; i++) { + nm_sprintf_buf (name, "t-%05u", i); + + pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, name); + g_assert (pllink); + g_assert_cmpint (pllink->type, ==, NM_LINK_TYPE_DUMMY); + g_assert_cmpstr (pllink->name, ==, name); + + g_array_append_val (ifindexes, pllink->ifindex); + } + + _LOGI (">>> delete devices..."); + + g_assert_cmpint (ifindexes->len, ==, n_devices); + for (i = 0; i < n_devices; i++) { + nm_sprintf_buf (name, "t-%05u", i); + + if (EX == 2) + nmtstp_run_command_check ("ip link delete %s", name); + else + nmtstp_link_del (EX, g_array_index (ifindexes, int, i), name); + } + + _LOGI (">>> process events after deleting devices..."); + nm_platform_process_events (NM_PLATFORM_GET); + + time = nm_utils_get_monotonic_timestamp_ns () - start_time; + _LOGI (">>> finished in %ld.%09ld seconds", (long) (time / NM_UTILS_NS_PER_SECOND), (long) (time % NM_UTILS_NS_PER_SECOND)); +} + +static void +test_create_many_links (gconstpointer user_data) +{ + guint n_devices = GPOINTER_TO_UINT (user_data); + + if (n_devices > 100 && nmtst_test_quick ()) { + g_print ("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", str_if_set (g_get_prgname (), "test-link-linux")); + g_test_skip ("Skip long running test"); + return; + } + + test_create_many_links_do (n_devices); +} + +/*****************************************************************************/ + +static void +test_nl_bugs_veth (void) +{ + const char *IFACE_VETH0 = "nm-test-veth0"; + const char *IFACE_VETH1 = "nm-test-veth1"; + int ifindex_veth0, ifindex_veth1; + int i; + const NMPlatformLink *pllink_veth0, *pllink_veth1; + gs_free_error GError *error = NULL; + NMTstpNamespaceHandle *ns_handle = NULL; + + /* create veth pair. */ + nmtstp_run_command_check ("ip link add dev %s type veth peer name %s", IFACE_VETH0, IFACE_VETH1); + ifindex_veth0 = nmtstp_assert_wait_for_link (IFACE_VETH0, NM_LINK_TYPE_VETH, 100)->ifindex; + ifindex_veth1 = nmtstp_assert_wait_for_link (IFACE_VETH1, NM_LINK_TYPE_VETH, 100)->ifindex; + + /* assert that nm_platform_link_veth_get_properties() returns the expected peer ifindexes. */ + g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth0, &i)); + g_assert_cmpint (i, ==, ifindex_veth1); + + g_assert (nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex_veth1, &i)); + g_assert_cmpint (i, ==, ifindex_veth0); + + /* assert that NMPlatformLink.parent is the peer-ifindex. */ + pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0); + g_assert (pllink_veth0); + if (pllink_veth0->parent == 0) { + /* pre-4.1 kernels don't support exposing the veth peer as IFA_LINK. skip the remainder + * of the test. */ + goto out; + } + g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1); + + + /* The following tests whether we have a workaround for kernel bug + * https://bugzilla.redhat.com/show_bug.cgi?id=1285827 in place. */ + pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1); + g_assert (pllink_veth1); + g_assert_cmpint (pllink_veth1->parent, ==, ifindex_veth0); + + + /* move one veth peer to another namespace and check that the + * parent/IFLA_LINK of the remaining peer properly updates + * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ + ns_handle = nmtstp_namespace_create (CLONE_NEWNET, &error); + g_assert_no_error (error); + g_assert (ns_handle); + + nmtstp_run_command_check ("ip link set %s netns %ld", IFACE_VETH1, (long) nmtstp_namespace_handle_get_pid (ns_handle)); + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (50); + nm_platform_process_events (NM_PLATFORM_GET); + + pllink_veth1 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth1); + pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0); + if ( !pllink_veth1 + && pllink_veth0 + && pllink_veth0->parent == NM_PLATFORM_LINK_OTHER_NETNS) { + break; + } + }); + +out: + nmtstp_link_del (-1, ifindex_veth0, IFACE_VETH0); + g_assert (!nmtstp_link_get (ifindex_veth0, IFACE_VETH0)); + g_assert (!nmtstp_link_get (ifindex_veth1, IFACE_VETH1)); + nmtstp_namespace_handle_release (ns_handle); +} + +/*****************************************************************************/ + +static void +test_nl_bugs_spuroius_newlink (void) +{ + const char *IFACE_BOND0 = "nm-test-bond0"; + const char *IFACE_DUMMY0 = "nm-test-dummy0"; + int ifindex_bond0, ifindex_dummy0; + const NMPlatformLink *pllink; + gboolean wait_for_settle; + + /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ + + nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0); + ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + nmtstp_run_command_check ("ip link add %s type bond", IFACE_BOND0); + ifindex_bond0 = nmtstp_assert_wait_for_link (IFACE_BOND0, NM_LINK_TYPE_BOND, 100)->ifindex; + + nmtstp_link_set_updown (-1, ifindex_bond0, TRUE); + + nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BOND0); + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (50); + + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0); + g_assert (pllink); + if (pllink->master == ifindex_bond0) + break; + }); + + nmtstp_run_command_check ("ip link del %s", IFACE_BOND0); + + wait_for_settle = TRUE; + nmtstp_wait_for_signal (50); +again: + nm_platform_process_events (NM_PLATFORM_GET); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bond0); + g_assert (!pllink); + + if (wait_for_settle) { + wait_for_settle = FALSE; + NMTST_WAIT (300, { nmtstp_wait_for_signal (50); }); + goto again; + } + + g_assert (!nmtstp_link_get (ifindex_bond0, IFACE_BOND0)); + nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0); +} + +/*****************************************************************************/ + +static void +test_nl_bugs_spuroius_dellink (void) +{ + const char *IFACE_BRIDGE0 = "nm-test-bridge0"; + const char *IFACE_DUMMY0 = "nm-test-dummy0"; + int ifindex_bridge0, ifindex_dummy0; + const NMPlatformLink *pllink; + gboolean wait_for_settle; + + /* see https://bugzilla.redhat.com/show_bug.cgi?id=1285719 */ + + nmtstp_run_command_check ("ip link add %s type dummy", IFACE_DUMMY0); + ifindex_dummy0 = nmtstp_assert_wait_for_link (IFACE_DUMMY0, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + nmtstp_run_command_check ("ip link add %s type bridge", IFACE_BRIDGE0); + ifindex_bridge0 = nmtstp_assert_wait_for_link (IFACE_BRIDGE0, NM_LINK_TYPE_BRIDGE, 100)->ifindex; + + nmtstp_link_set_updown (-1, ifindex_bridge0, TRUE); + + nmtstp_run_command_check ("ip link set %s master %s", IFACE_DUMMY0, IFACE_BRIDGE0); + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (50); + + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0); + g_assert (pllink); + if (pllink->master == ifindex_bridge0) + break; + }); + + nm_platform_process_events (NM_PLATFORM_GET); + + nmtstp_run_command_check ("ip link set %s nomaster", IFACE_DUMMY0); + + wait_for_settle = TRUE; + nmtstp_wait_for_signal (50); +again: + nm_platform_process_events (NM_PLATFORM_GET); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_bridge0); + g_assert (pllink); + pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex_dummy0); + g_assert (pllink); + g_assert_cmpint (pllink->parent, ==, 0); + + if (wait_for_settle) { + wait_for_settle = FALSE; + NMTST_WAIT (300, { nmtstp_wait_for_signal (50); }); + goto again; + } + + nmtstp_link_del (-1, ifindex_bridge0, IFACE_BRIDGE0); + nmtstp_link_del (-1, ifindex_dummy0, IFACE_DUMMY0); +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { @@ -599,7 +1843,28 @@ setup_tests (void) g_test_add_func ("/link/software/bond", test_bond); g_test_add_func ("/link/software/team", test_team); g_test_add_func ("/link/software/vlan", test_vlan); + g_test_add_func ("/link/software/bridge/addr", test_bridge_addr); - if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform")) + if (nmtstp_is_root_test ()) { g_test_add_func ("/link/external", test_external); + + test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0); + test_software_detect_add ("/link/software/detect/ip6tnl", NM_LINK_TYPE_IP6TNL, 0); + test_software_detect_add ("/link/software/detect/ipip", NM_LINK_TYPE_IPIP, 0); + test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0); + test_software_detect_add ("/link/software/detect/macvtap", NM_LINK_TYPE_MACVTAP, 0); + test_software_detect_add ("/link/software/detect/sit", NM_LINK_TYPE_SIT, 0); + test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0); + test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0); + test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1); + + g_test_add_func ("/link/software/vlan/set-xgress", test_vlan_set_xgress); + + g_test_add_data_func ("/link/create-many-links/20", GUINT_TO_POINTER (20), test_create_many_links); + g_test_add_data_func ("/link/create-many-links/1000", GUINT_TO_POINTER (1000), test_create_many_links); + + g_test_add_func ("/link/nl-bugs/veth", test_nl_bugs_veth); + g_test_add_func ("/link/nl-bugs/spurious-newlink", test_nl_bugs_spuroius_newlink); + g_test_add_func ("/link/nl-bugs/spurious-dellink", test_nl_bugs_spuroius_dellink); + } } diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 88bb0707..eba2383b 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -20,7 +20,7 @@ #include "nmp-object.h" -#include "nm-logging.h" +#include "nm-default.h" #include "nm-test-utils.h" @@ -148,8 +148,8 @@ _nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, NMPCacheOpsType ops_type; NMPObject *obj2; gboolean was_visible; - auto_nmp_obj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); - auto_nmp_obj NMPObject *new_clone = NULL; + nm_auto_nmpobj NMPObject *obj_clone = nmp_object_clone (obj, FALSE); + nm_auto_nmpobj NMPObject *new_clone = NULL; const NMPObject *obj_old; _NMPCacheUpdateData data = { .cache = cache, diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index cf322dec..60a0c15c 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -9,7 +9,7 @@ #define DEVICE_NAME "nm-test-device" static void -ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP4Route *received, NMPlatformSignalChangeType change_type, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -25,11 +25,11 @@ ip4_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, c g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } static void -ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, NMPlatformReason reason, SignalData *data) +ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, const NMPlatformIP6Route *received, NMPlatformSignalChangeType change_type, SignalData *data) { g_assert (received); g_assert_cmpint (received->ifindex, ==, ifindex); @@ -45,7 +45,7 @@ ip6_route_callback (NMPlatform *platform, NMPObjectType obj_type, int ifindex, c g_main_loop_quit (data->loop); data->received_count++; - debug ("Received signal '%s' %dth time.", data->name, data->received_count); + _LOGD ("Received signal '%s' %dth time.", data->name, data->received_count); } static void @@ -282,6 +282,31 @@ test_ip6_route (void) free_signal (route_removed); } +/*****************************************************************************/ + +static void +test_ip4_zero_gateway (void) +{ + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + + nmtstp_run_command_check ("ip route add 1.2.3.1/32 via 0.0.0.0 dev %s", DEVICE_NAME); + nmtstp_run_command_check ("ip route add 1.2.3.2/32 dev %s", DEVICE_NAME); + + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (10); + if ( nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0) + && nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0)) + break; + }); + + nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME); + + nmtstp_wait_for_signal (50); + nm_platform_process_events (NM_PLATFORM_GET); +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { @@ -295,7 +320,7 @@ setup_tests (void) nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME)); g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, DEVICE_NAME)); - g_assert (nm_platform_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); + g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, DEVICE_NAME, NULL) == NM_PLATFORM_ERROR_SUCCESS); accept_signal (link_added); free_signal (link_added); @@ -304,4 +329,7 @@ setup_tests (void) g_test_add_func ("/route/ip4", test_ip4_route); g_test_add_func ("/route/ip6", test_ip6_route); g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0); + + if (nmtstp_is_root_test ()) + g_test_add_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); } |