From 80ec1decc49c72efec2a8b87c06245c92c0ab807 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Thu, 11 Feb 2021 18:11:46 +0100 Subject: New upstream version 1.29.90 --- src/core/tests/config/NetworkManager-warn.conf | 26 + src/core/tests/config/NetworkManager.conf | 99 + src/core/tests/config/NetworkManager.state | 4 + src/core/tests/config/bad.conf | 1 + src/core/tests/config/conf.d/00-overrides.conf | 57 + src/core/tests/config/conf.d/10-more.conf | 40 + .../tests/config/conf.d/20-config-enable-1.conf | 5 + src/core/tests/config/conf.d/90-last.conf | 8 + src/core/tests/config/global-dns-invalid.conf | 9 + src/core/tests/config/meson.build | 24 + src/core/tests/config/nm-test-device.c | 79 + src/core/tests/config/nm-test-device.h | 27 + src/core/tests/config/test-config.c | 1398 +++++++++++ src/core/tests/meson.build | 52 + src/core/tests/test-core-with-expect.c | 649 +++++ src/core/tests/test-core.c | 2639 ++++++++++++++++++++ src/core/tests/test-dcb.c | 354 +++ src/core/tests/test-ip4-config.c | 380 +++ src/core/tests/test-ip6-config.c | 538 ++++ src/core/tests/test-l3cfg.c | 795 ++++++ src/core/tests/test-secret-agent.py | 91 + src/core/tests/test-systemd.c | 339 +++ src/core/tests/test-utils.c | 233 ++ src/core/tests/test-wired-defname.c | 126 + 24 files changed, 7973 insertions(+) create mode 100644 src/core/tests/config/NetworkManager-warn.conf create mode 100644 src/core/tests/config/NetworkManager.conf create mode 100644 src/core/tests/config/NetworkManager.state create mode 100644 src/core/tests/config/bad.conf create mode 100644 src/core/tests/config/conf.d/00-overrides.conf create mode 100644 src/core/tests/config/conf.d/10-more.conf create mode 100644 src/core/tests/config/conf.d/20-config-enable-1.conf create mode 100644 src/core/tests/config/conf.d/90-last.conf create mode 100644 src/core/tests/config/global-dns-invalid.conf create mode 100644 src/core/tests/config/meson.build create mode 100644 src/core/tests/config/nm-test-device.c create mode 100644 src/core/tests/config/nm-test-device.h create mode 100644 src/core/tests/config/test-config.c create mode 100644 src/core/tests/meson.build create mode 100644 src/core/tests/test-core-with-expect.c create mode 100644 src/core/tests/test-core.c create mode 100644 src/core/tests/test-dcb.c create mode 100644 src/core/tests/test-ip4-config.c create mode 100644 src/core/tests/test-ip6-config.c create mode 100644 src/core/tests/test-l3cfg.c create mode 100755 src/core/tests/test-secret-agent.py create mode 100644 src/core/tests/test-systemd.c create mode 100644 src/core/tests/test-utils.c create mode 100644 src/core/tests/test-wired-defname.c (limited to 'src/core/tests') diff --git a/src/core/tests/config/NetworkManager-warn.conf b/src/core/tests/config/NetworkManager-warn.conf new file mode 100644 index 00000000..80df7c52 --- /dev/null +++ b/src/core/tests/config/NetworkManager-warn.conf @@ -0,0 +1,26 @@ +[main] +dhcp=dhclient +plugin=foo,bar,baz +no-auto-default=11:11:11:11:11:11 +rc-managed=unmanaged +dns=none + +[logging] +level=INFO + +[connectivity] +uri=http://example.com +interval=100 +response=Hello +audit=true + +[connection] +ipv4.route-metric=50 +ipv4.addresses=1.2.3.4 +ipv4.dad-timeout=100 + +[connection-wifi] +match-device=type:wifi +wifi.powersave=2 +ipv6.ip6-privacy=1 +wifi.tx-power=99 diff --git a/src/core/tests/config/NetworkManager.conf b/src/core/tests/config/NetworkManager.conf new file mode 100644 index 00000000..a447b6d6 --- /dev/null +++ b/src/core/tests/config/NetworkManager.conf @@ -0,0 +1,99 @@ +[main] +dhcp=dhclient +plugins=foo,bar,baz +no-auto-default=11:11:11:11:11:11 + +[logging] +level=INFO + +[connectivity] +uri=http://example.com +interval=100 +response=Hello + +[extra-section] +extra-key=some value + + + +[connection] +ipv4.route-metric=50 +ipv6.ip6_privacy=0 +ethernet.mtu=1400 +ipv4.dns-priority=60 + +ord.key00=A-0.0.00 +ord.key01=A-0.0.01 +ord.key02=A-0.0.02 +ord.key03=A-0.0.03 +ord.key04=A-0.0.04 +ord.key05=A-0.0.05 +ord.key06=A-0.0.06 +ord.key07=A-0.0.07 +ord.key08=A-0.0.08 +ord.key09=A-0.0.09 + +[connection.dev51] +match-device=mac:00:00:00:00:00:51 +stop-match=yes +ipv4.route-metric=51 +ethernet.mtu=9000 + +[connection.dev52] +match-device=mac:00:00:00:00:00:52 +ipv4.route-metric=52 + +[connection.public] +match-device=interface-name:wlan1 +# match-wifi is not yet implemented. Just an idea what could be useful. +match-wifi=ssid:*[Ss]tarbucks*|*University* +ipv6.ip6_privacy=2 + + +# the following sections are tested for their order across +# multiple files. +[connection.ord.0.1] +ord.key03=A-0.1.03 +ord.key04=A-0.1.04 +ord.key05=A-0.1.05 +ord.key06=A-0.1.06 +ord.key07=A-0.1.07 +ord.key08=A-0.1.08 +ord.key09=A-0.1.09 +ord.ovw01=A-0.1.ovw01 +[connection.ord.0.2] +ord.key02=A-0.2.02 +ord.key03=A-0.2.03 +ord.key04=A-0.2.04 +ord.key05=A-0.2.05 +ord.key06=A-0.2.06 +ord.key07=A-0.2.07 +ord.key08=A-0.2.08 +ord.key09=A-0.2.09 +[connection.ord.0.3] +ord.key01=A-0.3.01 +ord.key02=A-0.3.02 +ord.key03=A-0.3.03 +ord.key04=A-0.3.04 +ord.key05=A-0.3.05 +ord.key06=A-0.3.06 +ord.key07=A-0.3.07 +ord.key08=A-0.3.08 +ord.key09=A-0.3.09 +ord.ovw01=A-0.3.ovw01 + +[global-dns] +enable=yes +searches=foo.com,bar.org +options=debug,edns0 + +[global-dns-domain-*] +servers=1.1.1.1,bad,1::128 +options=opt1,opt2 + +[global-dns-domain-example.com] +servers=2.2.2.2 + +# Invalid section: 'servers' key is missing +[global-dns-domain-test.com] +options=opt3 diff --git a/src/core/tests/config/NetworkManager.state b/src/core/tests/config/NetworkManager.state new file mode 100644 index 00000000..1b9d604a --- /dev/null +++ b/src/core/tests/config/NetworkManager.state @@ -0,0 +1,4 @@ +[main] +NetworkingEnabled=true +WirelessEnabled=true +WWANEnabled=true diff --git a/src/core/tests/config/bad.conf b/src/core/tests/config/bad.conf new file mode 100644 index 00000000..20d6a67a --- /dev/null +++ b/src/core/tests/config/bad.conf @@ -0,0 +1 @@ +This is not a keyfile. diff --git a/src/core/tests/config/conf.d/00-overrides.conf b/src/core/tests/config/conf.d/00-overrides.conf new file mode 100644 index 00000000..f26ed93b --- /dev/null +++ b/src/core/tests/config/conf.d/00-overrides.conf @@ -0,0 +1,57 @@ +[main] +dhcp=dhcpcd + +no-auto-default=spec1,spec2 +ignore-carrier=\s space1 \s + +[logging] +domains=PLATFORM,DNS,WIFI + +[appendable-test] +non-appendable-key1+=i-will-be-dropped +non-appendable-key2-=i-will-be-dropped + +[order] +a=0 +b=0 +c=0 + + +# the following sections are tested for their order across +# multiple files. +[connection.ord.1.1] +ord.key06=B-1.1.06 +ord.key07=B-1.1.07 +ord.key08=B-1.1.08 +ord.key09=B-1.1.09 +[connection.ord.1.2] +ord.key05=B-1.2.05 +ord.key06=B-1.2.06 +ord.key07=B-1.2.07 +ord.key08=B-1.2.08 +ord.key09=B-1.2.09 +[connection.ord.1.3] +ord.key04=B-1.3.04 +ord.key05=B-1.3.05 +ord.key06=B-1.3.06 +ord.key07=B-1.3.07 +ord.key08=B-1.3.08 +ord.key09=B-1.3.09 + + +[.test-append-stringlist.1] +val1=a,b + +val2-=VAL2 +val2=VAL2 + +val3=VAL3 +val3-=VAL3 + +val4=VAL4 +val4+=VAL4,va,vb,va,vb +val4-=VAL4,va + +val5=VAL5 +val5-=VAL5 +val5+=VAL5 diff --git a/src/core/tests/config/conf.d/10-more.conf b/src/core/tests/config/conf.d/10-more.conf new file mode 100644 index 00000000..eadb7f96 --- /dev/null +++ b/src/core/tests/config/conf.d/10-more.conf @@ -0,0 +1,40 @@ +[main] +extra=hello + +no-auto-default-=spec1 +no-auto-default+=spec3 + +ignore-carrier+=\sspace2\t + +[.test-append-stringlist.0] +new+=something + +[connectivity] +uri=http://example.net + +[order] +a=10 +b=10 + +# the following sections are tested for their order across +# multiple files. +[connection.ord.2.1] +ord.key09=C-2.1.09 +[connection.ord.2.2] +ord.key08=C-2.2.08 +ord.key09=C-2.2.09 +[connection.ord.2.3] +ord.key07=C-2.3.07 +ord.key08=C-2.3.08 +ord.key09=C-2.3.09 + +# you can overwrite individual settings in a file loaded +# previously. But note that this does not bump the priority +# of the section, i.e. [connection.ord.0.1] still has a pretty +# low priority and is shadowed by [connection.ord.2.1]. +[connection.ord.0.1] +ord.ovw01=C-0.1.ovw01 + + +[.test-append-stringlist.1] +val1-=b diff --git a/src/core/tests/config/conf.d/20-config-enable-1.conf b/src/core/tests/config/conf.d/20-config-enable-1.conf new file mode 100644 index 00000000..e6800f52 --- /dev/null +++ b/src/core/tests/config/conf.d/20-config-enable-1.conf @@ -0,0 +1,5 @@ +[.config] +enable=nm-version:1.5.32,env:test-match-env-1 + +[test-group-config-enable-1] +key1=enabled diff --git a/src/core/tests/config/conf.d/90-last.conf b/src/core/tests/config/conf.d/90-last.conf new file mode 100644 index 00000000..7d078788 --- /dev/null +++ b/src/core/tests/config/conf.d/90-last.conf @@ -0,0 +1,8 @@ +[main] +plugins+=one,two + +[order] +a=90 + +[.test-append-stringlist.1] +val1+=c,a diff --git a/src/core/tests/config/global-dns-invalid.conf b/src/core/tests/config/global-dns-invalid.conf new file mode 100644 index 00000000..e490f609 --- /dev/null +++ b/src/core/tests/config/global-dns-invalid.conf @@ -0,0 +1,9 @@ +# Invalid configuration, since there isn't a default domain section + +[global-dns] +searches=foo.com +options=timeout:5 + +[global-dns-domain-test.com] +servers=1.2.3.4 +options=myoption diff --git a/src/core/tests/config/meson.build b/src/core/tests/config/meson.build new file mode 100644 index 00000000..d0d8b1cf --- /dev/null +++ b/src/core/tests/config/meson.build @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +test_config_dir = meson.current_source_dir() + +test_unit = 'test-config' + +sources = files( + 'nm-test-device.c', + 'test-config.c', +) + +exe = executable( + test_unit, + sources, + dependencies: libNetworkManagerTest_dep, + c_args: test_c_flags, +) + +test( + 'config/' + test_unit, + test_script, + args: test_args + [exe.full_path()], + timeout: default_test_timeout, +) diff --git a/src/core/tests/config/nm-test-device.c b/src/core/tests/config/nm-test-device.c new file mode 100644 index 00000000..f724b03a --- /dev/null +++ b/src/core/tests/config/nm-test-device.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-test-device.h" + +#include "devices/nm-device-private.h" +#include "nm-utils.h" + +/*****************************************************************************/ + +struct _NMTestDevice { + NMDevice parent; +}; + +struct _NMTestDeviceClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE(NMTestDevice, nm_test_device, NM_TYPE_DEVICE) + +#define PARENT_CLASS (G_OBJECT_CLASS(g_type_class_peek_parent(nm_test_device_parent_class))) + +/*****************************************************************************/ + +static NMDeviceCapabilities +get_generic_capabilities(NMDevice *device) +{ + return NM_DEVICE_CAP_IS_NON_KERNEL; +} + +/*****************************************************************************/ + +static void +nm_test_device_init(NMTestDevice *self) +{} + +/* We jump over NMDevice's construct/destruct methods, which require NMPlatform + * and NMSettings to be initialized. + */ +static void +constructed(GObject *object) +{ + PARENT_CLASS->constructed(object); +} + +static void +dispose(GObject *object) +{ + PARENT_CLASS->dispose(object); +} + +NMDevice * +nm_test_device_new(const char *hwaddr) +{ + return g_object_new(NM_TYPE_TEST_DEVICE, + NM_DEVICE_IFACE, + "dummy", + NM_DEVICE_PERM_HW_ADDRESS, + hwaddr, + NM_DEVICE_DEVICE_TYPE, + NM_DEVICE_TYPE_ETHERNET, + NULL); +} + +static void +nm_test_device_class_init(NMTestDeviceClass *klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS(klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); + + object_class->constructed = constructed; + object_class->dispose = dispose; + + device_class->get_generic_capabilities = get_generic_capabilities; +} diff --git a/src/core/tests/config/nm-test-device.h b/src/core/tests/config/nm-test-device.h new file mode 100644 index 00000000..638177a0 --- /dev/null +++ b/src/core/tests/config/nm-test-device.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_TEST_DEVICE_H__ +#define __NETWORKMANAGER_TEST_DEVICE_H__ + +#include "devices/nm-device.h" + +#define NM_TYPE_TEST_DEVICE (nm_test_device_get_type()) +#define NM_TEST_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_TEST_DEVICE, NMTestDevice)) +#define NM_TEST_DEVICE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NM_TYPE_TEST_DEVICE, NMTestDeviceClass)) +#define NM_IS_TEST_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_TEST_DEVICE)) +#define NM_IS_TEST_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_TEST_DEVICE)) +#define NM_TEST_DEVICE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_TEST_DEVICE, NMTestDeviceClass)) + +typedef struct _NMTestDevice NMTestDevice; +typedef struct _NMTestDeviceClass NMTestDeviceClass; + +GType nm_test_device_get_type(void); + +NMDevice *nm_test_device_new(const char *hwaddr); + +#endif /* __NETWORKMANAGER_TEST_DEVICE_H__ */ diff --git a/src/core/tests/config/test-config.c b/src/core/tests/config/test-config.c new file mode 100644 index 00000000..3bb14815 --- /dev/null +++ b/src/core/tests/config/test-config.c @@ -0,0 +1,1398 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include + +#include "nm-config.h" +#include "nm-test-device.h" +#include "platform/nm-fake-platform.h" +#include "dhcp/nm-dhcp-manager.h" +#include "nm-dbus-manager.h" +#include "nm-connectivity.h" + +#include "nm-test-utils-core.h" + +#define TEST_DIR NM_BUILD_SRCDIR "/src/core/tests/config" +#define BUILD_DIR NM_BUILD_BUILDDIR "/src/core/tests/config" + +/*****************************************************************************/ + +static void +_assert_config_value(const NMConfigData *config_data, + const char * group, + const char * key, + const char * expected_value, + const char * file, + int line) +{ + gs_free char *value = NULL; + + value = nm_config_data_get_value(config_data, group, key, NM_CONFIG_GET_VALUE_NONE); + if (g_strcmp0(value, expected_value)) { + g_error("(%s:%d) invalid value in config-data %s.%s = %s%s%s (instead of %s%s%s)", + file, + line, + group, + key, + NM_PRINT_FMT_QUOTED(value, "\"", value, "\"", "(null)"), + NM_PRINT_FMT_QUOTED(expected_value, "\"", expected_value, "\"", "(null)")); + } +} +#define assert_config_value(config_data, group, key, expected_value) \ + _assert_config_value(config_data, group, key, expected_value, __FILE__, __LINE__) + +#define _config_get_dhcp_client_a(config) \ + ({ \ + gs_free char *_s = NULL; \ + \ + _s = nm_config_data_get_value(nm_config_get_data_orig(config), \ + NM_CONFIG_KEYFILE_GROUP_MAIN, \ + NM_CONFIG_KEYFILE_KEY_MAIN_DHCP, \ + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); \ + _s ? nm_sprintf_bufa(100, "%s", _s) : NULL; \ + }) + +/*****************************************************************************/ + +static NMConfig * +setup_config(GError ** error, + const char * config_file, + const char * intern_config, + const char *const *atomic_section_prefixes, + const char * config_dir, + const char * system_config_dir, + ...) +{ + va_list ap; + GPtrArray * args; + char ** argv, *arg; + int argc; + GOptionContext * context; + gboolean success; + NMConfig * config; + GError * local_error = NULL; + NMConfigCmdLineOptions *cli; + + g_assert(!error || !*error); + + args = g_ptr_array_new(); + g_ptr_array_add(args, "test-config"); + g_ptr_array_add(args, "--config"); + g_ptr_array_add(args, (char *) config_file); + if (intern_config) { + g_ptr_array_add(args, "--intern-config"); + g_ptr_array_add(args, (char *) intern_config); + } + g_ptr_array_add(args, "--config-dir"); + g_ptr_array_add(args, (char *) config_dir); + if (system_config_dir) { + g_ptr_array_add(args, "--system-config-dir"); + g_ptr_array_add(args, (char *) system_config_dir); + } + + va_start(ap, system_config_dir); + while ((arg = va_arg(ap, char *))) + g_ptr_array_add(args, arg); + va_end(ap); + + argv = (char **) args->pdata; + argc = args->len; + + cli = nm_config_cmd_line_options_new(FALSE); + + context = g_option_context_new(NULL); + nm_config_cmd_line_options_add_to_entries(cli, context); + success = g_option_context_parse(context, &argc, &argv, NULL); + g_option_context_free(context); + + if (!success) + g_printerr("Invalid options.\n"); + + g_ptr_array_free(args, TRUE); + + config = nm_config_setup(cli, (char **) atomic_section_prefixes, &local_error); + if (error) { + g_assert(!config); + g_assert(local_error); + g_propagate_error(error, local_error); + } else { + g_assert(config); + g_assert_no_error(local_error); + } + nm_config_cmd_line_options_free(cli); + + if (config) { + NMDhcpManager *dhcp_manager; + gpointer logging_old_state; + + logging_old_state = nmtst_logging_disable(FALSE); + + dhcp_manager = nm_dhcp_manager_get(); + g_test_assert_expected_messages(); + + nmtst_logging_reenable(logging_old_state); + + g_object_set_data_full(G_OBJECT(config), + "nmtst-config-keep-dhcp-manager-alive", + dhcp_manager, + nmtst_dhcp_manager_unget); + } + + return config; +} + +static void +test_config_simple(void) +{ + gs_unref_object NMConfig *config = NULL; + gs_strfreev char ** plugins = NULL; + char * value; + gs_unref_object NMDevice *dev50 = nm_test_device_new("00:00:00:00:00:50"); + gs_unref_object NMDevice *dev51 = nm_test_device_new("00:00:00:00:00:51"); + gs_unref_object NMDevice *dev52 = nm_test_device_new("00:00:00:00:00:52"); + + config = + setup_config(NULL, TEST_DIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", NULL); + + g_assert_cmpstr(nm_config_data_get_config_main_file(nm_config_get_data_orig(config)), + ==, + TEST_DIR "/NetworkManager.conf"); + g_assert_cmpstr(_config_get_dhcp_client_a(config), ==, "dhclient"); + g_assert_cmpstr(nm_config_get_log_level(config), ==, "INFO"); + g_assert_cmpint(nm_config_data_get_connectivity_interval(nm_config_get_data_orig(config)), + ==, + 100); + + plugins = nm_config_data_get_plugins(nm_config_get_data_orig(config), FALSE); + g_assert_cmpint(g_strv_length((char **) plugins), ==, 3); + g_assert_cmpstr(plugins[0], ==, "foo"); + g_assert_cmpstr(plugins[1], ==, "bar"); + g_assert_cmpstr(plugins[2], ==, "baz"); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "extra-section", + "extra-key", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "some value"); + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "extra-section", + "no-key", + NM_CONFIG_GET_VALUE_NONE); + g_assert(!value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "no-section", + "no-key", + NM_CONFIG_GET_VALUE_NONE); + g_assert(!value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "connection", + "ipv6.ip6_privacy", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "0"); + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "connection.dev51", + "ipv4.route-metric", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "51"); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ipv6.route-metric", + NULL); + g_assert_cmpstr(value, ==, NULL); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ipv4.route-metric", + NULL); + g_assert_cmpstr(value, ==, "50"); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ipv4.route-metric", + dev50); + g_assert_cmpstr(value, ==, "50"); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ipv4.route-metric", + dev51); + g_assert_cmpstr(value, ==, "51"); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ipv4.route-metric", + dev52); + g_assert_cmpstr(value, ==, "52"); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ethernet.mtu", + dev51); + g_assert_cmpstr(value, ==, "9000"); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ethernet.mtu", + dev50); + g_assert_cmpstr(value, ==, "1400"); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ipv4.dns-priority", + dev51); + g_assert_cmpstr(value, ==, NULL); + g_free(value); + + value = nm_config_data_get_connection_default(nm_config_get_data_orig(config), + "ipv4.dns-priority", + dev50); + g_assert_cmpstr(value, ==, "60"); + g_free(value); +} + +static void +test_config_non_existent(void) +{ + GError *error = NULL; + + setup_config(&error, TEST_DIR "/no-such-file", "", NULL, "/no/such/dir", "", NULL); + g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND); + g_clear_error(&error); +} + +static void +test_config_parse_error(void) +{ + GError *error = NULL; + + setup_config(&error, TEST_DIR "/bad.conf", "", NULL, "/no/such/dir", "", NULL); + g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); + g_clear_error(&error); +} + +static void +test_config_override(void) +{ + gs_unref_object NMConfig *config = NULL; + gs_strfreev char ** plugins = NULL; + + config = setup_config(NULL, + TEST_DIR "/NetworkManager.conf", + "", + NULL, + "/no/such/dir", + "", + "--plugins", + "alpha,beta,gamma,delta", + "--connectivity-interval", + "12", + NULL); + + g_assert_cmpstr(nm_config_data_get_config_main_file(nm_config_get_data_orig(config)), + ==, + TEST_DIR "/NetworkManager.conf"); + g_assert_cmpstr(_config_get_dhcp_client_a(config), ==, "dhclient"); + g_assert_cmpstr(nm_config_get_log_level(config), ==, "INFO"); + g_assert_cmpint(nm_config_data_get_connectivity_interval(nm_config_get_data_orig(config)), + ==, + 12); + + plugins = nm_config_data_get_plugins(nm_config_get_data_orig(config), FALSE); + g_assert_cmpint(g_strv_length((char **) plugins), ==, 4); + g_assert_cmpstr(plugins[0], ==, "alpha"); + g_assert_cmpstr(plugins[1], ==, "beta"); + g_assert_cmpstr(plugins[2], ==, "gamma"); + g_assert_cmpstr(plugins[3], ==, "delta"); +} + +static void +test_config_global_dns(void) +{ + NMConfig * config; + const NMGlobalDnsConfig *dns; + NMGlobalDnsDomain * domain; + const char *const * strv; + + config = + setup_config(NULL, TEST_DIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "", NULL); + + dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config)); + g_assert(dns); + + strv = nm_global_dns_config_get_searches(dns); + g_assert(strv); + g_assert_cmpuint(g_strv_length((char **) strv), ==, 2); + g_assert_cmpstr(strv[0], ==, "foo.com"); + g_assert_cmpstr(strv[1], ==, "bar.org"); + + strv = nm_global_dns_config_get_options(dns); + g_assert(strv); + g_assert_cmpuint(g_strv_length((char **) strv), ==, 2); + g_assert_cmpstr(strv[0], ==, "debug"); + g_assert_cmpstr(strv[1], ==, "edns0"); + + g_assert_cmpuint(nm_global_dns_config_get_num_domains(dns), ==, 2); + + /* Default domain */ + domain = nm_global_dns_config_lookup_domain(dns, "*"); + g_assert(domain); + + strv = nm_global_dns_domain_get_servers(domain); + g_assert(strv); + g_assert_cmpuint(g_strv_length((char **) strv), ==, 2); + g_assert_cmpstr(strv[0], ==, "1.1.1.1"); + g_assert_cmpstr(strv[1], ==, "1::128"); + + strv = nm_global_dns_domain_get_options(domain); + g_assert(strv); + g_assert_cmpuint(g_strv_length((char **) strv), ==, 2); + g_assert_cmpstr(strv[0], ==, "opt1"); + g_assert_cmpstr(strv[1], ==, "opt2"); + + /* 'example.com' domain */ + domain = nm_global_dns_config_lookup_domain(dns, "example.com"); + g_assert(domain); + + strv = nm_global_dns_domain_get_servers(domain); + g_assert(strv); + g_assert_cmpuint(g_strv_length((char **) strv), ==, 1); + g_assert_cmpstr(strv[0], ==, "2.2.2.2"); + + strv = nm_global_dns_domain_get_options(domain); + g_assert(!strv || g_strv_length((char **) strv) == 0); + + /* Non-existent domain 'test.com' */ + domain = nm_global_dns_config_lookup_domain(dns, "test.com"); + g_assert(!domain); + + g_object_unref(config); + + /* Check that a file without a default domain section gives a NULL configuration */ + config = + setup_config(NULL, TEST_DIR "/global-dns-invalid.conf", "", NULL, "/no/such/dir", "", NULL); + dns = nm_config_data_get_global_dns_config(nm_config_get_data_orig(config)); + g_assert(!dns); + g_object_unref(config); +} + +static void +test_config_connectivity_check(void) +{ +#if WITH_CONCHECK + const char * CONFIG_INTERN = BUILD_DIR "/test-connectivity-check-intern.conf"; + NMConfig * config; + NMConnectivity *connectivity; + + g_assert(g_file_set_contents(CONFIG_INTERN, "", 0, NULL)); + config = setup_config(NULL, + TEST_DIR "/NetworkManager.conf", + CONFIG_INTERN, + NULL, + "/no/such/dir", + "", + NULL); + connectivity = nm_connectivity_get(); + + g_assert(nm_connectivity_check_enabled(connectivity)); + + /* disable connectivity checking */ + NMTST_EXPECT_NM_INFO("config: signal: *"); + nm_config_set_connectivity_check_enabled(config, FALSE); + g_test_assert_expected_messages(); + + g_assert(!nm_connectivity_check_enabled(connectivity)); + + /* re-enable connectivity checking */ + NMTST_EXPECT_NM_INFO("config: signal: *"); + nm_config_set_connectivity_check_enabled(config, TRUE); + g_test_assert_expected_messages(); + + g_assert(nm_connectivity_check_enabled(connectivity)); + + g_object_unref(connectivity); + g_object_unref(config); + + g_assert(remove(CONFIG_INTERN) == 0); +#else + g_test_skip("concheck disabled"); +#endif +} + +static void +test_config_no_auto_default(void) +{ + NMConfig *config; + GError * error = NULL; + int fd, nwrote; + char * state_file; + NMDevice *dev1, *dev2, *dev3, *dev4; + + fd = g_file_open_tmp(NULL, &state_file, &error); + g_assert_no_error(error); + + nwrote = write(fd, "22:22:22:22:22:22\n", 18); + g_assert_cmpint(nwrote, ==, 18); + nwrote = write(fd, "44:44:44:44:44:44\n", 18); + g_assert_cmpint(nwrote, ==, 18); + nm_close(fd); + + config = setup_config(NULL, + TEST_DIR "/NetworkManager.conf", + "", + NULL, + "/no/such/dir", + "", + "--no-auto-default", + state_file, + NULL); + + dev1 = nm_test_device_new("11:11:11:11:11:11"); + dev2 = nm_test_device_new("22:22:22:22:22:22"); + dev3 = nm_test_device_new("33:33:33:33:33:33"); + dev4 = nm_test_device_new("44:44:44:44:44:44"); + + g_assert(nm_config_get_no_auto_default_for_device(config, dev1)); + g_assert(nm_config_get_no_auto_default_for_device(config, dev2)); + g_assert(!nm_config_get_no_auto_default_for_device(config, dev3)); + g_assert(nm_config_get_no_auto_default_for_device(config, dev4)); + + NMTST_EXPECT_NM_INFO("config: signal: NO_AUTO_DEFAULT,no-auto-default *"); + nm_config_set_no_auto_default_for_device(config, dev3); + g_test_assert_expected_messages(); + + g_assert(nm_config_get_no_auto_default_for_device(config, dev3)); + + g_object_unref(config); + + config = setup_config(NULL, + TEST_DIR "/NetworkManager.conf", + "", + NULL, + "/no/such/dir", + "", + "--no-auto-default", + state_file, + NULL); + + g_assert(nm_config_get_no_auto_default_for_device(config, dev1)); + g_assert(nm_config_get_no_auto_default_for_device(config, dev2)); + g_assert(nm_config_get_no_auto_default_for_device(config, dev3)); + g_assert(nm_config_get_no_auto_default_for_device(config, dev4)); + + g_object_unref(config); + + g_object_unref(dev1); + g_object_unref(dev2); + g_object_unref(dev3); + g_object_unref(dev4); + + unlink(state_file); + g_free(state_file); +} + +static void +test_config_confdir(void) +{ + gs_unref_object NMConfig *config = NULL; + gs_strfreev char ** plugins = NULL; + char * value; + GSList * specs; + + config = + setup_config(NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); + + g_assert_cmpstr(nm_config_data_get_config_main_file(nm_config_get_data_orig(config)), + ==, + TEST_DIR "/NetworkManager.conf"); + g_assert_cmpstr(_config_get_dhcp_client_a(config), ==, "dhcpcd"); + g_assert_cmpstr(nm_config_get_log_level(config), ==, "INFO"); + g_assert_cmpstr(nm_config_get_log_domains(config), ==, "PLATFORM,DNS,WIFI"); + g_assert_cmpstr(nm_config_data_get_connectivity_uri(nm_config_get_data_orig(config)), + ==, + "http://example.net"); + g_assert_cmpint(nm_config_data_get_connectivity_interval(nm_config_get_data_orig(config)), + ==, + 100); + + plugins = nm_config_data_get_plugins(nm_config_get_data_orig(config), FALSE); + g_assert_cmpint(g_strv_length((char **) plugins), ==, 5); + g_assert_cmpstr(plugins[0], ==, "foo"); + g_assert_cmpstr(plugins[1], ==, "bar"); + g_assert_cmpstr(plugins[2], ==, "baz"); + g_assert_cmpstr(plugins[3], ==, "one"); + g_assert_cmpstr(plugins[4], ==, "two"); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "main", + "extra", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "hello"); + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "main", + "no-auto-default", + NM_CONFIG_GET_VALUE_TYPE_SPEC); + specs = nm_match_spec_split(value); + g_free(value); + g_assert_cmpint(g_slist_length(specs), ==, 2); + g_assert_cmpstr(g_slist_nth_data(specs, 0), ==, "spec2"); + g_assert_cmpstr(g_slist_nth_data(specs, 1), ==, "spec3"); + g_slist_free_full(specs, g_free); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "main", + "ignore-carrier", + NM_CONFIG_GET_VALUE_TYPE_SPEC); + specs = nm_match_spec_split(value); + g_free(value); + g_assert_cmpint(g_slist_length(specs), ==, 2); + g_assert_cmpstr(g_slist_nth_data(specs, 0), ==, " space1 "); + g_assert_cmpstr(g_slist_nth_data(specs, 1), ==, " space2\t"); + g_slist_free_full(specs, g_free); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".0", + "new", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "something"); /* not ",something" */ + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "order", + "a", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "90"); + g_free(value); + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "order", + "b", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "10"); + g_free(value); + value = nm_config_data_get_value(nm_config_get_data_orig(config), + "order", + "c", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "0"); + g_free(value); + + g_assert(!nm_config_data_has_value(nm_config_get_data_orig(config), + "appendable-test", + "non-appendable-key1", + NM_CONFIG_GET_VALUE_RAW)); + g_assert(!nm_config_data_has_value(nm_config_get_data_orig(config), + "appendable-test", + "non-appendable-key1+", + NM_CONFIG_GET_VALUE_RAW)); + g_assert(!nm_config_data_has_value(nm_config_get_data_orig(config), + "appendable-test", + "non-appendable-key1-", + NM_CONFIG_GET_VALUE_RAW)); + g_assert(!nm_config_data_has_value(nm_config_get_data_orig(config), + "appendable-test", + "non-appendable-key2", + NM_CONFIG_GET_VALUE_RAW)); + g_assert(!nm_config_data_has_value(nm_config_get_data_orig(config), + "appendable-test", + "non-appendable-key2+", + NM_CONFIG_GET_VALUE_RAW)); + g_assert(!nm_config_data_has_value(nm_config_get_data_orig(config), + "appendable-test", + "non-appendable-key2-", + NM_CONFIG_GET_VALUE_RAW)); + +#define ASSERT_GET_CONN_DEFAULT(xconfig, xname, xvalue) \ + G_STMT_START \ + { \ + gs_free char *_value = \ + nm_config_data_get_connection_default(nm_config_get_data_orig(xconfig), \ + (xname), \ + NULL); \ + g_assert_cmpstr(_value, ==, (xvalue)); \ + } \ + G_STMT_END + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key00"), "A-0.0.00"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key01"), "A-0.3.01"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key02"), "A-0.2.02"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key03"), "A-0.1.03"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key04"), "B-1.3.04"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key05"), "B-1.2.05"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key06"), "B-1.1.06"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key07"), "C-2.3.07"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key08"), "C-2.2.08"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.key09"), "C-2.1.09"); + ASSERT_GET_CONN_DEFAULT(config, NM_CON_DEFAULT("ord.ovw01"), "C-0.1.ovw01"); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".1", + "val1", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "a,c"); + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".1", + "val2", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "VAL2"); + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".1", + "val3", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, NULL); + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".1", + "val4", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "vb,vb"); + g_free(value); + + value = nm_config_data_get_value(nm_config_get_data_orig(config), + NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".1", + "val5", + NM_CONFIG_GET_VALUE_NONE); + g_assert_cmpstr(value, ==, "VAL5"); + g_free(value); + + nm_config_data_log(nm_config_get_data_orig(config), ">>> TEST: ", " ", "/test/file/name", NULL); +} + +static void +test_config_confdir_parse_error(void) +{ + GError *error = NULL; + + /* Using TEST_DIR as the conf dir will pick up bad.conf */ + setup_config(&error, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR, "", NULL); + g_assert_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); + g_clear_error(&error); +} + +static void +test_config_warnings(void) +{ + gs_unref_object NMConfig *config = NULL; + const char *const * warnings; + + config = setup_config(NULL, + TEST_DIR "/NetworkManager-warn.conf", + "", + NULL, + "/no/such/dir", + "", + NULL); + + warnings = nm_config_get_warnings(config); + +#define check_warning(str, group, key) \ + { \ + gs_free char *expected = NULL; \ + \ + expected = g_strdup_printf("unknown key '%s' in section [%s] of file '" TEST_DIR \ + "/NetworkManager-warn.conf'", \ + key, \ + group); \ + g_assert_cmpstr(str, ==, expected); \ + } + + g_assert(warnings); + g_assert_cmpint(g_strv_length((char **) warnings), ==, 5); + check_warning(warnings[0], "main", "plugin"); + check_warning(warnings[1], "main", "rc-managed"); + check_warning(warnings[2], "connectivity", "audit"); + check_warning(warnings[3], "connection-wifi", "wifi.tx-power"); + check_warning(warnings[4], "connection", "ipv4.addresses"); + +#undef check_warning +} + +/*****************************************************************************/ + +typedef void (*TestSetValuesUserSetFcn)(NMConfig * config, + gboolean is_user, + GKeyFile * keyfile_user, + NMConfigChangeFlags *out_expected_changes); +typedef void (*TestSetValuesCheckStateFcn)(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data); + +typedef struct { + NMConfigChangeFlags changes; + TestSetValuesCheckStateFcn check_state_fcn; +} TestSetValuesConfigChangedData; + +static void +_set_values_config_changed_cb(NMConfig * config, + NMConfigData * config_data, + NMConfigChangeFlags changes, + NMConfigData * old_data, + TestSetValuesConfigChangedData *config_changed_data) +{ + g_assert(changes != NM_CONFIG_CHANGE_NONE); + g_assert(config_changed_data); + g_assert(config_changed_data->changes == NM_CONFIG_CHANGE_NONE); + + if (changes == NM_CONFIG_CHANGE_CAUSE_SIGHUP) + return; + changes &= ~NM_CONFIG_CHANGE_CAUSE_SIGHUP; + + config_changed_data->changes = changes; + + if (config_changed_data->check_state_fcn) + config_changed_data->check_state_fcn(config, config_data, TRUE, changes, old_data); +} + +static void +_set_values_user(NMConfig * config, + const char * CONFIG_USER, + TestSetValuesUserSetFcn set_fcn, + TestSetValuesCheckStateFcn check_state_fcn) +{ + GKeyFile * keyfile_user; + gboolean success; + gs_free_error GError * error = NULL; + TestSetValuesConfigChangedData config_changed_data = { + .changes = NM_CONFIG_CHANGE_NONE, + .check_state_fcn = check_state_fcn, + }; + NMConfigChangeFlags expected_changes = NM_CONFIG_CHANGE_NONE; + gs_unref_object NMConfigData *config_data_before = NULL; + + keyfile_user = nm_config_create_keyfile(); + + success = g_key_file_load_from_file(keyfile_user, CONFIG_USER, G_KEY_FILE_NONE, &error); + nmtst_assert_success(success, error); + + if (set_fcn) + set_fcn(config, TRUE, keyfile_user, &expected_changes); + + success = g_key_file_save_to_file(keyfile_user, CONFIG_USER, &error); + nmtst_assert_success(success, error); + + g_signal_connect(G_OBJECT(config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK(_set_values_config_changed_cb), + &config_changed_data); + + config_data_before = g_object_ref(nm_config_get_data(config)); + + if (expected_changes != NM_CONFIG_CHANGE_NONE) + NMTST_EXPECT_NM_INFO("config: signal: *"); + else + NMTST_EXPECT_NM_INFO("config: signal: SIGHUP (no changes from disk)*"); + + nm_config_reload(config, NM_CONFIG_CHANGE_CAUSE_SIGHUP, FALSE); + + g_test_assert_expected_messages(); + + g_assert(expected_changes == config_changed_data.changes); + + if (check_state_fcn) + check_state_fcn(config, + nm_config_get_data(config), + FALSE, + NM_CONFIG_CHANGE_NONE, + config_data_before); + + g_signal_handlers_disconnect_by_func(config, + _set_values_config_changed_cb, + &config_changed_data); + + g_key_file_unref(keyfile_user); +} + +static void +_set_values_intern(NMConfig * config, + TestSetValuesUserSetFcn set_fcn, + TestSetValuesCheckStateFcn check_state_fcn) +{ + GKeyFile * keyfile_intern; + TestSetValuesConfigChangedData config_changed_data = { + .changes = NM_CONFIG_CHANGE_NONE, + .check_state_fcn = check_state_fcn, + }; + NMConfigChangeFlags expected_changes = NM_CONFIG_CHANGE_NONE; + gs_unref_object NMConfigData *config_data_before = NULL; + + config_data_before = g_object_ref(nm_config_get_data(config)); + + keyfile_intern = nm_config_data_clone_keyfile_intern(config_data_before); + + if (set_fcn) + set_fcn(config, FALSE, keyfile_intern, &expected_changes); + + g_signal_connect(G_OBJECT(config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK(_set_values_config_changed_cb), + &config_changed_data); + + if (expected_changes != NM_CONFIG_CHANGE_NONE) + NMTST_EXPECT_NM_INFO("config: signal: *"); + + nm_config_set_values(config, keyfile_intern, TRUE, FALSE); + + g_test_assert_expected_messages(); + + g_assert(expected_changes == config_changed_data.changes); + + if (check_state_fcn) + check_state_fcn(config, + nm_config_get_data(config), + FALSE, + NM_CONFIG_CHANGE_NONE, + config_data_before); + + g_signal_handlers_disconnect_by_func(config, + _set_values_config_changed_cb, + &config_changed_data); + + g_key_file_unref(keyfile_intern); +} + +static void +_set_values_user_intern_section_set(NMConfig * config, + gboolean set_user, + GKeyFile * keyfile, + NMConfigChangeFlags *out_expected_changes) +{ + g_key_file_set_string(keyfile, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "section1", + "key", + "this-should-be-ignored"); +} + +static void +_set_values_user_intern_section_check(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data) +{ + g_assert(changes == NM_CONFIG_CHANGE_NONE); + g_assert( + !nm_config_data_has_group(config_data, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "section1")); +} + +static void +_set_values_user_initial_values_set(NMConfig * config, + gboolean set_user, + GKeyFile * keyfile, + NMConfigChangeFlags *out_expected_changes) +{ + g_key_file_remove_group(keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "section1", NULL); + g_key_file_set_string(keyfile, "section1", "key1", "value1"); + *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER; +} + +static void +_set_values_user_initial_values_check(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data) +{ + if (is_change_event) + g_assert(changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER)); + assert_config_value(config_data, "section1", "key1", "value1"); +} + +static void +_set_values_intern_internal_set(NMConfig * config, + gboolean set_user, + GKeyFile * keyfile, + NMConfigChangeFlags *out_expected_changes) +{ + g_key_file_set_string(keyfile, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "section1", + "key", + "internal-section"); + *out_expected_changes = NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES + | NM_CONFIG_CHANGE_VALUES_INTERN; +} + +static void +_set_values_intern_internal_check(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data) +{ + if (is_change_event) + g_assert(changes + == (NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES + | NM_CONFIG_CHANGE_VALUES_INTERN)); + assert_config_value(config_data, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "section1", + "key", + "internal-section"); +} + +static void +_set_values_user_atomic_section_1_set(NMConfig * config, + gboolean set_user, + GKeyFile * keyfile, + NMConfigChangeFlags *out_expected_changes) +{ + g_key_file_set_string(keyfile, "atomic-prefix-1.section-a", "key1", "user-value1"); + g_key_file_set_string(keyfile, "atomic-prefix-1.section-a", "key2", "user-value2"); + g_key_file_set_string(keyfile, "atomic-prefix-1.section-b", "key1", "user-value1"); + g_key_file_set_string(keyfile, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1"); + g_key_file_set_string(keyfile, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2"); + *out_expected_changes = NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER; +} + +static void +_set_values_user_atomic_section_1_check(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data) +{ + if (is_change_event) + g_assert(changes == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER)); + assert_config_value(config_data, "atomic-prefix-1.section-a", "key1", "user-value1"); + assert_config_value(config_data, "atomic-prefix-1.section-a", "key2", "user-value2"); + assert_config_value(config_data, "atomic-prefix-1.section-b", "key1", "user-value1"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2"); +} + +static void +_set_values_intern_atomic_section_1_set(NMConfig * config, + gboolean set_user, + GKeyFile * keyfile, + NMConfigChangeFlags *out_expected_changes) +{ + g_key_file_set_string(keyfile, "atomic-prefix-1.section-a", "key1", "intern-value1"); + g_key_file_set_string(keyfile, "atomic-prefix-1.section-a", "key3", "intern-value3"); + g_key_file_set_string(keyfile, "non-atomic-prefix-1.section-a", "nap1-key1", "intern-value1"); + g_key_file_set_string(keyfile, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3"); + *out_expected_changes = NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES + | NM_CONFIG_CHANGE_VALUES_INTERN; +} + +static void +_set_values_intern_atomic_section_1_check(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data) +{ + if (is_change_event) + g_assert(changes + == (NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES + | NM_CONFIG_CHANGE_VALUES_INTERN)); + assert_config_value(config_data, "atomic-prefix-1.section-a", "key1", "intern-value1"); + assert_config_value(config_data, "atomic-prefix-1.section-a", "key2", NULL); + assert_config_value(config_data, "atomic-prefix-1.section-a", "key3", "intern-value3"); + assert_config_value(config_data, "atomic-prefix-1.section-b", "key1", "user-value1"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key1", "intern-value1"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3"); + g_assert(nm_config_data_is_intern_atomic_group(config_data, "atomic-prefix-1.section-a")); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "atomic-prefix-1.section-b")); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "non-atomic-prefix-1.section-a")); +} + +static void +_set_values_user_atomic_section_2_set(NMConfig * config, + gboolean set_user, + GKeyFile * keyfile, + NMConfigChangeFlags *out_expected_changes) +{ + g_key_file_set_string(keyfile, "atomic-prefix-1.section-a", "key1", "user-value1-x"); + g_key_file_set_string(keyfile, "atomic-prefix-1.section-a", "key2", "user-value2"); + g_key_file_set_string(keyfile, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1-x"); + g_key_file_set_string(keyfile, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2-x"); + *out_expected_changes = + NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER | NM_CONFIG_CHANGE_VALUES_INTERN; +} + +static void +_set_values_user_atomic_section_2_check(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data) +{ + if (is_change_event) + g_assert(changes + == (NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER + | NM_CONFIG_CHANGE_VALUES_INTERN)); + assert_config_value(config_data, "atomic-prefix-1.section-a", "key1", "user-value1-x"); + assert_config_value(config_data, "atomic-prefix-1.section-a", "key2", "user-value2"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key1", "user-value1-x"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2-x"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3"); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "atomic-prefix-1.section-a")); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "atomic-prefix-1.section-b")); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "non-atomic-prefix-1.section-a")); +} + +static void +_set_values_intern_atomic_section_2_set(NMConfig * config, + gboolean set_user, + GKeyFile * keyfile, + NMConfigChangeFlags *out_expected_changes) +{ + /* let's hide an atomic section and one key. */ + g_key_file_set_string(keyfile, + "atomic-prefix-1.section-a", + NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, + "any-value"); + g_key_file_set_string(keyfile, + "non-atomic-prefix-1.section-a", + NM_CONFIG_KEYFILE_KEYPREFIX_WAS "nap1-key1", + "any-value"); + g_key_file_set_string(keyfile, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3"); + g_key_file_set_string(keyfile, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "with-whitespace", + "key1", + " b c\\, d "); + g_key_file_set_value(keyfile, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "with-whitespace", + "key2", + " b c\\, d "); + *out_expected_changes = NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES + | NM_CONFIG_CHANGE_VALUES_INTERN; +} + +static void +_set_values_intern_atomic_section_2_check(NMConfig * config, + NMConfigData * config_data, + gboolean is_change_event, + NMConfigChangeFlags changes, + NMConfigData * old_data) +{ + if (is_change_event) + g_assert(changes + == (NM_CONFIG_CHANGE_CAUSE_SET_VALUES | NM_CONFIG_CHANGE_VALUES + | NM_CONFIG_CHANGE_VALUES_INTERN)); + g_assert(!nm_config_data_has_group(config_data, "atomic-prefix-1.section-a")); + assert_config_value(config_data, "atomic-prefix-1.section-b", "key1", "user-value1"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key1", NULL); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key2", "user-value2-x"); + assert_config_value(config_data, "non-atomic-prefix-1.section-a", "nap1-key3", "intern-value3"); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "atomic-prefix-1.section-a")); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "atomic-prefix-1.section-b")); + g_assert(!nm_config_data_is_intern_atomic_group(config_data, "non-atomic-prefix-1.section-a")); + assert_config_value(config_data, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "with-whitespace", + "key1", + " b c\\, d "); + assert_config_value(config_data, + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN "with-whitespace", + "key2", + " b c\\, d "); +} + +static void +test_config_set_values(void) +{ + gs_unref_object NMConfig *config = NULL; + const char * CONFIG_USER = BUILD_DIR "/test-set-values-user.conf"; + const char * CONFIG_INTERN = BUILD_DIR "/test-set-values-intern.conf"; + const char * atomic_section_prefixes[] = { + "atomic-prefix-1.", + "atomic-prefix-2.", + NULL, + }; + + g_assert(g_file_set_contents(CONFIG_USER, "", 0, NULL)); + g_assert(g_file_set_contents(CONFIG_INTERN, "", 0, NULL)); + + config = setup_config(NULL, CONFIG_USER, CONFIG_INTERN, atomic_section_prefixes, "", "", NULL); + + _set_values_user(config, + CONFIG_USER, + _set_values_user_intern_section_set, + _set_values_user_intern_section_check); + + _set_values_user(config, + CONFIG_USER, + _set_values_user_initial_values_set, + _set_values_user_initial_values_check); + + _set_values_intern(config, _set_values_intern_internal_set, _set_values_intern_internal_check); + + _set_values_user(config, + CONFIG_USER, + _set_values_user_atomic_section_1_set, + _set_values_user_atomic_section_1_check); + + _set_values_intern(config, + _set_values_intern_atomic_section_1_set, + _set_values_intern_atomic_section_1_check); + + _set_values_user(config, + CONFIG_USER, + _set_values_user_atomic_section_2_set, + _set_values_user_atomic_section_2_check); + + _set_values_intern(config, + _set_values_intern_atomic_section_2_set, + _set_values_intern_atomic_section_2_check); + + g_assert(remove(CONFIG_USER) == 0); + g_assert(remove(CONFIG_INTERN) == 0); +} + +/*****************************************************************************/ + +static void +_test_signal_config_changed_cb(NMConfig * config, + NMConfigData * config_data, + NMConfigChangeFlags changes, + NMConfigData * old_data, + gpointer user_data) +{ + const NMConfigChangeFlags *expected = user_data; + + g_assert(changes); + g_assert_cmpint(changes, ==, *expected); + g_assert(NM_IS_CONFIG(config)); + g_assert(NM_IS_CONFIG_DATA(config_data)); + + g_assert(config_data == old_data); + g_assert(config_data == nm_config_get_data(config)); +} + +static void +_test_signal_config_changed_cb2(NMConfig * config, + NMConfigData * config_data, + NMConfigChangeFlags changes, + NMConfigData * old_data, + gpointer user_data) +{ + const NMConfigChangeFlags *expected = user_data; + + g_assert(changes); + g_assert_cmpint(changes, ==, *expected); +} + +static void +test_config_signal(void) +{ + gs_unref_object NMConfig *config = NULL; + NMConfigChangeFlags expected; + gs_unref_object NMConfigData *config_data_orig = NULL; + + config = + setup_config(NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); + + config_data_orig = g_object_ref(nm_config_get_data_orig(config)); + + g_signal_connect(G_OBJECT(config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK(_test_signal_config_changed_cb), + &expected); + + expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR1; + NMTST_EXPECT_NM_INFO("config: signal: SIGUSR1"); + nm_config_reload(config, expected, FALSE); + + expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2; + NMTST_EXPECT_NM_INFO("config: signal: SIGUSR2"); + nm_config_reload(config, expected, FALSE); + + expected = NM_CONFIG_CHANGE_CAUSE_SIGHUP; + NMTST_EXPECT_NM_INFO("config: signal: SIGHUP (no changes from disk)*"); + nm_config_reload(config, expected, FALSE); + + /* test with subscribing two signals... + * + * This test exposes glib bug https://bugzilla.redhat.com/show_bug.cgi?id=1260577 + * for which we however have a workaround in 'nm-config.c' */ + g_signal_connect(G_OBJECT(config), + NM_CONFIG_SIGNAL_CONFIG_CHANGED, + G_CALLBACK(_test_signal_config_changed_cb2), + &expected); + expected = NM_CONFIG_CHANGE_CAUSE_SIGUSR2; + NMTST_EXPECT_NM_INFO("config: signal: SIGUSR2"); + nm_config_reload(config, NM_CONFIG_CHANGE_CAUSE_SIGUSR2, FALSE); + g_signal_handlers_disconnect_by_func(config, _test_signal_config_changed_cb2, &expected); + + g_signal_handlers_disconnect_by_func(config, _test_signal_config_changed_cb, &expected); + + g_assert(config_data_orig == nm_config_get_data(config)); +} + +/*****************************************************************************/ + +static void +test_config_enable(void) +{ + gs_unref_object NMConfig *config = NULL; + guint match_nm_version = _nm_config_match_nm_version; + char * match_env = g_strdup(_nm_config_match_env); + + nm_clear_g_free(&_nm_config_match_env); + _nm_config_match_env = g_strdup("something-else"); + + _nm_config_match_nm_version = nm_encode_version(1, 3, 4); + config = + setup_config(NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); + assert_config_value(nm_config_get_data_orig(config), + "test-group-config-enable-1", + "key1", + NULL); + g_clear_object(&config); + + _nm_config_match_nm_version = nm_encode_version(1, 5, 32); + config = + setup_config(NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); + assert_config_value(nm_config_get_data_orig(config), + "test-group-config-enable-1", + "key1", + "enabled"); + g_clear_object(&config); + + _nm_config_match_nm_version = nm_encode_version(1, 5, 3); + nm_clear_g_free(&_nm_config_match_env); + _nm_config_match_env = g_strdup("test-match-env-1"); + config = + setup_config(NULL, TEST_DIR "/NetworkManager.conf", "", NULL, TEST_DIR "/conf.d", "", NULL); + assert_config_value(nm_config_get_data_orig(config), + "test-group-config-enable-1", + "key1", + "enabled"); + g_clear_object(&config); + + _nm_config_match_nm_version = match_nm_version; + nm_clear_g_free(&_nm_config_match_env); + _nm_config_match_env = match_env; +} + +/*****************************************************************************/ + +static void +test_config_state_file(void) +{ + NMConfig * config; + const NMConfigState *state; + gs_free_error GError *error = NULL; + gboolean ret; + gs_free char * file_data = NULL; + gsize file_size; + const char *const TMP_FILE = BUILD_DIR "/tmp.state"; + + ret = g_file_get_contents(TEST_DIR "/NetworkManager.state", &file_data, &file_size, &error); + nmtst_assert_success(ret, error); + ret = g_file_set_contents(TMP_FILE, file_data, file_size, &error); + nmtst_assert_success(ret, error); + + config = setup_config(NULL, + TEST_DIR "/NetworkManager.conf", + "", + NULL, + TEST_DIR "/conf.d", + "", + "--state-file", + TMP_FILE, + NULL); + g_assert(config); + + state = nm_config_state_get(config); + g_assert(state); + + g_assert_cmpint(state->net_enabled, ==, TRUE); + g_assert_cmpint(state->wifi_enabled, ==, TRUE); + g_assert_cmpint(state->wwan_enabled, ==, TRUE); + + nm_config_state_set(config, + TRUE, + TRUE, + NM_CONFIG_STATE_PROPERTY_NETWORKING_ENABLED, + FALSE, + NM_CONFIG_STATE_PROPERTY_WIFI_ENABLED, + TRUE, + NM_CONFIG_STATE_PROPERTY_WWAN_ENABLED, + FALSE); + + state = nm_config_state_get(config); + g_assert(state); + + g_assert_cmpint(state->net_enabled, ==, FALSE); + g_assert_cmpint(state->wifi_enabled, ==, TRUE); + g_assert_cmpint(state->wwan_enabled, ==, FALSE); + + g_object_unref(config); + + /* Reload configuration */ + config = setup_config(NULL, + TEST_DIR "/NetworkManager.conf", + "", + NULL, + TEST_DIR "/conf.d", + "", + "--state-file", + TMP_FILE, + NULL); + g_assert(config); + + state = nm_config_state_get(config); + g_assert(state); + + g_assert_cmpint(state->net_enabled, ==, FALSE); + g_assert_cmpint(state->wifi_enabled, ==, TRUE); + g_assert_cmpint(state->wwan_enabled, ==, FALSE); + + g_object_unref(config); + unlink(TMP_FILE); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT"); + + nm_fake_platform_setup(); + + g_test_add_func("/config/simple", test_config_simple); + g_test_add_func("/config/non-existent", test_config_non_existent); + g_test_add_func("/config/parse-error", test_config_parse_error); + g_test_add_func("/config/no-auto-default", test_config_no_auto_default); + g_test_add_func("/config/confdir", test_config_confdir); + g_test_add_func("/config/confdir-parse-error", test_config_confdir_parse_error); + g_test_add_func("/config/warnings", test_config_warnings); + + g_test_add_func("/config/set-values", test_config_set_values); + g_test_add_func("/config/global-dns", test_config_global_dns); + g_test_add_func("/config/connectivity-check", test_config_connectivity_check); + + g_test_add_func("/config/signal", test_config_signal); + + g_test_add_func("/config/enable", test_config_enable); + + g_test_add_func("/config/state-file", test_config_state_file); + + /* This one has to come last, because it leaves its values in + * nm-config.c's global variables, and there's no way to reset + * those to NULL. + */ + g_test_add_func("/config/override", test_config_override); + + return g_test_run(); +} diff --git a/src/core/tests/meson.build b/src/core/tests/meson.build new file mode 100644 index 00000000..994563c4 --- /dev/null +++ b/src/core/tests/meson.build @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +subdir('config') + +test_units = [ + 'test-core', + 'test-core-with-expect', + 'test-dcb', + 'test-ip4-config', + 'test-ip6-config', + 'test-l3cfg', + 'test-utils', + 'test-wired-defname', +] + +foreach test_unit: test_units + exe = executable( + test_unit, + test_unit + '.c', + dependencies: libNetworkManagerTest_dep, + c_args: test_c_flags, + ) + + test( + test_unit, + test_script, + args: test_args + [exe.full_path()], + timeout: default_test_timeout, + ) +endforeach + +exe = executable( + 'test-systemd', + 'test-systemd.c', + include_directories: [ + top_inc, + src_inc, + ], + dependencies: [ + libnm_systemd_core_dep, + libnm_systemd_shared_dep, + ], + c_args: [ + '-DG_LOG_DOMAIN="test"', + ], +) + +test( + 'test-systemd', + test_script, + args: test_args + [exe.full_path()], +) diff --git a/src/core/tests/test-core-with-expect.c b/src/core/tests/test-core-with-expect.c new file mode 100644 index 00000000..022cf8f3 --- /dev/null +++ b/src/core/tests/test-core-with-expect.c @@ -0,0 +1,649 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include +#include +#include +#include + +#include "NetworkManagerUtils.h" + +#include "nm-test-utils-core.h" + +/*****************************************************************************/ + +static void +test_nm_utils_monotonic_timestamp_as_boottime(void) +{ + gint64 timestamp_nsec_per_tick, now, now_boottime, now_boottime_2, now_boottime_3; + struct timespec tp; + clockid_t clockid; + guint i; + + if (clock_gettime(CLOCK_BOOTTIME, &tp) != 0 && errno == EINVAL) + clockid = CLOCK_MONOTONIC; + else + clockid = CLOCK_BOOTTIME; + + for (i = 0; i < 10; i++) { + if (clock_gettime(clockid, &tp) != 0) + g_assert_not_reached(); + now_boottime = (((gint64) tp.tv_sec) * NM_UTILS_NSEC_PER_SEC) + ((gint64) tp.tv_nsec); + + now = nm_utils_get_monotonic_timestamp_nsec(); + + now_boottime_2 = nm_utils_monotonic_timestamp_as_boottime(now, 1); + g_assert_cmpint(now_boottime_2, >=, 0); + g_assert_cmpint(now_boottime_2, >=, now_boottime); + g_assert_cmpint(now_boottime_2 - now_boottime, <=, NM_UTILS_NSEC_PER_SEC / 10); + + g_assert_cmpint(now, ==, nm_utils_monotonic_timestamp_from_boottime(now_boottime_2, 1)); + + for (timestamp_nsec_per_tick = 1; timestamp_nsec_per_tick <= NM_UTILS_NSEC_PER_SEC; + timestamp_nsec_per_tick *= 10) { + now_boottime_3 = nm_utils_monotonic_timestamp_as_boottime(now / timestamp_nsec_per_tick, + timestamp_nsec_per_tick); + + g_assert_cmpint(now_boottime_2 / timestamp_nsec_per_tick, ==, now_boottime_3); + g_assert_cmpint(now / timestamp_nsec_per_tick, + ==, + nm_utils_monotonic_timestamp_from_boottime(now_boottime_3, + timestamp_nsec_per_tick)); + } + } +} + +/*****************************************************************************/ + +struct test_nm_utils_kill_child_async_data { + GMainLoop *loop; + pid_t pid; + gboolean called; + gboolean expected_success; + const int *expected_child_status; +}; + +static void +test_nm_utils_kill_child_async_cb(pid_t pid, gboolean success, int child_status, void *user_data) +{ + struct test_nm_utils_kill_child_async_data *data = user_data; + + g_assert(success == !!data->expected_success); + g_assert(pid == data->pid); + if (data->expected_child_status) + g_assert_cmpint(*data->expected_child_status, ==, child_status); + if (!success) + g_assert_cmpint(child_status, ==, -1); + + data->called = TRUE; + + g_assert(data->loop); + g_main_loop_quit(data->loop); +} + +static gboolean +test_nm_utils_kill_child_async_fail_cb(void *user_data) +{ + g_assert_not_reached(); +} + +static void +test_nm_utils_kill_child_async_do(const char *name, + pid_t pid, + int sig, + guint32 wait_before_kill_msec, + gboolean expected_success, + const int * expected_child_status) +{ + gboolean success; + struct test_nm_utils_kill_child_async_data data = {}; + int timeout_id; + + data.pid = pid; + data.expected_success = expected_success; + data.expected_child_status = expected_child_status; + + nm_utils_kill_child_async(pid, + sig, + LOGD_CORE, + name, + wait_before_kill_msec, + test_nm_utils_kill_child_async_cb, + &data); + g_assert(!data.called); + + timeout_id = g_timeout_add_seconds(5, test_nm_utils_kill_child_async_fail_cb, &data); + + data.loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(data.loop); + + g_assert(data.called); + success = g_source_remove(timeout_id); + g_assert(success); + + g_main_loop_unref(data.loop); +} + +static void +test_nm_utils_kill_child_sync_do(const char *name, + pid_t pid, + int sig, + guint32 wait_before_kill_msec, + gboolean expected_success, + const int * expected_child_status) +{ + gboolean success; + int child_status = -1; + + success = nm_utils_kill_child_sync(pid, + sig, + LOGD_CORE, + name, + &child_status, + wait_before_kill_msec, + 0); + g_assert(success == !!expected_success); + if (expected_child_status) + g_assert_cmpint(*expected_child_status, ==, child_status); + + g_test_assert_expected_messages(); +} + +static pid_t +test_nm_utils_kill_child_spawn(char **argv, gboolean do_not_reap_child) +{ + GError *error = NULL; + int success; + GPid child_pid; + + success = + g_spawn_async(NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH | (do_not_reap_child ? G_SPAWN_DO_NOT_REAP_CHILD : 0), + NULL, + NULL, + &child_pid, + &error); + g_assert(success && !error); + return child_pid; +} + +static pid_t +do_test_nm_utils_kill_child_create_and_join_pgroup(void) +{ + int err, tmp = 0; + int pipefd[2]; + pid_t pgid; + + err = pipe2(pipefd, O_CLOEXEC); + g_assert(err == 0); + + pgid = fork(); + g_assert(pgid >= 0); + + if (pgid == 0) { + /* child process... */ + nm_close(pipefd[0]); + + err = setpgid(0, 0); + g_assert(err == 0); + + err = write(pipefd[1], &tmp, sizeof(tmp)); + g_assert(err == sizeof(tmp)); + + nm_close(pipefd[1]); + exit(0); + } + + nm_close(pipefd[1]); + + err = read(pipefd[0], &tmp, sizeof(tmp)); + g_assert(err == sizeof(tmp)); + + nm_close(pipefd[0]); + + err = setpgid(0, pgid); + g_assert(err == 0); + + do { + err = waitpid(pgid, &tmp, 0); + } while (err == -1 && errno == EINTR); + g_assert(err == pgid); + g_assert(WIFEXITED(tmp) && WEXITSTATUS(tmp) == 0); + + return pgid; +} + +#define TEST_TOKEN "nm_test_kill_child_process" + +static void +do_test_nm_utils_kill_child(void) +{ + GLogLevelFlags fatal_mask; + char * argv_watchdog[] = { + "bash", + "-c", + "sleep 4; " + "kill -KILL 0; #watchdog for #" TEST_TOKEN, + NULL, + }; + char *argv1[] = { + "bash", + "-c", + "trap \"sleep 0.5; exit 10\" EXIT; " + "sleep 100000; exit $? #" TEST_TOKEN, + NULL, + }; + char *argv2[] = { + "bash", + "-c", + "exit 47; #" TEST_TOKEN, + NULL, + }; + char *argv3[] = { + "bash", + "-c", + "trap \"exit 47\" TERM; while true; do :; done; #" TEST_TOKEN, + NULL, + }; + char *argv4[] = { + "bash", + "-c", + "trap \"while true; do :; done\" TERM; while true; do :; done; #" TEST_TOKEN, + NULL, + }; + pid_t pid1a_1, pid1a_2, pid1a_3, pid2a, pid3a, pid4a; + pid_t pid1s_1, pid1s_2, pid1s_3, pid2s, pid3s, pid4s; + + const int expected_exit_47 = 12032; /* exit with status 47 */ + const int expected_signal_TERM = SIGTERM; + const int expected_signal_KILL = SIGKILL; + + test_nm_utils_kill_child_spawn(argv_watchdog, FALSE); + + pid1s_1 = test_nm_utils_kill_child_spawn(argv1, TRUE); + pid1s_2 = test_nm_utils_kill_child_spawn(argv1, TRUE); + pid1s_3 = test_nm_utils_kill_child_spawn(argv1, TRUE); + pid2s = test_nm_utils_kill_child_spawn(argv2, TRUE); + pid3s = test_nm_utils_kill_child_spawn(argv3, TRUE); + pid4s = test_nm_utils_kill_child_spawn(argv4, TRUE); + + pid1a_1 = test_nm_utils_kill_child_spawn(argv1, TRUE); + pid1a_2 = test_nm_utils_kill_child_spawn(argv1, TRUE); + pid1a_3 = test_nm_utils_kill_child_spawn(argv1, TRUE); + pid2a = test_nm_utils_kill_child_spawn(argv2, TRUE); + pid3a = test_nm_utils_kill_child_spawn(argv3, TRUE); + pid4a = test_nm_utils_kill_child_spawn(argv4, TRUE); + + /* give processes time to start (and potentially block signals) ... */ + g_usleep(G_USEC_PER_SEC / 10); + + fatal_mask = g_log_set_always_fatal(G_LOG_FATAL_MASK); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-1-1' (*): waiting up to 3000 milliseconds " + "for process to terminate normally after sending SIGTERM (15)..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-1-1' (*): after sending SIGTERM (15), " + "process * exited by signal 15 (* usec elapsed)"); + test_nm_utils_kill_child_sync_do("test-s-1-1", + pid1s_1, + SIGTERM, + 3000, + TRUE, + &expected_signal_TERM); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-1-2' (*): waiting for process to terminate " + "after sending SIGKILL (9)..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-1-2' (*): after sending SIGKILL (9), process " + "* exited by signal 9 (* usec elapsed)"); + test_nm_utils_kill_child_sync_do("test-s-1-2", + pid1s_2, + SIGKILL, + 1000 / 2, + TRUE, + &expected_signal_KILL); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-1-3' (*): waiting up to 1 milliseconds for " + "process to terminate normally after sending no signal (0)..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-1-3' (*): sending SIGKILL..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-1-3' (*): after sending no signal (0) and " + "SIGKILL, process * exited by signal 9 (* usec elapsed)"); + test_nm_utils_kill_child_sync_do("test-s-1-3", pid1s_3, 0, 1, TRUE, &expected_signal_KILL); + + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-s-2' (*): process * already terminated normally with status 47"); + test_nm_utils_kill_child_sync_do("test-s-2", pid2s, SIGTERM, 3000, TRUE, &expected_exit_47); + + /* send invalid signal. */ + NMTST_EXPECT_NM_ERROR("kill child process 'test-s-3-0' (*): failed to send Unexpected signal: " + "Invalid argument (22)"); + test_nm_utils_kill_child_sync_do("test-s-3-0", pid3s, -1, 0, FALSE, NULL); + + /* really kill pid3s */ + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-3-1' (*): waiting up to 3000 milliseconds " + "for process to terminate normally after sending SIGTERM (15)..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-3-1' (*): after sending SIGTERM (15), " + "process * exited normally with status 47 (* usec elapsed)"); + test_nm_utils_kill_child_sync_do("test-s-3-1", pid3s, SIGTERM, 3000, TRUE, &expected_exit_47); + + /* pid3s should not be a valid process, hence the call should fail. Note, that there + * is a race here. */ + NMTST_EXPECT_NM_ERROR( + "kill child process 'test-s-3-2' (*): failed due to unexpected return value -1 by waitpid " + "(No child process*, 10) after sending no signal (0)"); + test_nm_utils_kill_child_sync_do("test-s-3-2", pid3s, 0, 0, FALSE, NULL); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-4' (*): waiting up to 1 milliseconds for " + "process to terminate normally after sending SIGTERM (15)..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-4' (*): sending SIGKILL..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-s-4' (*): after sending SIGTERM (15) and " + "SIGKILL, process * exited by signal 9 (* usec elapsed)"); + test_nm_utils_kill_child_sync_do("test-s-4", pid4s, SIGTERM, 1, TRUE, &expected_signal_KILL); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-1-1' (*): wait for process to terminate " + "after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-1-1' (*): terminated by signal 15 (* usec elapsed)"); + test_nm_utils_kill_child_async_do("test-a-1-1", + pid1a_1, + SIGTERM, + 3000, + TRUE, + &expected_signal_TERM); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-1-2' (*): wait for process to terminate " + "after sending SIGKILL (9)..."); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-1-2' (*): terminated by signal 9 (* usec elapsed)"); + test_nm_utils_kill_child_async_do("test-a-1-2", + pid1a_2, + SIGKILL, + 1000 / 2, + TRUE, + &expected_signal_KILL); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-1-3' (*): wait for process to terminate " + "after sending no signal (0) (send SIGKILL in 1 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-1-3' (*): process not terminated after * " + "usec. Sending SIGKILL signal"); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-1-3' (*): terminated by signal 9 (* usec elapsed)"); + test_nm_utils_kill_child_async_do("test-a-1-3", pid1a_3, 0, 1, TRUE, &expected_signal_KILL); + + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-2' (*): process * already terminated normally with status 47"); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-2' (*): invoke callback: terminated normally with status 47"); + test_nm_utils_kill_child_async_do("test-a-2", pid2a, SIGTERM, 3000, TRUE, &expected_exit_47); + + NMTST_EXPECT_NM_ERROR("kill child process 'test-a-3-0' (*): unexpected error sending " + "Unexpected signal: Invalid argument (22)"); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-3-0' (*): invoke callback: killing child failed"); + /* coverity[negative_returns] */ + test_nm_utils_kill_child_async_do("test-a-3-0", pid3a, -1, 1000 / 2, FALSE, NULL); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-3-1' (*): wait for process to terminate " + "after sending SIGTERM (15) (send SIGKILL in 3000 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-3-1' (*): terminated normally with status 47 (* usec elapsed)"); + test_nm_utils_kill_child_async_do("test-a-3-1", pid3a, SIGTERM, 3000, TRUE, &expected_exit_47); + + /* pid3a should not be a valid process, hence the call should fail. Note, that there + * is a race here. */ + NMTST_EXPECT_NM_ERROR( + "kill child process 'test-a-3-2' (*): failed due to unexpected return value -1 by waitpid " + "(No child process*, 10) after sending no signal (0)"); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-3-2' (*): invoke callback: killing child failed"); + test_nm_utils_kill_child_async_do("test-a-3-2", pid3a, 0, 0, FALSE, NULL); + + NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-4' (*): wait for process to terminate after " + "sending SIGTERM (15) (send SIGKILL in 1 milliseconds)..."); + NMTST_EXPECT_NM_DEBUG("kill child process 'test-a-4' (*): process not terminated after * usec. " + "Sending SIGKILL signal"); + NMTST_EXPECT_NM_DEBUG( + "kill child process 'test-a-4' (*): terminated by signal 9 (* usec elapsed)"); + test_nm_utils_kill_child_async_do("test-a-4", pid4a, SIGTERM, 1, TRUE, &expected_signal_KILL); + + g_log_set_always_fatal(fatal_mask); + + g_test_assert_expected_messages(); +} + +static void +test_nm_utils_kill_child(void) +{ + int err; + int exit_status; + pid_t gpid; + pid_t child_pid; + + /* the tests spawns several processes, we want to clean them up + * by sending a SIGKILL to the process group. + * + * The current process might be a session leader, which prevents it from + * creating a new process group. Hence, first fork and let the child + * create a new process group, run the tests, and kill all pending + * processes. */ + child_pid = fork(); + g_assert(child_pid >= 0); + + if (child_pid == 0) { + gpid = do_test_nm_utils_kill_child_create_and_join_pgroup(); + + do_test_nm_utils_kill_child(); + + err = setpgid(0, 0); + g_assert(err == 0); + + kill(-gpid, SIGKILL); + + exit(0); + }; + + do { + err = waitpid(child_pid, &exit_status, 0); + } while (err == -1 && errno == EINTR); + g_assert(err == child_pid); + if (WIFEXITED(exit_status)) + g_assert_cmpint(WEXITSTATUS(exit_status), ==, 0); + else { + g_assert_cmpint(exit_status, ==, 0); + g_assert_not_reached(); + } +} + +/*****************************************************************************/ + +static void +_remove_at_indexes_init_random_idx(GArray *idx, guint array_len, guint idx_len) +{ + GRand * rand = nmtst_get_rand(); + gs_free char *mask = NULL; + guint i, max_test_idx; + + g_assert(idx); + g_assert(array_len > 0); + g_assert(idx_len >= 1 && idx_len <= array_len); + + mask = g_new0(char, array_len); + + max_test_idx = array_len - 1; + for (i = 0; i < idx_len; i++) { + guint itest; + + /* find a index itest that is not yet taken */ + if (max_test_idx == 0) + itest = 0; + else + itest = g_rand_int_range(rand, 0, max_test_idx); + while (itest < array_len && mask[itest]) + itest++; + g_assert(itest <= max_test_idx); + g_assert(!mask[itest]); + + mask[itest] = TRUE; + if (itest == max_test_idx) { + g_assert(max_test_idx > 0 || i == idx_len - 1); + + if (max_test_idx == 0) + g_assert_cmpint(i, ==, idx_len - 1); + else { + max_test_idx--; + while (max_test_idx > 0 && mask[max_test_idx]) + max_test_idx--; + if (mask[max_test_idx]) + g_assert_cmpint(i, ==, idx_len - 1); + } + } + } + + g_array_set_size(idx, 0); + for (i = 0; i < array_len; i++) { + if (mask[i]) + g_array_append_val(idx, i); + } + g_assert_cmpint(idx->len, ==, idx_len); +} + +static void +test_nm_utils_array_remove_at_indexes(void) +{ + gs_unref_array GArray *idx = NULL, *array = NULL; + gs_unref_hashtable GHashTable *unique = NULL; + guint i_len, i_idx_len, i_rnd, i; + + idx = g_array_new(FALSE, FALSE, sizeof(guint)); + array = g_array_new(FALSE, FALSE, sizeof(gssize)); + unique = g_hash_table_new(nm_direct_hash, NULL); + for (i_len = 1; i_len < 20; i_len++) { + for (i_idx_len = 1; i_idx_len <= i_len; i_idx_len++) { + for (i_rnd = 0; i_rnd < 20; i_rnd++) { + _remove_at_indexes_init_random_idx(idx, i_len, i_idx_len); + g_array_set_size(array, i_len); + for (i = 0; i < i_len; i++) + g_array_index(array, gssize, i) = i; + + nm_utils_array_remove_at_indexes(array, &g_array_index(idx, guint, 0), i_idx_len); + + g_hash_table_remove_all(unique); + /* ensure that all the indexes are still unique */ + for (i = 0; i < array->len; i++) + g_hash_table_add(unique, GUINT_TO_POINTER(g_array_index(array, gssize, i))); + g_assert_cmpint(g_hash_table_size(unique), ==, array->len); + + for (i = 0; i < idx->len; i++) + g_hash_table_add(unique, GUINT_TO_POINTER(g_array_index(idx, guint, i))); + g_assert_cmpint(g_hash_table_size(unique), ==, i_len); + + /* ensure proper sort order in array */ + for (i = 0; i < array->len; i++) { + gssize i1 = g_array_index(array, gssize, i); + + g_assert(i1 >= 0 && i1 < i_len); + if (i > 0) { + gsize i0 = g_array_index(array, gssize, i - 1); + g_assert_cmpint(i0, <, i1); + } + } + } + } + } +} + +/*****************************************************************************/ + +static void +test_nm_ethernet_address_is_valid(void) +{ + g_assert(!nm_ether_addr_is_valid_str(NULL)); + g_assert(!nm_ether_addr_is_valid(NULL)); + + g_assert(!nm_ether_addr_is_valid_str("FF:FF:FF:FF:FF:FF")); + g_assert(!nm_ether_addr_is_valid_str("00:00:00:00:00:00")); + g_assert(!nm_ether_addr_is_valid_str("44:44:44:44:44:44")); + g_assert(!nm_ether_addr_is_valid_str("00:30:b4:00:00:00")); + + g_assert(!nm_ether_addr_is_valid_str("")); + g_assert(!nm_ether_addr_is_valid_str("1")); + g_assert(!nm_ether_addr_is_valid_str("2")); + + g_assert(!nm_ether_addr_is_valid(&NM_ETHER_ADDR_INIT(0x00, 0x30, 0xb4, 0x00, 0x00, 0x00))); + g_assert(nm_ether_addr_is_valid(&NM_ETHER_ADDR_INIT(0x00, 0x30, 0xb4, 0x00, 0x00, 0x01))); + + /* some Broad cast addresses (with MSB of first octet set). */ + g_assert(!nm_ether_addr_is_valid_str("57:44:44:44:44:44")); + g_assert(nm_ether_addr_is_valid_str("56:44:44:44:44:44")); + g_assert(!nm_ether_addr_is_valid(&NM_ETHER_ADDR_INIT(0x03, 0x30, 0xb4, 0x00, 0x00, 0x00))); + g_assert(nm_ether_addr_is_valid(&NM_ETHER_ADDR_INIT(0x02, 0x30, 0xb4, 0x00, 0x00, 0x01))); +} + +/*****************************************************************************/ + +static void +test_nm_utils_new_vlan_name(void) +{ + guint i, j; + const char *parent_names[] = { + "a", + "a2", + "a23", + "a23456789", + "a2345678901", + "a23456789012", + "a234567890123", + "a2345678901234", + "a23456789012345", + "a234567890123456", + "a2345678901234567", + }; + + for (i = 0; i < G_N_ELEMENTS(parent_names); i++) { + for (j = 0; j < 10; j++) { + gs_free char *ifname = NULL; + gs_free char *vlan_id_s = NULL; + guint vlan_id; + + /* Create a random VLAN id between 0 and 4094 */ + vlan_id = nmtst_get_rand_uint32() % 4095; + + vlan_id_s = g_strdup_printf(".%d", vlan_id); + + ifname = nm_utils_new_vlan_name(parent_names[i], vlan_id); + g_assert(ifname && ifname[0]); + g_assert_cmpint(strlen(ifname), + ==, + MIN(15, strlen(parent_names[i]) + strlen(vlan_id_s))); + g_assert(g_str_has_suffix(ifname, vlan_id_s)); + g_assert(ifname[strlen(ifname) - strlen(vlan_id_s)] == '.'); + g_assert(strncmp(ifname, parent_names[i], strlen(ifname) - strlen(vlan_id_s)) == 0); + if (!g_str_has_prefix(ifname, parent_names[i])) + g_assert_cmpint(strlen(ifname), ==, 15); + } + } +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_assert_logging(&argc, &argv, "DEBUG", "DEFAULT"); + + g_test_add_func("/general/nm_utils_monotonic_timestamp_as_boottime", + test_nm_utils_monotonic_timestamp_as_boottime); + g_test_add_func("/general/nm_utils_kill_child", test_nm_utils_kill_child); + g_test_add_func("/general/nm_utils_array_remove_at_indexes", + test_nm_utils_array_remove_at_indexes); + g_test_add_func("/general/nm_ethernet_address_is_valid", test_nm_ethernet_address_is_valid); + g_test_add_func("/general/nm_utils_new_vlan_name", test_nm_utils_new_vlan_name); + + return g_test_run(); +} diff --git a/src/core/tests/test-core.c b/src/core/tests/test-core.c new file mode 100644 index 00000000..8698c406 --- /dev/null +++ b/src/core/tests/test-core.c @@ -0,0 +1,2639 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include +#include + +/* need math.h for isinf() and INFINITY. No need to link with -lm */ +#include + +#include "NetworkManagerUtils.h" +#include "nm-core-internal.h" +#include "nm-core-utils.h" +#include "systemd/nm-sd-utils-core.h" + +#include "dns/nm-dns-manager.h" +#include "nm-connectivity.h" + +#include "nm-test-utils-core.h" + +/* Reference implementation for nm_utils_ip6_address_clear_host_address. + * Taken originally from set_address_masked(), src/ndisc/nm-lndp-ndisc.c + **/ +static void +ip6_address_clear_host_address_reference(struct in6_addr *dst, struct in6_addr *src, guint8 plen) +{ + guint nbytes = plen / 8; + guint nbits = plen % 8; + + g_return_if_fail(plen <= 128); + g_assert(src); + g_assert(dst); + + if (plen >= 128) + *dst = *src; + else { + memset(dst, 0, sizeof(*dst)); + memcpy(dst, src, nbytes); + dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits))); + } +} + +static void +_randomize_in6_addr(struct in6_addr *addr, GRand *r) +{ + int i; + + for (i = 0; i < 4; i++) + ((guint32 *) addr)[i] = g_rand_int(r); +} + +static void +test_nm_utils_ip6_address_clear_host_address(void) +{ + GRand *r = g_rand_new(); + int plen, i; + + g_rand_set_seed(r, 0); + + for (plen = 0; plen <= 128; plen++) { + for (i = 0; i < 50; i++) { + struct in6_addr addr_src, addr_ref; + struct in6_addr addr1, addr2; + + _randomize_in6_addr(&addr_src, r); + _randomize_in6_addr(&addr_ref, r); + _randomize_in6_addr(&addr1, r); + _randomize_in6_addr(&addr2, r); + + addr1 = addr_src; + ip6_address_clear_host_address_reference(&addr_ref, &addr1, plen); + + _randomize_in6_addr(&addr1, r); + _randomize_in6_addr(&addr2, r); + addr1 = addr_src; + nm_utils_ip6_address_clear_host_address(&addr2, &addr1, plen); + g_assert_cmpint(memcmp(&addr1, &addr_src, sizeof(struct in6_addr)), ==, 0); + g_assert_cmpint(memcmp(&addr2, &addr_ref, sizeof(struct in6_addr)), ==, 0); + + /* test for self assignment/inplace update. */ + _randomize_in6_addr(&addr1, r); + addr1 = addr_src; + nm_utils_ip6_address_clear_host_address(&addr1, &addr1, plen); + g_assert_cmpint(memcmp(&addr1, &addr_ref, sizeof(struct in6_addr)), ==, 0); + } + } + + g_rand_free(r); +} + +/*****************************************************************************/ + +static void +test_logging_domains(void) +{ + const char *s; + + s = nm_logging_all_domains_to_string(); + g_assert(s && s[0]); +} + +/*****************************************************************************/ + +static void +test_logging_error(void) +{ + gs_free_error GError *error = NULL; + gboolean success; + + g_assert_cmpint(NM_MANAGER_ERROR, ==, _NM_MANAGER_ERROR); + G_STATIC_ASSERT_EXPR(NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL == _NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL); + G_STATIC_ASSERT_EXPR(NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN + == _NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN); + + success = nm_logging_setup("bogus", "ALL", NULL, &error); + nmtst_assert_no_success(success, error); + g_assert_cmpint(error->domain, ==, NM_MANAGER_ERROR); + g_assert_cmpint(error->code, ==, NM_MANAGER_ERROR_UNKNOWN_LOG_LEVEL); + nm_clear_pointer(&error, g_error_free); + + success = nm_logging_setup("debug", "bogus", NULL, &error); + nmtst_assert_no_success(success, error); + g_assert_cmpint(error->domain, ==, NM_MANAGER_ERROR); + g_assert_cmpint(error->code, ==, NM_MANAGER_ERROR_UNKNOWN_LOG_DOMAIN); + nm_clear_pointer(&error, g_error_free); +} + +/*****************************************************************************/ + +static void +_test_same_prefix(const char *a1, const char *a2, guint8 plen) +{ + struct in6_addr a = *nmtst_inet6_from_string(a1); + struct in6_addr b = *nmtst_inet6_from_string(a2); + + g_assert(nm_utils_ip6_address_same_prefix(&a, &b, plen)); +} + +static void +test_nm_utils_ip6_address_same_prefix(void) +{ + guint n, i; + const guint N = 100; + union { + guint8 ptr[sizeof(struct in6_addr)]; + struct in6_addr val; + } a, b, addrmask, addrmask_bit; + guint8 plen; + + /* test#1 */ + for (n = 0; n < N; n++) { + gboolean is_same = n < N / 2; + gboolean result; + + nmtst_rand_buf(NULL, a.ptr, sizeof(a)); + nmtst_rand_buf(NULL, b.ptr, sizeof(b)); +again_plen: + plen = nmtst_get_rand_uint32() % 129; + if (!is_same && NM_IN_SET(plen, 0, 128)) + goto again_plen; + + if (plen < 128) { + for (i = 0; (i + 1) * 8 <= plen; i++) + b.ptr[i] = a.ptr[i]; + if (plen % 8) { + guint8 mask; + + g_assert(i < sizeof(a)); + mask = ~((1 << (8 - (plen % 8))) - 1); + b.ptr[i] = (a.ptr[i] & mask) | (b.ptr[i] & ~mask); + if (!is_same) { + mask = (1 << (8 - (plen % 8))); + b.ptr[i] = (b.ptr[i] & ~mask) | ~(b.ptr[i] & mask); + } + } else if (!is_same) { + g_assert(i > 0); + + b.ptr[i - 1] = (b.ptr[i - 1] & ~0x1) | ~(b.ptr[i - 1] & 0x1); + } + } else + b = a; + + result = nm_utils_ip6_address_same_prefix(&a.val, &b.val, plen); + g_assert(result == is_same); + g_assert(NM_IN_SET(result, TRUE, FALSE)); + } + + /* test#2 */ + for (n = 0; n < N; n++) { + nmtst_rand_buf(NULL, a.ptr, sizeof(a)); + nmtst_rand_buf(NULL, b.ptr, sizeof(b)); + plen = nmtst_get_rand_uint32() % 129; + + memset(addrmask.ptr, 0xFF, sizeof(addrmask)); + nm_utils_ip6_address_clear_host_address(&addrmask.val, &addrmask.val, plen); + + for (i = 0; i < sizeof(a); i++) + b.ptr[i] = (a.ptr[i] & addrmask.ptr[i]) | (b.ptr[i] & ~addrmask.ptr[i]); + + g_assert(nm_utils_ip6_address_same_prefix(&a.val, &b.val, plen) == TRUE); + } + + /* test#3 */ + for (n = 0; n < N; n++) { + gboolean reached = FALSE; + + nmtst_rand_buf(NULL, a.ptr, sizeof(a)); + nmtst_rand_buf(NULL, b.ptr, sizeof(b)); + plen = nmtst_get_rand_uint32() % 129; + + if (!plen) + continue; + + memset(addrmask.ptr, 0xFF, sizeof(addrmask)); + nm_utils_ip6_address_clear_host_address(&addrmask.val, &addrmask.val, plen); + + memset(addrmask_bit.ptr, 0xFF, sizeof(addrmask_bit)); + nm_utils_ip6_address_clear_host_address(&addrmask_bit.val, &addrmask_bit.val, plen - 1); + + for (i = 0; i < sizeof(a); i++) + b.ptr[i] = (a.ptr[i] & addrmask.ptr[i]) | (b.ptr[i] & ~addrmask.ptr[i]); + + /* flip the last bit. */ + for (i = 0; i < sizeof(a); i++) { + guint8 mask = addrmask.ptr[i] ^ addrmask_bit.ptr[i]; + if (mask) { + g_assert(!reached); + g_assert(nm_utils_is_power_of_two(mask)); + reached = TRUE; + b.ptr[i] = (b.ptr[i] & ~mask) | ~(b.ptr[i] & mask); + } + } + g_assert(reached); + + g_assert(nm_utils_ip6_address_same_prefix(&a.val, &b.val, plen) == FALSE); + } + + /* test#4 */ + _test_same_prefix("::", "::1", 10); + _test_same_prefix("abcd::", "abcd::1", 10); +} + +/*****************************************************************************/ + +static void +test_nm_utils_log_connection_diff(void) +{ + NMConnection *connection; + NMConnection *connection2; + + /* if logging is disabled (the default), nm_utils_log_connection_diff() returns + * early without doing anything. Hence, in the normal testing, this test does nothing. + * It only gets interesting, when run verbosely with NMTST_DEBUG=debug ... */ + + nm_log(LOGL_DEBUG, LOGD_CORE, NULL, NULL, "START TEST test_nm_utils_log_connection_diff..."); + + connection = nm_simple_connection_new(); + nm_connection_add_setting(connection, nm_setting_connection_new()); + nm_utils_log_connection_diff(connection, NULL, LOGL_DEBUG, LOGD_CORE, "test1", ">>> ", NULL); + + nm_connection_add_setting(connection, nm_setting_wired_new()); + nm_utils_log_connection_diff(connection, NULL, LOGL_DEBUG, LOGD_CORE, "test2", ">>> ", NULL); + + connection2 = nm_simple_connection_new_clone(connection); + nm_utils_log_connection_diff(connection, + connection2, + LOGL_DEBUG, + LOGD_CORE, + "test3", + ">>> ", + NULL); + + g_object_set(nm_connection_get_setting_connection(connection), + NM_SETTING_CONNECTION_ID, + "id", + NM_SETTING_CONNECTION_UUID, + "uuid", + NULL); + g_object_set(nm_connection_get_setting_connection(connection2), + NM_SETTING_CONNECTION_ID, + "id2", + NM_SETTING_CONNECTION_MASTER, + "master2", + NULL); + nm_utils_log_connection_diff(connection, + connection2, + LOGL_DEBUG, + LOGD_CORE, + "test4", + ">>> ", + NULL); + + nm_connection_add_setting(connection, nm_setting_802_1x_new()); + nm_utils_log_connection_diff(connection, + connection2, + LOGL_DEBUG, + LOGD_CORE, + "test5", + ">>> ", + NULL); + + g_object_set(nm_connection_get_setting_802_1x(connection), + NM_SETTING_802_1X_PASSWORD, + "id2", + NM_SETTING_802_1X_PASSWORD_FLAGS, + NM_SETTING_SECRET_FLAG_NOT_SAVED, + NULL); + nm_utils_log_connection_diff(connection, NULL, LOGL_DEBUG, LOGD_CORE, "test6", ">>> ", NULL); + nm_utils_log_connection_diff(connection, + connection2, + LOGL_DEBUG, + LOGD_CORE, + "test7", + ">>> ", + NULL); + nm_utils_log_connection_diff(connection2, + connection, + LOGL_DEBUG, + LOGD_CORE, + "test8", + ">>> ", + NULL); + + g_clear_object(&connection); + g_clear_object(&connection2); + + connection = + nmtst_create_minimal_connection("id-vpn-1", NULL, NM_SETTING_VPN_SETTING_NAME, NULL); + nm_utils_log_connection_diff(connection, + NULL, + LOGL_DEBUG, + LOGD_CORE, + "test-vpn-1", + ">>> ", + NULL); + + g_clear_object(&connection); +} + +/*****************************************************************************/ + +static void +do_test_sysctl_ip_conf(int addr_family, const char *iface, const char *property) +{ + char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + const char *pp; + + pp = nm_utils_sysctl_ip_conf_path(addr_family, path, iface, property); + g_assert(pp == path); + g_assert(path[0] == '/'); + + g_assert(nm_utils_sysctl_ip_conf_is_path(addr_family, path, iface, property)); + g_assert(nm_utils_sysctl_ip_conf_is_path(addr_family, path, NULL, property)); +} + +static void +test_nm_utils_sysctl_ip_conf_path(void) +{ + do_test_sysctl_ip_conf(AF_INET6, "a", "mtu"); + do_test_sysctl_ip_conf(AF_INET6, "eth0", "mtu"); + do_test_sysctl_ip_conf(AF_INET6, "e23456789012345", "mtu"); +} + +/*****************************************************************************/ + +static NMConnection * +_match_connection_new(void) +{ + NMConnection * connection; + NMSettingConnection *s_con; + NMSettingWired * s_wired; + NMSettingIPConfig * s_ip4, *s_ip6; + char * uuid; + + connection = nm_simple_connection_new(); + + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(connection, (NMSetting *) s_con); + uuid = nm_utils_uuid_generate(); + g_object_set(G_OBJECT(s_con), + NM_SETTING_CONNECTION_ID, + "blahblah", + NM_SETTING_CONNECTION_UUID, + uuid, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_AUTOCONNECT, + FALSE, + NULL); + g_free(uuid); + + s_wired = (NMSettingWired *) nm_setting_wired_new(); + nm_connection_add_setting(connection, (NMSetting *) s_wired); + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new(); + nm_connection_add_setting(connection, (NMSetting *) s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new(); + nm_connection_add_setting(connection, (NMSetting *) s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + + return connection; +} + +static NMConnection * +_match_connection(GSList * connections, + NMConnection *original, + gboolean device_has_carrier, + gint64 default_v4_metric, + gint64 default_v6_metric) +{ + gs_free NMConnection **list = NULL; + guint i, len; + + len = g_slist_length(connections); + g_assert(len < 10); + + list = g_malloc((len + 1) * sizeof(NMConnection *)); + for (i = 0; i < len; i++, connections = connections->next) { + g_assert(connections); + g_assert(connections->data); + list[i] = connections->data; + } + list[i] = NULL; + + return nm_utils_match_connection(list, + original, + FALSE, + device_has_carrier, + default_v4_metric, + default_v6_metric, + NULL, + NULL); +} + +static void +test_connection_match_basic(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingIPConfig *s_ip4; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == copy); + + /* Now change a material property like IPv4 method and ensure matching fails */ + s_ip4 = nm_connection_get_setting_ip4_config(orig); + g_assert(s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, + NULL); + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == NULL); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_ip6_method(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingIPConfig *s_ip6; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that if the generated connection is IPv6 method=link-local, and the + * candidate is both method=auto and may-faily=true, that the candidate is + * matched. + */ + s_ip6 = nm_connection_get_setting_ip6_config(orig); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NULL); + + s_ip6 = nm_connection_get_setting_ip6_config(copy); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_ip6_method_ignore(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingIPConfig *s_ip6; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that if the generated connection is IPv6 method=link-local, and the + * candidate is method=ignore, that the candidate is matched. + */ + s_ip6 = nm_connection_get_setting_ip6_config(orig); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NULL); + + s_ip6 = nm_connection_get_setting_ip6_config(copy); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_ip6_method_ignore_auto(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingIPConfig *s_ip6; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that if the generated connection is IPv6 method=auto, and the + * candidate is method=ignore, that the candidate is matched. + */ + s_ip6 = nm_connection_get_setting_ip6_config(orig); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NULL); + + s_ip6 = nm_connection_get_setting_ip6_config(copy); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_ip4_method(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingIPConfig *s_ip4; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that if the generated connection is IPv4 method=disabled, and the + * candidate is both method=auto and may-faily=true, and the device has no + * carrier that the candidate is matched. + */ + s_ip4 = nm_connection_get_setting_ip4_config(orig); + g_assert(s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL); + + s_ip4 = nm_connection_get_setting_ip4_config(copy); + g_assert(s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP_CONFIG_MAY_FAIL, + TRUE, + NULL); + + matched = _match_connection(connections, orig, FALSE, 0, 0); + g_assert(matched == copy); + + /* Ensure when carrier=true matching fails */ + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == NULL); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_interface_name(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingConnection *s_con; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that if the generated connection has an interface name and the + * candidate's interface name is NULL, that the candidate is matched. + */ + s_con = nm_connection_get_setting_connection(orig); + g_assert(s_con); + g_object_set(G_OBJECT(s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, "em1", NULL); + + s_con = nm_connection_get_setting_connection(copy); + g_assert(s_con); + g_object_set(G_OBJECT(s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, NULL, NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_wired(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingWired *s_wired; + char * subchan_arr[] = {"0.0.8000", "0.0.8001", "0.0.8002", NULL}; + const char * mac = "52:54:00:ab:db:23"; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + s_wired = nm_connection_get_setting_wired(orig); + g_assert(s_wired); + g_object_set(G_OBJECT(s_wired), + NM_SETTING_WIRED_PORT, + "tp", /* port is not compared */ + NM_SETTING_WIRED_MAC_ADDRESS, + mac, /* we allow MAC address just in one connection */ + NM_SETTING_WIRED_S390_SUBCHANNELS, + subchan_arr, + NM_SETTING_WIRED_S390_NETTYPE, + "qeth", + NULL); + + s_wired = nm_connection_get_setting_wired(copy); + g_assert(s_wired); + g_object_set(G_OBJECT(s_wired), + NM_SETTING_WIRED_S390_SUBCHANNELS, + subchan_arr, + NM_SETTING_WIRED_S390_NETTYPE, + "qeth", + NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_wired2(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingWired *s_wired; + const char * mac = "52:54:00:ab:db:23"; + + orig = _match_connection_new(); + s_wired = nm_connection_get_setting_wired(orig); + g_assert(s_wired); + g_object_set(G_OBJECT(s_wired), + NM_SETTING_WIRED_PORT, + "tp", /* port is not compared */ + NM_SETTING_WIRED_MAC_ADDRESS, + mac, /* we allow MAC address just in one connection */ + NULL); + + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that if the generated connection do not have wired setting + * and s390 properties in the existing connection's setting are default, + * the connections match. It can happen if assuming VLAN devices. */ + nm_connection_remove_setting(orig, NM_TYPE_SETTING_WIRED); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_cloned_mac(void) +{ + NMConnection * orig, *exact, *fuzzy, *matched; + GSList * connections = NULL; + NMSettingWired *s_wired; + + orig = _match_connection_new(); + + fuzzy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, fuzzy); + s_wired = nm_connection_get_setting_wired(orig); + g_assert(s_wired); + g_object_set(G_OBJECT(s_wired), NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "52:54:00:ab:db:23", NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == fuzzy); + + exact = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, exact); + s_wired = nm_connection_get_setting_wired(exact); + g_assert(s_wired); + g_object_set(G_OBJECT(s_wired), NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "52:54:00:ab:db:23", NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == exact); + + g_object_set(G_OBJECT(s_wired), NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "52:54:00:ab:db:24", NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched == fuzzy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(fuzzy); + g_object_unref(exact); +} + +static void +test_connection_no_match_ip4_addr(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingIPConfig *s_ip4, *s_ip6; + NMIPAddress * nm_addr; + GError * error = NULL; + + orig = _match_connection_new(); + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that if we have two differences, ipv6.method (exception we allow) and + * ipv4.addresses (which is fatal), we don't match the connections. + */ + s_ip6 = nm_connection_get_setting_ip6_config(orig); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, + NULL); + + s_ip6 = nm_connection_get_setting_ip6_config(copy); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + s_ip4 = nm_connection_get_setting_ip4_config(orig); + g_assert(s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "1.1.1.254", + NULL); + nm_addr = nm_ip_address_new(AF_INET, "1.1.1.4", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, nm_addr); + nm_ip_address_unref(nm_addr); + + s_ip4 = nm_connection_get_setting_ip4_config(copy); + g_assert(s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, + "2.2.2.254", + NULL); + nm_addr = nm_ip_address_new(AF_INET, "2.2.2.4", 24, &error); + g_assert_no_error(error); + nm_setting_ip_config_add_address(s_ip4, nm_addr); + nm_ip_address_unref(nm_addr); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched != copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_no_match_vlan(void) +{ + NMConnection * orig, *copy, *matched; + GSList * connections = NULL; + NMSettingConnection *s_con; + NMSettingVlan * s_vlan_orig, *s_vlan_copy; + char * uuid; + + orig = nm_simple_connection_new(); + s_con = (NMSettingConnection *) nm_setting_connection_new(); + nm_connection_add_setting(orig, (NMSetting *) s_con); + uuid = nm_utils_uuid_generate(); + g_object_set(G_OBJECT(s_con), + NM_SETTING_CONNECTION_ID, + "vlan-test", + NM_SETTING_CONNECTION_UUID, + uuid, + NM_SETTING_CONNECTION_TYPE, + NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_CONNECTION_AUTOCONNECT, + FALSE, + NULL); + g_free(uuid); + nm_connection_add_setting(orig, nm_setting_vlan_new()); + + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Check that the connections do not match if VLAN flags differ */ + s_vlan_orig = nm_connection_get_setting_vlan(orig); + g_assert(s_vlan_orig); + g_object_set(G_OBJECT(s_vlan_orig), NM_SETTING_VLAN_FLAGS, NM_VLAN_FLAG_REORDER_HEADERS, NULL); + + s_vlan_copy = nm_connection_get_setting_vlan(copy); + g_assert(s_vlan_copy); + g_object_set(G_OBJECT(s_vlan_copy), NM_SETTING_VLAN_FLAGS, 0, NULL); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched != copy); + + /* Check that the connections do not match if VLAN priorities differ */ + g_object_set(G_OBJECT(s_vlan_orig), NM_SETTING_VLAN_FLAGS, 0, NULL); + nm_setting_vlan_add_priority_str(s_vlan_orig, NM_VLAN_INGRESS_MAP, "1:3"); + + g_object_set(G_OBJECT(s_vlan_copy), NM_SETTING_VLAN_FLAGS, 0, NULL); + nm_setting_vlan_add_priority_str(s_vlan_copy, NM_VLAN_INGRESS_MAP, "4:2"); + + matched = _match_connection(connections, orig, TRUE, 0, 0); + g_assert(matched != copy); + + g_slist_free(connections); + g_object_unref(orig); + g_object_unref(copy); +} + +static void +test_connection_match_ip4_routes1(void) +{ + gs_unref_object NMConnection *orig = NULL, *copy = NULL; + NMConnection * matched; + nm_auto_free_slist GSList *connections = NULL; + NMSettingIPConfig * s_ip4; + + orig = _match_connection_new(); + + s_ip4 = nm_connection_get_setting_ip4_config(orig); + g_assert(s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + + nmtst_setting_ip_config_add_address(s_ip4, "10.0.0.1", 8); + + /* Clone connection */ + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Set routes on original connection */ + nmtst_setting_ip_config_add_route(s_ip4, "172.25.16.0", 24, "10.0.0.2", -1); + nmtst_setting_ip_config_add_route(s_ip4, "172.25.17.0", 24, "10.0.0.3", 20); + + /* Set single route on cloned connection */ + s_ip4 = nm_connection_get_setting_ip4_config(copy); + g_assert(s_ip4); + nmtst_setting_ip_config_add_route(s_ip4, "172.25.17.0", 24, "10.0.0.3", 20); + + /* Try to match the connections */ + matched = _match_connection(connections, orig, FALSE, 100, 0); + g_assert(matched == NULL); +} + +static void +test_connection_match_ip4_routes2(void) +{ + gs_unref_object NMConnection *orig = NULL, *copy = NULL; + NMConnection * matched; + nm_auto_free_slist GSList *connections = NULL; + NMSettingIPConfig * s_ip4; + + orig = _match_connection_new(); + + s_ip4 = nm_connection_get_setting_ip4_config(orig); + g_assert(s_ip4); + g_object_set(G_OBJECT(s_ip4), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NULL); + + nmtst_setting_ip_config_add_address(s_ip4, "10.0.0.1", 8); + + /* Clone connection */ + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Set routes on original connection */ + nmtst_setting_ip_config_add_route(s_ip4, "172.25.16.0", 24, "10.0.0.2", -1); + nmtst_setting_ip_config_add_route(s_ip4, "172.25.17.0", 24, "10.0.0.3", 20); + + /* Set routes on cloned connection, changing order and using explicit metrics */ + s_ip4 = nm_connection_get_setting_ip4_config(copy); + g_assert(s_ip4); + nmtst_setting_ip_config_add_route(s_ip4, "172.25.17.0", 24, "10.0.0.3", 20); + nmtst_setting_ip_config_add_route(s_ip4, "172.25.16.0", 24, "10.0.0.2", 100); + + /* Try to match the connections using different default metrics */ + matched = _match_connection(connections, orig, FALSE, 100, 0); + g_assert(matched == copy); + matched = _match_connection(connections, orig, FALSE, 500, 0); + g_assert(matched == NULL); +} + +static void +test_connection_match_ip6_routes(void) +{ + gs_unref_object NMConnection *orig = NULL, *copy = NULL; + NMConnection * matched; + nm_auto_free_slist GSList *connections = NULL; + NMSettingIPConfig * s_ip6; + + orig = _match_connection_new(); + + s_ip6 = nm_connection_get_setting_ip6_config(orig); + g_assert(s_ip6); + g_object_set(G_OBJECT(s_ip6), + NM_SETTING_IP_CONFIG_METHOD, + NM_SETTING_IP6_CONFIG_METHOD_MANUAL, + NULL); + + nmtst_setting_ip_config_add_address(s_ip6, "fd01::15", 64); + + /* Clone connection */ + copy = nm_simple_connection_new_clone(orig); + connections = g_slist_append(connections, copy); + + /* Set routes on original connection */ + nmtst_setting_ip_config_add_route(s_ip6, "2001:db8:a:b:0:0:0:0", 64, "fd01::16", -1); + + /* Set routes on cloned connection */ + s_ip6 = nm_connection_get_setting_ip6_config(copy); + g_assert(s_ip6); + nmtst_setting_ip_config_add_route(s_ip6, "2001:db8:a:b:0:0:0:0", 64, "fd01::16", 50); + + /* Try to match the connections */ + matched = _match_connection(connections, orig, FALSE, 0, 100); + g_assert(matched == NULL); + matched = _match_connection(connections, orig, FALSE, 0, 50); + g_assert(matched == copy); +} + +#define do_test_wildcard_match_eval(str, ...) \ + nm_wildcard_match_check(str, (const char *const[]){__VA_ARGS__}, NM_NARG(__VA_ARGS__)) + +#define do_test_wildcard_match(str, result, ...) \ + g_assert(do_test_wildcard_match_eval(str, __VA_ARGS__) == result) + +static void +test_wildcard_match(void) +{ + do_test_wildcard_match("foobar", TRUE); + + do_test_wildcard_match("foo", TRUE, "foo", "bar", "baz"); + do_test_wildcard_match("bar", TRUE, "foo", "bar", "baz"); + do_test_wildcard_match("baz", TRUE, "foo", "bar", "baz"); + do_test_wildcard_match("aaa", FALSE, "foo", "bar", "baz"); + do_test_wildcard_match("", FALSE, "foo", "bar", "baz"); + + do_test_wildcard_match("ens1", TRUE, "ens1*"); + do_test_wildcard_match("ens10", TRUE, "ens1*"); + do_test_wildcard_match("ens11", TRUE, "ens1*"); + do_test_wildcard_match("ens12", TRUE, "ens1*"); + do_test_wildcard_match("eth0", FALSE, "ens1*"); + do_test_wildcard_match("ens", FALSE, "ens1*"); + + do_test_wildcard_match("ens1*", TRUE, "ens1\\*"); + do_test_wildcard_match("ens1", FALSE, "ens1\\*"); + do_test_wildcard_match("ens10", FALSE, "ens1\\*"); + + do_test_wildcard_match("abcd", TRUE, "ab??"); + do_test_wildcard_match("ab", FALSE, "ab??"); + + do_test_wildcard_match("ab??", TRUE, "ab\\?\\?"); + do_test_wildcard_match("abcd", FALSE, "ab\\?\\?"); + + do_test_wildcard_match("ens10", TRUE, "ens1*", "!ens11"); + do_test_wildcard_match("ens11", FALSE, "ens1*", "!ens11"); + do_test_wildcard_match("ens12", TRUE, "ens1*", "!ens11"); + + do_test_wildcard_match("a", FALSE, "!a", "!b"); + do_test_wildcard_match("b", FALSE, "!a", "!b"); + do_test_wildcard_match("c", TRUE, "!a", "!b"); + do_test_wildcard_match("!a", TRUE, "!a", "!b"); + + do_test_wildcard_match("!net", TRUE, "\\!net"); + do_test_wildcard_match("net", FALSE, "\\!net"); + do_test_wildcard_match("ens10", FALSE, "\\!net"); + do_test_wildcard_match("\\!net", FALSE, "\\!net"); + + do_test_wildcard_match("eth0", FALSE, "*eth?", "!veth*", "!*0"); + do_test_wildcard_match("eth1", TRUE, "*eth?", "!veth*", "!*0"); + do_test_wildcard_match("myeth0", FALSE, "*eth?", "!veth*", "!*0"); + do_test_wildcard_match("myeth2", TRUE, "*eth?", "!veth*", "!*0"); + do_test_wildcard_match("veth0", FALSE, "*eth?", "!veth*", "!*0"); + do_test_wildcard_match("veth1", FALSE, "*eth?", "!veth*", "!*0"); + do_test_wildcard_match("dummy1", FALSE, "*eth?", "!veth*", "!*0"); + + do_test_wildcard_match("a", TRUE, "!!a"); + do_test_wildcard_match("b", TRUE, "!!a"); + do_test_wildcard_match("!a", FALSE, "!!a"); + + do_test_wildcard_match("\\", TRUE, "\\\\\\"); + do_test_wildcard_match("\\\\", FALSE, "\\\\"); + do_test_wildcard_match("", FALSE, "\\\\"); + + do_test_wildcard_match("\\a", TRUE, "\\\\\\a"); + do_test_wildcard_match("b", TRUE, "&!a"); + do_test_wildcard_match("a", FALSE, "&!a"); + do_test_wildcard_match("!a", TRUE, "&\\!a"); + do_test_wildcard_match("!a", TRUE, "|\\!a"); + do_test_wildcard_match("!a", TRUE, "\\!a"); + + do_test_wildcard_match("name", FALSE, "name[123]"); + do_test_wildcard_match("name1", TRUE, "name[123]"); + do_test_wildcard_match("name2", TRUE, "name[123]"); + do_test_wildcard_match("name3", TRUE, "name[123]"); + do_test_wildcard_match("name4", FALSE, "name[123]"); + + if (do_test_wildcard_match_eval("[a]", "\\[a\\]") != TRUE) { +#if defined(__GLIBC__) + do_test_wildcard_match("[a]", TRUE, "\\[a\\]"); + g_assert_not_reached(); +#endif + /* It seems musl's fnmatch() does not like such ranges. */ + g_test_skip("libc does not support ranges with fnmatch()!!"); + } + + do_test_wildcard_match("aa", FALSE, "!a*"); + do_test_wildcard_match("aa", FALSE, "&!a*"); + do_test_wildcard_match("aa", FALSE, "|!a*"); + do_test_wildcard_match("aa", FALSE, "&!a*", "aa"); + do_test_wildcard_match("aa", TRUE, "|!a*", "aa"); +} + +static NMConnection * +_create_connection_autoconnect(const char *id, gboolean autoconnect, int autoconnect_priority) +{ + NMConnection * c; + NMSettingConnection *s_con; + + c = nmtst_create_minimal_connection(id, NULL, NM_SETTING_WIRED_SETTING_NAME, &s_con); + g_object_set(s_con, + NM_SETTING_CONNECTION_AUTOCONNECT, + autoconnect, + NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, + autoconnect_priority, + NULL); + nmtst_connection_normalize(c); + return c; +} + +static int +_cmp_autoconnect_priority_p_with_data(gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + return nm_utils_cmp_connection_by_autoconnect_priority(*((NMConnection **) pa), + *((NMConnection **) pb)); +} + +static void +_test_connection_sort_autoconnect_priority_one(NMConnection **list, gboolean shuffle) +{ + int i, j; + int count = 0; + gs_unref_ptrarray GPtrArray *connections = g_ptr_array_new(); + + while (list[count]) + count++; + g_assert(count > 1); + + /* copy the list of connections over to @connections and shuffle. */ + for (i = 0; i < count; i++) + g_ptr_array_add(connections, list[i]); + if (shuffle) { + for (i = count - 1; i > 0; i--) { + j = g_rand_int(nmtst_get_rand()) % (i + 1); + NM_SWAP(&connections->pdata[i], &connections->pdata[j]); + } + } + + /* sort it... */ + g_ptr_array_sort_with_data(connections, _cmp_autoconnect_priority_p_with_data, NULL); + + for (i = 0; i < count; i++) { + if (list[i] == connections->pdata[i]) + continue; + if (shuffle + && nm_utils_cmp_connection_by_autoconnect_priority(list[i], connections->pdata[i]) == 0) + continue; + g_message( + "After sorting, the order of connections is not as expected!! Offending index: %d", + i); + for (j = 0; j < count; j++) + g_message(" %3d: %p/%-20s - %p/%-20s", + j, + list[j], + nm_connection_get_id(list[j]), + connections->pdata[j], + nm_connection_get_id(connections->pdata[j])); + g_assert_not_reached(); + } +} + +static void +_test_connection_sort_autoconnect_priority_free(NMConnection **list) +{ + while (*list) { + g_object_unref(*list); + *list = NULL; + } +} + +static void +test_connection_sort_autoconnect_priority(void) +{ + NMConnection *c1[] = { + _create_connection_autoconnect("AC/100", TRUE, 100), + _create_connection_autoconnect("AC/100", TRUE, 100), + _create_connection_autoconnect("AC/99", TRUE, 99), + _create_connection_autoconnect("AC/0", TRUE, 0), + _create_connection_autoconnect("AC/0", TRUE, 0), + _create_connection_autoconnect("AC/-1", TRUE, -1), + _create_connection_autoconnect("AC/-3", TRUE, -3), + _create_connection_autoconnect("ac/0", FALSE, 0), + _create_connection_autoconnect("ac/0", FALSE, 0), + _create_connection_autoconnect("ac/1", FALSE, 1), + _create_connection_autoconnect("ac/-1", FALSE, -1), + _create_connection_autoconnect("ac/1", FALSE, 1), + _create_connection_autoconnect("ac/0", FALSE, 0), + NULL, + }; + NMConnection *c2[] = { + _create_connection_autoconnect("AC/100", TRUE, 100), + _create_connection_autoconnect("AC/99", TRUE, 99), + _create_connection_autoconnect("AC/0", TRUE, 0), + _create_connection_autoconnect("AC/-1", TRUE, -1), + _create_connection_autoconnect("AC/-3", TRUE, -3), + _create_connection_autoconnect("ac/0", FALSE, 0), + NULL, + }; + + _test_connection_sort_autoconnect_priority_one(c1, FALSE); + _test_connection_sort_autoconnect_priority_one(c2, FALSE); + _test_connection_sort_autoconnect_priority_one(c1, TRUE); + _test_connection_sort_autoconnect_priority_one(c2, TRUE); + + _test_connection_sort_autoconnect_priority_free(c1); + _test_connection_sort_autoconnect_priority_free(c2); +} + +/*****************************************************************************/ + +#define MATCH_S390 "S390:" +#define MATCH_DRIVER "DRIVER:" + +static NMMatchSpecMatchType +_test_match_spec_device(const GSList *specs, const char *match_str) +{ + if (match_str && g_str_has_prefix(match_str, MATCH_S390)) + return nm_match_spec_device(specs, + NULL, + NULL, + NULL, + NULL, + NULL, + &match_str[NM_STRLEN(MATCH_S390)], + NULL); + if (match_str && g_str_has_prefix(match_str, MATCH_DRIVER)) { + gs_free char *s = g_strdup(&match_str[NM_STRLEN(MATCH_DRIVER)]); + char * t; + + t = strchr(s, '|'); + if (t) { + t[0] = '\0'; + t++; + } + return nm_match_spec_device(specs, NULL, NULL, s, t, NULL, NULL, NULL); + } + return nm_match_spec_device(specs, match_str, NULL, NULL, NULL, NULL, NULL, NULL); +} + +static void +_do_test_match_spec_device(const char * spec_str, + const char *const *matches, + const char *const *no_matches, + const char *const *neg_matches) +{ + GSList * specs, *specs_randperm = NULL, *specs_resplit, *specs_i, *specs_j; + guint i; + gs_free char * specs_joined = NULL; + const char * s; + static const char *no_matches_default[] = {"e", + "em", + "em*", + "em\\", + "em\\*", + "em\\1", + "em\\11", + "em\\2", + "em1", + "em11", + "em2", + "=em*", + NULL}; + + g_assert(spec_str); + + specs = nm_match_spec_split(spec_str); + + /* assert that split(join(specs)) == specs */ + specs_joined = nm_match_spec_join(specs); + specs_resplit = nm_match_spec_split(specs_joined); + specs_i = specs; + specs_j = specs_resplit; + while (specs_i && specs_j && g_strcmp0(specs_i->data, specs_j->data) == 0) { + specs_i = specs_i->next; + specs_j = specs_j->next; + } + g_assert(!specs_i); + g_assert(!specs_j); + g_slist_free_full(specs_resplit, g_free); + + /* also check the matches in the random order. They must yield the same result because + * matches are inclusive -- except "except:" which always wins. */ + specs_randperm = nmtst_rand_perm_gslist(NULL, g_slist_copy(specs)); + + for (i = 0; matches && matches[i]; i++) { + g_assert(_test_match_spec_device(specs, matches[i]) == NM_MATCH_SPEC_MATCH); + g_assert(_test_match_spec_device(specs_randperm, matches[i]) == NM_MATCH_SPEC_MATCH); + } + for (i = 0; neg_matches && neg_matches[i]; i++) { + g_assert(_test_match_spec_device(specs, neg_matches[i]) == NM_MATCH_SPEC_NEG_MATCH); + g_assert(_test_match_spec_device(specs_randperm, neg_matches[i]) + == NM_MATCH_SPEC_NEG_MATCH); + } + for (i = 0; no_matches && no_matches[i]; i++) { + g_assert(_test_match_spec_device(specs, no_matches[i]) == NM_MATCH_SPEC_NO_MATCH); + g_assert(_test_match_spec_device(specs_randperm, no_matches[i]) == NM_MATCH_SPEC_NO_MATCH); + } + if (!no_matches) { + for (i = 0; (s = no_matches_default[i]); i++) { + if ((matches && g_strv_contains(matches, s)) + || (neg_matches && g_strv_contains(neg_matches, s))) + continue; + g_assert(_test_match_spec_device(specs, s) == NM_MATCH_SPEC_NO_MATCH); + g_assert(_test_match_spec_device(specs_randperm, s) == NM_MATCH_SPEC_NO_MATCH); + } + } + + g_slist_free(specs_randperm); + g_slist_free_full(specs, g_free); +} + +static void +test_match_spec_device(void) +{ + _do_test_match_spec_device("em1", NM_MAKE_STRV("em1"), NULL, NULL); + _do_test_match_spec_device("em1,em2", NM_MAKE_STRV("em1", "em2"), NULL, NULL); + _do_test_match_spec_device("em1,em2,interface-name:em2", + NM_MAKE_STRV("em1", "em2"), + NULL, + NULL); + _do_test_match_spec_device("interface-name:em1", NM_MAKE_STRV("em1"), NULL, NULL); + _do_test_match_spec_device("interface-name:em*", + NM_MAKE_STRV("em", + "em*", + "em\\", + "em\\*", + "em\\1", + "em\\11", + "em\\2", + "em1", + "em11", + "em2", + "em3"), + NULL, + NULL); + _do_test_match_spec_device("interface-name:em\\*", + NM_MAKE_STRV("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), + NULL, + NULL); + _do_test_match_spec_device("interface-name:~em\\*", + NM_MAKE_STRV("em\\", "em\\*", "em\\1", "em\\11", "em\\2"), + NULL, + NULL); + _do_test_match_spec_device("except:*", NULL, NM_MAKE_STRV(NULL), NM_MAKE_STRV("a")); + _do_test_match_spec_device("interface-name:=em*", NM_MAKE_STRV("em*"), NULL, NULL); + _do_test_match_spec_device( + "interface-name:em*,except:interface-name:em1*", + NM_MAKE_STRV("em", "em*", "em\\", "em\\*", "em\\1", "em\\11", "em\\2", "em2", "em3"), + NULL, + NM_MAKE_STRV("em1", "em11")); + _do_test_match_spec_device("interface-name:em*,except:interface-name:=em*", + NM_MAKE_STRV("em", + "em\\", + "em\\*", + "em\\1", + "em\\11", + "em\\2", + "em1", + "em11", + "em2", + "em3"), + NULL, + NM_MAKE_STRV("em*")); + _do_test_match_spec_device("except:interface-name:em*", + NM_MAKE_STRV("", "eth", "eth1", "e1"), + NM_MAKE_STRV(NULL), + NM_MAKE_STRV("em", + "em\\", + "em\\*", + "em\\1", + "em\\11", + "em\\2", + "em1", + "em11", + "em2", + "em3")); + _do_test_match_spec_device("aa,bb,cc\\,dd,e,,", + NM_MAKE_STRV("aa", "bb", "cc,dd", "e"), + NULL, + NULL); + _do_test_match_spec_device("aa;bb;cc\\;dd;e,;", + NM_MAKE_STRV("aa", "bb", "cc;dd", "e"), + NULL, + NULL); + _do_test_match_spec_device("interface-name:em\\;1,em\\,2,\\,,\\\\,,em\\\\x", + NM_MAKE_STRV("em;1", "em,2", ",", "\\", "em\\x"), + NULL, + NULL); + _do_test_match_spec_device("\\s\\s,\\sinterface-name:a,\\s,", + NM_MAKE_STRV(" ", " ", " interface-name:a"), + NULL, + NULL); + _do_test_match_spec_device(" aa ; bb ; cc\\;dd ;e , ; \t\\t , ", + NM_MAKE_STRV("aa", "bb", "cc;dd", "e", "\t"), + NULL, + NULL); + + _do_test_match_spec_device("s390-subchannels:0.0.1000\\,0.0.1001", + NM_MAKE_STRV(MATCH_S390 "0.0.1000", + MATCH_S390 "0.0.1000,deadbeef", + MATCH_S390 "0.0.1000,0.0.1001", + MATCH_S390 "0.0.1000,0.0.1002"), + NM_MAKE_STRV(MATCH_S390 "0.0.1001"), + NULL); + _do_test_match_spec_device("*,except:s390-subchannels:0.0.1000\\,0.0.1001", + NULL, + NM_MAKE_STRV(NULL), + NM_MAKE_STRV(MATCH_S390 "0.0.1000", + MATCH_S390 "0.0.1000,deadbeef", + MATCH_S390 "0.0.1000,0.0.1001", + MATCH_S390 "0.0.1000,0.0.1002")); + + _do_test_match_spec_device("driver:DRV", + NM_MAKE_STRV(MATCH_DRIVER "DRV", MATCH_DRIVER "DRV|1.6"), + NM_MAKE_STRV(MATCH_DRIVER "DR", MATCH_DRIVER "DR*"), + NULL); + _do_test_match_spec_device( + "driver:DRV//", + NM_MAKE_STRV(MATCH_DRIVER "DRV/"), + NM_MAKE_STRV(MATCH_DRIVER "DRV/|1.6", MATCH_DRIVER "DR", MATCH_DRIVER "DR*"), + NULL); + _do_test_match_spec_device("driver:DRV//*", + NM_MAKE_STRV(MATCH_DRIVER "DRV/", MATCH_DRIVER "DRV/|1.6"), + NM_MAKE_STRV(MATCH_DRIVER "DR", MATCH_DRIVER "DR*"), + NULL); + _do_test_match_spec_device("driver:DRV//1.5*", + NM_MAKE_STRV(MATCH_DRIVER "DRV/|1.5", MATCH_DRIVER "DRV/|1.5.2"), + NM_MAKE_STRV(MATCH_DRIVER "DRV/", + MATCH_DRIVER "DRV/|1.6", + MATCH_DRIVER "DR", + MATCH_DRIVER "DR*"), + NULL); +} + +/*****************************************************************************/ + +static void +_do_test_match_spec_config(const char * file, + int line, + const char * spec_str, + guint version, + guint v_maj, + guint v_min, + guint v_mic, + NMMatchSpecMatchType expected) +{ + GSList * specs; + NMMatchSpecMatchType match_result; + guint c_maj, c_min, c_mic; + + g_assert_cmpint(version, ==, nm_encode_version(v_maj, v_min, v_mic)); + + nm_decode_version(version, &c_maj, &c_min, &c_mic); + g_assert_cmpint(c_maj, ==, c_maj); + g_assert_cmpint(c_min, ==, c_min); + g_assert_cmpint(c_mic, ==, c_mic); + + specs = nm_match_spec_split(spec_str); + + match_result = nm_match_spec_config(specs, version, NULL); + + if (expected != match_result) + g_error("%s:%d: failed comparing \"%s\" with %u.%u.%u. Expected %d, but got %d", + file, + line, + spec_str, + v_maj, + v_min, + v_mic, + (int) expected, + (int) match_result); + + if (g_slist_length(specs) == 1 && !g_str_has_prefix(specs->data, "except:")) { + /* there is only one spec in the list... test that we match except: */ + char * sss = g_strdup_printf("except:%s", (char *) specs->data); + GSList * specs2 = g_slist_append(NULL, sss); + NMMatchSpecMatchType match_result2; + + match_result2 = nm_match_spec_config(specs2, version, NULL); + if (match_result == NM_MATCH_SPEC_NO_MATCH) + g_assert_cmpint(match_result2, ==, NM_MATCH_SPEC_MATCH); + else + g_assert_cmpint(match_result2, ==, NM_MATCH_SPEC_NEG_MATCH); + + g_slist_free_full(specs2, g_free); + } + + g_slist_free_full(specs, g_free); +} +#define do_test_match_spec_config(spec, v_maj, v_min, v_mic, expected) \ + _do_test_match_spec_config(__FILE__, \ + __LINE__, \ + ("" spec), \ + NM_ENCODE_VERSION((v_maj), (v_min), (v_mic)), \ + (v_maj), \ + (v_min), \ + (v_mic), \ + (expected)) + +static void +test_match_spec_config(void) +{ + do_test_match_spec_config("", 1, 2, 3, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version:1.2.3", 1, 2, 2, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version:1.2.3", 1, 2, 4, NM_MATCH_SPEC_NO_MATCH); + + do_test_match_spec_config("nm-version:1.2", 1, 1, 2, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version:1.2", 1, 2, 2, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version:1.2", 1, 2, 4, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version:1.2", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH); + + do_test_match_spec_config("nm-version-min:1.2.3", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1.2.3", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1.2.3", 1, 2, 2, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.2.3", 1, 2, 5, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.2.3", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1.2.3", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1.2.3", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH); + + do_test_match_spec_config("nm-version-min:1.2", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1.2", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.2", 1, 2, 5, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.2", 1, 3, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.2", 1, 3, 30, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.2", 1, 4, 30, NM_MATCH_SPEC_MATCH); + + do_test_match_spec_config("nm-version-min:1", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-min:1", 1, 1, 1, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1", 1, 2, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1", 1, 2, 5, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1", 1, 3, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1", 1, 3, 30, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1", 1, 4, 30, NM_MATCH_SPEC_MATCH); + + do_test_match_spec_config("nm-version-max:1.2.3", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 1, 1, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 2, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 2, 1, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 2, 2, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 2, 5, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2.3", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH); + + do_test_match_spec_config("nm-version-max:1.2", 0, 2, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2", 1, 1, 1, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2", 1, 2, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2", 1, 2, 5, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1.2", 1, 3, 0, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2", 1, 3, 30, NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config("nm-version-max:1.2", 1, 4, 30, NM_MATCH_SPEC_NO_MATCH); + + do_test_match_spec_config("nm-version-max:1", 0, 2, 30, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 1, 1, 1, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 1, 2, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 1, 2, 3, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 1, 2, 5, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 1, 3, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 1, 3, 30, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 1, 4, 30, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-max:1", 2, 4, 30, NM_MATCH_SPEC_NO_MATCH); + + do_test_match_spec_config("except:nm-version:1.4.8", 1, 6, 0, NM_MATCH_SPEC_MATCH); + do_test_match_spec_config("nm-version-min:1.6,except:nm-version:1.4.8", + 1, + 6, + 0, + NM_MATCH_SPEC_MATCH); + + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 2, + 0, + NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 2, + 0, + NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 2, + 15, + NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 2, + 16, + NM_MATCH_SPEC_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 2, + 17, + NM_MATCH_SPEC_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 2, + 20, + NM_MATCH_SPEC_MATCH); + + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 3, + 0, + NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 4, + 0, + NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 4, + 5, + NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 4, + 6, + NM_MATCH_SPEC_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 4, + 7, + NM_MATCH_SPEC_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 4, + 8, + NM_MATCH_SPEC_NEG_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 4, + 9, + NM_MATCH_SPEC_MATCH); + + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 5, + 0, + NM_MATCH_SPEC_NO_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 6, + 0, + NM_MATCH_SPEC_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 6, + 5, + NM_MATCH_SPEC_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 7, + 7, + NM_MATCH_SPEC_MATCH); + do_test_match_spec_config( + "nm-version-min:1.6,nm-version-min:1.4.6,nm-version-min:1.2.16,except:nm-version:1.4.8", + 1, + 8, + 8, + NM_MATCH_SPEC_MATCH); +} + +/*****************************************************************************/ + +static void +test_nm_utils_strbuf_append(void) +{ +#define BUF_ORIG "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define STR_ORIG "abcdefghijklmnopqrstuvwxyz" + int buf_len; + int rep; + char buf[NM_STRLEN(BUF_ORIG) + 1]; + char str[NM_STRLEN(BUF_ORIG) + 1]; + +#define _strbuf_append(buf, len, format, ...) \ + G_STMT_START \ + { \ + char ** _buf = (buf); \ + gsize * _len = (len); \ + const char * _str_iter; \ + gs_free char *_str = NULL; \ + \ + switch (nmtst_get_rand_uint32() % 4) { \ + case 0: \ + nm_utils_strbuf_append(_buf, _len, (format), __VA_ARGS__); \ + break; \ + case 1: \ + _str = g_strdup_printf((format), __VA_ARGS__); \ + nm_utils_strbuf_append_str(_buf, _len, _str); \ + break; \ + case 2: \ + _str = g_strdup_printf((format), __VA_ARGS__); \ + nm_utils_strbuf_append_bin(_buf, _len, _str, strlen(_str)); \ + break; \ + case 3: \ + _str = g_strdup_printf((format), __VA_ARGS__); \ + if (!_str[0]) \ + nm_utils_strbuf_append_str(_buf, _len, _str); \ + for (_str_iter = _str; _str_iter[0]; _str_iter++) \ + nm_utils_strbuf_append_c(_buf, _len, _str_iter[0]); \ + break; \ + } \ + } \ + G_STMT_END + +#define _strbuf_append_str(buf, len, str) \ + G_STMT_START \ + { \ + char ** _buf = (buf); \ + gsize * _len = (len); \ + const char *_str = (str); \ + \ + switch (nmtst_get_rand_uint32() % 4) { \ + case 0: \ + nm_utils_strbuf_append(_buf, _len, "%s", _str ?: ""); \ + break; \ + case 1: \ + nm_utils_strbuf_append_str(_buf, _len, _str); \ + break; \ + case 2: \ + nm_utils_strbuf_append_bin(_buf, _len, _str, _str ? strlen(_str) : 0); \ + break; \ + case 3: \ + if (!_str || !_str[0]) \ + nm_utils_strbuf_append_str(_buf, _len, _str); \ + for (; _str && _str[0]; _str++) \ + nm_utils_strbuf_append_c(_buf, _len, _str[0]); \ + break; \ + } \ + } \ + G_STMT_END + +#define _strbuf_append_c(buf, len, ch) \ + G_STMT_START \ + { \ + char **_buf = (buf); \ + gsize *_len = (len); \ + char _ch = (ch); \ + \ + switch (nmtst_get_rand_uint32() % 4) { \ + case 0: \ + nm_utils_strbuf_append(_buf, _len, "%c", _ch); \ + break; \ + case 1: \ + nm_utils_strbuf_append_str(_buf, _len, ((char[2]){_ch, 0})); \ + break; \ + case 2: \ + nm_utils_strbuf_append_bin(_buf, _len, &_ch, 1); \ + break; \ + case 3: \ + nm_utils_strbuf_append_c(_buf, _len, _ch); \ + break; \ + } \ + } \ + G_STMT_END + + for (buf_len = 0; buf_len < 10; buf_len++) { + for (rep = 0; rep < 50; rep++) { + const int s_len = nmtst_get_rand_uint32() % (sizeof(str) - 5); + char * t_buf; + gsize t_len; + int test_mode; + + strcpy(str, STR_ORIG); + str[s_len] = '\0'; + + g_assert_cmpint(str[sizeof(str) - 1], ==, '\0'); + g_assert_cmpint(strlen(str), ==, s_len); + + strcpy(buf, BUF_ORIG); + + t_buf = buf; + t_len = buf_len; + + test_mode = nmtst_get_rand_uint32() % 5; + + switch (test_mode) { + case 0: + if (s_len == 1) { + _strbuf_append_c(&t_buf, &t_len, str[0]); + break; + } + /* fall-through */ + case 1: + _strbuf_append_str(&t_buf, &t_len, str); + break; + case 2: + if (s_len == 1) { + _strbuf_append(&t_buf, &t_len, "%c", str[0]); + break; + } + /* fall-through */ + case 3: + _strbuf_append(&t_buf, &t_len, "%s", str); + break; + case 4: + g_snprintf(t_buf, t_len, "%s", str); + if (t_len > 0 && strlen(str) >= buf_len && (nmtst_get_rand_uint32() % 2)) { + /* the string was truncated by g_snprintf(). That means, at the last position in the + * buffer is now NUL. + * Replace the NUL by the actual character, and check that nm_utils_strbuf_seek_end() + * does the right thing: NUL terminate the buffer and seek past the end of the buffer. */ + g_assert_cmpmem(t_buf, t_len - 1, str, t_len - 1); + g_assert(t_buf[t_len - 1] == '\0'); + g_assert(str[t_len - 1] != '\0'); + t_buf[t_len - 1] = str[t_len - 1]; + nm_utils_strbuf_seek_end(&t_buf, &t_len); + g_assert(t_len == 0); + g_assert(t_buf == &buf[buf_len]); + g_assert(t_buf[-1] == '\0'); + } else { + nm_utils_strbuf_seek_end(&t_buf, &t_len); + if (buf_len > 0 && strlen(str) + 1 > buf_len) { + /* the buffer was truncated by g_snprintf() above. + * + * But nm_utils_strbuf_seek_end() does not recognize that and returns + * a remaining length of 1. + * + * Note that other nm_utils_strbuf_append*() functions recognize + * truncation, and properly set the remaining length to zero. + * As the assertions below check for the behavior of nm_utils_strbuf_append*(), + * we assert here that nm_utils_strbuf_seek_end() behaved as expected, and then + * adjust t_buf/t_len according to the "is-truncated" case. */ + g_assert(t_len == 1); + g_assert(t_buf == &buf[buf_len - 1]); + g_assert(t_buf[0] == '\0'); + t_len = 0; + t_buf++; + } + } + break; + } + + /* Assert that the source-buffer is unmodified. */ + g_assert_cmpint(str[s_len], ==, '\0'); + str[s_len] = STR_ORIG[s_len]; + g_assert(!memcmp(str, STR_ORIG, sizeof(str))); + str[s_len] = '\0'; + + g_assert_cmpint(t_len, >=, 0); + g_assert_cmpint(t_len, <=, buf_len); + g_assert(t_buf >= buf); + + /* Assert what was written to the destination buffer. */ + switch (buf_len) { + case 0: + g_assert_cmpint(t_len, ==, 0); + g_assert(t_buf == buf); + g_assert(!memcmp(buf, BUF_ORIG, sizeof(buf))); + break; + case 1: + if (s_len == 0) { + g_assert_cmpint(t_len, ==, 1); + g_assert(t_buf == buf); + g_assert(buf[0] == '\0'); + g_assert(!memcmp(&buf[1], &BUF_ORIG[1], sizeof(buf) - 1)); + } else { + g_assert_cmpint(t_len, ==, 0); + g_assert(t_buf == &buf[1]); + g_assert(buf[0] == '\0'); + g_assert(!memcmp(&buf[1], &BUF_ORIG[1], sizeof(buf) - 1)); + } + break; + default: + if (s_len == 0) { + g_assert_cmpint(t_len, ==, buf_len); + g_assert(t_buf == buf); + g_assert(buf[0] == '\0'); + g_assert(!memcmp(&buf[1], &BUF_ORIG[1], sizeof(buf) - 1)); + } else if (buf_len <= s_len) { + g_assert_cmpint(t_len, ==, 0); + g_assert(t_buf == &buf[buf_len]); + g_assert(!memcmp(buf, STR_ORIG, buf_len - 1)); + g_assert(buf[buf_len - 1] == '\0'); + g_assert(!memcmp(&buf[buf_len], &BUF_ORIG[buf_len], sizeof(buf) - buf_len)); + } else { + g_assert_cmpint(t_len, >, 0); + g_assert_cmpint(buf_len - t_len, ==, s_len); + g_assert_cmpint(strlen(buf), ==, s_len); + g_assert(t_buf == &buf[s_len]); + g_assert(!memcmp(buf, STR_ORIG, s_len)); + g_assert(buf[s_len] == '\0'); + g_assert( + !memcmp(&buf[s_len + 1], &BUF_ORIG[s_len + 1], sizeof(buf) - s_len - 1)); + } + break; + } + } + } +} + +/*****************************************************************************/ + +static void +test_duplicate_decl_specifier(void) +{ + /* We're intentionally assigning values to static arrays v_const + * and v_result without using it afterwards just so that valgrind + * doesn't complain about the leak. */ + NM_PRAGMA_WARNING_DISABLE("-Wunused-but-set-variable") + + /* have some static variables, so that the result is certainly not optimized out. */ + static const int v_const[1] = {1}; + static int v_result[1] = {}; + const int v2 = 3; + + /* Test that we don't get a compiler warning about duplicate const specifier. + * C99 allows that and it can easily happen in macros. */ + +#define TEST_MAX(a, b) \ + ({ \ + const typeof(a) _a = (a); \ + const typeof(b) _b = (b); \ + \ + (_a > _b ? _a : _b); \ + }) + + v_result[0] = TEST_MAX(v_const[0], nmtst_get_rand_uint32() % 5) + v2; + + NM_PRAGMA_WARNING_REENABLE +} + +static void +test_reverse_dns_ip4(void) +{ + guint32 addr; + GPtrArray *domains = g_ptr_array_new_full(8, g_free); + + inet_pton(AF_INET, "7.2.3.0", &addr); + nm_utils_get_reverse_dns_domains_ip_4(addr, 27, domains); + g_assert_cmpuint(domains->len, ==, 32); + g_assert_cmpstr(domains->pdata[0], ==, "0.3.2.7.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[31], ==, "31.3.2.7.in-addr.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET, "10.155.16.0", &addr); + nm_utils_get_reverse_dns_domains_ip_4(addr, 22, domains); + g_assert_cmpuint(domains->len, ==, 4); + g_assert_cmpstr(domains->pdata[0], ==, "16.155.10.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[1], ==, "17.155.10.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[2], ==, "18.155.10.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[3], ==, "19.155.10.in-addr.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET, "4.5.6.7", &addr); + nm_utils_get_reverse_dns_domains_ip_4(addr, 32, domains); + g_assert_cmpuint(domains->len, ==, 1); + g_assert_cmpstr(domains->pdata[0], ==, "7.6.5.4.in-addr.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET, "4.5.6.7", &addr); + nm_utils_get_reverse_dns_domains_ip_4(addr, 8, domains); + g_assert_cmpuint(domains->len, ==, 1); + g_assert_cmpstr(domains->pdata[0], ==, "4.in-addr.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET, "4.180.6.7", &addr); + nm_utils_get_reverse_dns_domains_ip_4(addr, 9, domains); + g_assert_cmpuint(domains->len, ==, 128); + g_assert_cmpstr(domains->pdata[0], ==, "128.4.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[1], ==, "129.4.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[127], ==, "255.4.in-addr.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET, "172.16.0.0", &addr); + nm_utils_get_reverse_dns_domains_ip_4(addr, 12, domains); + g_assert_cmpuint(domains->len, ==, 16); + g_assert_cmpstr(domains->pdata[0], ==, "16.172.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[1], ==, "17.172.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[14], ==, "30.172.in-addr.arpa"); + g_assert_cmpstr(domains->pdata[15], ==, "31.172.in-addr.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET, "1.2.3.4", &addr); + nm_utils_get_reverse_dns_domains_ip_4(addr, 0, domains); + g_assert_cmpuint(domains->len, ==, 0); + + g_ptr_array_unref(domains); +} + +static void +test_reverse_dns_ip6(void) +{ + struct in6_addr addr; + GPtrArray * domains = g_ptr_array_new_full(8, g_free); + + inet_pton(AF_INET6, "1234::56", &addr); + nm_utils_get_reverse_dns_domains_ip_6(&addr, 16, domains); + g_assert_cmpuint(domains->len, ==, 1); + g_assert_cmpstr(domains->pdata[0], ==, "4.3.2.1.ip6.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET6, "1234::56", &addr); + nm_utils_get_reverse_dns_domains_ip_6(&addr, 17, domains); + g_assert_cmpuint(domains->len, ==, 8); + g_assert_cmpstr(domains->pdata[0], ==, "0.4.3.2.1.ip6.arpa"); + g_assert_cmpstr(domains->pdata[1], ==, "1.4.3.2.1.ip6.arpa"); + g_assert_cmpstr(domains->pdata[7], ==, "7.4.3.2.1.ip6.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET6, "2001:db8::", &addr); + nm_utils_get_reverse_dns_domains_ip_6(&addr, 29, domains); + g_assert_cmpuint(domains->len, ==, 8); + g_assert_cmpstr(domains->pdata[0], ==, "8.b.d.0.1.0.0.2.ip6.arpa"); + g_assert_cmpstr(domains->pdata[1], ==, "9.b.d.0.1.0.0.2.ip6.arpa"); + g_assert_cmpstr(domains->pdata[7], ==, "f.b.d.0.1.0.0.2.ip6.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET6, "0123:4567:89ab:cdef::", &addr); + nm_utils_get_reverse_dns_domains_ip_6(&addr, 63, domains); + g_assert_cmpuint(domains->len, ==, 2); + g_assert_cmpstr(domains->pdata[0], ==, "e.e.d.c.b.a.9.8.7.6.5.4.3.2.1.0.ip6.arpa"); + g_assert_cmpstr(domains->pdata[1], ==, "f.e.d.c.b.a.9.8.7.6.5.4.3.2.1.0.ip6.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET6, "fec0:1234:5678:9ab0::", &addr); + nm_utils_get_reverse_dns_domains_ip_6(&addr, 61, domains); + g_assert_cmpuint(domains->len, ==, 8); + g_assert_cmpstr(domains->pdata[0], ==, "0.b.a.9.8.7.6.5.4.3.2.1.0.c.e.f.ip6.arpa"); + g_assert_cmpstr(domains->pdata[7], ==, "7.b.a.9.8.7.6.5.4.3.2.1.0.c.e.f.ip6.arpa"); + + g_ptr_array_set_size(domains, 0); + + inet_pton(AF_INET6, "0123:4567:89ab:cdee::", &addr); + nm_utils_get_reverse_dns_domains_ip_6(&addr, 0, domains); + g_assert_cmpuint(domains->len, ==, 0); + + g_ptr_array_unref(domains); +} + +/*****************************************************************************/ + +static void +do_test_stable_id_parse(const char * stable_id, + NMUtilsStableType expected_stable_type, + const char * expected_generated) +{ + gs_free char * generated = NULL; + NMUtilsStableType stable_type; + + if (expected_stable_type == NM_UTILS_STABLE_TYPE_GENERATED) + g_assert(expected_generated); + else + g_assert(!expected_generated); + + if (expected_stable_type == NM_UTILS_STABLE_TYPE_UUID) + g_assert(!stable_id); + else + g_assert(stable_id); + + stable_type = + nm_utils_stable_id_parse(stable_id, "_DEVICE", "_MAC", "_BOOT", "_CONNECTION", &generated); + + g_assert_cmpint(expected_stable_type, ==, stable_type); + + if (stable_type == NM_UTILS_STABLE_TYPE_GENERATED) { + g_assert_cmpstr(expected_generated, ==, generated); + g_assert(generated); + } else + g_assert(!generated); +} + +static void +test_stable_id_parse(void) +{ +#define _parse_stable_id(stable_id) \ + do_test_stable_id_parse("" stable_id "", NM_UTILS_STABLE_TYPE_STABLE_ID, NULL) +#define _parse_generated(stable_id, expected_generated) \ + do_test_stable_id_parse("" stable_id "", \ + NM_UTILS_STABLE_TYPE_GENERATED, \ + "" expected_generated "") +#define _parse_random(stable_id) \ + do_test_stable_id_parse("" stable_id "", NM_UTILS_STABLE_TYPE_RANDOM, NULL) + do_test_stable_id_parse(NULL, NM_UTILS_STABLE_TYPE_UUID, NULL); + _parse_stable_id(""); + _parse_stable_id("a"); + _parse_stable_id("a$"); + _parse_stable_id("a$x"); + _parse_stable_id(" ${a$x"); + _parse_stable_id("${"); + _parse_stable_id("${="); + _parse_stable_id("${a"); + _parse_stable_id("${a$x"); + _parse_stable_id("a$$"); + _parse_stable_id("a$$x"); + _parse_stable_id("a$${CONNECTION}"); + _parse_stable_id("a$${CONNECTION}x"); + _parse_generated("${CONNECTION}", "${CONNECTION}=11{_CONNECTION}"); + _parse_generated("${${CONNECTION}", "${${CONNECTION}=11{_CONNECTION}"); + _parse_generated("${CONNECTION}x", "${CONNECTION}=11{_CONNECTION}x"); + _parse_generated("x${CONNECTION}", "x${CONNECTION}=11{_CONNECTION}"); + _parse_generated("${BOOT}x", "${BOOT}=5{_BOOT}x"); + _parse_generated("x${BOOT}", "x${BOOT}=5{_BOOT}"); + _parse_generated("x${BOOT}${CONNECTION}", "x${BOOT}=5{_BOOT}${CONNECTION}=11{_CONNECTION}"); + _parse_generated("xX${BOOT}yY${CONNECTION}zZ", + "xX${BOOT}=5{_BOOT}yY${CONNECTION}=11{_CONNECTION}zZ"); + _parse_generated("${MAC}x", "${MAC}=4{_MAC}x"); + _parse_random("${RANDOM}"); + _parse_random(" ${RANDOM}"); + _parse_random("${BOOT}${RANDOM}"); +} + +/*****************************************************************************/ + +static void +test_stable_id_generated_complete(void) +{ +#define ASSERT(str, expected) \ + G_STMT_START \ + { \ + gs_free char *_s = NULL; \ + \ + _s = nm_utils_stable_id_generated_complete((str)); \ + g_assert_cmpstr((expected), ==, _s); \ + } \ + G_STMT_END + + ASSERT("", "2jmj7l5rSw0yVb/vlWAYkK/YBwk"); + ASSERT("a", "hvfkN/qlp/zhXR3cuerq6jd2Z7g"); + ASSERT("password", "W6ph5Mm5Pz8GgiULbPgzG37mj9g"); +#undef ASSERT +} + +/*****************************************************************************/ + +static void +test_nm_utils_exp10(void) +{ +#define FLOAT_CMP(a, b) \ + G_STMT_START \ + { \ + double _a = (a); \ + double _b = (b); \ + \ + if (isinf(_b)) \ + g_assert(isinf(_a)); \ + else if (_b >= 0.0 && _b <= 0.0) \ + g_assert(_a - _b < G_MINFLOAT); \ + else { \ + double _x = (_a) - (_b); \ + g_assert(_b > 0.0); \ + if (_x < 0.0) \ + _x = -_x; \ + g_assert(_x / _b < 1E-10); \ + } \ + } \ + G_STMT_END + + FLOAT_CMP(nm_utils_exp10(G_MININT16), 0.0); + FLOAT_CMP(nm_utils_exp10(-310), 0.0); + FLOAT_CMP(nm_utils_exp10(-309), 0.0); + FLOAT_CMP(nm_utils_exp10(-308), 1e-308); + FLOAT_CMP(nm_utils_exp10(-307), 1e-307); + FLOAT_CMP(nm_utils_exp10(-1), 1e-1); + FLOAT_CMP(nm_utils_exp10(-2), 1e-2); + FLOAT_CMP(nm_utils_exp10(0), 1e0); + FLOAT_CMP(nm_utils_exp10(1), 1e1); + FLOAT_CMP(nm_utils_exp10(2), 1e2); + FLOAT_CMP(nm_utils_exp10(3), 1e3); + FLOAT_CMP(nm_utils_exp10(4), 1e4); + FLOAT_CMP(nm_utils_exp10(5), 1e5); + FLOAT_CMP(nm_utils_exp10(6), 1e6); + FLOAT_CMP(nm_utils_exp10(7), 1e7); + FLOAT_CMP(nm_utils_exp10(122), 1e122); + FLOAT_CMP(nm_utils_exp10(200), 1e200); + FLOAT_CMP(nm_utils_exp10(307), 1e307); + FLOAT_CMP(nm_utils_exp10(308), 1e308); + FLOAT_CMP(nm_utils_exp10(309), INFINITY); + FLOAT_CMP(nm_utils_exp10(310), INFINITY); + FLOAT_CMP(nm_utils_exp10(G_MAXINT16), INFINITY); +} + +/*****************************************************************************/ + +static void +test_utils_file_is_in_path(void) +{ + g_assert(!nm_utils_file_is_in_path("/", "/")); + g_assert(!nm_utils_file_is_in_path("//", "/")); + g_assert(!nm_utils_file_is_in_path("/a/", "/")); + g_assert(nm_utils_file_is_in_path("/a", "/")); + g_assert(nm_utils_file_is_in_path("///a", "/")); + g_assert(nm_utils_file_is_in_path("//b/a", "/b//")); + g_assert(nm_utils_file_is_in_path("//b///a", "/b//")); + g_assert(!nm_utils_file_is_in_path("//b///a/", "/b//")); + g_assert(!nm_utils_file_is_in_path("//b///a/", "/b/a/")); + g_assert(!nm_utils_file_is_in_path("//b///a", "/b/a/")); + g_assert(nm_utils_file_is_in_path("//b///a/.", "/b/a/")); + g_assert(nm_utils_file_is_in_path("//b///a/..", "/b/a/")); +} + +/*****************************************************************************/ + +#define _TEST_RC(searches, nameservers, options, expected) \ + G_STMT_START \ + { \ + const char *const *const _searches = (searches); \ + const char *const *const _nameservers = (nameservers); \ + const char *const *const _options = (options); \ + gs_free char * _content = NULL; \ + \ + _content = nmtst_dns_create_resolv_conf(_searches, _nameservers, _options); \ + g_assert_cmpstr(_content, ==, expected); \ + } \ + G_STMT_END + +static void +test_dns_create_resolv_conf(void) +{ + _TEST_RC(NM_MAKE_STRV("a"), + NULL, + NULL, + "# Generated by NetworkManager\n" + "search a\n" + ""); + + _TEST_RC(NM_MAKE_STRV("a", "b.com"), + NM_MAKE_STRV("192.168.55.1", "192.168.56.1"), + NM_MAKE_STRV("opt1", "opt2"), + "# Generated by NetworkManager\n" + "search a b.com\n" + "nameserver 192.168.55.1\n" + "nameserver 192.168.56.1\n" + "options opt1 opt2\n" + ""); + + _TEST_RC(NM_MAKE_STRV("a2x456789.b2x456789.c2x456789.d2x456789.e2x456789.f2x456789.g2x456789." + "h2x456789.i2x456789.j2x4567890", + "a2y456789.b2y456789.c2y456789.d2y456789.e2y456789.f2y456789.g2y456789." + "h2y456789.i2y456789.j2y4567890", + "a2z456789.b2z456789.c2z456789.d2z456789.e2z456789.f2z456789.g2z456789." + "h2z456789.i2z456789.j2z4567890"), + NULL, + NULL, + "# Generated by NetworkManager\n" + "search " + "a2x456789.b2x456789.c2x456789.d2x456789.e2x456789.f2x456789.g2x456789.h2x456789." + "i2x456789.j2x4567890 " + "a2y456789.b2y456789.c2y456789.d2y456789.e2y456789.f2y456789.g2y456789.h2y456789." + "i2y456789.j2y4567890 " + "a2z456789.b2z456789.c2z456789.d2z456789.e2z456789.f2z456789.g2z456789.h2z456789." + "i2z456789.j2z4567890\n" + ""); +} + +/*****************************************************************************/ + +static void +test_machine_id_read(void) +{ + NMUuid machine_id_sd; + const NMUuid *machine_id; + char machine_id_str[33]; + gpointer logstate; + + logstate = nmtst_logging_disable(FALSE); + /* If you run this test as root, without a valid /etc/machine-id, + * the code will try to get the secret-key. That is a bit ugly, + * but no real problem. */ + machine_id = nm_utils_machine_id_bin(); + nmtst_logging_reenable(logstate); + + g_assert(machine_id); + g_assert(nm_utils_bin2hexstr_full(machine_id, sizeof(NMUuid), '\0', FALSE, machine_id_str) + == machine_id_str); + g_assert(strlen(machine_id_str) == 32); + g_assert_cmpstr(machine_id_str, ==, nm_utils_machine_id_str()); + + /* double check with systemd's implementation... */ + if (!nm_sd_utils_id128_get_machine(&machine_id_sd)) { + /* if systemd failed to read /etc/machine-id, the file likely + * is invalid. Our machine-id is fake, and we have nothing to + * compare against. */ + + /* NOTE: this test will fail, if you don't have /etc/machine-id, + * but a valid "LOCALSTATEDIR/lib/dbus/machine-id" file. + * Just don't do that. */ + g_assert(nm_utils_machine_id_is_fake()); + } else { + g_assert(!nm_utils_machine_id_is_fake()); + g_assert_cmpmem(&machine_id_sd, sizeof(NMUuid), machine_id, 16); + } +} + +/*****************************************************************************/ + +static void +test_nm_utils_dhcp_client_id_systemd_node_specific(gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT(test_data); + const guint8 HASH_KEY[16] = {0x80, + 0x11, + 0x8c, + 0xc2, + 0xfe, + 0x4a, + 0x03, + 0xee, + 0x3e, + 0xd6, + 0x0c, + 0x6f, + 0x36, + 0x39, + 0x14, + 0x09}; + const guint16 duid_type_en = htons(2); + const guint32 systemd_pen = htonl(43793); + const struct { + NMUuid machine_id; + const char *ifname; + guint64 ifname_hash_1; + guint32 iaid_ifname; + guint64 duid_id; + } d_array[] = { + [0] = + { + .machine_id.uuid = {0xcb, + 0xc2, + 0x2e, + 0x47, + 0x41, + 0x8e, + 0x40, + 0x2a, + 0xa7, + 0xb3, + 0x0d, + 0xea, + 0x92, + 0x83, + 0x94, + 0xef}, + .ifname = "lo", + .ifname_hash_1 = 0x7297085c2b12c911llu, + .iaid_ifname = htobe32(0x5985c14du), + .duid_id = htobe64(0x3d769bb2c14d29e1u), + }, + [1] = + { + .machine_id.uuid = {0x11, + 0x4e, + 0xb4, + 0xda, + 0xd3, + 0x22, + 0x4a, + 0xff, + 0x9f, + 0xc3, + 0x30, + 0x83, + 0x38, + 0xa0, + 0xeb, + 0xb7}, + .ifname = "eth0", + .ifname_hash_1 = 0x9e1cb083b54cd7b6llu, + .iaid_ifname = htobe32(0x2b506735u), + .duid_id = htobe64(0x551572e0f2a2a10fu), + }, + }; + int i; + typeof(d_array[0]) *d = &d_array[TEST_IDX]; + gint64 u64; + gint32 u32; + + /* the test already hard-codes the expected values iaid_ifname and duid_id + * above. Still, redo the steps to derive them from the ifname/machine-id + * and double check. */ + u64 = c_siphash_hash(HASH_KEY, (const guint8 *) d->ifname, strlen(d->ifname)); + g_assert_cmpint(u64, ==, d->ifname_hash_1); + u32 = be32toh((u64 & 0xffffffffu) ^ (u64 >> 32)); + g_assert_cmpint(u32, ==, d->iaid_ifname); + + u64 = htole64(c_siphash_hash(HASH_KEY, (const guint8 *) &d->machine_id, sizeof(d->machine_id))); + g_assert_cmpint(u64, ==, d->duid_id); + + for (i = 0; i < 2; i++) { + const gboolean legacy_unstable_byteorder = (i != 0); + gs_unref_bytes GBytes *client_id = NULL; + const guint8 * cid; + guint32 iaid = d->iaid_ifname; + guint32 tmp; + + tmp = nm_utils_create_dhcp_iaid(legacy_unstable_byteorder, + (const guint8 *) d->ifname, + strlen(d->ifname)); + client_id = + nm_utils_dhcp_client_id_systemd_node_specific_full(tmp, + (const guint8 *) &d->machine_id, + sizeof(d->machine_id)); + + g_assert(client_id); + g_assert_cmpint(g_bytes_get_size(client_id), ==, 19); + cid = g_bytes_get_data(client_id, NULL); + g_assert_cmpint(cid[0], ==, 255); +#if __BYTE_ORDER == __BIG_ENDIAN + if (legacy_unstable_byteorder) { + /* on non-little endian, the legacy behavior is to have the bytes + * swapped. */ + iaid = bswap_32(iaid); + } +#endif + g_assert_cmpmem(&cid[1], 4, &iaid, sizeof(iaid)); + g_assert_cmpmem(&cid[5], 2, &duid_type_en, sizeof(duid_type_en)); + g_assert_cmpmem(&cid[7], 4, &systemd_pen, sizeof(systemd_pen)); + g_assert_cmpmem(&cid[11], 8, &d->duid_id, sizeof(d->duid_id)); + + g_assert_cmpint(iaid, + ==, + htonl(nm_utils_create_dhcp_iaid(legacy_unstable_byteorder, + (const guint8 *) d->ifname, + strlen(d->ifname)))); + } +} + +/*****************************************************************************/ + +static void +_kernel_cmdline_match(gboolean expected_match, + const char *const *proc_cmdline, + const char *const *patterns) +{ + gs_free_error GError *error = NULL; + GError ** p_error = nmtst_get_rand_bool() ? &error : NULL; + gboolean match; + + nm_assert(proc_cmdline); + nm_assert(patterns); + + match = nm_utils_kernel_cmdline_match_check(proc_cmdline, + patterns, + NM_PTRARRAY_LEN(patterns), + p_error); + if (expected_match) + nmtst_assert_success(match, error); + else { + g_assert(!p_error || error); + g_assert(!match); + } +} + +static void +test_kernel_cmdline_match_check(void) +{ + _kernel_cmdline_match(TRUE, NM_MAKE_STRV(""), NM_MAKE_STRV("")); + _kernel_cmdline_match(FALSE, NM_MAKE_STRV(""), NM_MAKE_STRV("a")); + _kernel_cmdline_match(TRUE, NM_MAKE_STRV("a"), NM_MAKE_STRV("a")); + _kernel_cmdline_match(TRUE, NM_MAKE_STRV("a=b"), NM_MAKE_STRV("a")); + _kernel_cmdline_match(TRUE, NM_MAKE_STRV("a=b", "b"), NM_MAKE_STRV("a", "b")); + _kernel_cmdline_match(TRUE, NM_MAKE_STRV("a=b", "b"), NM_MAKE_STRV("&a", "&b")); + _kernel_cmdline_match(FALSE, NM_MAKE_STRV("a=b", "bc"), NM_MAKE_STRV("&a", "&b")); + _kernel_cmdline_match(FALSE, NM_MAKE_STRV("a=b", "b"), NM_MAKE_STRV("&a", "&b", "c")); + _kernel_cmdline_match(TRUE, NM_MAKE_STRV("a=b", "b"), NM_MAKE_STRV("&a", "&b", "b", "c")); + _kernel_cmdline_match(TRUE, NM_MAKE_STRV("a=b", "b", "c=dd"), NM_MAKE_STRV("&a", "&b", "c")); + _kernel_cmdline_match(FALSE, NM_MAKE_STRV("a", "b"), NM_MAKE_STRV("a", "&c")); + _kernel_cmdline_match(TRUE, NM_MAKE_STRV("a", "b"), NM_MAKE_STRV("a", "|\\c")); +} + +/*****************************************************************************/ + +static void +test_connectivity_state_cmp(void) +{ + NMConnectivityState a; + +#define _cmp(a, b, cmp) \ + G_STMT_START \ + { \ + const NMConnectivityState _a = (a); \ + const NMConnectivityState _b = (b); \ + const int _cmp = (cmp); \ + \ + g_assert(NM_IN_SET(_cmp, -1, 0, 1)); \ + g_assert_cmpint(nm_connectivity_state_cmp(_a, _b), ==, _cmp); \ + g_assert_cmpint(nm_connectivity_state_cmp(_b, _a), ==, -_cmp); \ + } \ + G_STMT_END + + for (a = NM_CONNECTIVITY_UNKNOWN; a <= NM_CONNECTIVITY_FULL; a++) + _cmp(a, a, 0); + + _cmp(NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_UNKNOWN, 0); + _cmp(NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_NONE, -1); + _cmp(NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_LIMITED, -1); + _cmp(NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_PORTAL, -1); + _cmp(NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, -1); + + _cmp(NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp(NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_NONE, 0); + _cmp(NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_LIMITED, -1); + _cmp(NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_PORTAL, -1); + _cmp(NM_CONNECTIVITY_NONE, NM_CONNECTIVITY_FULL, -1); + + _cmp(NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp(NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_NONE, 1); + _cmp(NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_LIMITED, 0); + _cmp(NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_PORTAL, -1); + _cmp(NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_FULL, -1); + + _cmp(NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp(NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_NONE, 1); + _cmp(NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_LIMITED, 1); + _cmp(NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_PORTAL, 0); + _cmp(NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_FULL, -1); + + _cmp(NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, 1); + _cmp(NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_NONE, 1); + _cmp(NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_LIMITED, 1); + _cmp(NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_PORTAL, 1); + _cmp(NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_FULL, 0); + +#undef _cmp +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_with_logging(&argc, &argv, NULL, "ALL"); + + g_test_add_func("/general/test_logging_domains", test_logging_domains); + g_test_add_func("/general/test_logging_error", test_logging_error); + + g_test_add_func("/general/nm_utils_strbuf_append", test_nm_utils_strbuf_append); + + g_test_add_func("/general/nm_utils_ip6_address_clear_host_address", + test_nm_utils_ip6_address_clear_host_address); + g_test_add_func("/general/nm_utils_ip6_address_same_prefix", + test_nm_utils_ip6_address_same_prefix); + g_test_add_func("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff); + + g_test_add_func("/general/nm_utils_sysctl_ip_conf_path", test_nm_utils_sysctl_ip_conf_path); + + g_test_add_func("/general/exp10", test_nm_utils_exp10); + + g_test_add_func("/general/connection-match/basic", test_connection_match_basic); + g_test_add_func("/general/connection-match/ip6-method", test_connection_match_ip6_method); + g_test_add_func("/general/connection-match/ip6-method-ignore", + test_connection_match_ip6_method_ignore); + g_test_add_func("/general/connection-match/ip6-method-ignore-auto", + test_connection_match_ip6_method_ignore_auto); + g_test_add_func("/general/connection-match/ip4-method", test_connection_match_ip4_method); + g_test_add_func("/general/connection-match/con-interface-name", + test_connection_match_interface_name); + g_test_add_func("/general/connection-match/wired", test_connection_match_wired); + g_test_add_func("/general/connection-match/wired2", test_connection_match_wired2); + g_test_add_func("/general/connection-match/cloned_mac", test_connection_match_cloned_mac); + g_test_add_func("/general/connection-match/no-match-ip4-addr", + test_connection_no_match_ip4_addr); + g_test_add_func("/general/connection-match/no-match-vlan", test_connection_no_match_vlan); + g_test_add_func("/general/connection-match/routes/ip4/1", test_connection_match_ip4_routes1); + g_test_add_func("/general/connection-match/routes/ip4/2", test_connection_match_ip4_routes2); + g_test_add_func("/general/connection-match/routes/ip6", test_connection_match_ip6_routes); + + g_test_add_func("/general/wildcard-match", test_wildcard_match); + + g_test_add_func("/general/connection-sort/autoconnect-priority", + test_connection_sort_autoconnect_priority); + + g_test_add_func("/general/match-spec/device", test_match_spec_device); + g_test_add_func("/general/match-spec/config", test_match_spec_config); + g_test_add_func("/general/duplicate_decl_specifier", test_duplicate_decl_specifier); + + g_test_add_func("/general/reverse_dns/ip4", test_reverse_dns_ip4); + g_test_add_func("/general/reverse_dns/ip6", test_reverse_dns_ip6); + + g_test_add_func("/general/stable-id/parse", test_stable_id_parse); + g_test_add_func("/general/stable-id/generated-complete", test_stable_id_generated_complete); + + g_test_add_func("/general/machine-id/read", test_machine_id_read); + + g_test_add_func("/general/test_utils_file_is_in_path", test_utils_file_is_in_path); + + g_test_add_func("/general/test_dns_create_resolv_conf", test_dns_create_resolv_conf); + + g_test_add_data_func("/general/nm_utils_dhcp_client_id_systemd_node_specific/0", + GINT_TO_POINTER(0), + test_nm_utils_dhcp_client_id_systemd_node_specific); + g_test_add_data_func("/general/nm_utils_dhcp_client_id_systemd_node_specific/1", + GINT_TO_POINTER(1), + test_nm_utils_dhcp_client_id_systemd_node_specific); + + g_test_add_func("/core/general/test_connectivity_state_cmp", test_connectivity_state_cmp); + g_test_add_func("/core/general/test_kernel_cmdline_match_check", + test_kernel_cmdline_match_check); + + return g_test_run(); +} diff --git a/src/core/tests/test-dcb.c b/src/core/tests/test-dcb.c new file mode 100644 index 00000000..fc591fcb --- /dev/null +++ b/src/core/tests/test-dcb.c @@ -0,0 +1,354 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-dcb.h" + +#include "nm-test-utils-core.h" + +typedef struct { + guint num; + const char *cmds[]; +} DcbExpected; + +static gboolean +test_dcb_func(char **argv, guint which, gpointer user_data, GError **error) +{ + DcbExpected *e = user_data; + char * f; + + g_assert(argv[0] == NULL); + argv[0] = (which == DCBTOOL) ? "dcbtool" : "fcoeadm"; + + f = g_strjoinv(" ", argv); + if (e->cmds[e->num] == NULL) + g_assert_cmpstr(f, ==, NULL); + g_assert_cmpstr(e->cmds[e->num], !=, NULL); + g_assert_cmpstr(f, ==, e->cmds[e->num++]); + g_free(f); + return TRUE; +} + +#define DCB_FLAGS_ALL \ + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + +static void +test_dcb_fcoe(void) +{ + static DcbExpected expected = { + 0, + {"dcbtool sc eth0 app:fcoe e:1 a:1 w:1", + "dcbtool sc eth0 app:fcoe appcfg:40", + "dcbtool sc eth0 app:iscsi e:0 a:0 w:0", + "dcbtool sc eth0 app:fip e:0 a:0 w:0", + "dcbtool sc eth0 pfc e:0 a:0 w:0", + "dcbtool sc eth0 pg e:0", + NULL}, + }; + NMSettingDcb *s_dcb; + GError * error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + g_object_set(G_OBJECT(s_dcb), + NM_SETTING_DCB_APP_FCOE_FLAGS, + DCB_FLAGS_ALL, + NM_SETTING_DCB_APP_FCOE_PRIORITY, + 6, + NULL); + + success = _dcb_setup("eth0", s_dcb, test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + g_assert_cmpstr(expected.cmds[expected.num], ==, NULL); + g_object_unref(s_dcb); +} + +static void +test_dcb_iscsi(void) +{ + static DcbExpected expected = { + 0, + {"dcbtool sc eth0 app:fcoe e:0 a:0 w:0", + "dcbtool sc eth0 app:iscsi e:1 a:0 w:1", + "dcbtool sc eth0 app:iscsi appcfg:08", + "dcbtool sc eth0 app:fip e:0 a:0 w:0", + "dcbtool sc eth0 pfc e:0 a:0 w:0", + "dcbtool sc eth0 pg e:0", + NULL}, + }; + NMSettingDcb *s_dcb; + GError * error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + g_object_set(G_OBJECT(s_dcb), + NM_SETTING_DCB_APP_ISCSI_FLAGS, + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_WILLING), + NM_SETTING_DCB_APP_ISCSI_PRIORITY, + 3, + NULL); + + success = _dcb_setup("eth0", s_dcb, test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + g_assert_cmpstr(expected.cmds[expected.num], ==, NULL); + g_object_unref(s_dcb); +} + +static void +test_dcb_fip(void) +{ + static DcbExpected expected = { + 0, + {"dcbtool sc eth0 app:fcoe e:0 a:0 w:0", + "dcbtool sc eth0 app:iscsi e:0 a:0 w:0", + "dcbtool sc eth0 app:fip e:1 a:1 w:0", + "dcbtool sc eth0 app:fip appcfg:01", + "dcbtool sc eth0 pfc e:0 a:0 w:0", + "dcbtool sc eth0 pg e:0", + NULL}, + }; + NMSettingDcb *s_dcb; + GError * error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + g_object_set(G_OBJECT(s_dcb), + NM_SETTING_DCB_APP_FIP_FLAGS, + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE), + NM_SETTING_DCB_APP_FIP_PRIORITY, + 0, + NULL); + + success = _dcb_setup("eth0", s_dcb, test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + g_assert_cmpstr(expected.cmds[expected.num], ==, NULL); + g_object_unref(s_dcb); +} + +static void +test_dcb_fip_default_prio(void) +{ + static DcbExpected expected = { + 0, + {"dcbtool sc eth0 app:fcoe e:0 a:0 w:0", + "dcbtool sc eth0 app:iscsi e:0 a:0 w:0", + "dcbtool sc eth0 app:fip e:1 a:1 w:0", + "dcbtool sc eth0 pfc e:0 a:0 w:0", + "dcbtool sc eth0 pg e:0", + NULL}, + }; + NMSettingDcb *s_dcb; + GError * error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + g_object_set(G_OBJECT(s_dcb), + NM_SETTING_DCB_APP_FIP_FLAGS, + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE), + NM_SETTING_DCB_APP_FIP_PRIORITY, + -1, + NULL); + + success = _dcb_setup("eth0", s_dcb, test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + g_assert_cmpstr(expected.cmds[expected.num], ==, NULL); + g_object_unref(s_dcb); +} + +static void +test_dcb_pfc(void) +{ + static DcbExpected expected = { + 0, + {"dcbtool sc eth0 app:fcoe e:0 a:0 w:0", + "dcbtool sc eth0 app:iscsi e:0 a:0 w:0", + "dcbtool sc eth0 app:fip e:0 a:0 w:0", + "dcbtool sc eth0 pfc e:1 a:1 w:1", + "dcbtool sc eth0 pfc pfcup:01101100", + "dcbtool sc eth0 pg e:0", + NULL}, + }; + NMSettingDcb *s_dcb; + GError * error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + g_object_set(G_OBJECT(s_dcb), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL, NULL); + + nm_setting_dcb_set_priority_flow_control(s_dcb, 0, FALSE); + nm_setting_dcb_set_priority_flow_control(s_dcb, 1, TRUE); + nm_setting_dcb_set_priority_flow_control(s_dcb, 2, TRUE); + nm_setting_dcb_set_priority_flow_control(s_dcb, 3, FALSE); + nm_setting_dcb_set_priority_flow_control(s_dcb, 4, TRUE); + nm_setting_dcb_set_priority_flow_control(s_dcb, 5, TRUE); + nm_setting_dcb_set_priority_flow_control(s_dcb, 6, FALSE); + nm_setting_dcb_set_priority_flow_control(s_dcb, 7, FALSE); + + success = _dcb_setup("eth0", s_dcb, test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + g_assert_cmpstr(expected.cmds[expected.num], ==, NULL); + g_object_unref(s_dcb); +} + +static void +test_dcb_priority_groups(void) +{ + static DcbExpected expected = { + 0, + {"dcbtool sc eth0 app:fcoe e:0 a:0 w:0", + "dcbtool sc eth0 app:iscsi e:0 a:0 w:0", + "dcbtool sc eth0 app:fip e:0 a:0 w:0", + "dcbtool sc eth0 pfc e:0 a:0 w:0", + "dcbtool sc eth0 pg e:1 a:1 w:1" + " pgid:765f3210" + " pgpct:10,40,5,10,5,20,7,3" + " uppct:100,50,33,25,20,16,14,12" + " strict:01010101" + " up2tc:01201201", + NULL}, + }; + NMSettingDcb *s_dcb; + GError * error = NULL; + gboolean success; + guint i; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + g_object_set(G_OBJECT(s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL, NULL); + + for (i = 0; i < 8; i++) { + /* Make sure at least one 15/f is present in the group IDs */ + nm_setting_dcb_set_priority_group_id(s_dcb, i, (i == 3) ? 15 : 7 - i); + nm_setting_dcb_set_priority_bandwidth(s_dcb, i, 100 / (i + 1)); + nm_setting_dcb_set_priority_strict_bandwidth(s_dcb, i, i % 2); + nm_setting_dcb_set_priority_traffic_class(s_dcb, i, i % 3); + } + + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 0, 10); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 1, 40); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 2, 5); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 3, 10); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 4, 5); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 5, 20); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 6, 7); + nm_setting_dcb_set_priority_group_bandwidth(s_dcb, 7, 3); + + success = _dcb_setup("eth0", s_dcb, test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + g_assert_cmpstr(expected.cmds[expected.num], ==, NULL); + g_object_unref(s_dcb); +} + +static void +test_dcb_cleanup(void) +{ + static DcbExpected expected = { + 0, + {"fcoeadm -d eth0", + "dcbtool sc eth0 app:fcoe e:0", + "dcbtool sc eth0 app:iscsi e:0", + "dcbtool sc eth0 app:fip e:0", + "dcbtool sc eth0 pfc e:0", + "dcbtool sc eth0 pg e:0", + "dcbtool sc eth0 dcb off", + NULL}, + }; + GError * error = NULL; + gboolean success; + + success = _fcoe_cleanup("eth0", test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + success = _dcb_cleanup("eth0", test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); + + g_assert_cmpstr(expected.cmds[expected.num], ==, NULL); +} + +static void +test_fcoe_create(void) +{ + static DcbExpected expected1 = { + 0, + {"fcoeadm -m fabric -c eth0", NULL}, + }; + static DcbExpected expected2 = { + 0, + {"fcoeadm -m vn2vn -c eth0", NULL}, + }; + GError * error = NULL; + gboolean success; + NMSettingDcb *s_dcb; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new(); + g_object_set(G_OBJECT(s_dcb), NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL, NULL); + + /* Default mode is fabric */ + success = _fcoe_setup("eth0", s_dcb, test_dcb_func, &expected1, &error); + g_assert_no_error(error); + g_assert(success); + + /* Test VN2VN */ + g_object_set(G_OBJECT(s_dcb), + NM_SETTING_DCB_APP_FCOE_MODE, + NM_SETTING_DCB_FCOE_MODE_VN2VN, + NULL); + success = _fcoe_setup("eth0", s_dcb, test_dcb_func, &expected2, &error); + g_assert_no_error(error); + g_assert(success); + + g_object_unref(s_dcb); +} + +static void +test_fcoe_cleanup(void) +{ + static DcbExpected expected = { + 0, + {"fcoeadm -d eth0", NULL}, + }; + GError * error = NULL; + gboolean success; + + success = _fcoe_cleanup("eth0", test_dcb_func, &expected, &error); + g_assert_no_error(error); + g_assert(success); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT"); + + g_test_add_func("/dcb/fcoe", test_dcb_fcoe); + g_test_add_func("/dcb/iscsi", test_dcb_iscsi); + g_test_add_func("/dcb/fip", test_dcb_fip); + g_test_add_func("/dcb/fip-default-priority", test_dcb_fip_default_prio); + g_test_add_func("/dcb/pfc", test_dcb_pfc); + g_test_add_func("/dcb/priority-groups", test_dcb_priority_groups); + g_test_add_func("/dcb/cleanup", test_dcb_cleanup); + g_test_add_func("/fcoe/create", test_fcoe_create); + g_test_add_func("/fcoe/cleanup", test_fcoe_cleanup); + + return g_test_run(); +} diff --git a/src/core/tests/test-ip4-config.c b/src/core/tests/test-ip4-config.c new file mode 100644 index 00000000..27b1d611 --- /dev/null +++ b/src/core/tests/test-ip4-config.c @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 - 2014 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include + +#include "nm-ip4-config.h" +#include "platform/nm-platform.h" + +#include "nm-test-utils-core.h" + +static NMIP4Config * +build_test_config(void) +{ + NMIP4Config * config; + NMPlatformIP4Address addr; + NMPlatformIP4Route route; + + /* Build up the config to subtract */ + config = nmtst_ip4_config_new(1); + + nm_assert(NM_IP_CONFIG_CAST(config)); + + addr = *nmtst_platform_ip4_address("192.168.1.10", "1.2.3.4", 24); + nm_ip4_config_add_address(config, &addr); + + route = *nmtst_platform_ip4_route("10.0.0.0", 8, "192.168.1.1"); + nm_ip4_config_add_route(config, &route, NULL); + + route = *nmtst_platform_ip4_route("172.16.0.0", 16, "192.168.1.1"); + nm_ip4_config_add_route(config, &route, NULL); + + { + const NMPlatformIP4Route r = { + .rt_source = NM_IP_CONFIG_SOURCE_DHCP, + .gateway = nmtst_inet4_from_string("192.168.1.1"), + .table_coerced = 0, + .metric = 100, + }; + + nm_ip4_config_add_route(config, &r, NULL); + } + + nm_ip4_config_add_nameserver(config, nmtst_inet4_from_string("4.2.2.1")); + nm_ip4_config_add_nameserver(config, nmtst_inet4_from_string("4.2.2.2")); + nm_ip4_config_add_domain(config, "foobar.com"); + nm_ip4_config_add_domain(config, "baz.com"); + nm_ip4_config_add_search(config, "blahblah.com"); + nm_ip4_config_add_search(config, "beatbox.com"); + + nm_ip4_config_add_nis_server(config, nmtst_inet4_from_string("1.2.3.9")); + nm_ip4_config_add_nis_server(config, nmtst_inet4_from_string("1.2.3.10")); + + nm_ip4_config_add_wins(config, nmtst_inet4_from_string("4.2.3.9")); + nm_ip4_config_add_wins(config, nmtst_inet4_from_string("4.2.3.10")); + + return config; +} + +static void +test_replace(void) +{ + gs_unref_object NMIP4Config *config1 = NULL; + gs_unref_object NMIP4Config *config2 = NULL; + NMPlatformIP4Address addr; + gboolean relevant_changes; + + config1 = nmtst_ip4_config_new(1); + + addr = *nmtst_platform_ip4_address("172.16.0.1", NULL, 24); + addr.timestamp = 10; + addr.preferred = 3600; + addr.lifetime = 7200; + nm_ip4_config_add_address(config1, &addr); + + addr = *nmtst_platform_ip4_address("172.16.0.2", NULL, 24); + addr.timestamp = 10; + addr.preferred = 3600; + addr.lifetime = 7200; + nm_ip4_config_add_address(config1, &addr); + + config2 = nmtst_ip4_config_new(1); + + addr = *nmtst_platform_ip4_address("192.168.1.1", NULL, 24); + addr.timestamp = 40; + addr.preferred = 60; + addr.lifetime = 120; + nm_ip4_config_add_address(config2, &addr); + + addr = *nmtst_platform_ip4_address("172.16.0.2", NULL, 24); + addr.timestamp = 40; + addr.preferred = 60; + addr.lifetime = 120; + nm_ip4_config_add_address(config2, &addr); + + g_assert(nm_ip4_config_replace(config2, config1, &relevant_changes)); + g_assert(relevant_changes); + g_assert(nm_ip4_config_equal(config1, config2)); +} + +static void +test_subtract(void) +{ + NMIP4Config * src, *dst; + NMPlatformIP4Address addr; + NMPlatformIP4Route route; + const NMPlatformIP4Address *test_addr; + const NMPlatformIP4Route * test_route; + const char * expected_addr = "192.168.1.12"; + guint32 expected_addr_plen = 24; + const char * expected_route_dest = "8.0.0.0"; + guint32 expected_route_plen = 8; + const char * expected_route_next_hop = "192.168.1.1"; + guint32 expected_ns1 = nmtst_inet4_from_string("8.8.8.8"); + guint32 expected_ns2 = nmtst_inet4_from_string("8.8.8.9"); + const char * expected_domain = "wonderfalls.com"; + const char * expected_search = "somewhere.com"; + guint32 expected_nis = nmtst_inet4_from_string("1.2.3.13"); + guint32 expected_wins = nmtst_inet4_from_string("2.3.4.5"); + guint32 expected_mtu = 1492; + + src = build_test_config(); + + /* add a couple more things to the test config */ + dst = build_test_config(); + addr = *nmtst_platform_ip4_address(expected_addr, NULL, expected_addr_plen); + nm_ip4_config_add_address(dst, &addr); + + route = *nmtst_platform_ip4_route(expected_route_dest, + expected_route_plen, + expected_route_next_hop); + nm_ip4_config_add_route(dst, &route, NULL); + + nm_ip4_config_add_nameserver(dst, expected_ns1); + nm_ip4_config_add_nameserver(dst, expected_ns2); + nm_ip4_config_add_domain(dst, expected_domain); + nm_ip4_config_add_search(dst, expected_search); + + nm_ip4_config_add_nis_server(dst, expected_nis); + nm_ip4_config_add_wins(dst, expected_wins); + + nm_ip4_config_set_mtu(dst, expected_mtu, NM_IP_CONFIG_SOURCE_UNKNOWN); + + nm_ip4_config_subtract(dst, src, 0); + + /* ensure what's left is what we expect */ + g_assert_cmpuint(nm_ip4_config_get_num_addresses(dst), ==, 1); + test_addr = _nmtst_ip4_config_get_address(dst, 0); + g_assert(test_addr != NULL); + g_assert_cmpuint(test_addr->address, ==, nmtst_inet4_from_string(expected_addr)); + g_assert_cmpuint(test_addr->peer_address, ==, test_addr->address); + g_assert_cmpuint(test_addr->plen, ==, expected_addr_plen); + + g_assert(!nm_ip4_config_best_default_route_get(dst)); + g_assert_cmpuint(nmtst_ip4_config_get_gateway(dst), ==, 0); + + g_assert_cmpuint(nm_ip4_config_get_num_routes(dst), ==, 1); + test_route = _nmtst_ip4_config_get_route(dst, 0); + g_assert(test_route != NULL); + g_assert_cmpuint(test_route->network, ==, nmtst_inet4_from_string(expected_route_dest)); + g_assert_cmpuint(test_route->plen, ==, expected_route_plen); + g_assert_cmpuint(test_route->gateway, ==, nmtst_inet4_from_string(expected_route_next_hop)); + + g_assert_cmpuint(nm_ip4_config_get_num_nameservers(dst), ==, 2); + g_assert_cmpuint(nm_ip4_config_get_nameserver(dst, 0), ==, expected_ns1); + g_assert_cmpuint(nm_ip4_config_get_nameserver(dst, 1), ==, expected_ns2); + + g_assert_cmpuint(nm_ip4_config_get_num_domains(dst), ==, 1); + g_assert_cmpstr(nm_ip4_config_get_domain(dst, 0), ==, expected_domain); + g_assert_cmpuint(nm_ip4_config_get_num_searches(dst), ==, 1); + g_assert_cmpstr(nm_ip4_config_get_search(dst, 0), ==, expected_search); + + g_assert_cmpuint(nm_ip4_config_get_num_nis_servers(dst), ==, 1); + g_assert_cmpuint(nm_ip4_config_get_nis_server(dst, 0), ==, expected_nis); + + g_assert_cmpuint(nm_ip4_config_get_num_wins(dst), ==, 1); + g_assert_cmpuint(nm_ip4_config_get_wins(dst, 0), ==, expected_wins); + + g_assert_cmpuint(nm_ip4_config_get_mtu(dst), ==, expected_mtu); + + g_object_unref(src); + g_object_unref(dst); +} + +static void +test_compare_with_source(void) +{ + NMIP4Config * a, *b; + NMPlatformIP4Address addr; + NMPlatformIP4Route route; + + a = nmtst_ip4_config_new(1); + b = nmtst_ip4_config_new(2); + + /* Address */ + addr = *nmtst_platform_ip4_address("1.2.3.4", NULL, 24); + addr.addr_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip4_config_add_address(a, &addr); + + addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip4_config_add_address(b, &addr); + + /* Route */ + route = *nmtst_platform_ip4_route("10.0.0.0", 8, "192.168.1.1"); + route.rt_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip4_config_add_route(a, &route, NULL); + + route.rt_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip4_config_add_route(b, &route, NULL); + + /* Assert that the configs are basically the same, eg that the source is ignored */ + g_assert(nm_ip4_config_equal(a, b)); + + g_object_unref(a); + g_object_unref(b); +} + +static void +test_add_address_with_source(void) +{ + NMIP4Config * a; + NMPlatformIP4Address addr; + const NMPlatformIP4Address *test_addr; + + a = nmtst_ip4_config_new(1); + + /* Test that a higher priority source is not overwritten */ + addr = *nmtst_platform_ip4_address("1.2.3.4", NULL, 24); + addr.addr_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip4_config_add_address(a, &addr); + + test_addr = _nmtst_ip4_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); + + addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip4_config_add_address(a, &addr); + + test_addr = _nmtst_ip4_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); + + /* Test that a lower priority address source is overwritten */ + _nmtst_ip4_config_del_address(a, 0); + addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; + nm_ip4_config_add_address(a, &addr); + + test_addr = _nmtst_ip4_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); + + addr.addr_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip4_config_add_address(a, &addr); + + test_addr = _nmtst_ip4_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); + + g_object_unref(a); +} + +static void +test_add_route_with_source(void) +{ + gs_unref_object NMIP4Config *a = NULL; + NMPlatformIP4Route route; + const NMPlatformIP4Route * test_route; + + a = nmtst_ip4_config_new(1); + + /* Test that a higher priority source is not overwritten */ + route = *nmtst_platform_ip4_route("1.2.3.0", 24, "1.2.3.1"); + route.rt_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip4_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip4_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); + + route.rt_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip4_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip4_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); + + _nmtst_ip4_config_del_route(a, 0); + g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 0); + + /* Test that a lower priority address source is overwritten */ + route.rt_source = NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; + nm_ip4_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip4_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_RTPROT_KERNEL); + + route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + nm_ip4_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip4_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip4_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); +} + +static void +test_merge_subtract_mtu(void) +{ + NMIP4Config *cfg1, *cfg2, *cfg3; + guint32 expected_mtu2 = 1492; + guint32 expected_mtu3 = 666; + + cfg1 = build_test_config(); + cfg2 = build_test_config(); + cfg3 = build_test_config(); + + /* add MSS, MTU to configs to test them */ + nm_ip4_config_set_mtu(cfg2, expected_mtu2, NM_IP_CONFIG_SOURCE_UNKNOWN); + nm_ip4_config_set_mtu(cfg3, expected_mtu3, NM_IP_CONFIG_SOURCE_UNKNOWN); + + nm_ip4_config_merge(cfg1, cfg2, NM_IP_CONFIG_MERGE_DEFAULT, 0); + /* ensure MSS and MTU are in cfg1 */ + g_assert_cmpuint(nm_ip4_config_get_mtu(cfg1), ==, expected_mtu2); + + nm_ip4_config_merge(cfg1, cfg3, NM_IP_CONFIG_MERGE_DEFAULT, 0); + /* ensure again the MSS and MTU in cfg1 got overridden */ + g_assert_cmpuint(nm_ip4_config_get_mtu(cfg1), ==, expected_mtu3); + + nm_ip4_config_subtract(cfg1, cfg3, 0); + /* ensure MSS and MTU are zero in cfg1 */ + g_assert_cmpuint(nm_ip4_config_get_mtu(cfg1), ==, 0); + + g_object_unref(cfg1); + g_object_unref(cfg2); + g_object_unref(cfg3); +} + +static void +test_strip_search_trailing_dot(void) +{ + NMIP4Config *config; + + config = nmtst_ip4_config_new(1); + + nm_ip4_config_add_search(config, "."); + nm_ip4_config_add_search(config, "foo"); + nm_ip4_config_add_search(config, "bar."); + nm_ip4_config_add_search(config, "baz.com"); + nm_ip4_config_add_search(config, "baz.com."); + nm_ip4_config_add_search(config, "foobar.."); + nm_ip4_config_add_search(config, ".foobar"); + nm_ip4_config_add_search(config, "~."); + + g_assert_cmpuint(nm_ip4_config_get_num_searches(config), ==, 4); + g_assert_cmpstr(nm_ip4_config_get_search(config, 0), ==, "foo"); + g_assert_cmpstr(nm_ip4_config_get_search(config, 1), ==, "bar"); + g_assert_cmpstr(nm_ip4_config_get_search(config, 2), ==, "baz.com"); + g_assert_cmpstr(nm_ip4_config_get_search(config, 3), ==, "~"); + + g_object_unref(config); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_with_logging(&argc, &argv, NULL, "DEFAULT"); + + g_test_add_func("/ip4-config/replace", test_replace); + g_test_add_func("/ip4-config/subtract", test_subtract); + g_test_add_func("/ip4-config/compare-with-source", test_compare_with_source); + g_test_add_func("/ip4-config/add-address-with-source", test_add_address_with_source); + g_test_add_func("/ip4-config/add-route-with-source", test_add_route_with_source); + g_test_add_func("/ip4-config/merge-subtract-mtu", test_merge_subtract_mtu); + g_test_add_func("/ip4-config/strip-search-trailing-dot", test_strip_search_trailing_dot); + + return g_test_run(); +} diff --git a/src/core/tests/test-ip6-config.c b/src/core/tests/test-ip6-config.c new file mode 100644 index 00000000..3b48fb7f --- /dev/null +++ b/src/core/tests/test-ip6-config.c @@ -0,0 +1,538 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include +#include + +#include "nm-ip6-config.h" + +#include "platform/nm-platform.h" +#include "nm-test-utils-core.h" + +static NMIP6Config * +build_test_config(void) +{ + NMIP6Config *config; + + /* Build up the config to subtract */ + config = nmtst_ip6_config_new(1); + + nm_ip6_config_add_address(config, + nmtst_platform_ip6_address("abcd:1234:4321::cdde", "1:2:3:4::5", 64)); + nm_ip6_config_add_route( + config, + nmtst_platform_ip6_route("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL), + NULL); + nm_ip6_config_add_route(config, + nmtst_platform_ip6_route("2001::", 16, "2001:abba::2234", NULL), + NULL); + + nm_ip6_config_add_route(config, + nmtst_platform_ip6_route("::", 0, "3001:abba::3234", NULL), + NULL); + + nm_ip6_config_add_nameserver(config, nmtst_inet6_from_string("1:2:3:4::1")); + nm_ip6_config_add_nameserver(config, nmtst_inet6_from_string("1:2:3:4::2")); + nm_ip6_config_add_domain(config, "foobar.com"); + nm_ip6_config_add_domain(config, "baz.com"); + nm_ip6_config_add_search(config, "blahblah.com"); + nm_ip6_config_add_search(config, "beatbox.com"); + + return config; +} + +static void +test_subtract(void) +{ + NMIP6Config * src, *dst; + const NMPlatformIP6Address *test_addr; + const NMPlatformIP6Route * test_route; + const char * expected_addr = "1122:3344:5566::7788"; + guint32 expected_addr_plen = 96; + const char * expected_route_dest = "9991:8800::"; + guint32 expected_route_plen = 24; + const char * expected_route_next_hop = "1119:2228:3337:4446::5555"; + struct in6_addr expected_ns1; + struct in6_addr expected_ns2; + const char * expected_domain = "wonderfalls.com"; + const char * expected_search = "somewhere.com"; + struct in6_addr tmp; + + src = build_test_config(); + + /* add a couple more things to the test config */ + dst = build_test_config(); + nm_ip6_config_add_address(dst, + nmtst_platform_ip6_address(expected_addr, NULL, expected_addr_plen)); + nm_ip6_config_add_route(dst, + nmtst_platform_ip6_route(expected_route_dest, + expected_route_plen, + expected_route_next_hop, + NULL), + NULL); + + expected_ns1 = *nmtst_inet6_from_string("2222:3333:4444::5555"); + nm_ip6_config_add_nameserver(dst, &expected_ns1); + expected_ns2 = *nmtst_inet6_from_string("2222:3333:4444::5556"); + nm_ip6_config_add_nameserver(dst, &expected_ns2); + + nm_ip6_config_add_domain(dst, expected_domain); + nm_ip6_config_add_search(dst, expected_search); + + nm_ip6_config_subtract(dst, src, 0); + + /* ensure what's left is what we expect */ + g_assert_cmpuint(nm_ip6_config_get_num_addresses(dst), ==, 1); + test_addr = _nmtst_ip6_config_get_address(dst, 0); + g_assert(test_addr != NULL); + tmp = *nmtst_inet6_from_string(expected_addr); + g_assert(memcmp(&test_addr->address, &tmp, sizeof(tmp)) == 0); + g_assert(memcmp(&test_addr->peer_address, &in6addr_any, sizeof(tmp)) == 0); + g_assert_cmpuint(test_addr->plen, ==, expected_addr_plen); + + g_assert(nm_ip6_config_best_default_route_get(dst) == NULL); + + g_assert_cmpuint(nm_ip6_config_get_num_routes(dst), ==, 1); + test_route = _nmtst_ip6_config_get_route(dst, 0); + g_assert(test_route != NULL); + + tmp = *nmtst_inet6_from_string(expected_route_dest); + g_assert(memcmp(&test_route->network, &tmp, sizeof(tmp)) == 0); + g_assert_cmpuint(test_route->plen, ==, expected_route_plen); + tmp = *nmtst_inet6_from_string(expected_route_next_hop); + g_assert(memcmp(&test_route->gateway, &tmp, sizeof(tmp)) == 0); + + g_assert_cmpuint(nm_ip6_config_get_num_nameservers(dst), ==, 2); + g_assert(memcmp(nm_ip6_config_get_nameserver(dst, 0), &expected_ns1, sizeof(expected_ns1)) + == 0); + g_assert(memcmp(nm_ip6_config_get_nameserver(dst, 1), &expected_ns2, sizeof(expected_ns2)) + == 0); + + g_assert_cmpuint(nm_ip6_config_get_num_domains(dst), ==, 1); + g_assert_cmpstr(nm_ip6_config_get_domain(dst, 0), ==, expected_domain); + g_assert_cmpuint(nm_ip6_config_get_num_searches(dst), ==, 1); + g_assert_cmpstr(nm_ip6_config_get_search(dst, 0), ==, expected_search); + + g_object_unref(src); + g_object_unref(dst); +} + +static void +test_compare_with_source(void) +{ + NMIP6Config * a, *b; + NMPlatformIP6Address addr; + NMPlatformIP6Route route; + + a = nmtst_ip6_config_new(1); + b = nmtst_ip6_config_new(2); + + /* Address */ + addr = *nmtst_platform_ip6_address("1122:3344:5566::7788", NULL, 64); + addr.addr_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip6_config_add_address(a, &addr); + + addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip6_config_add_address(b, &addr); + + /* Route */ + route = *nmtst_platform_ip6_route("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL); + route.rt_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip6_config_add_route(a, &route, NULL); + + route.rt_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip6_config_add_route(b, &route, NULL); + + /* Assert that the configs are basically the same, eg that the source is ignored */ + g_assert(nm_ip6_config_equal(a, b)); + + g_object_unref(a); + g_object_unref(b); +} + +static void +test_add_address_with_source(void) +{ + NMIP6Config * a; + NMPlatformIP6Address addr; + const NMPlatformIP6Address *test_addr; + + a = nmtst_ip6_config_new(1); + + /* Test that a higher priority source is not overwritten */ + addr = *nmtst_platform_ip6_address("1122:3344:5566::7788", NULL, 64); + addr.addr_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip6_config_add_address(a, &addr); + + test_addr = _nmtst_ip6_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); + + addr.addr_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip6_config_add_address(a, &addr); + + test_addr = _nmtst_ip6_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); + + /* Test that a lower priority address source is overwritten */ + _nmtst_ip6_config_del_address(a, 0); + addr.addr_source = NM_IP_CONFIG_SOURCE_KERNEL; + nm_ip6_config_add_address(a, &addr); + + test_addr = _nmtst_ip6_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); + + addr.addr_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip6_config_add_address(a, &addr); + + test_addr = _nmtst_ip6_config_get_address(a, 0); + g_assert_cmpint(test_addr->addr_source, ==, NM_IP_CONFIG_SOURCE_USER); + + g_object_unref(a); +} + +static void +test_add_route_with_source(void) +{ + gs_unref_object NMIP6Config *a = NULL; + NMPlatformIP6Route route; + const NMPlatformIP6Route * test_route; + + a = nmtst_ip6_config_new(1); + + /* Test that a higher priority source is not overwritten */ + route = *nmtst_platform_ip6_route("abcd:1200::", 24, "abcd:1234:4321:cdde::2", NULL); + route.rt_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip6_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip6_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); + + route.rt_source = NM_IP_CONFIG_SOURCE_VPN; + nm_ip6_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip6_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); + + _nmtst_ip6_config_del_route(a, 0); + g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 0); + + /* Test that a lower priority address source is overwritten */ + route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + nm_ip6_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip6_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_KERNEL); + + route.rt_source = NM_IP_CONFIG_SOURCE_USER; + nm_ip6_config_add_route(a, &route, NULL); + + g_assert_cmpint(nm_ip6_config_get_num_routes(a), ==, 1); + test_route = _nmtst_ip6_config_get_route(a, 0); + g_assert_cmpint(test_route->rt_source, ==, NM_IP_CONFIG_SOURCE_USER); +} + +static void +test_nm_ip6_config_addresses_sort_check(NMIP6Config * config, + NMSettingIP6ConfigPrivacy use_tempaddr, + int repeat) +{ + int addr_count = nm_ip6_config_get_num_addresses(config); + int i, irepeat; + NMIP6Config *copy, *copy2; + int * idx = g_new(int, addr_count); + + nm_ip6_config_set_privacy(config, use_tempaddr); + copy = nm_ip6_config_clone(config); + g_assert(copy); + copy2 = nm_ip6_config_clone(config); + g_assert(copy2); + + /* initialize the array of indices, and keep shuffling them for every @repeat iteration. */ + for (i = 0; i < addr_count; i++) + idx[i] = i; + + for (irepeat = 0; irepeat < repeat; irepeat++) { + /* randomly shuffle the addresses. */ + nm_ip6_config_reset_addresses(copy); + for (i = 0; i < addr_count; i++) { + int j = g_rand_int_range(nmtst_get_rand(), i, addr_count); + + NM_SWAP(&idx[i], &idx[j]); + nm_ip6_config_add_address(copy, _nmtst_ip6_config_get_address(config, idx[i])); + } + + /* reorder them again */ + _nmtst_ip6_config_addresses_sort(copy); + + /* check equality using nm_ip6_config_equal() */ + if (!nm_ip6_config_equal(copy, config)) { + g_message("%s", "SORTING yields unexpected output:"); + for (i = 0; i < addr_count; i++) { + g_message( + " >> [%d] = %s", + i, + nm_platform_ip6_address_to_string(_nmtst_ip6_config_get_address(config, i), + NULL, + 0)); + g_message(" << [%d] = %s", + i, + nm_platform_ip6_address_to_string(_nmtst_ip6_config_get_address(copy, i), + NULL, + 0)); + } + g_assert_not_reached(); + } + + /* also check equality using nm_ip6_config_replace() */ + g_assert(nm_ip6_config_replace(copy2, copy, NULL) == FALSE); + } + + g_free(idx); + g_object_unref(copy); + g_object_unref(copy2); +} + +static void +test_nm_ip6_config_addresses_sort(void) +{ + NMIP6Config *config = build_test_config(); + +#define ADDR_ADD(...) \ + nm_ip6_config_add_address(config, nmtst_platform_ip6_address_full(__VA_ARGS__)) + + nm_ip6_config_reset_addresses(config); + ADDR_ADD("2607:f0d0:1002:51::4", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::5", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::6", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_NDISC, + 0, + 0, + 0, + IFA_F_MANAGETEMPADDR); + ADDR_ADD("2607:f0d0:1002:51::3", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0, + 0, + IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::8", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0, + 0, + IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::0", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_KERNEL, + 0, + 0, + 0, + IFA_F_TEMPORARY); + ADDR_ADD("fec0::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::2", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0, + 0, + IFA_F_TENTATIVE); + test_nm_ip6_config_addresses_sort_check(config, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, 8); + test_nm_ip6_config_addresses_sort_check(config, NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED, 8); + test_nm_ip6_config_addresses_sort_check(config, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR, + 8); + + nm_ip6_config_reset_addresses(config); + ADDR_ADD("2607:f0d0:1002:51::3", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0, + 0, + IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::4", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::5", NULL, 64, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::8", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0, + 0, + IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::0", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_KERNEL, + 0, + 0, + 0, + IFA_F_TEMPORARY); + ADDR_ADD("2607:f0d0:1002:51::6", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_NDISC, + 0, + 0, + 0, + IFA_F_MANAGETEMPADDR); + ADDR_ADD("fec0::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625c", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("fe80::208:74ff:feda:625d", NULL, 128, 0, NM_IP_CONFIG_SOURCE_KERNEL, 0, 0, 0, 0); + ADDR_ADD("::1", NULL, 128, 0, NM_IP_CONFIG_SOURCE_USER, 0, 0, 0, 0); + ADDR_ADD("2607:f0d0:1002:51::2", + NULL, + 64, + 0, + NM_IP_CONFIG_SOURCE_USER, + 0, + 0, + 0, + IFA_F_TENTATIVE); + test_nm_ip6_config_addresses_sort_check(config, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + 8); + +#undef ADDR_ADD + g_object_unref(config); +} + +static void +test_strip_search_trailing_dot(void) +{ + NMIP6Config *config; + + config = nmtst_ip6_config_new(1); + + nm_ip6_config_add_search(config, "."); + nm_ip6_config_add_search(config, "foo"); + nm_ip6_config_add_search(config, "bar."); + nm_ip6_config_add_search(config, "baz.com"); + nm_ip6_config_add_search(config, "baz.com."); + nm_ip6_config_add_search(config, "foobar.."); + nm_ip6_config_add_search(config, ".foobar"); + nm_ip6_config_add_search(config, "~."); + + g_assert_cmpuint(nm_ip6_config_get_num_searches(config), ==, 4); + g_assert_cmpstr(nm_ip6_config_get_search(config, 0), ==, "foo"); + g_assert_cmpstr(nm_ip6_config_get_search(config, 1), ==, "bar"); + g_assert_cmpstr(nm_ip6_config_get_search(config, 2), ==, "baz.com"); + g_assert_cmpstr(nm_ip6_config_get_search(config, 3), ==, "~"); + + g_object_unref(config); +} + +/*****************************************************************************/ + +static void +test_replace(gconstpointer user_data) +{ + nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = nm_dedup_multi_index_new(); + const int TEST_IDX = GPOINTER_TO_INT(user_data); + const int IFINDEX = 1; + gs_unref_object NMIP6Config *src_conf = NULL; + gs_unref_object NMIP6Config *dst_conf = NULL; + NMPlatformIP6Address * addr; + NMPlatformIP6Address addrs[5] = {}; + guint addrs_n = 0; + guint i; + + dst_conf = nm_ip6_config_new(multi_idx, IFINDEX); + src_conf = nm_ip6_config_new(multi_idx, IFINDEX); + + switch (TEST_IDX) { + case 1: + addr = &addrs[addrs_n++]; + addr->ifindex = IFINDEX; + addr->address = *nmtst_inet6_from_string("fe80::78ec:7a6d:602d:20f2"); + addr->plen = 64; + addr->n_ifa_flags = IFA_F_PERMANENT; + addr->addr_source = NM_IP_CONFIG_SOURCE_KERNEL; + break; + case 2: + addr = &addrs[addrs_n++]; + addr->ifindex = IFINDEX; + addr->address = *nmtst_inet6_from_string("fe80::78ec:7a6d:602d:20f2"); + addr->plen = 64; + addr->n_ifa_flags = IFA_F_PERMANENT; + addr->addr_source = NM_IP_CONFIG_SOURCE_KERNEL; + + addr = &addrs[addrs_n++]; + addr->ifindex = IFINDEX; + addr->address = *nmtst_inet6_from_string("1::1"); + addr->plen = 64; + addr->addr_source = NM_IP_CONFIG_SOURCE_USER; + + nm_ip6_config_add_address(dst_conf, addr); + break; + default: + g_assert_not_reached(); + } + + g_assert(addrs_n < G_N_ELEMENTS(addrs)); + + for (i = 0; i < addrs_n; i++) + nm_ip6_config_add_address(src_conf, &addrs[i]); + + nm_ip6_config_replace(dst_conf, src_conf, NULL); + + for (i = 0; i < addrs_n; i++) { + const NMPlatformIP6Address *a = _nmtst_ip6_config_get_address(dst_conf, i); + const NMPlatformIP6Address *b = _nmtst_ip6_config_get_address(src_conf, i); + + g_assert(nm_platform_ip6_address_cmp(&addrs[i], a) == 0); + g_assert(nm_platform_ip6_address_cmp(&addrs[i], b) == 0); + } + g_assert(addrs_n == nm_ip6_config_get_num_addresses(dst_conf)); + g_assert(addrs_n == nm_ip6_config_get_num_addresses(src_conf)); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_with_logging(&argc, &argv, NULL, "ALL"); + + g_test_add_func("/ip6-config/subtract", test_subtract); + g_test_add_func("/ip6-config/compare-with-source", test_compare_with_source); + g_test_add_func("/ip6-config/add-address-with-source", test_add_address_with_source); + g_test_add_func("/ip6-config/add-route-with-source", test_add_route_with_source); + g_test_add_func("/ip6-config/test_nm_ip6_config_addresses_sort", + test_nm_ip6_config_addresses_sort); + g_test_add_func("/ip6-config/strip-search-trailing-dot", test_strip_search_trailing_dot); + g_test_add_data_func("/ip6-config/replace/1", GINT_TO_POINTER(1), test_replace); + g_test_add_data_func("/ip6-config/replace/2", GINT_TO_POINTER(2), test_replace); + + return g_test_run(); +} diff --git a/src/core/tests/test-l3cfg.c b/src/core/tests/test-l3cfg.c new file mode 100644 index 00000000..43132c6d --- /dev/null +++ b/src/core/tests/test-l3cfg.c @@ -0,0 +1,795 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-l3cfg.h" +#include "nm-l3-ipv4ll.h" +#include "nm-netns.h" +#include "platform/nm-platform.h" + +#include "platform/tests/test-common.h" + +/*****************************************************************************/ + +static NML3Cfg * +_netns_access_l3cfg(NMNetns *netns, int ifindex) +{ + NML3Cfg *l3cfg; + + g_assert(NM_IS_NETNS(netns)); + g_assert(ifindex > 0); + + g_assert(!nm_netns_get_l3cfg(netns, ifindex)); + + l3cfg = nm_netns_access_l3cfg(netns, ifindex); + g_assert(NM_IS_L3CFG(l3cfg)); + return l3cfg; +} + +/*****************************************************************************/ + +typedef struct { + int test_idx; + NMPlatform * platform; + NMNetns * netns; + NMDedupMultiIndex *multiidx; + const char * ifname0; + const char * ifname1; + NMPLinkAddress hwaddr0; + NMPLinkAddress hwaddr1; + int ifindex0; + int ifindex1; +} TestFixture1; + +static const TestFixture1 * +_test_fixture_1_setup(TestFixture1 *f, int test_idx) +{ + const NMPlatformLink *l0; + const NMPlatformLink *l1; + const NMEtherAddr addr0 = NM_ETHER_ADDR_INIT(0xAA, 0xAA, test_idx, 0x00, 0x00, 0x00); + const NMEtherAddr addr1 = NM_ETHER_ADDR_INIT(0xAA, 0xAA, test_idx, 0x00, 0x00, 0x11); + + g_assert_cmpint(test_idx, >, 0); + g_assert_cmpint(f->test_idx, ==, 0); + + f->test_idx = test_idx; + + f->ifname0 = "nm-test-veth0"; + f->ifname1 = "nm-test-veth1"; + + f->platform = g_object_ref(NM_PLATFORM_GET); + f->multiidx = nm_dedup_multi_index_ref(nm_platform_get_multi_idx(f->platform)); + f->netns = nm_netns_new(f->platform); + + nmtstp_link_veth_add(f->platform, -1, f->ifname0, f->ifname1); + + l0 = nmtstp_link_get_typed(f->platform, -1, f->ifname0, NM_LINK_TYPE_VETH); + l1 = nmtstp_link_get_typed(f->platform, -1, f->ifname1, NM_LINK_TYPE_VETH); + + f->ifindex0 = l0->ifindex; + f->ifindex1 = l1->ifindex; + + g_assert_cmpint(nm_platform_link_set_address(f->platform, f->ifindex0, &addr0, sizeof(addr0)), + ==, + 0); + g_assert_cmpint(nm_platform_link_set_address(f->platform, f->ifindex1, &addr1, sizeof(addr1)), + ==, + 0); + + l0 = nmtstp_link_get_typed(f->platform, f->ifindex0, f->ifname0, NM_LINK_TYPE_VETH); + l1 = nmtstp_link_get_typed(f->platform, f->ifindex1, f->ifname1, NM_LINK_TYPE_VETH); + + f->hwaddr0 = l0->l_address; + f->hwaddr1 = l1->l_address; + + g_assert(nm_platform_link_set_up(f->platform, f->ifindex0, NULL)); + g_assert(nm_platform_link_set_up(f->platform, f->ifindex1, NULL)); + + return f; +} + +static void +_test_fixture_1_teardown(TestFixture1 *f) +{ + g_assert(f); + + if (f->test_idx == 0) + return; + + _LOGD("test teatdown"); + + nmtstp_link_delete(f->platform, -1, f->ifindex0, f->ifname0, TRUE); + g_assert(!nm_platform_link_get(f->platform, f->ifindex0)); + g_assert(!nm_platform_link_get(f->platform, f->ifindex1)); + g_assert(!nm_platform_link_get_by_ifname(f->platform, f->ifname0)); + g_assert(!nm_platform_link_get_by_ifname(f->platform, f->ifname1)); + + g_object_unref(f->netns); + g_object_unref(f->platform); + nm_dedup_multi_index_unref(f->multiidx); + + *f = (TestFixture1){ + .test_idx = 0, + }; +} + +/*****************************************************************************/ + +typedef enum { + TEST_L3CFG_NOTIFY_TYPE_NONE, + TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL, + TEST_L3CFG_NOTIFY_TYPE_COMMIT_1, + TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1, +} TestL3cfgNotifyType; + +typedef struct { + const TestFixture1 *f; + + bool has_addr4_101 : 1; + bool add_addr4_101 : 1; + + guint32 acd_timeout_msec_a; + NML3AcdDefendType acd_defend_type_a; + + TestL3cfgNotifyType notify_type; + guint post_commit_event_count; + guint general_event_count; + guint general_event_flags; + union { + struct { + int cb_count; + bool expected_probe_result : 1; + bool acd_event_ready_45 : 1; + bool acd_event_ready_101 : 1; + } wait_for_acd_ready_1; + } notify_result; +} TestL3cfgData; + +static void +_test_l3cfg_data_set_notify_type(TestL3cfgData *tdata, TestL3cfgNotifyType notify_type) +{ + g_assert(tdata); + + tdata->notify_type = notify_type; + tdata->post_commit_event_count = 0; + tdata->general_event_count = 0; + tdata->general_event_flags = 0; + memset(&tdata->notify_result, 0, sizeof(tdata->notify_result)); +} + +static void +_test_l3cfg_signal_notify(NML3Cfg * l3cfg, + const NML3ConfigNotifyData *notify_data, + TestL3cfgData * tdata) +{ + guint i; + + g_assert(NM_IS_L3CFG(l3cfg)); + g_assert(tdata); + g_assert(notify_data); + g_assert(_NM_INT_NOT_NEGATIVE(notify_data->notify_type)); + g_assert(notify_data->notify_type < _NM_L3_CONFIG_NOTIFY_TYPE_NUM); + + if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE) + g_assert(notify_data->platform_change_on_idle.obj_type_flags != 0u); + else if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE) { + g_assert(NMP_OBJECT_IS_VALID(notify_data->platform_change.obj)); + g_assert(notify_data->platform_change.change_type != 0); + } else if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT) { + g_assert_cmpint(notify_data->acd_event.info.n_track_infos, >=, 1); + g_assert(notify_data->acd_event.info.track_infos); + for (i = 0; i < notify_data->acd_event.info.n_track_infos; i++) { + const NML3AcdAddrTrackInfo *ti = ¬ify_data->acd_event.info.track_infos[i]; + + nm_assert(NMP_OBJECT_GET_TYPE(ti->obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); + nm_assert(NMP_OBJECT_CAST_IP4_ADDRESS(ti->obj)->address + == notify_data->acd_event.info.addr); + nm_assert(NM_IS_L3_CONFIG_DATA(ti->l3cd)); + nm_assert(ti->tag); + } + } + + switch (tdata->notify_type) { + case TEST_L3CFG_NOTIFY_TYPE_NONE: + g_assert_not_reached(); + break; + case TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL: + if (NM_IN_SET(notify_data->notify_type, + NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE, + NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE)) + return; + g_assert_not_reached(); + return; + case TEST_L3CFG_NOTIFY_TYPE_COMMIT_1: + g_assert_cmpint(tdata->post_commit_event_count, ==, 0); + switch (notify_data->notify_type) { + case NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT: + tdata->post_commit_event_count++; + return; + case NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT: + switch (tdata->f->test_idx) { + case 2: + case 3: + nmtst_assert_ip4_address(notify_data->acd_event.info.addr, "192.168.133.45"); + if (tdata->f->test_idx == 2) + g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_DEFENDING); + else + g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_PROBING); + g_assert(tdata->general_event_count == 0); + tdata->general_event_count++; + return; + case 4: + if (notify_data->acd_event.info.addr == nmtst_inet4_from_string("192.168.133.45")) { + g_assert(!NM_FLAGS_HAS(tdata->general_event_flags, 0x1u)); + tdata->general_event_flags |= 0x1u; + g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_PROBING); + tdata->general_event_count++; + } else if (notify_data->acd_event.info.addr + == nmtst_inet4_from_string("192.168.133.101")) { + g_assert(!NM_FLAGS_HAS(tdata->general_event_flags, 0x4u)); + tdata->general_event_flags |= 0x4u; + g_assert(notify_data->acd_event.info.state == NM_L3_ACD_ADDR_STATE_PROBING); + tdata->general_event_count++; + } else + g_assert_not_reached(); + return; + default: + g_assert_not_reached(); + return; + } + case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE: + return; + default: + g_assert_not_reached(); + return; + } + case TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1: + { + int num_acd_completed_events = + 1 + 2 + (tdata->add_addr4_101 ? (tdata->has_addr4_101 ? 1 : 3) : 0); + + if (NM_IN_SET(notify_data->notify_type, + NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE, + NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE)) + return; + if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_ACD_EVENT) { + if (notify_data->acd_event.info.addr == nmtst_inet4_from_string("192.168.133.45")) { + g_assert(NM_IN_SET(notify_data->acd_event.info.state, + NM_L3_ACD_ADDR_STATE_READY, + NM_L3_ACD_ADDR_STATE_DEFENDING)); + tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_45 = TRUE; + } else if (notify_data->acd_event.info.addr + == nmtst_inet4_from_string("192.168.133.101")) { + if (tdata->has_addr4_101) { + g_assert( + NM_IN_SET(notify_data->acd_event.info.state, NM_L3_ACD_ADDR_STATE_USED)); + } else { + g_assert(NM_IN_SET(notify_data->acd_event.info.state, + NM_L3_ACD_ADDR_STATE_READY, + NM_L3_ACD_ADDR_STATE_DEFENDING)); + tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_101 = TRUE; + } + } else + g_assert_not_reached(); + + g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count, + <, + num_acd_completed_events); + tdata->notify_result.wait_for_acd_ready_1.cb_count++; + return; + } + if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_POST_COMMIT) { + g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count, >, 0); + g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count, + <, + num_acd_completed_events); + tdata->notify_result.wait_for_acd_ready_1.cb_count++; + nmtstp_platform_ip_addresses_assert( + tdata->f->platform, + tdata->f->ifindex0, + TRUE, + TRUE, + TRUE, + tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_45 ? "192.168.133.45" + : NULL, + tdata->notify_result.wait_for_acd_ready_1.acd_event_ready_101 ? "192.168.133.101" + : NULL, + "1:2:3:4::45"); + return; + } + g_assert_not_reached(); + return; + } + } + + g_assert_not_reached(); +} + +static void +test_l3cfg(gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT(test_data); + const guint32 ACD_TIMEOUT_BASE_MSEC = 1000; + nm_auto(_test_fixture_1_teardown) TestFixture1 test_fixture = {}; + const TestFixture1 * f; + NML3CfgCommitTypeHandle * commit_type_1; + NML3CfgCommitTypeHandle * commit_type_2; + gs_unref_object NML3Cfg *l3cfg0 = NULL; + nm_auto_unref_l3cd const NML3ConfigData *l3cd_a = NULL; + TestL3cfgData tdata_stack = { + .f = NULL, + }; + TestL3cfgData *const tdata = &tdata_stack; + + _LOGD("test start (/l3cfg/%d)", TEST_IDX); + + if (nmtst_test_quick()) { + gs_free char *msg = + g_strdup_printf("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", + g_get_prgname() ?: "test-l3cfg"); + + g_test_skip(msg); + return; + } + + f = _test_fixture_1_setup(&test_fixture, TEST_IDX); + + tdata->f = f; + tdata->has_addr4_101 = (f->test_idx == 4 && nmtst_get_rand_bool()); + tdata->add_addr4_101 = (f->test_idx == 4 && nmtst_get_rand_bool()); + + tdata->acd_timeout_msec_a = NM_IN_SET(f->test_idx, 3, 4) ? ACD_TIMEOUT_BASE_MSEC : 0u; + tdata->acd_defend_type_a = NM_IN_SET(f->test_idx, 4) + ? nmtst_rand_select(NM_L3_ACD_DEFEND_TYPE_NEVER, + NM_L3_ACD_DEFEND_TYPE_ONCE, + NM_L3_ACD_DEFEND_TYPE_ALWAYS) + : NM_L3_ACD_DEFEND_TYPE_NEVER; + + if (tdata->has_addr4_101) { + nmtstp_ip4_address_add(f->platform, + -1, + f->ifindex1, + nmtst_inet4_from_string("192.168.133.101"), + 24, + nmtst_inet4_from_string("192.168.133.101"), + 100000, + 0, + 0, + NULL); + } + + l3cfg0 = _netns_access_l3cfg(f->netns, f->ifindex0); + + g_signal_connect(l3cfg0, NM_L3CFG_SIGNAL_NOTIFY, G_CALLBACK(_test_l3cfg_signal_notify), tdata); + + commit_type_1 = nm_l3cfg_commit_type_register(l3cfg0, NM_L3_CFG_COMMIT_TYPE_UPDATE, NULL); + + if (!nmtst_get_rand_one_case_in(4)) { + commit_type_2 = + nm_l3cfg_commit_type_register(l3cfg0, + nmtst_rand_select(NM_L3_CFG_COMMIT_TYPE_NONE, + NM_L3_CFG_COMMIT_TYPE_ASSUME, + NM_L3_CFG_COMMIT_TYPE_UPDATE), + NULL); + } else + commit_type_2 = NULL; + + switch (f->test_idx) { + case 1: + break; + case 2: + case 3: + case 4: + { + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + + l3cd = nm_l3_config_data_new(f->multiidx, f->ifindex0); + + nm_l3_config_data_add_address_4( + l3cd, + NM_PLATFORM_IP4_ADDRESS_INIT(.address = nmtst_inet4_from_string("192.168.133.45"), + .peer_address = nmtst_inet4_from_string("192.168.133.45"), + .plen = 24, )); + + if (tdata->add_addr4_101) { + nm_l3_config_data_add_address_4( + l3cd, + NM_PLATFORM_IP4_ADDRESS_INIT(.address = nmtst_inet4_from_string("192.168.133.101"), + .peer_address = + nmtst_inet4_from_string("192.168.133.101"), + .plen = 24, )); + } + + nm_l3_config_data_add_address_6( + l3cd, + NM_PLATFORM_IP6_ADDRESS_INIT(.address = *nmtst_inet6_from_string("1:2:3:4::45"), + .plen = 64, )); + + if (nmtst_get_rand_one_case_in(2)) + nm_l3_config_data_seal(l3cd); + l3cd_a = g_steal_pointer(&l3cd); + break; + } + } + + nm_l3_config_data_log(l3cd_a, "l3cd_a", "platform-test: l3cd_a: ", LOGL_DEBUG, LOGD_PLATFORM); + + if (l3cd_a) { + nm_l3cfg_add_config(l3cfg0, + GINT_TO_POINTER('a'), + nmtst_get_rand_bool(), + l3cd_a, + 'a', + 0, + 0, + NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP4, + NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6, + 0, + 0, + tdata->acd_defend_type_a, + tdata->acd_timeout_msec_a, + NM_L3_CONFIG_MERGE_FLAGS_NONE); + } + + nm_l3_config_data_log(nm_l3cfg_get_combined_l3cd(l3cfg0, FALSE), + "test", + "platform-test: l3cfg0: ", + LOGL_DEBUG, + LOGD_PLATFORM); + + _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_COMMIT_1); + nm_l3cfg_commit(l3cfg0, NM_L3_CFG_COMMIT_TYPE_REAPPLY); + g_assert_cmpint(tdata->post_commit_event_count, ==, 1); + _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE); + + nmtstp_platform_ip_addresses_assert(tdata->f->platform, + tdata->f->ifindex0, + TRUE, + TRUE, + TRUE, + NM_IN_SET(f->test_idx, 2) ? "192.168.133.45" : NULL, + NM_IN_SET(f->test_idx, 2, 3, 4) ? "1:2:3:4::45" : NULL); + + if (NM_IN_SET(f->test_idx, 1, 2)) { + _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_IDLE_ASSERT_NO_SIGNAL); + _LOGT("poll 1 start"); + nmtst_main_context_iterate_until(NULL, + nmtst_get_rand_uint32() % (ACD_TIMEOUT_BASE_MSEC * 5u), + FALSE); + _LOGT("poll 1 end"); + _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE); + } + + if (NM_IN_SET(f->test_idx, 3, 4)) { + _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_WAIT_FOR_ACD_READY_1); + tdata->notify_result.wait_for_acd_ready_1.expected_probe_result = TRUE; + _LOGT("poll 2 start"); + nmtst_main_context_iterate_until( + NULL, + ACD_TIMEOUT_BASE_MSEC * 3u / 2u + + (nmtst_get_rand_uint32() % (2u * ACD_TIMEOUT_BASE_MSEC)), + FALSE); + _LOGT("poll 2 end"); + g_assert_cmpint(tdata->notify_result.wait_for_acd_ready_1.cb_count, + ==, + 1 + 2 + (tdata->add_addr4_101 ? (tdata->has_addr4_101 ? 1 : 3) : 0)); + _test_l3cfg_data_set_notify_type(tdata, TEST_L3CFG_NOTIFY_TYPE_NONE); + } + + g_signal_handlers_disconnect_by_func(l3cfg0, G_CALLBACK(_test_l3cfg_signal_notify), tdata); + + nm_l3cfg_commit_type_unregister(l3cfg0, commit_type_1); + nm_l3cfg_commit_type_unregister(l3cfg0, commit_type_2); + + if (nmtst_get_rand_one_case_in(3)) + _test_fixture_1_teardown(&test_fixture); + + nm_l3cfg_remove_config_all(l3cfg0, GINT_TO_POINTER('a'), FALSE); + + if (nmtst_get_rand_one_case_in(3)) + _test_fixture_1_teardown(&test_fixture); + + _LOGD("test end (/l3cfg/%d)", TEST_IDX); +} + +/*****************************************************************************/ + +#define L3IPV4LL_ACD_TIMEOUT_MSEC 1500u + +typedef struct { + const TestFixture1 * f; + NML3CfgCommitTypeHandle *l3cfg_commit_type_1; + guint acd_timeout_msec; + NML3IPv4LL * l3ipv4ll; + bool has_addr4_101; + gint8 ready_seen; + gint8 addr_commit; + in_addr_t addr_commit_addr; + bool add_conflict_checked : 1; + bool add_conflict_done; +} TestL3IPv4LLData; + +static gconstpointer +TEST_L3_IPV4LL_TAG(const TestL3IPv4LLData *tdata, guint offset) +{ + return (&(((const char *) tdata)[offset])); +} + +static void +_test_l3_ipv4ll_maybe_add_addr_4(const TestL3IPv4LLData *tdata, + int ifindex, + guint one_case_in_num, + bool * has_addr, + const char * addr) +{ + if (has_addr) { + if (*has_addr || !nmtst_get_rand_one_case_in(one_case_in_num)) + return; + *has_addr = TRUE; + } + + if (ifindex == 0) + ifindex = tdata->f->ifindex0; + + g_assert_cmpint(ifindex, >, 0); + + _LOGT("add test address: %s on ifindex=%d", addr, ifindex); + + nmtstp_ip4_address_add(tdata->f->platform, + -1, + ifindex, + nmtst_inet4_from_string(addr), + 24, + nmtst_inet4_from_string(addr), + 100000, + 0, + 0, + NULL); +} + +static void +_test_l3_ipv4ll_signal_notify(NML3Cfg * l3cfg, + const NML3ConfigNotifyData *notify_data, + TestL3IPv4LLData * tdata) +{ + char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + + g_assert(NM_IS_L3CFG(l3cfg)); + g_assert(tdata); + g_assert(notify_data); + g_assert(_NM_INT_NOT_NEGATIVE(notify_data->notify_type)); + g_assert(notify_data->notify_type < _NM_L3_CONFIG_NOTIFY_TYPE_NUM); + + if (notify_data->notify_type == NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT) { + g_assert(tdata->l3ipv4ll == notify_data->ipv4ll_event.ipv4ll); + g_assert(NM_IN_SET(tdata->ready_seen, 0, 1)); + g_assert(NM_IN_SET(tdata->addr_commit, 0, 1)); + + if (nm_l3_ipv4ll_get_state(tdata->l3ipv4ll) == NM_L3_IPV4LL_STATE_READY) { + g_assert_cmpint(tdata->ready_seen, ==, 0); + g_assert_cmpint(tdata->addr_commit, ==, 0); + tdata->ready_seen++; + + if (tdata->f->test_idx == 2 && nmtst_get_rand_bool()) { + tdata->addr_commit++; + tdata->addr_commit_addr = nm_l3_ipv4ll_get_addr(tdata->l3ipv4ll); + g_assert(nm_utils_ip4_address_is_link_local(tdata->addr_commit_addr)); + _LOGT("add address %s that passed ACD", + _nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); + if (!nm_l3cfg_add_config(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll), + TEST_L3_IPV4LL_TAG(tdata, 1), + nmtst_get_rand_bool(), + nm_l3_ipv4ll_get_l3cd(tdata->l3ipv4ll), + NM_L3CFG_CONFIG_PRIORITY_IPV4LL, + 0, + 0, + 104, + 105, + 0, + 0, + NM_L3_ACD_DEFEND_TYPE_ONCE, + nmtst_get_rand_bool() ? tdata->acd_timeout_msec : 0u, + NM_L3_CONFIG_MERGE_FLAGS_NONE)) + g_assert_not_reached(); + nm_l3cfg_commit_on_idle_schedule(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll)); + + tdata->l3cfg_commit_type_1 = + nm_l3cfg_commit_type_register(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll), + NM_L3_CFG_COMMIT_TYPE_UPDATE, + tdata->l3cfg_commit_type_1); + } + } else if (nm_l3_ipv4ll_get_state(tdata->l3ipv4ll) != NM_L3_IPV4LL_STATE_DEFENDING + && tdata->ready_seen > 0) { + g_assert_cmpint(tdata->ready_seen, ==, 1); + tdata->ready_seen--; + if (tdata->addr_commit > 0) { + g_assert_cmpint(tdata->addr_commit, ==, 1); + tdata->addr_commit--; + g_assert(nm_utils_ip4_address_is_link_local(tdata->addr_commit_addr)); + _LOGT("remove address %s that previously passed ACD", + _nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); + if (!nm_l3cfg_remove_config_all(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll), + TEST_L3_IPV4LL_TAG(tdata, 1), + FALSE)) + g_assert_not_reached(); + nm_l3cfg_commit_on_idle_schedule(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll)); + nm_l3cfg_commit_type_unregister(nm_l3_ipv4ll_get_l3cfg(tdata->l3ipv4ll), + g_steal_pointer(&tdata->l3cfg_commit_type_1)); + } + } + return; + } +} + +static void +test_l3_ipv4ll(gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT(test_data); + nm_auto(_test_fixture_1_teardown) TestFixture1 test_fixture = {}; + const TestFixture1 * f; + gs_unref_object NML3Cfg *l3cfg0 = NULL; + TestL3IPv4LLData tdata_stack = { + .f = NULL, + }; + TestL3IPv4LLData *const tdata = &tdata_stack; + NMTstpAcdDefender * acd_defender_1 = NULL; + NMTstpAcdDefender * acd_defender_2 = NULL; + nm_auto_unref_l3ipv4ll NML3IPv4LL * l3ipv4ll = NULL; + gint64 start_time_msec; + gint64 total_poll_time_msec; + nm_auto_remove_l3ipv4ll_registration NML3IPv4LLRegistration *l3ipv4ll_reg = NULL; + char sbuf_addr[NM_UTILS_INET_ADDRSTRLEN]; + + _LOGD("test start (/l3-ipv4ll/%d)", TEST_IDX); + + if (nmtst_test_quick()) { + gs_free char *msg = + g_strdup_printf("Skipping test: don't run long running test %s (NMTST_DEBUG=slow)\n", + g_get_prgname() ?: "test-l3-ipv4ll"); + + g_test_skip(msg); + return; + } + + f = _test_fixture_1_setup(&test_fixture, TEST_IDX); + + tdata->f = f; + + if (tdata->f->test_idx == 1) + tdata->acd_timeout_msec = 0; + else + tdata->acd_timeout_msec = L3IPV4LL_ACD_TIMEOUT_MSEC; + + _test_l3_ipv4ll_maybe_add_addr_4(tdata, 0, 4, &tdata->has_addr4_101, "192.168.133.101"); + + l3cfg0 = _netns_access_l3cfg(f->netns, f->ifindex0); + + g_signal_connect(l3cfg0, + NM_L3CFG_SIGNAL_NOTIFY, + G_CALLBACK(_test_l3_ipv4ll_signal_notify), + tdata); + + l3ipv4ll = nm_l3_ipv4ll_new(l3cfg0); + + tdata->l3ipv4ll = l3ipv4ll; + + g_assert_cmpint(nm_l3_ipv4ll_get_ifindex(l3ipv4ll), ==, f->ifindex0); + g_assert_cmpint(nm_l3_ipv4ll_get_state(l3ipv4ll), ==, NM_L3_IPV4LL_STATE_DISABLED); + g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll), ==, 0u); + + if (tdata->f->test_idx == 1) { + if (nmtst_get_rand_one_case_in(2)) + l3ipv4ll_reg = nm_l3_ipv4ll_register_new(l3ipv4ll, tdata->acd_timeout_msec); + } else + l3ipv4ll_reg = nm_l3_ipv4ll_register_new(l3ipv4ll, tdata->acd_timeout_msec); + + g_assert(tdata->acd_timeout_msec == 0 || l3ipv4ll_reg); + g_assert(!l3ipv4ll_reg || l3ipv4ll == nm_l3_ipv4ll_register_get_instance(l3ipv4ll_reg)); + + if (tdata->acd_timeout_msec == 0) { + g_assert_cmpint(nm_l3_ipv4ll_get_state(l3ipv4ll), ==, NM_L3_IPV4LL_STATE_DISABLED); + g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll), ==, 0u); + } else { + g_assert_cmpint(nm_l3_ipv4ll_get_state(l3ipv4ll), ==, NM_L3_IPV4LL_STATE_PROBING); + if (f->test_idx == 1) { + g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll), + ==, + nmtst_inet4_from_string("169.254.30.158")); + } else { + g_assert_cmpint(nm_l3_ipv4ll_get_addr(l3ipv4ll), + ==, + nmtst_inet4_from_string("169.254.17.45")); + } + g_assert(nm_l3_ipv4ll_get_l3cd(l3ipv4ll)); + } + + _test_l3_ipv4ll_maybe_add_addr_4(tdata, 0, 4, &tdata->has_addr4_101, "192.168.133.101"); + + if (tdata->f->test_idx == 2 && nmtst_get_rand_one_case_in(3)) { + in_addr_t a = nm_l3_ipv4ll_get_addr(l3ipv4ll); + + g_assert(nm_utils_ip4_address_is_link_local(a)); + _test_l3_ipv4ll_maybe_add_addr_4(tdata, + tdata->f->ifindex1, + 2, + &tdata->add_conflict_done, + _nm_utils_inet4_ntop(a, sbuf_addr)); + g_assert_cmpint(tdata->f->hwaddr1.len, ==, sizeof(NMEtherAddr)); + acd_defender_2 = + nmtstp_acd_defender_new(tdata->f->ifindex1, a, &tdata->f->hwaddr1.ether_addr); + } + + start_time_msec = nm_utils_get_monotonic_timestamp_msec(); + total_poll_time_msec = + (L3IPV4LL_ACD_TIMEOUT_MSEC * 3 / 2) + (nmtst_get_rand_uint32() % L3IPV4LL_ACD_TIMEOUT_MSEC); + _LOGT("poll 1 start (wait %" G_GINT64_FORMAT " msec)", total_poll_time_msec); + while (TRUE) { + gint64 next_timeout_msec; + + next_timeout_msec = + start_time_msec + total_poll_time_msec - nm_utils_get_monotonic_timestamp_msec(); + if (next_timeout_msec <= 0) + break; + + next_timeout_msec = NM_MIN(next_timeout_msec, nmtst_get_rand_uint32() % 1000u); + nmtst_main_context_iterate_until(NULL, next_timeout_msec, FALSE); + _LOGT("poll 1 intermezzo"); + + _test_l3_ipv4ll_maybe_add_addr_4(tdata, + 0, + 1 + total_poll_time_msec / 1000, + &tdata->has_addr4_101, + "192.168.133.101"); + + if (tdata->addr_commit == 1 && !tdata->add_conflict_checked) { + tdata->add_conflict_checked = TRUE; + _test_l3_ipv4ll_maybe_add_addr_4( + tdata, + tdata->f->ifindex1, + 2, + &tdata->add_conflict_done, + _nm_utils_inet4_ntop(tdata->addr_commit_addr, sbuf_addr)); + if (tdata->add_conflict_done) + total_poll_time_msec += L3IPV4LL_ACD_TIMEOUT_MSEC / 2; + g_assert_cmpint(tdata->f->hwaddr1.len, ==, sizeof(NMEtherAddr)); + acd_defender_2 = nmtstp_acd_defender_new(tdata->f->ifindex1, + tdata->addr_commit_addr, + &tdata->f->hwaddr1.ether_addr); + } + } + _LOGT("poll 1 end"); + + if (tdata->addr_commit || nmtst_get_rand_bool()) { + nm_l3cfg_remove_config_all(nm_l3_ipv4ll_get_l3cfg(l3ipv4ll), + TEST_L3_IPV4LL_TAG(tdata, 1), + FALSE); + } + + nmtstp_acd_defender_destroy(g_steal_pointer(&acd_defender_1)); + nmtstp_acd_defender_destroy(g_steal_pointer(&acd_defender_2)); + + nm_l3cfg_commit_type_unregister(l3cfg0, g_steal_pointer(&tdata->l3cfg_commit_type_1)); + + g_signal_handlers_disconnect_by_func(l3cfg0, G_CALLBACK(_test_l3_ipv4ll_signal_notify), tdata); +} + +/*****************************************************************************/ + +NMTstpSetupFunc const _nmtstp_setup_platform_func = nm_linux_platform_setup; + +void +_nmtstp_init_tests(int *argc, char ***argv) +{ + nmtst_init_with_logging(argc, argv, "ERR", "ALL"); +} + +void +_nmtstp_setup_tests(void) +{ + g_test_add_data_func("/l3cfg/1", GINT_TO_POINTER(1), test_l3cfg); + g_test_add_data_func("/l3cfg/2", GINT_TO_POINTER(2), test_l3cfg); + g_test_add_data_func("/l3cfg/3", GINT_TO_POINTER(3), test_l3cfg); + g_test_add_data_func("/l3cfg/4", GINT_TO_POINTER(4), test_l3cfg); + g_test_add_data_func("/l3-ipv4ll/1", GINT_TO_POINTER(1), test_l3_ipv4ll); + g_test_add_data_func("/l3-ipv4ll/2", GINT_TO_POINTER(2), test_l3_ipv4ll); +} diff --git a/src/core/tests/test-secret-agent.py b/src/core/tests/test-secret-agent.py new file mode 100755 index 00000000..46f82b34 --- /dev/null +++ b/src/core/tests/test-secret-agent.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +from gi.repository import GLib +import sys +import dbus +import dbus.service +import dbus.mainloop.glib + +IFACE_SECRET_AGENT = "org.freedesktop.NetworkManager.SecretAgent" +IFACE_AGENT_MANAGER = "org.freedesktop.NetworkManager.AgentManager" + + +class NotAuthorizedException(dbus.DBusException): + _dbus_error_name = IFACE_SECRET_AGENT + ".NotAuthorized" + + +class Agent(dbus.service.Object): + def __init__(self, bus, object_path): + self.agents = {} + self.bus = bus + dbus.service.Object.__init__(self, bus, object_path) + + @dbus.service.method( + IFACE_SECRET_AGENT, + in_signature="a{sa{sv}}osasb", + out_signature="a{sa{sv}}", + sender_keyword="sender", + ) + def GetSecrets( + self, + connection_hash, + connection_path, + setting_name, + hints, + request_new, + sender=None, + ): + if not sender: + raise NotAuthorizedException("Internal error: couldn't get sender") + uid = self.bus.get_unix_user(sender) + if uid != 0: + raise NotAuthorizedException("UID %d not authorized" % uid) + + print( + "Secrets requested path '%s' setting '%s' hints '%s' new %d" + % (connection_path, setting_name, str(hints), request_new) + ) + + # return some random GSM secrets + s_gsm = dbus.Dictionary({"password": "asdfadfasdfaf"}) + con = dbus.Dictionary({"gsm": s_gsm}) + return con + + +def register(proxy): + proxy.Register("test.agent.id", dbus_interface=IFACE_AGENT_MANAGER) + print("Registered!") + return False + + +def unregister(proxy, loop): + proxy.Unregister(dbus_interface=IFACE_AGENT_MANAGER) + loop.quit() + return False + + +def main(): + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + obj = Agent(bus, "/org/freedesktop/NetworkManager/SecretAgent") + proxy = bus.get_object( + "org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager/AgentManager" + ) + + mainloop = GLib.MainLoop() + + GLib.idle_add(register, proxy) + print("Running test secret agent") + + try: + mainloop.run() + except KeyboardInterrupt: + pass + + print("Unregistering...") + unregister(proxy, mainloop) + + +if __name__ == "__main__": + main() diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c new file mode 100644 index 00000000..03576c13 --- /dev/null +++ b/src/core/tests/test-systemd.c @@ -0,0 +1,339 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2016 Red Hat, Inc. + */ + +#include "src/core/systemd/nm-default-systemd.h" + +#include "systemd/nm-sd.h" +#include "systemd/nm-sd-utils-shared.h" + +#include "nm-test-utils-core.h" + +/***************************************************************************** + * Stub implementations of libNetworkManagerBase symbols + *****************************************************************************/ + +gboolean +nm_utils_get_testing_initialized(void) +{ + return TRUE; +} + +void +_nm_utils_set_testing(NMUtilsTestFlags flags) +{ + g_assert_not_reached(); +} + +gint32 +nm_utils_get_monotonic_timestamp_sec(void) +{ + return 1; +} + +NMLogDomain _nm_logging_enabled_state[_LOGL_N_REAL]; + +gboolean +_nm_log_enabled_impl(gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain) +{ + return FALSE; +} + +void +_nm_log_impl(const char *file, + guint line, + const char *func, + gboolean mt_require_locking, + NMLogLevel level, + NMLogDomain domain, + int error, + const char *ifname, + const char *con_uuid, + const char *fmt, + ...) +{} + +gboolean +nm_logging_setup(const char *level, const char *domains, char **bad_domains, GError **error) +{ + return TRUE; +} + +const char * +nm_strerror_native(int errsv) +{ + return g_strerror(errsv); +} + +/*****************************************************************************/ + +static void +test_dhcp_create(void) +{ + sd_dhcp_client *client4 = NULL; + int r; + + r = sd_dhcp_client_new(&client4, FALSE); + g_assert(r == 0); + g_assert(client4); + + if (/* never true */ client4 == (gpointer) &r) { + /* we don't want to call this, but ensure that the linker + * includes all these symbols. */ + sd_dhcp_client_start(client4); + } + + sd_dhcp_client_unref(client4); +} + +/*****************************************************************************/ + +static void +test_lldp_create(void) +{ + sd_lldp *lldp = NULL; + int r; + + r = sd_lldp_new(&lldp); + g_assert(r == 0); + g_assert(lldp); + + sd_lldp_unref(lldp); +} + +/*****************************************************************************/ + +typedef struct { + GMainLoop * mainloop; + sd_event_source *event_source; +} TestSdEventData; + +static int +_test_sd_event_timeout_cb(sd_event_source *s, uint64_t usec, void *userdata) +{ + TestSdEventData *user_data = userdata; + + g_assert(user_data); + g_assert(user_data->mainloop); + g_assert(user_data->event_source); + + user_data->event_source = sd_event_source_unref(user_data->event_source); + g_main_loop_quit(user_data->mainloop); + return 0; +} + +static void +test_sd_event(void) +{ + int repeat; + + for (repeat = 0; repeat < 2; repeat++) { + guint sd_id = 0; + int r; + int i, n; + sd_event * other_events[3] = {NULL}, *event = NULL; + TestSdEventData user_data = {0}; + + g_assert_cmpint(sd_event_default(NULL), ==, 0); + + for (i = 0, n = (nmtst_get_rand_uint32() % (G_N_ELEMENTS(other_events) + 1)); i < n; i++) { + r = sd_event_default(&other_events[i]); + g_assert(r >= 0 && other_events[i]); + } + + sd_id = nm_sd_event_attach_default(); + + r = sd_event_default(&event); + g_assert(r >= 0 && event); + + r = sd_event_add_time(event, + &user_data.event_source, + CLOCK_MONOTONIC, + 1, + 0, + _test_sd_event_timeout_cb, + &user_data); + g_assert(r >= 0 && user_data.event_source); + + user_data.mainloop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(user_data.mainloop); + g_main_loop_unref(user_data.mainloop); + + g_assert(!user_data.event_source); + + event = sd_event_unref(event); + for (i = 0, n = (nmtst_get_rand_uint32() % (G_N_ELEMENTS(other_events) + 1)); i < n; i++) + other_events[i] = sd_event_unref(other_events[i]); + nm_clear_g_source(&sd_id); + for (i = 0, n = G_N_ELEMENTS(other_events); i < n; i++) + other_events[i] = sd_event_unref(other_events[i]); + + g_assert_cmpint(sd_event_default(NULL), ==, 0); + } +} + +/*****************************************************************************/ + +static void +test_path_equal(void) +{ +#define _path_equal_check1(path, kill_dots, expected) \ + G_STMT_START \ + { \ + const gboolean _kill_dots = (kill_dots); \ + const char * _path0 = (path); \ + const char * _expected = (expected); \ + gs_free char * _path = g_strdup(_path0); \ + const char * _path_result; \ + \ + if (!_kill_dots && !nm_sd_utils_path_equal(_path0, _expected)) \ + g_error("Paths \"%s\" and \"%s\" don't compare equal", _path0, _expected); \ + \ + _path_result = nm_sd_utils_path_simplify(_path, _kill_dots); \ + g_assert(_path_result == _path); \ + g_assert_cmpstr(_path, ==, _expected); \ + } \ + G_STMT_END + +#define _path_equal_check(path, expected_no_kill_dots, expected_kill_dots) \ + G_STMT_START \ + { \ + _path_equal_check1(path, FALSE, expected_no_kill_dots); \ + _path_equal_check1(path, TRUE, expected_kill_dots ?: expected_no_kill_dots); \ + } \ + G_STMT_END + + _path_equal_check("", "", NULL); + _path_equal_check(".", ".", NULL); + _path_equal_check("..", "..", NULL); + _path_equal_check("/..", "/..", NULL); + _path_equal_check("//..", "/..", NULL); + _path_equal_check("/.", "/.", "/"); + _path_equal_check("./", ".", "."); + _path_equal_check("./.", "./.", "."); + _path_equal_check(".///.", "./.", "."); + _path_equal_check(".///./", "./.", "."); + _path_equal_check(".////", ".", "."); + _path_equal_check("//..//foo/", "/../foo", NULL); + _path_equal_check("///foo//./bar/.", "/foo/./bar/.", "/foo/bar"); + _path_equal_check(".//./foo//./bar/.", "././foo/./bar/.", "foo/bar"); +} + +/*****************************************************************************/ + +static void +_test_unbase64char(char ch, gboolean maybe_invalid) +{ + int r; + + r = nm_sd_utils_unbase64char(ch, FALSE); + + if (ch == '=') { + g_assert(!maybe_invalid); + g_assert_cmpint(r, <, 0); + g_assert_cmpint(nm_sd_utils_unbase64char(ch, TRUE), ==, G_MAXINT); + } else { + g_assert_cmpint(r, ==, nm_sd_utils_unbase64char(ch, TRUE)); + if (r >= 0) + g_assert_cmpint(r, <=, 255); + if (!maybe_invalid) + g_assert_cmpint(r, >=, 0); + } +} + +static void +_test_unbase64mem_mem(const char *base64, const guint8 *expected_arr, gsize expected_len) +{ + gs_free char *expected_base64 = NULL; + int r; + gs_free guint8 *exp2_arr = NULL; + gs_free guint8 *exp3_arr = NULL; + gsize exp2_len; + gsize exp3_len; + gsize i; + + expected_base64 = g_base64_encode(expected_arr, expected_len); + + for (i = 0; expected_base64[i]; i++) + _test_unbase64char(expected_base64[i], FALSE); + + r = nm_sd_utils_unbase64mem(expected_base64, + strlen(expected_base64), + TRUE, + &exp2_arr, + &exp2_len); + g_assert_cmpint(r, ==, 0); + g_assert_cmpmem(expected_arr, expected_len, exp2_arr, exp2_len); + + if (!nm_streq(base64, expected_base64)) { + r = nm_sd_utils_unbase64mem(base64, strlen(base64), TRUE, &exp3_arr, &exp3_len); + g_assert_cmpint(r, ==, 0); + g_assert_cmpmem(expected_arr, expected_len, exp3_arr, exp3_len); + } +} + +#define _test_unbase64mem(base64, expected_str) \ + _test_unbase64mem_mem(base64, (const guint8 *) "" expected_str "", NM_STRLEN(expected_str)) + +static void +_test_unbase64mem_inval(const char *base64) +{ + gs_free guint8 *exp_arr = NULL; + gsize exp_len = 0; + int r; + + r = nm_sd_utils_unbase64mem(base64, strlen(base64), TRUE, &exp_arr, &exp_len); + g_assert_cmpint(r, <, 0); + g_assert(!exp_arr); + g_assert(exp_len == 0); +} + +static void +test_nm_sd_utils_unbase64mem(void) +{ + gs_free char *rnd_base64 = NULL; + guint8 rnd_buf[30]; + guint i, rnd_len; + + _test_unbase64mem("", ""); + _test_unbase64mem(" ", ""); + _test_unbase64mem(" Y Q == ", "a"); + _test_unbase64mem(" Y WJjZGV mZ 2g = ", "abcdefgh"); + _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = "); + _test_unbase64mem_inval(" Y %WJjZGV mZ 2g = a"); + _test_unbase64mem("YQ==", "a"); + _test_unbase64mem_inval("YQ==a"); + + rnd_len = nmtst_get_rand_uint32() % sizeof(rnd_buf); + for (i = 0; i < rnd_len; i++) + rnd_buf[i] = nmtst_get_rand_uint32() % 256; + rnd_base64 = g_base64_encode(rnd_buf, rnd_len); + _test_unbase64mem_mem(rnd_base64, rnd_buf, rnd_len); + + _test_unbase64char('=', FALSE); + for (i = 0; i < 10; i++) { + char ch = nmtst_get_rand_uint32() % 256; + + if (ch != '=') + _test_unbase64char(ch, TRUE); + } +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_assert_logging(&argc, &argv, "INFO", "ALL"); + + g_test_add_func("/systemd/dhcp/create", test_dhcp_create); + g_test_add_func("/systemd/lldp/create", test_lldp_create); + g_test_add_func("/systemd/sd-event", test_sd_event); + g_test_add_func("/systemd/test_path_equal", test_path_equal); + g_test_add_func("/systemd/test_nm_sd_utils_unbase64mem", test_nm_sd_utils_unbase64mem); + + return g_test_run(); +} diff --git a/src/core/tests/test-utils.c b/src/core/tests/test-utils.c new file mode 100644 index 00000000..1d562bde --- /dev/null +++ b/src/core/tests/test-utils.c @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2015 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include +#include + +#include "nm-test-utils-core.h" + +static void +test_stable_privacy(void) +{ + struct in6_addr addr1; + + inet_pton(AF_INET6, "1234::", &addr1); + nm_utils_ipv6_addr_set_stable_privacy_impl(NM_UTILS_STABLE_TYPE_UUID, + &addr1, + "eth666", + "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", + 0, + (guint8 *) "key", + 3, + NULL); + nmtst_assert_ip6_address(&addr1, "1234::4ceb:14cd:3d54:793f"); + + /* We get an address without the UUID. */ + inet_pton(AF_INET6, "1::", &addr1); + nm_utils_ipv6_addr_set_stable_privacy_impl(NM_UTILS_STABLE_TYPE_UUID, + &addr1, + "eth666", + "", + 384, + (guint8 *) "key", + 3, + NULL); + nmtst_assert_ip6_address(&addr1, "1::11aa:2530:9144:dafa"); + + /* We get a different address in a different network. */ + inet_pton(AF_INET6, "2::", &addr1); + nm_utils_ipv6_addr_set_stable_privacy_impl(NM_UTILS_STABLE_TYPE_UUID, + &addr1, + "eth666", + "", + 384, + (guint8 *) "key", + 3, + NULL); + nmtst_assert_ip6_address(&addr1, "2::338e:8d:c11:8726"); + + inet_pton(AF_INET6, "1234::", &addr1); + nm_utils_ipv6_addr_set_stable_privacy_impl(NM_UTILS_STABLE_TYPE_STABLE_ID, + &addr1, + "eth666", + "6b138152-9f3e-4b97-aaf7-e6e553f2a24e", + 0, + (guint8 *) "key", + 3, + NULL); + nmtst_assert_ip6_address(&addr1, "1234::ad4c:ae44:3d30:af1e"); + + inet_pton(AF_INET6, "1234::", &addr1); + nm_utils_ipv6_addr_set_stable_privacy_impl(NM_UTILS_STABLE_TYPE_STABLE_ID, + &addr1, + "eth666", + "stable-id-1", + 0, + (guint8 *) "key", + 3, + NULL); + nmtst_assert_ip6_address(&addr1, "1234::4944:67b0:7a6c:1cf"); +} + +/*****************************************************************************/ + +static void +_do_test_hw_addr(NMUtilsStableType stable_type, + const char * stable_id, + const guint8 * secret_key, + gsize key_len, + const char * ifname, + const char * current_mac_address, + const char * generate_mac_address_mask, + const char *const *expected) +{ + gs_free char * generated = NULL; + const char *const *e; + gboolean found = FALSE; + + for (e = expected; *e; e++) { + g_assert(*e); + g_assert(nm_utils_hwaddr_valid(*e, ETH_ALEN)); + } + + generated = nm_utils_hw_addr_gen_stable_eth_impl(stable_type, + stable_id, + secret_key, + key_len, + ifname, + current_mac_address, + generate_mac_address_mask); + + g_assert(generated); + g_assert(nm_utils_hwaddr_valid(generated, ETH_ALEN)); + for (e = expected; *e; e++) { + if (!nm_utils_hwaddr_matches(generated, -1, *e, -1)) + continue; + g_assert(!found); + found = TRUE; + g_assert_cmpstr(generated, ==, *e); + } + g_assert(found); +} +#define do_test_hw_addr(stable_type, \ + stable_id, \ + secret_key, \ + ifname, \ + current_mac_address, \ + generate_mac_address_mask, \ + ...) \ + _do_test_hw_addr((stable_type), \ + (stable_id), \ + (const guint8 *) "" secret_key "", \ + NM_STRLEN(secret_key), \ + (ifname), \ + "" current_mac_address "", \ + generate_mac_address_mask, \ + NM_MAKE_STRV(__VA_ARGS__)) + +static void +test_hw_addr_gen_stable_eth(void) +{ + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + NULL, + "06:0D:CD:0C:9E:2C"); + do_test_hw_addr(NM_UTILS_STABLE_TYPE_STABLE_ID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + NULL, + "C6:AE:A9:9A:76:09"); + + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "FF:FF:FF:00:00:00", + "00:23:45:0C:9E:2C"); + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "03:23:45:67:89:ab", + "FF:FF:FF:00:00:00", + "02:23:45:0C:9E:2C"); + + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "00:00:00:00:00:00", + "06:0D:CD:0C:9E:2C"); + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "02:00:00:00:00:00", + "04:0D:CD:0C:9E:2C"); + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "02:00:00:00:00:00", + "04:0D:CD:0C:9E:2C"); + + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "02:00:00:00:00:00 00:00:00:00:00:00", + "04:0D:CD:0C:9E:2C"); + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "02:00:00:00:00:00 02:00:00:00:00:00", + "06:0D:CD:0C:9E:2C"); + + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "00:00:00:00:00:00 E9:60:CE:F5:ED:2F", + "06:0D:CD:0C:9E:2C"); + + do_test_hw_addr(NM_UTILS_STABLE_TYPE_UUID, + "stable-1", + "key1", + "eth0", + "01:23:45:67:89:ab", + "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00", + "06:0D:CD:0C:9E:2C", + "04:0D:CD:0C:9E:2C"); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_with_logging(&argc, &argv, NULL, "ALL"); + + g_test_add_func("/utils/stable_privacy", test_stable_privacy); + g_test_add_func("/utils/hw_addr_gen_stable_eth", test_hw_addr_gen_stable_eth); + + return g_test_run(); +} diff --git a/src/core/tests/test-wired-defname.c b/src/core/tests/test-wired-defname.c new file mode 100644 index 00000000..f810c56f --- /dev/null +++ b/src/core/tests/test-wired-defname.c @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-simple-connection.h" +#include "nm-setting-connection.h" +#include "devices/nm-device-ethernet-utils.h" + +#include "nm-test-utils-core.h" + +static NMConnection * +_new_connection(const char *id) +{ + NMConnection *a; + NMSetting * setting; + + a = nm_simple_connection_new(); + setting = nm_setting_connection_new(); + g_object_set(setting, NM_SETTING_CONNECTION_ID, id, NULL); + nm_connection_add_setting(a, setting); + return a; +} + +/*****************************************************************************/ + +static char * +_get_default_wired_name(GSList *list) +{ + gs_unref_hashtable GHashTable *existing_ids = NULL; + + if (list) { + existing_ids = g_hash_table_new(nm_str_hash, g_str_equal); + for (; list; list = list->next) + g_hash_table_add(existing_ids, (char *) nm_connection_get_id(list->data)); + } + return nm_device_ethernet_utils_get_default_wired_name(existing_ids); +} + +/*****************************************************************************/ + +static void +test_defname_no_connections(void) +{ + gs_free char *name = NULL; + + name = _get_default_wired_name(NULL); + g_assert_cmpstr(name, ==, "Wired connection 1"); +} + +/*****************************************************************************/ + +static void +test_defname_no_conflict(void) +{ + GSList * list = NULL; + gs_free char *name = NULL; + + list = g_slist_append(list, _new_connection("asdfasdfasdfadf")); + list = g_slist_append(list, _new_connection("work wifi")); + list = g_slist_append(list, _new_connection("random gsm connection")); + + name = _get_default_wired_name(list); + g_assert_cmpstr(name, ==, "Wired connection 1"); + + g_slist_free_full(list, g_object_unref); +} + +/*****************************************************************************/ + +static void +test_defname_conflict(void) +{ + GSList * list = NULL; + gs_free char *name = NULL; + + list = g_slist_append(list, _new_connection("asdfasdfasdfadf")); + list = g_slist_append(list, _new_connection("Wired connection 1")); + list = g_slist_append(list, _new_connection("random gsm connection")); + + name = _get_default_wired_name(list); + g_assert_cmpstr(name, ==, "Wired connection 2"); + + g_slist_free_full(list, g_object_unref); +} + +/*****************************************************************************/ + +static void +test_defname_multiple_conflicts(void) +{ + GSList * list = NULL; + gs_free char *name = NULL; + + list = g_slist_append(list, _new_connection("random gsm connection")); + list = g_slist_append(list, _new_connection("home wifi")); + list = g_slist_append(list, _new_connection("Wired connection 1")); + list = g_slist_append(list, _new_connection("Wired connection 2")); + list = g_slist_append(list, _new_connection("Wired connection 3")); + list = g_slist_append(list, _new_connection("work wifi")); + list = g_slist_append(list, _new_connection("a vpn")); + + name = _get_default_wired_name(list); + g_assert_cmpstr(name, ==, "Wired connection 4"); + + g_slist_free_full(list, g_object_unref); +} + +/*****************************************************************************/ + +NMTST_DEFINE(); + +int +main(int argc, char **argv) +{ + nmtst_init_assert_logging(&argc, &argv, "INFO", "DEFAULT"); + + g_test_add_func("/defname/no_connections", test_defname_no_connections); + g_test_add_func("/defname/no_conflict", test_defname_no_conflict); + g_test_add_func("/defname/conflict", test_defname_conflict); + g_test_add_func("/defname/multiple_conflicts", test_defname_multiple_conflicts); + + return g_test_run(); +} -- cgit 1.3.0-6-gf8a5