NetworkManager/libnm-core/tests/test-setting.c

3234 lines
121 KiB
C
Raw Normal View History

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright 2008 - 2017 Red Hat, Inc.
*
*/
#include "nm-default.h"
#include <linux/pkt_sched.h>
#include <net/if.h>
#include "nm-utils.h"
#include "nm-utils-private.h"
#include "nm-core-internal.h"
#include "nm-setting-8021x.h"
#include "nm-setting-bond.h"
#include "nm-setting-dcb.h"
#include "nm-setting-ethtool.h"
#include "nm-setting-team.h"
#include "nm-setting-team-port.h"
#include "nm-setting-tc-config.h"
#include "nm-setting-dummy.h"
#include "nm-connection.h"
#include "nm-simple-connection.h"
#include "nm-setting-connection.h"
#include "nm-errors.h"
#include "nm-keyfile-internal.h"
#include "nm-utils/nm-test-utils.h"
#define TEST_CERT_DIR NM_BUILD_SRCDIR"/libnm-core/tests/certs"
/*****************************************************************************/
/* converts @dict to a connection. In this case, @dict must be good, without warnings, so that
* NM_SETTING_PARSE_FLAGS_STRICT and NM_SETTING_PARSE_FLAGS_BEST_EFFORT yield the exact same results. */
static NMConnection *
_connection_new_from_dbus_strict (GVariant *dict,
gboolean normalize)
{
gs_unref_object NMConnection *con_x_0 = NULL;
gs_unref_object NMConnection *con_x_s = NULL;
gs_unref_object NMConnection *con_x_e = NULL;
gs_unref_object NMConnection *con_n_0 = NULL;
gs_unref_object NMConnection *con_n_s = NULL;
gs_unref_object NMConnection *con_n_e = NULL;
gs_free_error GError *error = NULL;
guint i;
g_assert (g_variant_is_of_type (dict, NM_VARIANT_TYPE_CONNECTION));
con_x_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NONE, &error);
nmtst_assert_success (NM_IS_CONNECTION (con_x_0), error);
con_x_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT, &error);
nmtst_assert_success (NM_IS_CONNECTION (con_x_s), error);
con_x_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error);
nmtst_assert_success (NM_IS_CONNECTION (con_x_e), error);
con_n_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
nmtst_assert_success (NM_IS_CONNECTION (con_n_0), error);
con_n_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
nmtst_assert_success (NM_IS_CONNECTION (con_n_s), error);
con_n_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
nmtst_assert_success (NM_IS_CONNECTION (con_n_e), error);
nmtst_assert_connection_verifies (con_x_0);
nmtst_assert_connection_verifies (con_x_e);
nmtst_assert_connection_verifies (con_x_s);
nmtst_assert_connection_verifies_without_normalization (con_n_0);
nmtst_assert_connection_verifies_without_normalization (con_n_e);
nmtst_assert_connection_verifies_without_normalization (con_n_s);
/* randomly compare some pairs that we created. They must all be equal,
* after accounting for normalization. */
for (i = 0; i < 10; i++) {
NMConnection *cons[] = { con_x_0, con_x_s, con_x_e, con_n_0, con_n_s, con_n_e };
guint idx_a = (nmtst_get_rand_int () % G_N_ELEMENTS (cons));
guint idx_b = (nmtst_get_rand_int () % G_N_ELEMENTS (cons));
gboolean normalize_a, normalize_b;
if (idx_a <= 2 && idx_b <= 2) {
normalize_a = nmtst_get_rand_bool ();
normalize_b = normalize_a;
} else if (idx_a > 2 && idx_b > 2) {
normalize_a = nmtst_get_rand_bool ();
normalize_b = nmtst_get_rand_bool ();
} else {
normalize_a = (idx_a <= 2) ? TRUE : nmtst_get_rand_bool ();
normalize_b = (idx_b <= 2) ? TRUE : nmtst_get_rand_bool ();
}
nmtst_assert_connection_equals (cons[idx_a], normalize_a, cons[idx_b], normalize_b);
}
return (normalize)
? g_steal_pointer (&con_x_0)
: g_steal_pointer (&con_n_0);
}
/*****************************************************************************/
static char *
_create_random_ipaddr (int addr_family, gboolean as_service)
{
char delimiter = as_service ? ':' : '/';
int num;
if (addr_family == AF_UNSPEC)
addr_family = nmtst_rand_select (AF_INET, AF_INET6);
g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
if (as_service)
num = (nmtst_get_rand_int () % 1000) + 30000;
else
num = addr_family == AF_INET ? 32 : 128;
if (addr_family == AF_INET)
return g_strdup_printf ("192.168.%u.%u%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
else
return g_strdup_printf ("a:b:c::%02x:%02x%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
}
/*****************************************************************************/
static void
compare_blob_data (const char *test,
const char *key_path,
GBytes *key)
{
gs_free char *contents = NULL;
gsize len = 0;
GError *error = NULL;
gboolean success;
g_assert (key && g_bytes_get_size (key) > 0);
success = g_file_get_contents (key_path, &contents, &len, &error);
nmtst_assert_success (success, error);
g_assert_cmpmem (contents, len, g_bytes_get_data (key, NULL), g_bytes_get_size (key));
}
static void
check_scheme_path (GBytes *value, const char *path)
{
const guint8 *p;
libnm/802-1x: refactor setting certificate from path NMSetting8021x has various utility functions to set the certificate: - nm_setting_802_1x_set_ca_cert() - nm_setting_802_1x_set_client_cert() - nm_setting_802_1x_set_private_key() - nm_setting_802_1x_set_phase2_ca_cert() - nm_setting_802_1x_set_phase2_client_cert() - nm_setting_802_1x_set_phase2_private_key() They support: - accepting a plain PKCS11 URI, with scheme set to NM_SETTING_802_1X_CK_SCHEME_PKCS11. - accepting a filename, with scheme set to NM_SETTING_802_1X_CK_SCHEME_BLOB or NM_SETTING_802_1X_CK_SCHEME_PATH. In the latter case, the function tries to load the file and verify it. In case of the private-key setters, this also involves accepting a password. Depending on whether the scheme is BLOB or PATH, the function will either set the certificate to a PATH blob, or take the blob that was read from file. The functions seem misdesigned to me, because their behavior is rather obscure. E.g. they behave fundamentally different, depending on whether scheme is PKCS11 or BLOB/PATH. Anyway, improve them: - refactor the common code into a function _cert_impl_set(). Previously, their non-trivial implementations were copy+pasted several times, now they all use the same implementation. - if the function is going to fail, don't touch the setting. Previously, the functions would first clear the certificate before trying to validate the input. It's more logical, that if a functions is going to fail to check for failure first and don't modify the settings. - not every blob can be represented. For example, if we have a blob which starts with "file://", then there is no way to set it, simply because we don't support a prefix for blobs (like "data:;base64,"). This means, if we try to set the certificate to a particular binary, we must check that the binary is interpreted with the expected scheme. Add this check.
2018-09-01 18:08:33 +02:00
gsize l;
g_assert (value);
libnm/802-1x: refactor setting certificate from path NMSetting8021x has various utility functions to set the certificate: - nm_setting_802_1x_set_ca_cert() - nm_setting_802_1x_set_client_cert() - nm_setting_802_1x_set_private_key() - nm_setting_802_1x_set_phase2_ca_cert() - nm_setting_802_1x_set_phase2_client_cert() - nm_setting_802_1x_set_phase2_private_key() They support: - accepting a plain PKCS11 URI, with scheme set to NM_SETTING_802_1X_CK_SCHEME_PKCS11. - accepting a filename, with scheme set to NM_SETTING_802_1X_CK_SCHEME_BLOB or NM_SETTING_802_1X_CK_SCHEME_PATH. In the latter case, the function tries to load the file and verify it. In case of the private-key setters, this also involves accepting a password. Depending on whether the scheme is BLOB or PATH, the function will either set the certificate to a PATH blob, or take the blob that was read from file. The functions seem misdesigned to me, because their behavior is rather obscure. E.g. they behave fundamentally different, depending on whether scheme is PKCS11 or BLOB/PATH. Anyway, improve them: - refactor the common code into a function _cert_impl_set(). Previously, their non-trivial implementations were copy+pasted several times, now they all use the same implementation. - if the function is going to fail, don't touch the setting. Previously, the functions would first clear the certificate before trying to validate the input. It's more logical, that if a functions is going to fail to check for failure first and don't modify the settings. - not every blob can be represented. For example, if we have a blob which starts with "file://", then there is no way to set it, simply because we don't support a prefix for blobs (like "data:;base64,"). This means, if we try to set the certificate to a particular binary, we must check that the binary is interpreted with the expected scheme. Add this check.
2018-09-01 18:08:33 +02:00
p = g_bytes_get_data (value, &l);
g_assert_cmpint (l, ==, strlen (path) + NM_STRLEN (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH) + 1);
g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0);
p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH);
g_assert (memcmp (p, path, strlen (path)) == 0);
p += strlen (path);
g_assert (*p == '\0');
}
static void
test_private_key_import (const char *path,
const char *password,
NMSetting8021xCKScheme scheme)
{
NMSetting8021x *s_8021x;
gboolean success;
NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
NMSetting8021xCKFormat tmp_fmt;
GError *error = NULL;
GBytes *tmp_key = NULL, *client_cert = NULL;
const char *pw;
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
g_assert (s_8021x);
success = nm_setting_802_1x_set_private_key (s_8021x,
path,
password,
scheme,
&format,
&error);
nmtst_assert_success (success, error);
g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
tmp_fmt = nm_setting_802_1x_get_private_key_format (s_8021x);
g_assert (tmp_fmt == format);
/* Make sure the password is what we expect */
pw = nm_setting_802_1x_get_private_key_password (s_8021x);
g_assert (pw != NULL);
g_assert_cmpstr (pw, ==, password);
if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
tmp_key = nm_setting_802_1x_get_private_key_blob (s_8021x);
compare_blob_data ("private-key-import", path, tmp_key);
} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL);
check_scheme_path (tmp_key, path);
g_bytes_unref (tmp_key);
} else
g_assert_not_reached ();
/* If it's PKCS#12 ensure the client cert is the same value */
if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL);
g_assert (tmp_key);
g_object_get (s_8021x, NM_SETTING_802_1X_CLIENT_CERT, &client_cert, NULL);
g_assert (client_cert);
/* make sure they are the same */
g_assert (g_bytes_equal (tmp_key, client_cert));
g_bytes_unref (tmp_key);
g_bytes_unref (client_cert);
}
g_object_unref (s_8021x);
}
static void
test_phase2_private_key_import (const char *path,
const char *password,
NMSetting8021xCKScheme scheme)
{
NMSetting8021x *s_8021x;
gboolean success;
NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
NMSetting8021xCKFormat tmp_fmt;
GError *error = NULL;
GBytes *tmp_key = NULL, *client_cert = NULL;
const char *pw;
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
g_assert (s_8021x);
success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
path,
password,
scheme,
&format,
&error);
nmtst_assert_success (success, error);
g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
tmp_fmt = nm_setting_802_1x_get_phase2_private_key_format (s_8021x);
g_assert (tmp_fmt == format);
/* Make sure the password is what we expect */
pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
g_assert (pw);
g_assert_cmpstr (pw, ==, password);
if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
tmp_key = nm_setting_802_1x_get_phase2_private_key_blob (s_8021x);
compare_blob_data ("phase2-private-key-import", path, tmp_key);
} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL);
check_scheme_path (tmp_key, path);
g_bytes_unref (tmp_key);
} else
g_assert_not_reached ();
/* If it's PKCS#12 ensure the client cert is the same value */
if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL);
g_assert (tmp_key);
g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &client_cert, NULL);
g_assert (client_cert);
/* make sure they are the same */
g_assert (g_bytes_equal (tmp_key, client_cert));
g_bytes_unref (tmp_key);
g_bytes_unref (client_cert);
}
g_object_unref (s_8021x);
}
static void
test_wrong_password_keeps_data (const char *path, const char *password)
{
NMSetting8021x *s_8021x;
gboolean success;
NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
GError *error = NULL;
const char *pw;
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
g_assert (s_8021x);
success = nm_setting_802_1x_set_private_key (s_8021x,
path,
password,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
&format,
&error);
nmtst_assert_success (success, error);
g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
/* Now try to set it to something that's not a certificate */
format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
success = nm_setting_802_1x_set_private_key (s_8021x,
"Makefile.am",
password,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
&format,
&error);
nmtst_assert_no_success (success, error);
g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
g_clear_error (&error);
/* Make sure the password hasn't changed */
pw = nm_setting_802_1x_get_private_key_password (s_8021x);
g_assert (pw);
g_assert_cmpstr (pw, ==, password);
g_object_unref (s_8021x);
}
static void
test_clear_private_key (const char *path, const char *password)
{
NMSetting8021x *s_8021x;
gboolean success;
NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
GError *error = NULL;
const char *pw;
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
g_assert (s_8021x);
success = nm_setting_802_1x_set_private_key (s_8021x,
path,
password,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
&format,
&error);
nmtst_assert_success (success, error);
g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
/* Make sure the password is what we expect */
pw = nm_setting_802_1x_get_private_key_password (s_8021x);
g_assert (pw);
g_assert_cmpstr (pw, ==, password);
/* Now clear it */
success = nm_setting_802_1x_set_private_key (s_8021x,
NULL,
NULL,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
NULL,
&error);
nmtst_assert_success (success, error);
/* Ensure the password is also now clear */
g_assert (!nm_setting_802_1x_get_private_key_password (s_8021x));
g_object_unref (s_8021x);
}
static void
test_wrong_phase2_password_keeps_data (const char *path, const char *password)
{
NMSetting8021x *s_8021x;
gboolean success;
NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
GError *error = NULL;
const char *pw;
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
g_assert (s_8021x);
success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
path,
password,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
&format,
&error);
nmtst_assert_success (success, error);
g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
/* Now try to set it to something that's not a certificate */
format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
"Makefile.am",
password,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
&format,
&error);
nmtst_assert_no_success (success, error);
g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
g_clear_error (&error);
/* Make sure the password hasn't changed */
pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
g_assert (pw);
g_assert_cmpstr (pw, ==, password);
g_object_unref (s_8021x);
}
static void
test_clear_phase2_private_key (const char *path, const char *password)
{
NMSetting8021x *s_8021x;
gboolean success;
NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
GError *error = NULL;
const char *pw;
s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
g_assert (s_8021x);
success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
path,
password,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
&format,
&error);
nmtst_assert_success (success, error);
g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN);
/* Make sure the password is what we expect */
pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
g_assert (pw);
g_assert_cmpstr (pw, ==, password);
/* Now clear it */
success = nm_setting_802_1x_set_phase2_private_key (s_8021x,
NULL,
NULL,
NM_SETTING_802_1X_CK_SCHEME_BLOB,
NULL,
&error);
nmtst_assert_success (success, error);
/* Ensure the password is also now clear */
g_assert (!nm_setting_802_1x_get_phase2_private_key_password (s_8021x));
g_object_unref (s_8021x);
}
static void
test_8021x (gconstpointer test_data)
{
char **parts, *path, *password;
parts = g_strsplit ((const char *) test_data, ", ", -1);
g_assert_cmpint (g_strv_length (parts), ==, 2);
path = g_build_filename (TEST_CERT_DIR, parts[0], NULL);
password = parts[1];
/* Test phase1 and phase2 path scheme */
test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH);
test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH);
/* Test phase1 and phase2 blob scheme */
test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB);
test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB);
/* Test that using a wrong password does not change existing data */
test_wrong_password_keeps_data (path, password);
test_wrong_phase2_password_keeps_data (path, password);
/* Test clearing the private key */
test_clear_private_key (path, password);
test_clear_phase2_private_key (path, password);
g_free (path);
g_strfreev (parts);
}
/*****************************************************************************/
static void
create_bond_connection (NMConnection **con, NMSettingBond **s_bond)
{
NMSettingConnection *s_con;
g_assert (con);
g_assert (s_bond);
*con = nmtst_create_minimal_connection ("bond",
NULL,
NM_SETTING_BOND_SETTING_NAME,
&s_con);
g_assert (*con);
g_assert (s_con);
g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "bond0", NULL);
*s_bond = (NMSettingBond *) nm_setting_bond_new ();
g_assert (*s_bond);
nm_connection_add_setting (*con, NM_SETTING (*s_bond));
}
#define test_verify_options(exp, ...) \
2018-11-30 11:37:21 +01:00
_test_verify_options (NM_MAKE_STRV (__VA_ARGS__), exp)
static void
2018-11-30 11:37:21 +01:00
_test_verify_options (const char *const *options,
gboolean expected_result)
{
gs_unref_object NMConnection *con = NULL;
NMSettingBond *s_bond;
GError *error = NULL;
gboolean success;
2018-11-30 11:37:21 +01:00
const char *const *option;
create_bond_connection (&con, &s_bond);
for (option = options; option[0] && option[1]; option += 2)
g_assert (nm_setting_bond_add_option (s_bond, option[0], option[1]));
if (expected_result) {
nmtst_assert_connection_verifies_and_normalizable (con);
nmtst_connection_normalize (con);
success = nm_setting_verify ((NMSetting *) s_bond, con, &error);
nmtst_assert_success (success, error);
} else {
nmtst_assert_connection_unnormalizable (con,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY);
}
}
static void
test_bond_verify (void)
{
test_verify_options (TRUE,
"mode", "3",
"arp_interval", "0");
test_verify_options (FALSE,
/* arp_interval not supported in balance-alb mode */
"mode", "balance-alb",
"arp_interval", "1",
"arp_ip_target", "1.2.3.4");
test_verify_options (FALSE,
/* arp_ip_target requires arp_interval */
"mode", "balance-rr",
"arp_ip_target", "1.2.3.4");
test_verify_options (TRUE,
"mode", "balance-rr",
"arp_interval", "1",
"arp_ip_target", "1.2.3.4");
test_verify_options (FALSE,
/* num_grat_arp, num_unsol_na cannot be different */
"mode", "balance-rr",
"num_grat_arp", "3",
"num_unsol_na", "4");
test_verify_options (TRUE,
"mode", "balance-rr",
"num_grat_arp", "5",
"num_unsol_na", "5");
test_verify_options (TRUE,
"mode", "active-backup",
"primary", "eth0");
test_verify_options (FALSE,
/* primary requires mode=active-backup */
"mode", "802.3ad",
"primary", "eth0");
test_verify_options (TRUE,
"mode", "802.3ad",
"lacp_rate", "fast");
test_verify_options (FALSE,
/* lacp_rate=fast requires mode=802.3ad */
"mode", "balance-rr",
"lacp_rate", "fast");
test_verify_options (TRUE,
"mode", "802.3ad",
"ad_actor_system", "ae:00:11:33:44:55");
}
static void
test_bond_compare_options (gboolean exp_res, const char **opts1, const char **opts2)
{
gs_unref_object NMSettingBond *s_bond1 = NULL, *s_bond2 = NULL;
const char **p;
s_bond1 = (NMSettingBond *) nm_setting_bond_new ();
g_assert (s_bond1);
s_bond2 = (NMSettingBond *) nm_setting_bond_new ();
g_assert (s_bond2);
for (p = opts1; p[0] && p[1]; p += 2)
g_assert (nm_setting_bond_add_option (s_bond1, p[0], p[1]));
for (p = opts2; p[0] && p[1]; p += 2)
g_assert (nm_setting_bond_add_option (s_bond2, p[0], p[1]));
g_assert_cmpint (nm_setting_compare ((NMSetting *) s_bond1,
(NMSetting *) s_bond2,
NM_SETTING_COMPARE_FLAG_EXACT),
==,
exp_res);
}
static void
test_bond_compare (void)
{
test_bond_compare_options (TRUE,
((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }),
((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }));
test_bond_compare_options (FALSE,
((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }),
((const char *[]){ "mode", "balance-rr", "miimon", "2", NULL }));
/* ignore default values */
test_bond_compare_options (TRUE,
((const char *[]){ "miimon", "1", NULL }),
((const char *[]){ "miimon", "1", "updelay", "0", NULL }));
/* special handling of num_grat_arp, num_unsol_na */
test_bond_compare_options (FALSE,
((const char *[]){ "num_grat_arp", "2", NULL }),
((const char *[]){ "num_grat_arp", "1", NULL }));
test_bond_compare_options (TRUE,
((const char *[]){ "num_grat_arp", "3", NULL }),
((const char *[]){ "num_unsol_na", "3", NULL }));
test_bond_compare_options (TRUE,
((const char *[]){ "num_grat_arp", "4", NULL }),
((const char *[]){ "num_unsol_na", "4", "num_grat_arp", "4", NULL }));
}
static void
test_bond_normalize_options (const char **opts1, const char **opts2)
{
gs_unref_object NMConnection *con = NULL;
NMSettingBond *s_bond;
GError *error = NULL;
gboolean success;
const char **p;
int num = 0;
create_bond_connection (&con, &s_bond);
for (p = opts1; p[0] && p[1]; p += 2)
g_assert (nm_setting_bond_add_option (s_bond, p[0], p[1]));
nmtst_assert_connection_verifies_and_normalizable (con);
nmtst_connection_normalize (con);
success = nm_setting_verify ((NMSetting *) s_bond, con, &error);
nmtst_assert_success (success, error);
for (p = opts2; p[0] && p[1]; p += 2) {
g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, p[0]), ==, p[1]);
num++;
}
g_assert_cmpint (num, ==, nm_setting_bond_get_num_options (s_bond));
}
static void
test_bond_normalize (void)
{
test_bond_normalize_options (
((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL }),
((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL }));
test_bond_normalize_options (
((const char *[]){ "mode", "1", "miimon", "1", NULL }),
((const char *[]){ "mode", "active-backup", "miimon", "1", NULL }));
test_bond_normalize_options (
((const char *[]){ "mode", "balance-alb", "tlb_dynamic_lb", "1", NULL }),
((const char *[]){ "mode", "balance-alb", NULL }));
test_bond_normalize_options (
((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL }),
((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL }));
test_bond_normalize_options (
((const char *[]){ "mode", "balance-rr", "ad_actor_sys_prio", "4", "packets_per_slave", "3", NULL }),
((const char *[]){ "mode", "balance-rr", "packets_per_slave", "3", NULL }));
}
/*****************************************************************************/
#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \
NM_SETTING_DCB_FLAG_ADVERTISE | \
NM_SETTING_DCB_FLAG_WILLING)
static void
test_dcb_flags_valid (void)
{
gs_unref_object NMSettingDcb *s_dcb = NULL;
GError *error = NULL;
gboolean success;
guint i;
s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
g_assert (s_dcb);
g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, 0);
g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, 0);
g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, 0);
g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, 0);
g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, 0);
g_object_set (G_OBJECT (s_dcb),
NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL,
NM_SETTING_DCB_APP_ISCSI_FLAGS, DCB_FLAGS_ALL,
NM_SETTING_DCB_APP_FIP_FLAGS, DCB_FLAGS_ALL,
NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL,
NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL,
NULL);
/* Priority Group Bandwidth must total 100% */
for (i = 0; i < 7; i++)
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 12);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16);
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error);
g_assert_no_error (error);
g_assert (success);
g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, DCB_FLAGS_ALL);
g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, DCB_FLAGS_ALL);
g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, DCB_FLAGS_ALL);
g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, DCB_FLAGS_ALL);
g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, DCB_FLAGS_ALL);
}
#define TEST_FLAG(p, f, v) \
{ \
/* GObject property min/max should ensure the property does not get set to \
* the invalid value, so we ensure the value we just tried to set is 0 and \
* that verify is successful since the property never got set. \
*/ \
g_object_set (G_OBJECT (s_dcb), p, v, NULL); \
g_assert_cmpint (f (s_dcb), ==, 0); \
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
g_assert_no_error (error); \
g_assert (success); \
}
static void
test_dcb_flags_invalid (void)
{
gs_unref_object NMSettingDcb *s_dcb = NULL;
GError *error = NULL;
gboolean success;
s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
g_assert (s_dcb);
NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523);
g_test_assert_expected_messages ();
NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF);
g_test_assert_expected_messages ();
NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111);
g_test_assert_expected_messages ();
NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32);
g_test_assert_expected_messages ();
NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags,
(NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1);
g_test_assert_expected_messages ();
}
#define TEST_APP_PRIORITY(lcprop, ucprop, v) \
{ \
g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \
\
g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, v, NULL); \
g_assert_cmpint (nm_setting_dcb_get_app_##lcprop##_priority (s_dcb), ==, v); \
\
/* Assert that the setting is invalid while the app is disabled unless v is default */ \
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
if (v >= 0) { \
g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \
g_assert (success == FALSE); \
} else { \
g_assert_no_error (error); \
g_assert (success); \
} \
g_clear_error (&error); \
\
/* Set the enable flag and re-verify, this time it should be valid */ \
g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
g_assert_no_error (error); \
g_assert (success); \
\
g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, 0, NULL); \
}
static void
test_dcb_app_priorities (void)
{
gs_unref_object NMSettingDcb *s_dcb = NULL;
GError *error = NULL;
gboolean success;
s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
g_assert (s_dcb);
/* Defaults */
g_assert_cmpint (nm_setting_dcb_get_app_fcoe_priority (s_dcb), ==, -1);
g_assert_cmpint (nm_setting_dcb_get_app_iscsi_priority (s_dcb), ==, -1);
g_assert_cmpint (nm_setting_dcb_get_app_fip_priority (s_dcb), ==, -1);
TEST_APP_PRIORITY (fcoe, FCOE, 6);
TEST_APP_PRIORITY (iscsi, ISCSI, 5);
TEST_APP_PRIORITY (fip, FIP, 4);
TEST_APP_PRIORITY (fcoe, FCOE, -1);
TEST_APP_PRIORITY (iscsi, ISCSI, -1);
TEST_APP_PRIORITY (fip, FIP, -1);
}
#define TEST_PRIORITY_VALID(fn, id, val, flagsprop, verify) \
{ \
/* Assert that setting the value gets the same value back out */ \
nm_setting_dcb_set_priority_##fn (s_dcb, id, val); \
g_assert_cmpint (nm_setting_dcb_get_priority_##fn (s_dcb, id), ==, val); \
\
if (verify) { \
if (val != 0) { \
/* Assert that verify fails because the flags do not include 'enabled' \
* and a value has been set. \
*/ \
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \
g_assert (success == FALSE); \
g_clear_error (&error); \
} \
\
/* Assert that adding the 'enabled' flag verifies the setting */ \
g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \
g_assert_no_error (error); \
g_assert (success); \
} \
\
/* Reset everything */ \
g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \
nm_setting_dcb_set_priority_##fn (s_dcb, id, 0); \
}
/* If Priority Groups are enabled, PG bandwidth must equal 100% */
#define SET_VALID_PRIORITY_GROUP_BANDWIDTH \
{ \
guint x; \
for (x = 0; x < 7; x++) \
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, x, 12); \
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); \
}
static void
test_dcb_priorities_valid (void)
{
gs_unref_object NMSettingDcb *s_dcb = NULL;
GError *error = NULL;
gboolean success;
guint i;
s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
g_assert (s_dcb);
for (i = 0; i < 8; i++)
TEST_PRIORITY_VALID (flow_control, i, TRUE, FLOW_CONTROL, TRUE);
SET_VALID_PRIORITY_GROUP_BANDWIDTH
for (i = 0; i < 8; i++) {
TEST_PRIORITY_VALID (group_id, i, i, GROUP, TRUE);
TEST_PRIORITY_VALID (group_id, i, 7 - i, GROUP, TRUE);
}
/* Clear PG bandwidth from earlier tests */
for (i = 0; i < 8; i++)
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 0);
/* Priority Group Bandwidth must add up to 100% if enabled, which requires
* some dancing for verifying individual values here.
*/
for (i = 0; i < 8; i++) {
guint other = 7 - (i % 8);
/* Set another priority group to the remaining bandwidth */
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - i);
TEST_PRIORITY_VALID (group_bandwidth, i, i, GROUP, TRUE);
/* Set another priority group to the remaining bandwidth */
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - (7 - i));
TEST_PRIORITY_VALID (group_bandwidth, i, 7 - i, GROUP, TRUE);
/* Clear remaining bandwidth */
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 0);
}
SET_VALID_PRIORITY_GROUP_BANDWIDTH
for (i = 0; i < 8; i++) {
TEST_PRIORITY_VALID (bandwidth, i, i, GROUP, TRUE);
TEST_PRIORITY_VALID (bandwidth, i, 7 - i, GROUP, TRUE);
}
SET_VALID_PRIORITY_GROUP_BANDWIDTH
for (i = 0; i < 8; i++)
TEST_PRIORITY_VALID (strict_bandwidth, i, TRUE, GROUP, TRUE);
SET_VALID_PRIORITY_GROUP_BANDWIDTH
for (i = 0; i < 8; i++) {
TEST_PRIORITY_VALID (traffic_class, i, i, GROUP, TRUE);
TEST_PRIORITY_VALID (traffic_class, i, 7 - i, GROUP, TRUE);
}
}
static void
test_dcb_bandwidth_sums (void)
{
gs_unref_object NMSettingDcb *s_dcb = NULL;
GError *error = NULL;
gboolean success;
s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
g_assert (s_dcb);
/* Assert that setting the value gets the same value back out */
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 0, 9);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 1, 10);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 2, 11);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 3, 12);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 13);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 5, 14);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 6, 15);
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16);
/* Assert verify success when sums total 100% */
g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL);
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error);
g_assert_no_error (error);
g_assert (success);
/* Assert verify fails when sums do not total 100% */
nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 20);
success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error);
g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);
g_assert (success == FALSE);
g_clear_error (&error);
}
/*****************************************************************************/
#if WITH_JSON_VALIDATION
static void
_test_team_config_sync (const char *team_config,
int notify_peer_count,
int notify_peers_interval,
int mcast_rejoin_count,
int mcast_rejoin_interval,
char *runner,
char *runner_hwaddr_policy, /* activebackup */
GPtrArray *runner_tx_hash, /* lacp, loadbalance */
char *runner_tx_balancer, /* lacp, loadbalance */
int runner_tx_balancer_interval, /* lacp, loadbalance */
gboolean runner_active, /* lacp */
gboolean runner_fast_rate, /* lacp */
int runner_sys_prio, /* lacp */
int runner_min_ports, /* lacp */
char *runner_agg_select_policy, /* lacp */
GPtrArray *link_watchers)
{
gs_unref_object NMSettingTeam *s_team = NULL;
guint i, j;
gboolean found;
s_team = (NMSettingTeam *) nm_setting_team_new ();
g_assert (s_team);
g_object_set (s_team, NM_SETTING_TEAM_CONFIG, team_config, NULL);
2019-05-10 12:19:31 +02:00
g_assert_cmpint (nm_setting_team_get_notify_peers_count (s_team), ==, notify_peer_count);
g_assert_cmpint (nm_setting_team_get_notify_peers_interval (s_team), ==, notify_peers_interval);
g_assert_cmpint (nm_setting_team_get_mcast_rejoin_count (s_team), ==, mcast_rejoin_count);
g_assert_cmpint (nm_setting_team_get_mcast_rejoin_interval (s_team), ==, mcast_rejoin_interval);
g_assert_cmpint (nm_setting_team_get_runner_tx_balancer_interval (s_team), ==, runner_tx_balancer_interval);
g_assert_cmpint (nm_setting_team_get_runner_active (s_team), ==, runner_active);
g_assert_cmpint (nm_setting_team_get_runner_fast_rate (s_team), ==, runner_fast_rate);
g_assert_cmpint (nm_setting_team_get_runner_sys_prio (s_team), ==, runner_sys_prio);
g_assert_cmpint (nm_setting_team_get_runner_min_ports (s_team), ==, runner_min_ports);
g_assert_cmpstr (nm_setting_team_get_runner (s_team), ==, runner);
g_assert_cmpstr (nm_setting_team_get_runner_hwaddr_policy (s_team), ==, runner_hwaddr_policy);
g_assert_cmpstr (nm_setting_team_get_runner_tx_balancer (s_team), ==, runner_tx_balancer);
g_assert_cmpstr (nm_setting_team_get_runner_agg_select_policy (s_team), ==, runner_agg_select_policy);
if (runner_tx_hash) {
2019-05-10 12:19:31 +02:00
g_assert_cmpint (runner_tx_hash->len, ==, nm_setting_team_get_num_runner_tx_hash (s_team));
for (i = 0; i < runner_tx_hash->len; i++) {
found = FALSE;
for (j = 0; j < nm_setting_team_get_num_runner_tx_hash (s_team); j++) {
if (nm_streq0 (nm_setting_team_get_runner_tx_hash (s_team, j),
runner_tx_hash->pdata[i])) {
found = TRUE;
break;
}
}
g_assert (found);
}
}
if (link_watchers) {
2019-05-10 12:19:31 +02:00
g_assert_cmpint (link_watchers->len, ==, nm_setting_team_get_num_link_watchers (s_team));
for (i = 0; i < link_watchers->len; i++) {
found = FALSE;
for (j = 0; j < nm_setting_team_get_num_link_watchers (s_team); j++) {
if (nm_team_link_watcher_equal (link_watchers->pdata[i],
nm_setting_team_get_link_watcher (s_team, j))) {
found = TRUE;
break;
}
}
g_assert (found);
}
}
g_assert (nm_setting_verify ((NMSetting *) s_team, NULL, NULL));
}
static void
test_runner_roundrobin_sync_from_config (void)
{
_test_team_config_sync ("",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_ROUNDROBIN,
NULL,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
NULL);
}
static void
test_runner_broadcast_sync_from_config (void)
{
_test_team_config_sync ("{\"runner\": {\"name\": \"broadcast\"}}",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_BROADCAST,
NULL,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
NULL);
}
static void
test_runner_random_sync_from_config (void)
{
_test_team_config_sync ("{\"runner\": {\"name\": \"random\"}}",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_RANDOM,
NULL,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
NULL);
}
static void
test_runner_activebackup_sync_from_config (void)
{
_test_team_config_sync ("{\"runner\": {\"name\": \"activebackup\"}}",
NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT, 0,
NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT, 0,
NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP,
NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
NULL);
}
static void
test_runner_loadbalance_sync_from_config (void)
{
gs_unref_ptrarray GPtrArray *tx_hash = NULL;
tx_hash = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (tx_hash, g_strdup ("eth"));
g_ptr_array_add (tx_hash, g_strdup ("ipv4"));
g_ptr_array_add (tx_hash, g_strdup ("ipv6"));
_test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\"}}",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_LOADBALANCE,
NULL,
tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT,
FALSE, FALSE, -1, -1, NULL,
NULL);
_test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\", "
"\"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"]}}",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_LOADBALANCE,
NULL,
tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT,
FALSE, FALSE, -1, -1, NULL,
NULL);
_test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"], "
"\"tx_balancer\": {\"name\": \"basic\", \"balancing_interval\": 30}}}",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_LOADBALANCE,
NULL,
tx_hash, "basic", 30,
FALSE, FALSE, -1, -1, NULL,
NULL);
}
static void
test_runner_lacp_sync_from_config (void)
{
gs_unref_ptrarray GPtrArray *tx_hash = NULL;
tx_hash = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (tx_hash, g_strdup ("eth"));
g_ptr_array_add (tx_hash, g_strdup ("ipv4"));
g_ptr_array_add (tx_hash, g_strdup ("ipv6"));
_test_team_config_sync ("{\"runner\": {\"name\": \"lacp\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"]}}",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_LACP,
NULL,
tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT,
TRUE, FALSE, NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT, 0,
NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT,
NULL);
_test_team_config_sync ("{\"runner\": {\"name\": \"lacp\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"], "
"\"active\": false, \"fast_rate\": true, \"sys_prio\": 10, \"min_ports\": 5, "
"\"agg_select_policy\": \"port_config\"}}",
0, 0, 0, 0,
NM_SETTING_TEAM_RUNNER_LACP,
NULL,
tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT,
FALSE, TRUE, 10, 5, "port_config",
NULL);
}
static void
test_watcher_ethtool_sync_from_config (void)
{
gs_unref_ptrarray GPtrArray *link_watchers = NULL;
link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
g_ptr_array_add (link_watchers, nm_team_link_watcher_new_ethtool (0, 0, NULL));
_test_team_config_sync ("{\"link_watch\": {\"name\": \"ethtool\"}}",
0, 0, 0, 0,
"roundrobin",
NULL,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
link_watchers);
}
static void
test_watcher_nsna_ping_sync_from_config (void)
{
gs_unref_ptrarray GPtrArray *link_watchers = NULL;
link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
g_ptr_array_add (link_watchers, nm_team_link_watcher_new_nsna_ping (0, 0, 3, "target.host", NULL));
_test_team_config_sync ("{\"link_watch\": {\"name\": \"nsna_ping\", \"target_host\": \"target.host\"}}",
0, 0, 0, 0,
"roundrobin",
NULL,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
link_watchers);
}
static void
test_watcher_arp_ping_sync_from_config (void)
{
gs_unref_ptrarray GPtrArray *link_watchers = NULL;
link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
g_ptr_array_add (link_watchers,
nm_team_link_watcher_new_arp_ping (0, 0, 3, "target.host", "source.host", 0, NULL));
_test_team_config_sync ("{\"link_watch\": {\"name\": \"arp_ping\", \"target_host\": \"target.host\", "
"\"source_host\": \"source.host\"}}",
0, 0, 0, 0,
"roundrobin",
NULL,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
link_watchers);
}
static void
test_multiple_watchers_sync_from_config (void)
{
gs_unref_ptrarray GPtrArray *link_watchers = NULL;
link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
g_ptr_array_add (link_watchers, nm_team_link_watcher_new_ethtool (2, 4, NULL));
g_ptr_array_add (link_watchers, nm_team_link_watcher_new_nsna_ping (3, 6, 9, "target.host", NULL));
g_ptr_array_add (link_watchers,
nm_team_link_watcher_new_arp_ping (5, 10, 15, "target.host", "source.host",
NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE
| NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE
| NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS,
NULL));
_test_team_config_sync ("{\"link_watch\": ["
"{\"name\": \"ethtool\", \"delay_up\": 2, \"delay_down\": 4}, "
"{\"name\": \"arp_ping\", \"init_wait\": 5, \"interval\": 10, \"missed_max\": 15, "
"\"target_host\": \"target.host\", \"source_host\": \"source.host\", "
"\"validate_active\": true, \"validate_inactive\": true, \"send_always\": true}, "
"{\"name\": \"nsna_ping\", \"init_wait\": 3, \"interval\": 6, \"missed_max\": 9, "
"\"target_host\": \"target.host\"}]}",
0, 0, 0, 0,
"roundrobin",
NULL,
NULL, NULL, -1,
FALSE, FALSE, -1, -1, NULL,
link_watchers);
}
/*****************************************************************************/
static void
_test_team_port_config_sync (const char *team_port_config,
int queue_id,
int prio,
gboolean sticky,
int lacp_prio,
int lacp_key,
GPtrArray *link_watchers)
{
2017-12-08 08:48:02 +01:00
gs_unref_object NMSettingTeamPort *s_team_port = NULL;
guint i, j;
gboolean found;
s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
g_assert (s_team_port);
g_object_set (s_team_port, NM_SETTING_TEAM_CONFIG, team_port_config, NULL);
g_assert (nm_setting_team_port_get_queue_id (s_team_port) == queue_id);
g_assert (nm_setting_team_port_get_prio (s_team_port) == prio);
g_assert (nm_setting_team_port_get_sticky (s_team_port) == sticky);
g_assert (nm_setting_team_port_get_lacp_prio (s_team_port) == lacp_prio);
g_assert (nm_setting_team_port_get_lacp_key (s_team_port) == lacp_key);
if (link_watchers) {
g_assert (link_watchers->len == nm_setting_team_port_get_num_link_watchers (s_team_port));
for (i = 0; i < link_watchers->len; i++) {
found = FALSE;
for (j = 0; j < nm_setting_team_port_get_num_link_watchers (s_team_port); j++) {
if (nm_team_link_watcher_equal (link_watchers->pdata[i],
nm_setting_team_port_get_link_watcher (s_team_port,
j))) {
found = TRUE;
break;
}
}
g_assert (found);
}
}
g_assert (nm_setting_verify ((NMSetting *) s_team_port, NULL, NULL));
}
static void
test_team_port_default (void)
{
_test_team_port_config_sync ("", -1, 0, FALSE, 255, 0, NULL);
}
static void
test_team_port_queue_id (void)
{
_test_team_port_config_sync ("{\"queue_id\": 3}",
3, 0, FALSE, 255, 0, NULL);
_test_team_port_config_sync ("{\"queue_id\": 0}",
0, 0, FALSE, 255, 0, NULL);
}
static void
test_team_port_prio (void)
{
_test_team_port_config_sync ("{\"prio\": 6}",
-1, 6, FALSE, 255, 0, NULL);
_test_team_port_config_sync ("{\"prio\": 0}",
-1, 0, FALSE, 255, 0, NULL);
}
static void
test_team_port_sticky (void)
{
_test_team_port_config_sync ("{\"sticky\": true}",
-1, 0, TRUE, 255, 0, NULL);
_test_team_port_config_sync ("{\"sticky\": false}",
-1, 0, FALSE, 255, 0, NULL);
}
static void
test_team_port_lacp_prio (void)
{
_test_team_port_config_sync ("{\"lacp_prio\": 9}",
-1, 0, FALSE, 9, 0, NULL);
_test_team_port_config_sync ("{\"lacp_prio\": 0}",
-1, 0, FALSE, 0, 0, NULL);
}
static void
test_team_port_lacp_key (void)
{
_test_team_port_config_sync ("{\"lacp_key\": 12}",
-1, 0, FALSE, 255, 12, NULL);
_test_team_port_config_sync ("{\"lacp_key\": 0}",
-1, 0, FALSE, 255, 0, NULL);
}
static void
test_team_port_full_config (void)
{
gs_unref_ptrarray GPtrArray *link_watchers = NULL;
link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
g_ptr_array_add (link_watchers,
nm_team_link_watcher_new_arp_ping (0, 3, 3, "1.2.3.2", "1.2.3.1",
NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE,
NULL));
g_ptr_array_add (link_watchers,
nm_team_link_watcher_new_arp_ping (1, 1, 0, "1.2.3.4", "1.2.3.1",
NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS,
NULL));
_test_team_port_config_sync ("{\"queue_id\": 10, \"prio\": 20, \"sticky\": true, \"lacp_prio\": 30, "
"\"lacp_key\": 40, \"link_watch\": ["
"{\"name\": \"arp_ping\", \"interval\": 3, \"target_host\": \"1.2.3.2\", "
"\"source_host\": \"1.2.3.1\", \"validate_inactive\": true}, "
"{\"name\": \"arp_ping\", \"init_wait\": 1, \"interval\": 1, "
"\"target_host\": \"1.2.3.4\", \"source_host\": \"1.2.3.1\", "
"\"send_always\": true}]}",
10, 20, true, 30, 40, NULL);
}
#endif
/*****************************************************************************/
static void
test_ethtool_1 (void)
{
gs_unref_object NMConnection *con = NULL;
gs_unref_object NMConnection *con2 = NULL;
gs_unref_object NMConnection *con3 = NULL;
gs_unref_variant GVariant *variant = NULL;
gs_free_error GError *error = NULL;
gs_unref_keyfile GKeyFile *keyfile = NULL;
NMSettingConnection *s_con;
NMSettingEthtool *s_ethtool;
NMSettingEthtool *s_ethtool2;
NMSettingEthtool *s_ethtool3;
con = nmtst_create_minimal_connection ("ethtool-1",
NULL,
NM_SETTING_WIRED_SETTING_NAME,
&s_con);
s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ());
nm_connection_add_setting (con, NM_SETTING (s_ethtool));
nm_setting_ethtool_set_feature (s_ethtool,
NM_ETHTOOL_OPTNAME_FEATURE_RX,
NM_TERNARY_TRUE);
nm_setting_ethtool_set_feature (s_ethtool,
NM_ETHTOOL_OPTNAME_FEATURE_LRO,
NM_TERNARY_FALSE);
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE);
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_SG), ==, NM_TERNARY_DEFAULT);
nmtst_connection_normalize (con);
variant = nm_connection_to_dbus (con, NM_CONNECTION_SERIALIZE_ALL);
con2 = nm_simple_connection_new_from_dbus (variant, &error);
nmtst_assert_success (con2, error);
s_ethtool2 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con2, NM_TYPE_SETTING_ETHTOOL));
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE);
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool2, NM_ETHTOOL_OPTNAME_FEATURE_SG), ==, NM_TERNARY_DEFAULT);
nmtst_assert_connection_verifies_without_normalization (con2);
nmtst_assert_connection_equals (con, FALSE, con2, FALSE);
keyfile = nm_keyfile_write (con, NULL, NULL, &error);
nmtst_assert_success (keyfile, error);
con3 = nm_keyfile_read (keyfile,
keyfile: split automatically setting ID/UUID for keyfile keyfile already supports omitting the "connection.id" and "connection.uuid". In that case, the ID would be taken from the keyfile's name, and the UUID was generated by md5 hashing the full filename. No longer do this during nm_keyfile_read(), instead let all callers call nm_keyfile_read_ensure_*() to their liking. This is done for two reasons: - a minor reason is, that one day we want to expose keyfile API as public API. That means, we also want to read keyfiles from stdin, where there is no filename available. The implementation which parses stdio needs to define their own way of auto-generating ID and UUID. Note how nm_keyfile_read()'s API no longer takes a filename as argument, which would be awkward for the stdin case. - Currently, we only support one keyfile directory, which (configurably) is "/etc/NetworkManager/system-connections". In the future, we want to support multiple keyfile dirctories, like "/var/run/NetworkManager/profiles" or "/usr/lib/NetworkManager/profiles". Here we want that a file "foo" (which does not specify a UUID) gets the same UUID regardless of the directory it is in. That seems better, because then the UUID won't change as you move the file between directories. Yes, that means, that the same UUID will be provided by multiple files, but NetworkManager must already cope with that situation anyway. Unfortunately, the UUID generation scheme hashes the full path. That means, we must hash the path name of the file "foo" inside the original "system-connections" directory. Refactor the code so that it accounds for a difference between the filename of the keyfile, and the profile_dir used for generating the UUID.
2018-10-02 19:53:54 +02:00
"/ignored/current/working/directory/for/loading/relative/paths",
NULL,
NULL,
&error);
nmtst_assert_success (con3, error);
keyfile: split automatically setting ID/UUID for keyfile keyfile already supports omitting the "connection.id" and "connection.uuid". In that case, the ID would be taken from the keyfile's name, and the UUID was generated by md5 hashing the full filename. No longer do this during nm_keyfile_read(), instead let all callers call nm_keyfile_read_ensure_*() to their liking. This is done for two reasons: - a minor reason is, that one day we want to expose keyfile API as public API. That means, we also want to read keyfiles from stdin, where there is no filename available. The implementation which parses stdio needs to define their own way of auto-generating ID and UUID. Note how nm_keyfile_read()'s API no longer takes a filename as argument, which would be awkward for the stdin case. - Currently, we only support one keyfile directory, which (configurably) is "/etc/NetworkManager/system-connections". In the future, we want to support multiple keyfile dirctories, like "/var/run/NetworkManager/profiles" or "/usr/lib/NetworkManager/profiles". Here we want that a file "foo" (which does not specify a UUID) gets the same UUID regardless of the directory it is in. That seems better, because then the UUID won't change as you move the file between directories. Yes, that means, that the same UUID will be provided by multiple files, but NetworkManager must already cope with that situation anyway. Unfortunately, the UUID generation scheme hashes the full path. That means, we must hash the path name of the file "foo" inside the original "system-connections" directory. Refactor the code so that it accounds for a difference between the filename of the keyfile, and the profile_dir used for generating the UUID.
2018-10-02 19:53:54 +02:00
nm_keyfile_read_ensure_id (con3, "unused-because-already-has-id");
nm_keyfile_read_ensure_uuid (con3, "unused-because-already-has-uuid");
nmtst_connection_normalize (con3);
nmtst_assert_connection_equals (con, FALSE, con3, FALSE);
s_ethtool3 = NM_SETTING_ETHTOOL (nm_connection_get_setting (con3, NM_TYPE_SETTING_ETHTOOL));
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_RX), ==, NM_TERNARY_TRUE);
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_LRO), ==, NM_TERNARY_FALSE);
g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool3, NM_ETHTOOL_OPTNAME_FEATURE_SG), ==, NM_TERNARY_DEFAULT);
}
/*****************************************************************************/
static void
test_sriov_vf (void)
{
NMSriovVF *vf1, *vf2;
GError *error = NULL;
char *str;
vf1 = nm_sriov_vf_new (1);
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22:33:44:55"));
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (100));
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (500));
str = nm_utils_sriov_vf_to_str (vf1, FALSE, &error);
g_assert_no_error (error);
g_assert_cmpstr (str, ==, "1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100 spoof-check=true trust=false");
g_free (str);
vf2 = nm_utils_sriov_vf_from_str (" 1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100", &error);
nmtst_assert_success (vf2, error);
nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (FALSE));
nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE));
nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (TRUE));
nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, NULL);
nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
g_assert (nm_sriov_vf_equal (vf1, vf2));
nm_sriov_vf_unref (vf1);
nm_sriov_vf_unref (vf2);
}
static void
test_sriov_vf_dup (void)
{
NMSriovVF *vf1, *vf2;
vf1 = nm_sriov_vf_new (1);
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("foobar"));
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE));
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (10));
nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (1000));
nm_sriov_vf_add_vlan (vf1, 80);
nm_sriov_vf_set_vlan_qos (vf1, 80, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
vf2 = nm_sriov_vf_dup (vf1);
g_assert (nm_sriov_vf_equal (vf1, vf2));
nm_sriov_vf_unref (vf1);
nm_sriov_vf_unref (vf2);
}
static void
test_sriov_vf_vlan (void)
{
NMSriovVF *vf;
const guint *vlan_ids;
guint num;
GError *error = NULL;
gs_free char *str = NULL;
vf = nm_sriov_vf_new (19);
nm_sriov_vf_set_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22"));
g_assert (nm_sriov_vf_add_vlan (vf, 80));
g_assert (!nm_sriov_vf_add_vlan (vf, 80));
g_assert (nm_sriov_vf_add_vlan (vf, 82));
g_assert (nm_sriov_vf_add_vlan (vf, 83));
g_assert (nm_sriov_vf_add_vlan (vf, 81));
g_assert (!nm_sriov_vf_remove_vlan (vf, 100));
g_assert (nm_sriov_vf_remove_vlan (vf, 82));
nm_sriov_vf_set_vlan_qos (vf, 81, 0xabba);
nm_sriov_vf_set_vlan_protocol (vf, 81, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
g_assert (vlan_ids);
g_assert_cmpint (num, ==, 3);
g_assert_cmpint (vlan_ids[0], ==, 80);
g_assert_cmpint (vlan_ids[1], ==, 81);
g_assert_cmpint (vlan_ids[2], ==, 83);
g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 80), ==, 0x0);
g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 80), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 0xabba);
g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
nm_sriov_vf_unref (vf);
vf = nm_utils_sriov_vf_from_str ("20 spoof-check=false vlans=85.0.q;4000.0x20.ad;81.10;83", &error);
nmtst_assert_success (vf, error);
vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num);
g_assert (vlan_ids);
g_assert_cmpint (num, ==, 4);
g_assert_cmpint (vlan_ids[0], ==, 81);
g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 10);
g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
g_assert_cmpint (vlan_ids[1], ==, 83);
g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 83), ==, 0);
g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 83), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
g_assert_cmpint (vlan_ids[2], ==, 85);
g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 85), ==, 0);
g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 85), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
g_assert_cmpint (vlan_ids[3], ==, 4000);
g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 4000), ==, 0x20);
g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 4000), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD);
str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "20 spoof-check=false vlans=81.10;83;85;4000.32.ad");
nm_sriov_vf_unref (vf);
}
static void
test_sriov_setting (void)
{
gs_unref_object NMConnection *con = NULL;
NMSettingConnection *s_con;
NMSettingSriov *s_sriov = NULL;
NMSriovVF *vf1, *vf2, *vf3;
GError *error = NULL;
gboolean success;
con = nm_simple_connection_new ();
s_con = (NMSettingConnection *) nm_setting_connection_new ();
nm_connection_add_setting (con, NM_SETTING (s_con));
g_object_set (s_con,
NM_SETTING_CONNECTION_ID, "Test SR-IOV connection",
NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0",
NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
NULL);
nm_connection_add_setting (con, nm_setting_wired_new ());
s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
nm_connection_add_setting (con, NM_SETTING (s_sriov));
g_object_set (s_sriov, NM_SETTING_SRIOV_TOTAL_VFS, 16, NULL);
nm_setting_sriov_add_vf (s_sriov, (vf1 = nm_sriov_vf_new (0)));
nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (4)));
nm_setting_sriov_add_vf (s_sriov, (vf3 = nm_sriov_vf_new (10)));
g_assert (nm_setting_sriov_remove_vf_by_index (s_sriov, 4));
nm_sriov_vf_unref (vf2);
nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (2)));
nmtst_assert_connection_verifies_and_normalizable (con);
nmtst_connection_normalize (con);
success = nm_setting_verify ((NMSetting *) s_sriov, con, &error);
nmtst_assert_success (success, error);
g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3);
g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 0)), ==, 0);
g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 1)), ==, 2);
g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 2)), ==, 10);
nm_sriov_vf_unref (vf1);
nm_sriov_vf_unref (vf2);
nm_sriov_vf_unref (vf3);
}
typedef struct {
guint id;
guint qos;
bool proto_ad;
} VlanData;
static void
_test_sriov_parse_vlan_one (const char *string, gboolean exp_res, VlanData *data, guint data_length)
{
NMSriovVF *vf;
gboolean res;
guint i, num_vlans;
const guint *vlan_ids;
vf = nm_sriov_vf_new (1);
g_assert (vf);
res = _nm_sriov_vf_parse_vlans (vf, string, NULL);
g_assert_cmpint (res, ==, exp_res);
if (exp_res) {
vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
g_assert_cmpint (num_vlans, ==, data_length);
for (i = 0; i < num_vlans; i++) {
g_assert_cmpint (vlan_ids[i], ==, data[i].id);
g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]), ==, data[i].qos);
g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]),
==,
data[i].proto_ad ? NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q);
}
}
nm_sriov_vf_unref (vf);
}
#define test_sriov_parse_vlan_one(string, result, ...) \
{ \
VlanData _data[] = { __VA_ARGS__ }; \
guint _length = G_N_ELEMENTS (_data); \
\
_test_sriov_parse_vlan_one (string, result, _data, _length); \
}
static void
test_sriov_parse_vlans (void)
{
test_sriov_parse_vlan_one ("", FALSE, {});
test_sriov_parse_vlan_one ("1", TRUE, {1, 0, 0});
test_sriov_parse_vlan_one ("1;2", TRUE, {1, 0, 0}, {2, 0, 0});
test_sriov_parse_vlan_one ("4095;;2", TRUE, {2, 0, 0}, {4095, 0, 0});
test_sriov_parse_vlan_one ("1 2", FALSE, {});
test_sriov_parse_vlan_one ("4096", FALSE, {});
test_sriov_parse_vlan_one ("1.10", TRUE, {1, 10, 0});
test_sriov_parse_vlan_one ("1.20.ad", TRUE, {1, 20, 1});
test_sriov_parse_vlan_one ("1.21.q", TRUE, {1, 21, 0});
test_sriov_parse_vlan_one ("9.20.foo", FALSE, {});
test_sriov_parse_vlan_one ("1.20.ad.12", FALSE, {});
test_sriov_parse_vlan_one ("1;1.10", FALSE, {});
test_sriov_parse_vlan_one ("1..1;2", FALSE, {});
test_sriov_parse_vlan_one ("1..ad;2", FALSE, {});
test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0});
}
static void
test_bridge_vlans (void)
{
NMBridgeVlan *v1, *v2;
GError *error = NULL;
guint16 vid_start, vid_end;
char *str;
v1 = nm_bridge_vlan_from_str ("1 foobar", &error);
nmtst_assert_no_success (v1, error);
g_clear_error (&error);
v1 = nm_bridge_vlan_from_str ("4095", &error);
nmtst_assert_no_success (v1, error);
g_clear_error (&error);
/* test ranges */
v1 = nm_bridge_vlan_from_str ("2-1000 untagged", &error);
nmtst_assert_success (v1, error);
g_assert_cmpint (nm_bridge_vlan_get_vid_range (v1, &vid_start, &vid_end), ==, TRUE);
g_assert_cmpuint (vid_start, ==, 2);
g_assert_cmpuint (vid_end, ==, 1000);
g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE);
nm_bridge_vlan_unref (v1);
/* test comparison (1) */
v1 = nm_bridge_vlan_from_str ("10 untagged", &error);
nmtst_assert_success (v1, error);
g_assert_cmpint (nm_bridge_vlan_get_vid_range (v1, &vid_start, &vid_end), ==, FALSE);
g_assert_cmpuint (vid_start, ==, 10);
g_assert_cmpuint (vid_end, ==, 10);
g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, FALSE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, TRUE);
nm_bridge_vlan_set_pvid (v1, TRUE);
nm_bridge_vlan_set_untagged (v1, FALSE);
nm_bridge_vlan_seal (v1);
g_assert_cmpint (nm_bridge_vlan_is_sealed (v1), ==, TRUE);
g_assert_cmpint (nm_bridge_vlan_is_pvid (v1), ==, TRUE);
g_assert_cmpint (nm_bridge_vlan_is_untagged (v1), ==, FALSE);
str = nm_bridge_vlan_to_str (v1, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "10 pvid");
g_clear_pointer (&str, g_free);
v2 = nm_bridge_vlan_from_str (" 10 pvid ", &error);
nmtst_assert_success (v2, error);
g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), ==, 0);
nm_bridge_vlan_unref (v1);
nm_bridge_vlan_unref (v2);
/* test comparison (2) */
v1 = nm_bridge_vlan_from_str ("10", &error);
nmtst_assert_success (v1, error);
v2 = nm_bridge_vlan_from_str ("20", &error);
nmtst_assert_success (v2, error);
g_assert_cmpint (nm_bridge_vlan_cmp (v1, v2), <, 0);
nm_bridge_vlan_unref (v1);
nm_bridge_vlan_unref (v2);
}
/*****************************************************************************/
static void
test_tc_config_qdisc (void)
{
NMTCQdisc *qdisc1, *qdisc2;
char *str;
GError *error = NULL;
qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error);
nmtst_assert_success (qdisc1, error);
qdisc2 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error);
nmtst_assert_success (qdisc2, error);
g_assert (nm_tc_qdisc_equal (qdisc1, qdisc2));
nm_tc_qdisc_unref (qdisc2);
qdisc2 = nm_tc_qdisc_dup (qdisc1);
g_assert (nm_tc_qdisc_equal (qdisc1, qdisc2));
g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc1), ==, "fq_codel");
g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_UNSPEC);
g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_ROOT);
str = nm_utils_tc_qdisc_to_str (qdisc1, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "root fq_codel");
g_free (str);
nm_tc_qdisc_unref (qdisc1);
qdisc1 = nm_tc_qdisc_new ("ingress", TC_H_INGRESS, &error);
nmtst_assert_success (qdisc1, error);
g_assert (!nm_tc_qdisc_equal (qdisc1, qdisc2));
str = nm_utils_tc_qdisc_to_str (qdisc1, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "ingress");
g_free (str);
nm_tc_qdisc_unref (qdisc1);
qdisc1 = nm_utils_tc_qdisc_from_str ("narodil sa kristus pan", &error);
nmtst_assert_no_success (qdisc1, error);
g_clear_error (&error);
qdisc1 = nm_utils_tc_qdisc_from_str ("handle 1234 parent fff1:1 pfifo_fast", &error);
nmtst_assert_success (qdisc1, error);
g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc1), ==, "pfifo_fast");
g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_MAKE (0x1234 << 16, 0x0000));
g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_MAKE (0xfff1 << 16, 0x0001));
str = nm_utils_tc_qdisc_to_str (qdisc1, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "parent fff1:1 handle 1234: pfifo_fast");
g_free (str);
nm_tc_qdisc_unref (qdisc2);
str = nm_utils_tc_qdisc_to_str (qdisc1, &error);
nmtst_assert_success (str, error);
qdisc2 = nm_utils_tc_qdisc_from_str (str, &error);
nmtst_assert_success (qdisc2, error);
g_free (str);
g_assert (nm_tc_qdisc_equal (qdisc1, qdisc2));
nm_tc_qdisc_unref (qdisc1);
nm_tc_qdisc_unref (qdisc2);
}
static void
test_tc_config_action (void)
{
NMTCAction *action1, *action2;
char *str;
GError *error = NULL;
action1 = nm_tc_action_new ("drop", &error);
nmtst_assert_success (action1, error);
action2 = nm_tc_action_new ("drop", &error);
nmtst_assert_success (action2, error);
g_assert (nm_tc_action_equal (action1, action2));
g_assert_cmpstr (nm_tc_action_get_kind (action1), ==, "drop");
nm_tc_action_unref (action1);
action1 = nm_tc_action_new ("simple", &error);
nmtst_assert_success (action1, error);
nm_tc_action_set_attribute (action1, "sdata", g_variant_new_bytestring ("Hello"));
g_assert (!nm_tc_action_equal (action1, action2));
str = nm_utils_tc_action_to_str (action1, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "simple sdata Hello");
g_free (str);
str = nm_utils_tc_action_to_str (action2, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "drop");
g_free (str);
nm_tc_action_unref (action2);
action2 = nm_tc_action_dup (action1);
g_assert (nm_tc_action_equal (action1, action2));
nm_tc_action_unref (action1);
action1 = nm_utils_tc_action_from_str ("narodil sa kristus pan", &error);
nmtst_assert_no_success (action1, error);
g_clear_error (&error);
action1 = nm_utils_tc_action_from_str ("simple sdata Hello", &error);
nmtst_assert_success (action1, error);
g_assert_cmpstr (nm_tc_action_get_kind (action1), ==, "simple");
g_assert_cmpstr (g_variant_get_bytestring (nm_tc_action_get_attribute (action1, "sdata")), ==, "Hello");
nm_tc_action_unref (action1);
nm_tc_action_unref (action2);
}
static void
test_tc_config_tfilter (void)
{
NMTCAction *action1;
NMTCTfilter *tfilter1, *tfilter2;
char *str;
GError *error = NULL;
tfilter1 = nm_tc_tfilter_new ("matchall",
TC_H_MAKE (0x1234 << 16, 0x0000),
&error);
nmtst_assert_success (tfilter1, error);
tfilter2 = nm_tc_tfilter_new ("matchall",
TC_H_MAKE (0x1234 << 16, 0x0000),
&error);
nmtst_assert_success (tfilter2, error);
g_assert (nm_tc_tfilter_equal (tfilter1, tfilter2));
action1 = nm_tc_action_new ("simple", &error);
nmtst_assert_success (action1, error);
nm_tc_action_set_attribute (action1, "sdata", g_variant_new_bytestring ("Hello"));
nm_tc_tfilter_set_action (tfilter1, action1);
nm_tc_action_unref (action1);
g_assert (!nm_tc_tfilter_equal (tfilter1, tfilter2));
str = nm_utils_tc_tfilter_to_str (tfilter1, &error);
nmtst_assert_success (str, error);
g_assert_cmpstr (str, ==, "parent 1234: matchall action simple sdata Hello");
g_free (str);
nm_tc_tfilter_unref (tfilter2);
tfilter2 = nm_tc_tfilter_dup (tfilter1);
g_assert (nm_tc_tfilter_equal (tfilter1, tfilter2));
nm_tc_tfilter_unref (tfilter1);
tfilter1 = nm_utils_tc_tfilter_from_str ("narodil sa kristus pan", &error);
nmtst_assert_no_success (tfilter1, error);
g_clear_error (&error);
str = nm_utils_tc_tfilter_to_str (tfilter2, &error);
nmtst_assert_success (str, error);
tfilter1 = nm_utils_tc_tfilter_from_str (str, &error);
nmtst_assert_success (tfilter1, error);
g_free (str);
g_assert (nm_tc_tfilter_equal (tfilter1, tfilter2));
nm_tc_tfilter_unref (tfilter1);
nm_tc_tfilter_unref (tfilter2);
}
static void
test_tc_config_setting_valid (void)
{
gs_unref_object NMSettingTCConfig *s_tc = NULL;
NMTCQdisc *qdisc1, *qdisc2;
GError *error = NULL;
s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new ();
qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error);
nmtst_assert_success (qdisc1, error);
qdisc2 = nm_tc_qdisc_new ("pfifo_fast",
TC_H_MAKE (0xfff1 << 16, 0x0001),
&error);
nmtst_assert_success (qdisc2, error);
nm_tc_qdisc_set_handle (qdisc2, TC_H_MAKE (0x1234 << 16, 0x0000));
g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 0);
g_assert (nm_setting_tc_config_add_qdisc (s_tc, qdisc1) == TRUE);
g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 1);
g_assert (nm_setting_tc_config_get_qdisc (s_tc, 0) != NULL);
g_assert (nm_setting_tc_config_remove_qdisc_by_value (s_tc, qdisc2) == FALSE);
g_assert (nm_setting_tc_config_add_qdisc (s_tc, qdisc2) == TRUE);
g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 2);
g_assert (nm_setting_tc_config_remove_qdisc_by_value (s_tc, qdisc1) == TRUE);
g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 1);
nm_setting_tc_config_clear_qdiscs (s_tc);
g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 0);
nm_tc_qdisc_unref (qdisc1);
nm_tc_qdisc_unref (qdisc2);
}
static void
test_tc_config_setting_duplicates (void)
{
gs_unref_ptrarray GPtrArray *qdiscs = NULL;
gs_unref_ptrarray GPtrArray *tfilters = NULL;
NMSettingConnection *s_con;
NMConnection *con;
NMSetting *s_tc;
NMTCQdisc *qdisc;
NMTCTfilter *tfilter;
GError *error = NULL;
con = nmtst_create_minimal_connection ("dummy",
NULL,
NM_SETTING_DUMMY_SETTING_NAME,
&s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME, "dummy1",
NULL);
s_tc = nm_setting_tc_config_new ();
nm_connection_add_setting (con, s_tc);
qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref);
tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref);
/* 1. add duplicate qdiscs */
qdisc = nm_utils_tc_qdisc_from_str ("handle 1234 parent fff1:1 pfifo_fast", &error);
nmtst_assert_success (qdisc, error);
g_ptr_array_add (qdiscs, qdisc);
qdisc = nm_utils_tc_qdisc_from_str ("handle 1234 parent fff1:1 pfifo_fast", &error);
nmtst_assert_success (qdisc, error);
g_ptr_array_add (qdiscs, qdisc);
g_object_set (s_tc, NM_SETTING_TC_CONFIG_QDISCS, qdiscs, NULL);
nmtst_assert_connection_unnormalizable (con,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY);
/* 2. make qdiscs unique */
g_ptr_array_remove_index (qdiscs, 0);
g_object_set (s_tc, NM_SETTING_TC_CONFIG_QDISCS, qdiscs, NULL);
nmtst_assert_connection_verifies_and_normalizable (con);
/* 3. add duplicate filters */
tfilter = nm_utils_tc_tfilter_from_str ("parent 1234: matchall action simple sdata Hello", &error);
nmtst_assert_success (tfilter, error);
g_ptr_array_add (tfilters, tfilter);
tfilter = nm_utils_tc_tfilter_from_str ("parent 1234: matchall action simple sdata Hello", &error);
nmtst_assert_success (tfilter, error);
g_ptr_array_add (tfilters, tfilter);
g_object_set (s_tc, NM_SETTING_TC_CONFIG_TFILTERS, tfilters, NULL);
nmtst_assert_connection_unnormalizable (con,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY);
/* 4. make filters unique */
g_ptr_array_remove_index (tfilters, 0);
g_object_set (s_tc, NM_SETTING_TC_CONFIG_TFILTERS, tfilters, NULL);
nmtst_assert_connection_verifies_and_normalizable (con);
}
static void
test_tc_config_dbus (void)
{
NMConnection *connection1, *connection2;
NMSetting *s_tc;
NMTCQdisc *qdisc1, *qdisc2;
NMTCTfilter *tfilter1, *tfilter2;
NMTCAction *action;
GVariant *dbus, *tc_dbus, *var1, *var2;
GError *error = NULL;
gboolean success;
connection1 = nmtst_create_minimal_connection ("dummy",
NULL,
NM_SETTING_DUMMY_SETTING_NAME,
NULL);
s_tc = nm_setting_tc_config_new ();
qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error);
nmtst_assert_success (qdisc1, error);
nm_tc_qdisc_set_handle (qdisc1, TC_H_MAKE (0x1234 << 16, 0x0000));
nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc1);
qdisc2 = nm_tc_qdisc_new ("ingress", TC_H_INGRESS, &error);
nmtst_assert_success (qdisc2, error);
nm_tc_qdisc_set_handle (qdisc2, TC_H_MAKE (TC_H_INGRESS, 0));
nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc2);
tfilter1 = nm_tc_tfilter_new ("matchall",
TC_H_MAKE (0x1234 << 16, 0x0000),
&error);
nmtst_assert_success (tfilter1, error);
action = nm_tc_action_new ("drop", &error);
nmtst_assert_success (action, error);
nm_tc_tfilter_set_action (tfilter1, action);
nm_tc_action_unref (action);
nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter1);
nm_tc_tfilter_unref (tfilter1);
tfilter2 = nm_tc_tfilter_new ("matchall",
TC_H_MAKE (TC_H_INGRESS, 0),
&error);
nmtst_assert_success (tfilter2, error);
action = nm_tc_action_new ("simple", &error);
nmtst_assert_success (action, error);
nm_tc_action_set_attribute (action, "sdata", g_variant_new_bytestring ("Hello"));
nm_tc_tfilter_set_action (tfilter2, action);
nm_tc_action_unref (action);
nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter2);
nm_tc_tfilter_unref (tfilter2);
nm_connection_add_setting (connection1, s_tc);
dbus = nm_connection_to_dbus (connection1, NM_CONNECTION_SERIALIZE_ALL);
tc_dbus = g_variant_lookup_value (dbus, "tc", G_VARIANT_TYPE_VARDICT);
g_assert (tc_dbus);
var1 = g_variant_lookup_value (tc_dbus, "qdiscs", G_VARIANT_TYPE ("aa{sv}"));
var2 = g_variant_new_parsed ("[{'kind': <'fq_codel'>,"
" 'handle': <uint32 0x12340000>,"
" 'parent': <uint32 0xffffffff>},"
" {'kind': <'ingress'>,"
" 'handle': <uint32 0xffff0000>,"
" 'parent': <uint32 0xfffffff1>}]");
g_assert (g_variant_equal (var1, var2));
g_variant_unref (var1);
g_variant_unref (var2);
var1 = g_variant_lookup_value (tc_dbus, "tfilters", G_VARIANT_TYPE ("aa{sv}"));
var2 = g_variant_new_parsed ("[{'kind': <'matchall'>,"
" 'handle': <uint32 0>,"
" 'parent': <uint32 0x12340000>,"
" 'action': <{'kind': <'drop'>}>},"
" {'kind': <'matchall'>,"
" 'handle': <uint32 0>,"
" 'parent': <uint32 0xffff0000>,"
" 'action': <{'kind': <'simple'>,"
" 'sdata': <b'Hello'>}>}]");
g_variant_unref (var1);
g_variant_unref (var2);
g_variant_unref (tc_dbus);
connection2 = nm_simple_connection_new ();
success = nm_connection_replace_settings (connection2, dbus, &error);
nmtst_assert_success (success, error);
g_assert (nm_connection_diff (connection1, connection2, NM_SETTING_COMPARE_FLAG_EXACT, NULL));
g_variant_unref (dbus);
nm_tc_qdisc_unref (qdisc1);
nm_tc_qdisc_unref (qdisc2);
g_object_unref (connection1);
g_object_unref (connection2);
}
/*****************************************************************************/
static void
_rndt_wired_add_s390_options (NMSettingWired *s_wired,
char **out_keyfile_entries)
{
gsize n_opts;
gsize i, j;
const char *const*option_names;
gs_free const char **opt_keys = NULL;
gs_strfreev char **opt_vals = NULL;
gs_free bool *opt_found = NULL;
GString *keyfile_entries;
nm_auto_free_gstring GString *str_tmp = NULL;
option_names = nm_setting_wired_get_valid_s390_options (nmtst_get_rand_bool () ? NULL : s_wired);
n_opts = NM_PTRARRAY_LEN (option_names);
opt_keys = g_new (const char *, (n_opts + 1));
nmtst_rand_perm (NULL, opt_keys, option_names, sizeof (const char *), n_opts);
n_opts = nmtst_get_rand_int () % (n_opts + 1);
opt_keys[n_opts] = NULL;
opt_vals = g_new0 (char *, n_opts + 1);
opt_found = g_new0 (bool, n_opts + 1);
for (i = 0; i < n_opts; i++) {
guint p = nmtst_get_rand_int () % 1000;
if (p < 200)
opt_vals[i] = nm_strdup_int (i);
else {
opt_vals[i] = g_strdup_printf ("%s%s%s%s-%zu",
((p % 5) % 2) ? "\n" : "",
((p % 7) % 2) ? "\t" : "",
((p % 11) % 2) ? "x" : "",
((p % 13) % 2) ? "=" : "",
i);
}
}
if (nmtst_get_rand_bool ()) {
gs_unref_hashtable GHashTable *hash = NULL;
hash = g_hash_table_new (nm_str_hash, g_str_equal);
for (i = 0; i < n_opts; i++)
g_hash_table_insert (hash, (char *) opt_keys[i], opt_vals[i]);
g_object_set (s_wired,
NM_SETTING_WIRED_S390_OPTIONS,
hash,
NULL);
} else {
_nm_setting_wired_clear_s390_options (s_wired);
for (i = 0; i < n_opts; i++) {
if (!nm_setting_wired_add_s390_option (s_wired, opt_keys[i], opt_vals[i]))
g_assert_not_reached ();
}
}
g_assert_cmpint (nm_setting_wired_get_num_s390_options (s_wired), ==, n_opts);
keyfile_entries = g_string_new (NULL);
str_tmp = g_string_new (NULL);
if (n_opts > 0)
g_string_append_printf (keyfile_entries, "[ethernet-s390-options]\n");
for (i = 0; i < n_opts; i++) {
gssize idx;
const char *k, *v;
nm_setting_wired_get_s390_option (s_wired, i, &k, &v);
g_assert (k);
g_assert (v);
idx = nm_utils_strv_find_first ((char **) opt_keys, n_opts, k);
g_assert (idx >= 0);
g_assert (!opt_found[idx]);
opt_found[idx] = TRUE;
g_assert_cmpstr (opt_keys[idx], ==, k);
g_assert_cmpstr (opt_vals[idx], ==, v);
g_string_truncate (str_tmp, 0);
for (j = 0; v[j] != '\0'; j++) {
if (v[j] == '\n')
g_string_append (str_tmp, "\\n");
else if (v[j] == '\t')
g_string_append (str_tmp, "\\t");
else
g_string_append_c (str_tmp, v[j]);
}
g_string_append_printf (keyfile_entries,
"%s=%s\n",
k,
str_tmp->str);
}
for (i = 0; i < n_opts; i++)
g_assert (opt_found[i]);
if (n_opts > 0)
g_string_append_printf (keyfile_entries, "\n");
*out_keyfile_entries = g_string_free (keyfile_entries, FALSE);
}
static GPtrArray *
_rndt_wg_peers_create (void)
{
GPtrArray *wg_peers;
guint i, n;
wg_peers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_wireguard_peer_unref);
n = nmtst_get_rand_int () % 10;
for (i = 0; i < n; i++) {
NMWireGuardPeer *peer;
guint8 public_key_buf[NM_WIREGUARD_PUBLIC_KEY_LEN];
guint8 preshared_key_buf[NM_WIREGUARD_SYMMETRIC_KEY_LEN];
gs_free char *public_key = NULL;
gs_free char *preshared_key = NULL;
gs_free char *s_endpoint = NULL;
guint i_aip, n_aip;
/* we don't bother to create a valid curve25519 public key. Of course, libnm cannot
* check whether the public key is bogus or not. Hence, for our purpose a random
* bogus key is good enough. */
public_key = g_base64_encode (nmtst_rand_buf (NULL, public_key_buf, sizeof (public_key_buf)), sizeof (public_key_buf));
preshared_key = g_base64_encode (nmtst_rand_buf (NULL, preshared_key_buf, sizeof (preshared_key_buf)), sizeof (preshared_key_buf));
s_endpoint = _create_random_ipaddr (AF_UNSPEC, TRUE);
peer = nm_wireguard_peer_new ();
if (!nm_wireguard_peer_set_public_key (peer, public_key, TRUE))
g_assert_not_reached ();
if (!nm_wireguard_peer_set_preshared_key (peer, nmtst_rand_select (NULL, preshared_key), TRUE))
g_assert_not_reached ();
nm_wireguard_peer_set_preshared_key_flags (peer, nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_SECRET_FLAG_AGENT_OWNED));
nm_wireguard_peer_set_persistent_keepalive (peer,
nmtst_rand_select ((guint32) 0, nmtst_get_rand_int ()));
if (!nm_wireguard_peer_set_endpoint (peer, nmtst_rand_select (s_endpoint, NULL), TRUE))
g_assert_not_reached ();
n_aip = nmtst_rand_select (0, nmtst_get_rand_int () % 10);
for (i_aip = 0; i_aip < n_aip; i_aip++) {
gs_free char *aip = NULL;
aip = _create_random_ipaddr (AF_UNSPEC, FALSE);
if (!nm_wireguard_peer_append_allowed_ip (peer, aip, FALSE))
g_assert_not_reached ();
}
g_assert (nm_wireguard_peer_is_valid (peer, TRUE, TRUE, NULL));
nm_wireguard_peer_seal (peer);
g_ptr_array_add (wg_peers, peer);
}
return wg_peers;
}
static const char *
_rndt_wg_peers_to_keyfile (GPtrArray *wg_peers,
gboolean strict,
char **out_str)
{
nm_auto_free_gstring GString *gstr = NULL;
nm_auto_free_gstring GString *gstr_aip = NULL;
guint i, j;
g_assert (wg_peers);
g_assert (out_str && !*out_str);
nm_gstring_prepare (&gstr);
for (i = 0; i < wg_peers->len; i++) {
const NMWireGuardPeer *peer = wg_peers->pdata[i];
gs_free char *s_endpoint = NULL;
gs_free char *s_preshared_key = NULL;
gs_free char *s_preshared_key_flags = NULL;
gs_free char *s_persistent_keepalive = NULL;
gs_free char *s_allowed_ips = NULL;
if (nm_wireguard_peer_get_endpoint (peer))
s_endpoint = g_strdup_printf ("endpoint=%s\n", nm_wireguard_peer_get_endpoint (peer));
else if (!strict)
s_endpoint = g_strdup_printf ("endpoint=\n");
if ( nm_wireguard_peer_get_preshared_key (peer)
|| !strict) {
if (nm_wireguard_peer_get_preshared_key_flags (peer) == NM_SETTING_SECRET_FLAG_NONE)
s_preshared_key = g_strdup_printf ("preshared-key=%s\n", nm_wireguard_peer_get_preshared_key (peer) ?: "");
}
if ( nm_wireguard_peer_get_preshared_key_flags (peer) != NM_SETTING_SECRET_FLAG_NOT_REQUIRED
|| !strict)
s_preshared_key_flags = g_strdup_printf ("preshared-key-flags=%d\n", (int) nm_wireguard_peer_get_preshared_key_flags (peer));
if ( nm_wireguard_peer_get_persistent_keepalive (peer) != 0
|| !strict)
s_persistent_keepalive = g_strdup_printf ("persistent-keepalive=%u\n", nm_wireguard_peer_get_persistent_keepalive (peer));
if ( nm_wireguard_peer_get_allowed_ips_len (peer) > 0
|| !strict) {
nm_gstring_prepare (&gstr_aip);
for (j = 0; j < nm_wireguard_peer_get_allowed_ips_len (peer); j++)
g_string_append_printf (gstr_aip, "%s;", nm_wireguard_peer_get_allowed_ip (peer, j, NULL));
s_allowed_ips = g_strdup_printf ("allowed-ips=%s\n", gstr_aip->str);
}
if ( !s_endpoint
&& !s_preshared_key
&& !s_preshared_key_flags
&& !s_persistent_keepalive
&& !s_allowed_ips)
s_endpoint = g_strdup_printf ("endpoint=\n");
g_string_append_printf (gstr,
"\n"
"[wireguard-peer.%s]\n"
"%s" /* endpoint */
"%s" /* preshared-key */
"%s" /* preshared-key-flags */
"%s" /* persistent-keepalive */
"%s" /* allowed-ips */
"",
nm_wireguard_peer_get_public_key (peer),
s_endpoint ?: "",
s_preshared_key ?: "",
s_preshared_key_flags ?: "",
s_persistent_keepalive ?: "",
s_allowed_ips ?: "");
}
return (*out_str = g_string_free (g_steal_pointer (&gstr), FALSE));
}
static void
_rndt_wg_peers_assert_equal (NMSettingWireGuard *s_wg,
GPtrArray *peers,
gboolean consider_persistent_secrets,
gboolean consider_all_secrets,
gboolean expect_no_secrets)
{
guint i;
g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
g_assert (peers);
g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
for (i = 0; i < peers->len; i++) {
const NMWireGuardPeer *a = peers->pdata[i];
const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
gboolean consider_secrets;
g_assert (a);
g_assert (b);
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
if ( consider_all_secrets
|| !nm_wireguard_peer_get_preshared_key (a))
consider_secrets = TRUE;
else if (nm_wireguard_peer_get_preshared_key (b))
consider_secrets = TRUE;
else if ( consider_persistent_secrets
&& nm_wireguard_peer_get_preshared_key_flags (b) == NM_SETTING_SECRET_FLAG_NONE)
consider_secrets = TRUE;
else
consider_secrets = FALSE;
if (consider_secrets) {
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
}
if (expect_no_secrets)
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
}
}
static void
_rndt_wg_peers_fix_secrets (NMSettingWireGuard *s_wg,
GPtrArray *peers)
{
guint i;
g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
g_assert (peers);
g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
for (i = 0; i < peers->len; i++) {
const NMWireGuardPeer *a = peers->pdata[i];
const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
nm_auto_unref_wgpeer NMWireGuardPeer *b_clone = NULL;
g_assert (a);
g_assert (b);
g_assert_cmpint (nm_wireguard_peer_get_preshared_key_flags (a), ==, nm_wireguard_peer_get_preshared_key_flags (b));
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
if (!nm_streq0 (nm_wireguard_peer_get_preshared_key (a),
nm_wireguard_peer_get_preshared_key (b))) {
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), !=, NULL);
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
NM_SETTING_SECRET_FLAG_NOT_SAVED));
b_clone = nm_wireguard_peer_new_clone (b, TRUE);
if (!nm_wireguard_peer_set_preshared_key (b_clone, nm_wireguard_peer_get_preshared_key (a), TRUE))
g_assert_not_reached ();
nm_setting_wireguard_set_peer (s_wg, b_clone, i);
b = nm_setting_wireguard_get_peer (s_wg, i);
g_assert (b == b_clone);
} else {
if (nm_wireguard_peer_get_preshared_key (a)) {
g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
} else {
g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
}
}
g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
}
}
static void
test_roundtrip_conversion (gconstpointer test_data)
{
const int MODE = GPOINTER_TO_INT (test_data);
const char *ID= nm_sprintf_bufa (100, "roundtip-conversion-%d", MODE);
const char *UUID= "63376701-b61e-4318-bf7e-664a1c1eeaab";
const char *INTERFACE_NAME = nm_sprintf_bufa (100, "ifname%d", MODE);
guint32 ETH_MTU = nmtst_rand_select ((guint32) 0u,
nmtst_get_rand_int ());
const char *WG_PRIVATE_KEY = nmtst_get_rand_bool ()
? "yGXGK+5bVnxSJUejH4vbpXbq+ZtaG4NB8IHRK/aVtE0="
: NULL;
const NMSettingSecretFlags WG_PRIVATE_KEY_FLAGS = nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
NM_SETTING_SECRET_FLAG_NOT_SAVED,
NM_SETTING_SECRET_FLAG_AGENT_OWNED);
const guint WG_LISTEN_PORT = nmtst_rand_select (0u,
nmtst_get_rand_int () % 0x10000);
const guint WG_FWMARK = nmtst_rand_select (0u,
nmtst_get_rand_int ());
gs_unref_ptrarray GPtrArray *kf_data_arr = g_ptr_array_new_with_free_func (g_free);
gs_unref_ptrarray GPtrArray *wg_peers = NULL;
const NMConnectionSerializationFlags dbus_serialization_flags[] = {
NM_CONNECTION_SERIALIZE_ALL,
NM_CONNECTION_SERIALIZE_NO_SECRETS,
NM_CONNECTION_SERIALIZE_ONLY_SECRETS,
};
guint dbus_serialization_flags_idx;
gs_unref_object NMConnection *con = NULL;
gs_free_error GError *error = NULL;
gs_free char *tmp_str = NULL;
guint kf_data_idx;
NMSettingConnection *s_con = NULL;
NMSettingWired *s_eth = NULL;
NMSettingWireGuard *s_wg = NULL;
union {
struct {
NMSettingIPConfig *s_6;
NMSettingIPConfig *s_4;
};
NMSettingIPConfig *s_x[2];
} s_ip;
int is_ipv4;
guint i;
gboolean success;
gs_free char *s390_keyfile_entries = NULL;
switch (MODE) {
case 0:
con = nmtst_create_minimal_connection (ID, UUID, NM_SETTING_WIRED_SETTING_NAME, &s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME,
INTERFACE_NAME,
NULL);
nmtst_connection_normalize (con);
s_eth = NM_SETTING_WIRED (nm_connection_get_setting (con, NM_TYPE_SETTING_WIRED));
g_assert (NM_IS_SETTING_WIRED (s_eth));
g_object_set (s_eth,
NM_SETTING_WIRED_MTU,
ETH_MTU,
NULL);
_rndt_wired_add_s390_options (s_eth, &s390_keyfile_entries);
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=ethernet\n"
"interface-name=%s\n"
"permissions=\n"
"\n"
"[ethernet]\n"
"mac-address-blacklist=\n"
"%s" /* mtu */
"\n"
"%s" /* [ethernet-s390-options] */
"[ipv4]\n"
"dns-search=\n"
"method=auto\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=auto\n"
"",
ID,
UUID,
INTERFACE_NAME,
(ETH_MTU != 0)
keyfile: rework handling of GObject properties from keyfile - previously, writer would use nm_keyfile_plugin_kf_set_integer() for G_TYPE_UINT types. That means, values larger than G_MAXINT would be stored as negative values. On the other hand, the reader would always reject negative values. Fix that, by parsing the integer ourself. Note that we still reject the old (negative) values and there is no compatibility for accepting such values. They were not accepted by reader in the past and so they are still rejected. This affects for example ethernet.mtu setting (arguably, the MTU is usually set to small values where the issue was not apparent). This is also covered by a test. - no longer use nm_keyfile_plugin_kf_set_integer(). nm_keyfile_plugin_kf_set_integer() calls g_key_file_get_integer(), which uses g_key_file_parse_integer_as_value(). That one has the odd behavior of accepting "<number><whitespace><bogus>" as valid. Note how that differs from g_key_file_parse_value_as_double() which rejects trailing data. Implement the parsing ourself. There are some changes here: - g_key_file_parse_value_as_integer() uses strtol() with base 10. We no longer require a certain the base, so '0x' hex values are allowed now as well. - bogus suffixes are now rejected but were accepted by g_key_file_parse_value_as_integer(). We however still accept leading and trailing whitespace, as before. - use nm_g_object_set_property*(). g_object_set() asserts that the value is in range. We cannot pass invalid values without checking that they are valid. - emit warnings when values cannot be parsed. Previously they would have been silently ignored or fail an assertion during g_object_set(). - don't use "helpers" like nm_keyfile_plugin_kf_set_uint64(). These merely call GKeyFile's setters (taking care of aliases). The setters of GKeyFile don't do anything miraculously, they merely call g_key_file_set_value() with the string that one would expect. Convert the numbers/boolean ourselfs. For one, we don't require a heap allocation to convert a number to string. Also, there is no point in leaving this GKeyFile API, because even if GKeyFile day would change, we still must continue to support the present format, as that is what users have on disk. So, even if a new way would be implemented by GKeyFile, the current way must forever be accepted too. Hence, we don't need this abstraction.
2019-01-03 09:26:54 +01:00
? nm_sprintf_bufa (100, "mtu=%u\n", ETH_MTU)
: "",
s390_keyfile_entries));
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=ethernet\n"
"interface-name=%s\n"
"permissions=\n"
"\n"
"[ethernet]\n"
"mac-address-blacklist=\n"
"%s" /* mtu */
"\n"
"%s" /* [ethernet-s390-options] */
"[ipv4]\n"
"dns-search=\n"
"method=auto\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=auto\n"
"",
ID,
UUID,
INTERFACE_NAME,
(ETH_MTU != 0)
keyfile: rework handling of GObject properties from keyfile - previously, writer would use nm_keyfile_plugin_kf_set_integer() for G_TYPE_UINT types. That means, values larger than G_MAXINT would be stored as negative values. On the other hand, the reader would always reject negative values. Fix that, by parsing the integer ourself. Note that we still reject the old (negative) values and there is no compatibility for accepting such values. They were not accepted by reader in the past and so they are still rejected. This affects for example ethernet.mtu setting (arguably, the MTU is usually set to small values where the issue was not apparent). This is also covered by a test. - no longer use nm_keyfile_plugin_kf_set_integer(). nm_keyfile_plugin_kf_set_integer() calls g_key_file_get_integer(), which uses g_key_file_parse_integer_as_value(). That one has the odd behavior of accepting "<number><whitespace><bogus>" as valid. Note how that differs from g_key_file_parse_value_as_double() which rejects trailing data. Implement the parsing ourself. There are some changes here: - g_key_file_parse_value_as_integer() uses strtol() with base 10. We no longer require a certain the base, so '0x' hex values are allowed now as well. - bogus suffixes are now rejected but were accepted by g_key_file_parse_value_as_integer(). We however still accept leading and trailing whitespace, as before. - use nm_g_object_set_property*(). g_object_set() asserts that the value is in range. We cannot pass invalid values without checking that they are valid. - emit warnings when values cannot be parsed. Previously they would have been silently ignored or fail an assertion during g_object_set(). - don't use "helpers" like nm_keyfile_plugin_kf_set_uint64(). These merely call GKeyFile's setters (taking care of aliases). The setters of GKeyFile don't do anything miraculously, they merely call g_key_file_set_value() with the string that one would expect. Convert the numbers/boolean ourselfs. For one, we don't require a heap allocation to convert a number to string. Also, there is no point in leaving this GKeyFile API, because even if GKeyFile day would change, we still must continue to support the present format, as that is what users have on disk. So, even if a new way would be implemented by GKeyFile, the current way must forever be accepted too. Hence, we don't need this abstraction.
2019-01-03 09:26:54 +01:00
? nm_sprintf_bufa (100, "mtu=%d\n", (int) ETH_MTU)
: "",
s390_keyfile_entries));
break;
case 1:
con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME,
INTERFACE_NAME,
NULL);
nmtst_connection_normalize (con);
s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=wireguard\n"
"interface-name=%s\n"
"permissions=\n"
"\n"
"[ipv4]\n"
"dns-search=\n"
"method=disabled\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=ignore\n"
"",
ID,
UUID,
INTERFACE_NAME));
break;
case 2:
con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME,
INTERFACE_NAME,
NULL);
nmtst_connection_normalize (con);
s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
g_object_set (s_wg,
NM_SETTING_WIREGUARD_PRIVATE_KEY,
WG_PRIVATE_KEY,
NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
WG_PRIVATE_KEY_FLAGS,
NM_SETTING_WIREGUARD_LISTEN_PORT,
WG_LISTEN_PORT,
NM_SETTING_WIREGUARD_FWMARK,
WG_FWMARK,
NULL);
wg_peers = _rndt_wg_peers_create ();
for (i = 0; i < wg_peers->len; i++)
nm_setting_wireguard_append_peer (s_wg, wg_peers->pdata[i]);
nm_clear_g_free (&tmp_str);
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=wireguard\n"
"interface-name=%s\n"
"permissions=\n"
"%s" /* [wireguard] */
"%s" /* fwmark */
"%s" /* listen-port */
"%s" /* private-key-flags */
"%s" /* private-key */
"%s" /* [wireguard-peers*] */
"\n"
"[ipv4]\n"
"dns-search=\n"
"method=disabled\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=ignore\n"
"",
ID,
UUID,
INTERFACE_NAME,
( ( (WG_FWMARK != 0)
|| (WG_LISTEN_PORT != 0)
|| (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
|| ( WG_PRIVATE_KEY
&& WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE))
? "\n[wireguard]\n"
: ""),
( (WG_FWMARK != 0)
? nm_sprintf_bufa (100, "fwmark=%u\n", WG_FWMARK)
: ""),
( (WG_LISTEN_PORT != 0)
? nm_sprintf_bufa (100, "listen-port=%u\n", WG_LISTEN_PORT)
: ""),
( (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
? nm_sprintf_bufa (100, "private-key-flags=%u\n", (guint) WG_PRIVATE_KEY_FLAGS)
: ""),
( ( WG_PRIVATE_KEY
&& WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE)
? nm_sprintf_bufa (100, "private-key=%s\n", WG_PRIVATE_KEY)
: ""),
_rndt_wg_peers_to_keyfile (wg_peers, TRUE, &tmp_str)));
_rndt_wg_peers_assert_equal (s_wg, wg_peers, TRUE, TRUE, FALSE);
break;
case 3:
con = nmtst_create_minimal_connection (ID, UUID, NM_SETTING_WIRED_SETTING_NAME, &s_con);
g_object_set (s_con,
NM_SETTING_CONNECTION_INTERFACE_NAME,
INTERFACE_NAME,
NULL);
nmtst_connection_normalize (con);
s_eth = NM_SETTING_WIRED (nm_connection_get_setting (con, NM_TYPE_SETTING_WIRED));
g_assert (NM_IS_SETTING_WIRED (s_eth));
g_object_set (s_eth,
NM_SETTING_WIRED_MTU,
ETH_MTU,
NULL);
s_ip.s_4 = NM_SETTING_IP_CONFIG (nm_connection_get_setting (con, NM_TYPE_SETTING_IP4_CONFIG));
g_assert (NM_IS_SETTING_IP4_CONFIG (s_ip.s_4));
s_ip.s_6 = NM_SETTING_IP_CONFIG (nm_connection_get_setting (con, NM_TYPE_SETTING_IP6_CONFIG));
g_assert (NM_IS_SETTING_IP6_CONFIG (s_ip.s_6));
for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) {
g_assert (NM_IS_SETTING_IP_CONFIG (s_ip.s_x[is_ipv4]));
for (i = 0; i < 3; i++) {
char addrstr[NM_UTILS_INET_ADDRSTRLEN];
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr = NULL;
rr = nm_ip_routing_rule_new (is_ipv4 ? AF_INET : AF_INET6);
nm_ip_routing_rule_set_priority (rr, i + 1);
if (i > 0) {
if (is_ipv4)
nm_sprintf_buf (addrstr, "192.168.%u.0", i);
else
nm_sprintf_buf (addrstr, "1:2:3:%x::", 10 + i);
nm_ip_routing_rule_set_from (rr, addrstr, is_ipv4 ? 24 + i : 64 + i);
}
nm_ip_routing_rule_set_table (rr, 1000 + i);
success = nm_ip_routing_rule_validate (rr, &error);
nmtst_assert_success (success, error);
nm_setting_ip_config_add_routing_rule (s_ip.s_x[is_ipv4], rr);
}
}
g_ptr_array_add (kf_data_arr,
g_strdup_printf ("[connection]\n"
"id=%s\n"
"uuid=%s\n"
"type=ethernet\n"
"interface-name=%s\n"
"permissions=\n"
"\n"
"[ethernet]\n"
"mac-address-blacklist=\n"
"%s" /* mtu */
"\n"
"[ipv4]\n"
"dns-search=\n"
"method=auto\n"
"routing-rule1=priority 1 from 0.0.0.0/0 table 1000\n"
"routing-rule2=priority 2 from 192.168.1.0/25 table 1001\n"
"routing-rule3=priority 3 from 192.168.2.0/26 table 1002\n"
"\n"
"[ipv6]\n"
"addr-gen-mode=stable-privacy\n"
"dns-search=\n"
"method=auto\n"
"routing-rule1=priority 1 from ::/0 table 1000\n"
"routing-rule2=priority 2 from 1:2:3:b::/65 table 1001\n"
"routing-rule3=priority 3 from 1:2:3:c::/66 table 1002\n"
"",
ID,
UUID,
INTERFACE_NAME,
(ETH_MTU != 0)
? nm_sprintf_bufa (100, "mtu=%u\n", ETH_MTU)
: ""));
break;
default:
g_assert_not_reached ();
}
/* the first kf_data_arr entry is special: it is the exact result of what we expect
* when converting @con to keyfile. Write @con to keyfile and compare the expected result
* literally. */
{
gs_unref_keyfile GKeyFile *kf = NULL;
kf = nm_keyfile_write (con, NULL, NULL, &error);
nmtst_assert_success (kf, error);
/* the first kf_data_arr entry is special: it must be what the writer would
* produce again. */
nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1);
}
/* check that reading any of kf_data_arr yields the same result that we expect. */
for (kf_data_idx = 0; kf_data_idx < kf_data_arr->len; kf_data_idx++) {
gs_unref_object NMConnection *con2 = NULL;
NMSettingWireGuard *s_wg2 = NULL;
NMSettingWired *s_eth2 = NULL;
con2 = nmtst_create_connection_from_keyfile (kf_data_arr->pdata[kf_data_idx], "/no/where/file.nmconnection");
switch (MODE) {
case 0:
s_eth2 = NM_SETTING_WIRED (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIRED));
g_assert (NM_IS_SETTING_WIRED (s_eth2));
if ( ETH_MTU > (guint32) G_MAXINT
&& kf_data_idx == 1) {
keyfile: rework handling of GObject properties from keyfile - previously, writer would use nm_keyfile_plugin_kf_set_integer() for G_TYPE_UINT types. That means, values larger than G_MAXINT would be stored as negative values. On the other hand, the reader would always reject negative values. Fix that, by parsing the integer ourself. Note that we still reject the old (negative) values and there is no compatibility for accepting such values. They were not accepted by reader in the past and so they are still rejected. This affects for example ethernet.mtu setting (arguably, the MTU is usually set to small values where the issue was not apparent). This is also covered by a test. - no longer use nm_keyfile_plugin_kf_set_integer(). nm_keyfile_plugin_kf_set_integer() calls g_key_file_get_integer(), which uses g_key_file_parse_integer_as_value(). That one has the odd behavior of accepting "<number><whitespace><bogus>" as valid. Note how that differs from g_key_file_parse_value_as_double() which rejects trailing data. Implement the parsing ourself. There are some changes here: - g_key_file_parse_value_as_integer() uses strtol() with base 10. We no longer require a certain the base, so '0x' hex values are allowed now as well. - bogus suffixes are now rejected but were accepted by g_key_file_parse_value_as_integer(). We however still accept leading and trailing whitespace, as before. - use nm_g_object_set_property*(). g_object_set() asserts that the value is in range. We cannot pass invalid values without checking that they are valid. - emit warnings when values cannot be parsed. Previously they would have been silently ignored or fail an assertion during g_object_set(). - don't use "helpers" like nm_keyfile_plugin_kf_set_uint64(). These merely call GKeyFile's setters (taking care of aliases). The setters of GKeyFile don't do anything miraculously, they merely call g_key_file_set_value() with the string that one would expect. Convert the numbers/boolean ourselfs. For one, we don't require a heap allocation to convert a number to string. Also, there is no point in leaving this GKeyFile API, because even if GKeyFile day would change, we still must continue to support the present format, as that is what users have on disk. So, even if a new way would be implemented by GKeyFile, the current way must forever be accepted too. Hence, we don't need this abstraction.
2019-01-03 09:26:54 +01:00
/* older versions wrote values > 2^21 as signed integers, but the reader would
* always reject such negative values for G_TYPE_UINT.
*
* The test case kf_data_idx #1 still writes the values in the old style.
* The behavior was fixed, but such values are still rejected as invalid.
*
* Patch the setting so that the comparison below succeeds are usual. */
g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, 0);
g_object_set (s_eth2,
NM_SETTING_WIRED_MTU,
ETH_MTU,
NULL);
}
g_assert_cmpint (nm_setting_wired_get_mtu (s_eth), ==, ETH_MTU);
g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, ETH_MTU);
g_assert_cmpint (nm_setting_wired_get_num_s390_options (s_eth2), ==, nm_setting_wired_get_num_s390_options (s_eth));
break;
case 1:
s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, NULL);
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
break;
case 2:
s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
/* the private key was lost due to the secret-flags. Patch it. */
if (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE) {
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
g_object_set (s_wg2,
NM_SETTING_WIREGUARD_PRIVATE_KEY,
WG_PRIVATE_KEY,
NULL);
}
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, WG_PRIVATE_KEY);
g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, WG_PRIVATE_KEY);
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, FALSE, FALSE);
_rndt_wg_peers_fix_secrets (s_wg2, wg_peers);
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
break;
}
nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ());
}
for (dbus_serialization_flags_idx = 0; dbus_serialization_flags_idx < G_N_ELEMENTS (dbus_serialization_flags); dbus_serialization_flags_idx++) {
NMConnectionSerializationFlags flag = dbus_serialization_flags[dbus_serialization_flags_idx];
gs_unref_variant GVariant *con_var = NULL;
gs_unref_object NMConnection *con2 = NULL;
NMSettingWireGuard *s_wg2 = NULL;
con_var = nm_connection_to_dbus (con, flag);
g_assert (g_variant_is_of_type (con_var, NM_VARIANT_TYPE_CONNECTION));
g_assert (g_variant_is_floating (con_var));
g_variant_ref_sink (con_var);
if (flag == NM_CONNECTION_SERIALIZE_ALL) {
con2 = _connection_new_from_dbus_strict (con_var, TRUE);
nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ());
{
gs_unref_keyfile GKeyFile *kf = NULL;
kf = nm_keyfile_write (con2, NULL, NULL, &error);
nmtst_assert_success (kf, error);
nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1);
}
}
switch (MODE) {
case 2:
if (flag == NM_CONNECTION_SERIALIZE_ALL) {
s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
if (flag == NM_CONNECTION_SERIALIZE_ALL)
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
else if (flag == NM_CONNECTION_SERIALIZE_NO_SECRETS)
_rndt_wg_peers_assert_equal (s_wg2, wg_peers, FALSE, FALSE, TRUE);
else
g_assert_not_reached ();
}
break;
}
}
}
/*****************************************************************************/
static NMIPRoutingRule *
_rr_from_str_get_impl (const char *str, const char *const*aliases)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr = NULL;
gs_free_error GError *error = NULL;
gboolean vbool;
int addr_family;
int i;
NMIPRoutingRuleAsStringFlags to_string_flags;
rr = nm_ip_routing_rule_from_string (str,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE,
NULL,
&error);
nmtst_assert_success (rr, error);
addr_family = nm_ip_routing_rule_get_addr_family (rr);
g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
if (addr_family == AF_INET)
to_string_flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET;
else
to_string_flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6;
for (i = 0; TRUE; i++) {
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr2 = NULL;
gs_free char *str1 = NULL;
gs_unref_variant GVariant *variant1 = NULL;
const char *cstr1;
switch (i) {
case 0:
rr2 = nm_ip_routing_rule_ref (rr);
break;
case 1:
rr2 = nm_ip_routing_rule_from_string (str,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (rr, error);
break;
case 2:
str1 = nm_ip_routing_rule_to_string (rr,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (str1 && str1[0], error);
g_assert_cmpstr (str, ==, str1);
rr2 = nm_ip_routing_rule_from_string (str1,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (rr, error);
break;
case 3:
variant1 = nm_ip_routing_rule_to_dbus (rr);
g_assert (variant1);
g_assert (g_variant_is_floating (variant1));
g_assert (g_variant_is_of_type (variant1, G_VARIANT_TYPE_VARDICT));
rr2 = nm_ip_routing_rule_from_dbus (variant1,
TRUE,
&error);
nmtst_assert_success (rr, error);
break;
default:
if (!aliases || !aliases[0])
goto done;
cstr1 = (aliases++)[0];
rr2 = nm_ip_routing_rule_from_string (cstr1,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (nmtst_get_rand_bool () ? to_string_flags : NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE),
NULL,
&error);
nmtst_assert_success (rr, error);
break;
}
g_assert (rr2);
vbool = nm_ip_routing_rule_validate (rr, &error);
nmtst_assert_success (vbool, error);
vbool = nm_ip_routing_rule_validate (rr2, &error);
nmtst_assert_success (vbool, error);
g_assert_cmpint (nm_ip_routing_rule_cmp (rr, rr2), ==, 0);
g_assert_cmpint (nm_ip_routing_rule_cmp (rr2, rr), ==, 0);
}
done:
return g_steal_pointer (&rr);
}
#define _rr_from_str_get(a, ...) _rr_from_str_get_impl (a, &(NM_MAKE_STRV (NULL, ##__VA_ARGS__))[1])
#define _rr_from_str(...) \
G_STMT_START { \
nm_auto_unref_ip_routing_rule NMIPRoutingRule *_rr = NULL; \
\
_rr = _rr_from_str_get (__VA_ARGS__); \
g_assert (_rr); \
} G_STMT_END
static void
test_routing_rule (gconstpointer test_data)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rr1 = NULL;
gboolean success;
char ifname_buf[16];
_rr_from_str ("priority 5 from 0.0.0.0 table 1",
libnm: use nm_utils_escaped_tokens_*() for parsing NMIPRoutingRule Replace nm_utils_str_simpletokens_extract_next() by nm_utils_escaped_tokens_split(). nm_utils_escaped_tokens_split() should become our first choice for parsing and tokenizing. Note that both nm_utils_str_simpletokens_extract_next() and nm_utils_escaped_tokens_split() need to strdup the string once, and tokenizing takes O(n). So, they are roughtly the same performance wise. The only difference is, that as we iterate through the tokens, we might abort early on error with nm_utils_str_simpletokens_extract_next() and not parse the entire string. But that is a small benefit, since we anyway always strdup() the string (being O(n) already). Note that to-string will no longer escape ',' and ';'. This is a change in behavior, of unreleased API. Also note, that escaping these is no longer necessary, because nmcli soon will also use nm_utils_escaped_tokens_*(). Another change in behavior is that nm_utils_str_simpletokens_extract_next() treated invalid escape sequences (backslashes followed by an arbitrary character), buy stripping the backslash. nm_utils_escaped_tokens_*() leaves such backslashes as is, and only honors them if they are followed by a whitespace (the delimiter) or another backslash. The disadvantage of the new approach is that backslashes are treated differently depending on the following character. The benefit is, that most backslashes can now be written verbatim, not requiring them to escape them with a double-backslash. Yes, there is a problem with these nested escape schemes: - the caller may already need to escape backslash in shell. - then nmcli will use backslash escaping to split the rules at ','. - then nm_ip_routing_rule_from_string() will honor backslash escaping for spaces. - then iifname and oifname use backslash escaping for nm_utils_buf_utf8safe_escape() to express non-UTF-8 characters (because interface names are not necessarily UTF-8). This is only redeamed because escaping is really only necessary for very unusual cases, if you want to embed a backslash, a space, a comma, or a non-UTF-8 character. But if you have to, now you will be able to express that. The other upside of these layers of escaping is that they become all indendent from each other: - shell can accept quoted/escaped arguments and will unescape them. - nmcli can do the tokenizing for ',' (and escape the content unconditionally when converting to string). - nm_ip_routing_rule_from_string() can do its tokenizing without special consideration of utf8safe escaping. - NMIPRoutingRule takes iifname/oifname as-is and is not concerned about nm_utils_buf_utf8safe_escape(). However, before configuring the rule in kernel, this utf8safe escape will be unescaped to get the interface name (which is non-UTF8 binary). (cherry picked from commit b6d0be2d3b4caa30685abf46ae6a355cada8bf4e)
2019-04-12 12:38:11 +02:00
" from 0.0.0.0 priority 5 lookup 1 ");
_rr_from_str ("priority 5 from 0.0.0.0/0 table 4");
_rr_from_str ("priority 5 to 0.0.0.0 table 6");
_rr_from_str ("priority 5 to 0.0.0.0 table 254",
"priority 5 to 0.0.0.0/32");
_rr_from_str ("priority 5 from 1.2.3.4 table 15",
"priority 5 from 1.2.3.4/32 table 0xF ",
"priority 5 from 1.2.3.4/32 to 0.0.0.0/0 lookup 15 ");
_rr_from_str ("priority 5 from 1.2.3.4 to 0.0.0.0 table 8");
_rr_from_str ("priority 5 to a:b:c:: tos 0x16 table 25",
"priority 5 to a:b:c::/128 table 0x19 tos 16",
"priority 5 to a:b:c::/128 lookup 0x19 dsfield 16",
"priority 5 to a:b:c::/128 lookup 0x19 dsfield 16 fwmark 0/0x00",
"priority 5 to a:b:c:: from all lookup 0x19 dsfield 16 fwmark 0x0/0");
_rr_from_str ("priority 5 from :: fwmark 0 table 25",
"priority 5 from ::/128 to all table 0x19 fwmark 0/0xFFFFFFFF",
"priority 5 from :: to ::/0 table 0x19 fwmark 0x00/4294967295");
_rr_from_str ("priority 5 from :: iif aab table 25");
_rr_from_str ("priority 5 from :: iif aab oif er table 25",
libnm: use nm_utils_escaped_tokens_*() for parsing NMIPRoutingRule Replace nm_utils_str_simpletokens_extract_next() by nm_utils_escaped_tokens_split(). nm_utils_escaped_tokens_split() should become our first choice for parsing and tokenizing. Note that both nm_utils_str_simpletokens_extract_next() and nm_utils_escaped_tokens_split() need to strdup the string once, and tokenizing takes O(n). So, they are roughtly the same performance wise. The only difference is, that as we iterate through the tokens, we might abort early on error with nm_utils_str_simpletokens_extract_next() and not parse the entire string. But that is a small benefit, since we anyway always strdup() the string (being O(n) already). Note that to-string will no longer escape ',' and ';'. This is a change in behavior, of unreleased API. Also note, that escaping these is no longer necessary, because nmcli soon will also use nm_utils_escaped_tokens_*(). Another change in behavior is that nm_utils_str_simpletokens_extract_next() treated invalid escape sequences (backslashes followed by an arbitrary character), buy stripping the backslash. nm_utils_escaped_tokens_*() leaves such backslashes as is, and only honors them if they are followed by a whitespace (the delimiter) or another backslash. The disadvantage of the new approach is that backslashes are treated differently depending on the following character. The benefit is, that most backslashes can now be written verbatim, not requiring them to escape them with a double-backslash. Yes, there is a problem with these nested escape schemes: - the caller may already need to escape backslash in shell. - then nmcli will use backslash escaping to split the rules at ','. - then nm_ip_routing_rule_from_string() will honor backslash escaping for spaces. - then iifname and oifname use backslash escaping for nm_utils_buf_utf8safe_escape() to express non-UTF-8 characters (because interface names are not necessarily UTF-8). This is only redeamed because escaping is really only necessary for very unusual cases, if you want to embed a backslash, a space, a comma, or a non-UTF-8 character. But if you have to, now you will be able to express that. The other upside of these layers of escaping is that they become all indendent from each other: - shell can accept quoted/escaped arguments and will unescape them. - nmcli can do the tokenizing for ',' (and escape the content unconditionally when converting to string). - nm_ip_routing_rule_from_string() can do its tokenizing without special consideration of utf8safe escaping. - NMIPRoutingRule takes iifname/oifname as-is and is not concerned about nm_utils_buf_utf8safe_escape(). However, before configuring the rule in kernel, this utf8safe escape will be unescaped to get the interface name (which is non-UTF8 binary). (cherry picked from commit b6d0be2d3b4caa30685abf46ae6a355cada8bf4e)
2019-04-12 12:38:11 +02:00
"priority 5 from :: table 0x19 dev aab oif er");
_rr_from_str ("priority 5 from :: iif a\\\\303b table 25");
_rr_from_str ("priority 5 to 0.0.0.0 sport 10 table 6",
"priority 5 to 0.0.0.0 sport 10-10 table 6");
_rr_from_str ("not priority 5 to 0.0.0.0 dport 10-133 table 6",
"priority 5 to 0.0.0.0 not dport 10-133 not table 6",
"priority 5 to 0.0.0.0 not dport 10-\\ 133 not table 6");
_rr_from_str ("priority 5 to 0.0.0.0 ipproto 10 sport 10 table 6");
rr1 = _rr_from_str_get ("priority 5 from :: iif aab table 25");
g_assert_cmpstr (nm_ip_routing_rule_get_iifname (rr1), ==, "aab");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert (!success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "aab");
g_assert (success);
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
rr1 = _rr_from_str_get ("priority 5 from :: iif a\\\\303\\\\261xb table 254");
g_assert_cmpstr (nm_ip_routing_rule_get_iifname (rr1), ==, "a\\303\\261xb");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert (!success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "a\303\261xb");
g_assert (success);
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
rr1 = _rr_from_str_get ("priority 5 from :: oif \\\\101=\\\\303\\\\261xb table 7");
g_assert_cmpstr (nm_ip_routing_rule_get_oifname (rr1), ==, "\\101=\\303\\261xb");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "A=\303\261xb");
g_assert (success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert (!success);
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
rr1 = _rr_from_str_get ("priority 5 to 0.0.0.0 tos 0x10 table 7");
g_assert_cmpstr (NULL, ==, nm_ip_routing_rule_get_from (rr1));
g_assert (!nm_ip_routing_rule_get_from_bin (rr1));
g_assert_cmpint (0, ==, nm_ip_routing_rule_get_from_len (rr1));
g_assert_cmpstr ("0.0.0.0", ==, nm_ip_routing_rule_get_to (rr1));
g_assert (nm_ip_addr_is_null (AF_INET, nm_ip_routing_rule_get_to_bin (rr1)));
g_assert_cmpint (32, ==, nm_ip_routing_rule_get_to_len (rr1));
g_assert_cmpint (7, ==, nm_ip_routing_rule_get_table (rr1));
g_assert_cmpint (0x10, ==, nm_ip_routing_rule_get_tos (rr1));
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
libnm: use nm_utils_escaped_tokens_*() for parsing NMIPRoutingRule Replace nm_utils_str_simpletokens_extract_next() by nm_utils_escaped_tokens_split(). nm_utils_escaped_tokens_split() should become our first choice for parsing and tokenizing. Note that both nm_utils_str_simpletokens_extract_next() and nm_utils_escaped_tokens_split() need to strdup the string once, and tokenizing takes O(n). So, they are roughtly the same performance wise. The only difference is, that as we iterate through the tokens, we might abort early on error with nm_utils_str_simpletokens_extract_next() and not parse the entire string. But that is a small benefit, since we anyway always strdup() the string (being O(n) already). Note that to-string will no longer escape ',' and ';'. This is a change in behavior, of unreleased API. Also note, that escaping these is no longer necessary, because nmcli soon will also use nm_utils_escaped_tokens_*(). Another change in behavior is that nm_utils_str_simpletokens_extract_next() treated invalid escape sequences (backslashes followed by an arbitrary character), buy stripping the backslash. nm_utils_escaped_tokens_*() leaves such backslashes as is, and only honors them if they are followed by a whitespace (the delimiter) or another backslash. The disadvantage of the new approach is that backslashes are treated differently depending on the following character. The benefit is, that most backslashes can now be written verbatim, not requiring them to escape them with a double-backslash. Yes, there is a problem with these nested escape schemes: - the caller may already need to escape backslash in shell. - then nmcli will use backslash escaping to split the rules at ','. - then nm_ip_routing_rule_from_string() will honor backslash escaping for spaces. - then iifname and oifname use backslash escaping for nm_utils_buf_utf8safe_escape() to express non-UTF-8 characters (because interface names are not necessarily UTF-8). This is only redeamed because escaping is really only necessary for very unusual cases, if you want to embed a backslash, a space, a comma, or a non-UTF-8 character. But if you have to, now you will be able to express that. The other upside of these layers of escaping is that they become all indendent from each other: - shell can accept quoted/escaped arguments and will unescape them. - nmcli can do the tokenizing for ',' (and escape the content unconditionally when converting to string). - nm_ip_routing_rule_from_string() can do its tokenizing without special consideration of utf8safe escaping. - NMIPRoutingRule takes iifname/oifname as-is and is not concerned about nm_utils_buf_utf8safe_escape(). However, before configuring the rule in kernel, this utf8safe escape will be unescaped to get the interface name (which is non-UTF8 binary). (cherry picked from commit b6d0be2d3b4caa30685abf46ae6a355cada8bf4e)
2019-04-12 12:38:11 +02:00
rr1 = _rr_from_str_get ("priority 5 from :: iif a\\\\303\\\\261,x;b table 254",
"priority 5 from :: iif a\\\\303\\\\261,x;b table 254");
g_assert_cmpstr (nm_ip_routing_rule_get_iifname (rr1), ==, "a\\303\\261,x;b");
success = nm_ip_routing_rule_get_xifname_bin (rr1, FALSE, ifname_buf);
g_assert (!success);
success = nm_ip_routing_rule_get_xifname_bin (rr1, TRUE, ifname_buf);
g_assert_cmpstr (ifname_buf, ==, "a\303\261,x;b");
g_assert (success);
nm_clear_pointer (&rr1, nm_ip_routing_rule_unref);
}
/*****************************************************************************/
static void
test_parse_tc_handle (void)
{
#define _parse_tc_handle(str, exp) \
G_STMT_START { \
gs_free_error GError *_error = NULL; \
GError **_perror = nmtst_get_rand_bool () ? &_error : NULL; \
guint32 _v; \
const guint32 _v_exp = (exp); \
\
_v = _nm_utils_parse_tc_handle (""str"", _perror); \
\
if (_v != _v_exp) \
g_error ("%s:%d: \"%s\" gave %08x but %08x expected.", __FILE__, __LINE__, ""str"", _v, _v_exp); \
\
if (_v == TC_H_UNSPEC) \
g_assert (!_perror || *_perror); \
else \
g_assert (!_perror || !*_perror); \
\
} G_STMT_END
#define _parse_tc_handle_inval(str) _parse_tc_handle (str, TC_H_UNSPEC)
#define _parse_tc_handle_valid(str, maj, min) _parse_tc_handle (str, TC_H_MAKE (((guint32) (maj)) << 16, ((guint16) (min))))
_parse_tc_handle_inval ("");
_parse_tc_handle_inval (" ");
_parse_tc_handle_inval (" \n");
_parse_tc_handle_valid ("1", 1, 0);
libnm: cleanup _nm_utils_parse_tc_handle() - g_ascii_strtoll() accepts leading spaces, but it leaves the end pointer at the first space after the digit. That means, we accepted "1: 0" but not "1 :0". We should either consistently accept spaces around the digits/colon or reject it. - g_ascii_strtoll() accepts "\v" as a space (just like `man 3 isspace` comments that "\v" is a space in C and POSIX locale. For some reasons (unknown to me) g_ascii_isspace() does not treat "\v" as space. And neither does NM_ASCII_SPACES and nm_str_skip_leading_spaces(). We should be consistent about what we consider spaces and what not. It's already odd to accept '\n' as spaces here, but well, lets do it for the sake of consistency (so that it matches with our understanding of ASCII spaces, albeit not POSIX's). - don't use bogus error domains in "g_set_error (error, 1, 0, ..." That is a bug and we have NM_UTILS_ERROR exactly for error instances with unspecified domain and code. - as before, accept a trailing ":" with omitted minor number. - reject all unexpected characters. strtoll() accepts '+' / '-' and a "0x" prefix of the numbers (and leading POSIX spaces). Be strict here and only accepts NM_ASCII_SPACES, ':', and hexdigits. In particular, don't accept the "0x" prefix. This parsing would be significantly simpler to implement, if we could just strdup() the string, split the string at the colon delimiter and use _nm_utils_ascii_str_to_int64() which gets leading/trailing spaces right. But let's save the "overhead" of an additional alloc.
2019-05-01 10:27:32 +02:00
_parse_tc_handle_valid(" 1 ", 1, 0);
_parse_tc_handle_valid ("1:", 1, 0);
libnm: cleanup _nm_utils_parse_tc_handle() - g_ascii_strtoll() accepts leading spaces, but it leaves the end pointer at the first space after the digit. That means, we accepted "1: 0" but not "1 :0". We should either consistently accept spaces around the digits/colon or reject it. - g_ascii_strtoll() accepts "\v" as a space (just like `man 3 isspace` comments that "\v" is a space in C and POSIX locale. For some reasons (unknown to me) g_ascii_isspace() does not treat "\v" as space. And neither does NM_ASCII_SPACES and nm_str_skip_leading_spaces(). We should be consistent about what we consider spaces and what not. It's already odd to accept '\n' as spaces here, but well, lets do it for the sake of consistency (so that it matches with our understanding of ASCII spaces, albeit not POSIX's). - don't use bogus error domains in "g_set_error (error, 1, 0, ..." That is a bug and we have NM_UTILS_ERROR exactly for error instances with unspecified domain and code. - as before, accept a trailing ":" with omitted minor number. - reject all unexpected characters. strtoll() accepts '+' / '-' and a "0x" prefix of the numbers (and leading POSIX spaces). Be strict here and only accepts NM_ASCII_SPACES, ':', and hexdigits. In particular, don't accept the "0x" prefix. This parsing would be significantly simpler to implement, if we could just strdup() the string, split the string at the colon delimiter and use _nm_utils_ascii_str_to_int64() which gets leading/trailing spaces right. But let's save the "overhead" of an additional alloc.
2019-05-01 10:27:32 +02:00
_parse_tc_handle_valid ("1: ", 1, 0);
_parse_tc_handle_valid ("1:0", 1, 0);
libnm: cleanup _nm_utils_parse_tc_handle() - g_ascii_strtoll() accepts leading spaces, but it leaves the end pointer at the first space after the digit. That means, we accepted "1: 0" but not "1 :0". We should either consistently accept spaces around the digits/colon or reject it. - g_ascii_strtoll() accepts "\v" as a space (just like `man 3 isspace` comments that "\v" is a space in C and POSIX locale. For some reasons (unknown to me) g_ascii_isspace() does not treat "\v" as space. And neither does NM_ASCII_SPACES and nm_str_skip_leading_spaces(). We should be consistent about what we consider spaces and what not. It's already odd to accept '\n' as spaces here, but well, lets do it for the sake of consistency (so that it matches with our understanding of ASCII spaces, albeit not POSIX's). - don't use bogus error domains in "g_set_error (error, 1, 0, ..." That is a bug and we have NM_UTILS_ERROR exactly for error instances with unspecified domain and code. - as before, accept a trailing ":" with omitted minor number. - reject all unexpected characters. strtoll() accepts '+' / '-' and a "0x" prefix of the numbers (and leading POSIX spaces). Be strict here and only accepts NM_ASCII_SPACES, ':', and hexdigits. In particular, don't accept the "0x" prefix. This parsing would be significantly simpler to implement, if we could just strdup() the string, split the string at the colon delimiter and use _nm_utils_ascii_str_to_int64() which gets leading/trailing spaces right. But let's save the "overhead" of an additional alloc.
2019-05-01 10:27:32 +02:00
_parse_tc_handle_valid ("1 :0", 1, 0);
_parse_tc_handle_valid ("1 \t\n\f\r:0", 1, 0);
_parse_tc_handle_inval ("1 \t\n\f\r\v:0");
libnm: cleanup _nm_utils_parse_tc_handle() - g_ascii_strtoll() accepts leading spaces, but it leaves the end pointer at the first space after the digit. That means, we accepted "1: 0" but not "1 :0". We should either consistently accept spaces around the digits/colon or reject it. - g_ascii_strtoll() accepts "\v" as a space (just like `man 3 isspace` comments that "\v" is a space in C and POSIX locale. For some reasons (unknown to me) g_ascii_isspace() does not treat "\v" as space. And neither does NM_ASCII_SPACES and nm_str_skip_leading_spaces(). We should be consistent about what we consider spaces and what not. It's already odd to accept '\n' as spaces here, but well, lets do it for the sake of consistency (so that it matches with our understanding of ASCII spaces, albeit not POSIX's). - don't use bogus error domains in "g_set_error (error, 1, 0, ..." That is a bug and we have NM_UTILS_ERROR exactly for error instances with unspecified domain and code. - as before, accept a trailing ":" with omitted minor number. - reject all unexpected characters. strtoll() accepts '+' / '-' and a "0x" prefix of the numbers (and leading POSIX spaces). Be strict here and only accepts NM_ASCII_SPACES, ':', and hexdigits. In particular, don't accept the "0x" prefix. This parsing would be significantly simpler to implement, if we could just strdup() the string, split the string at the colon delimiter and use _nm_utils_ascii_str_to_int64() which gets leading/trailing spaces right. But let's save the "overhead" of an additional alloc.
2019-05-01 10:27:32 +02:00
_parse_tc_handle_valid (" 1 : 0 ", 1, 0);
_parse_tc_handle_inval (" \t\v\n1: 0");
_parse_tc_handle_valid ("1:2", 1, 2);
_parse_tc_handle_valid ("01:02", 1, 2);
libnm: cleanup _nm_utils_parse_tc_handle() - g_ascii_strtoll() accepts leading spaces, but it leaves the end pointer at the first space after the digit. That means, we accepted "1: 0" but not "1 :0". We should either consistently accept spaces around the digits/colon or reject it. - g_ascii_strtoll() accepts "\v" as a space (just like `man 3 isspace` comments that "\v" is a space in C and POSIX locale. For some reasons (unknown to me) g_ascii_isspace() does not treat "\v" as space. And neither does NM_ASCII_SPACES and nm_str_skip_leading_spaces(). We should be consistent about what we consider spaces and what not. It's already odd to accept '\n' as spaces here, but well, lets do it for the sake of consistency (so that it matches with our understanding of ASCII spaces, albeit not POSIX's). - don't use bogus error domains in "g_set_error (error, 1, 0, ..." That is a bug and we have NM_UTILS_ERROR exactly for error instances with unspecified domain and code. - as before, accept a trailing ":" with omitted minor number. - reject all unexpected characters. strtoll() accepts '+' / '-' and a "0x" prefix of the numbers (and leading POSIX spaces). Be strict here and only accepts NM_ASCII_SPACES, ':', and hexdigits. In particular, don't accept the "0x" prefix. This parsing would be significantly simpler to implement, if we could just strdup() the string, split the string at the colon delimiter and use _nm_utils_ascii_str_to_int64() which gets leading/trailing spaces right. But let's save the "overhead" of an additional alloc.
2019-05-01 10:27:32 +02:00
_parse_tc_handle_inval ("0x01:0x02");
_parse_tc_handle_valid (" 01: 02", 1, 2);
_parse_tc_handle_valid ("019: 020", 0x19, 0x20);
_parse_tc_handle_valid ("FFFF: 020", 0xFFFF, 0x20);
_parse_tc_handle_valid ("FfFF: ffff", 0xFFFF, 0xFFFF);
_parse_tc_handle_valid ("FFFF", 0xFFFF, 0);
libnm: cleanup _nm_utils_parse_tc_handle() - g_ascii_strtoll() accepts leading spaces, but it leaves the end pointer at the first space after the digit. That means, we accepted "1: 0" but not "1 :0". We should either consistently accept spaces around the digits/colon or reject it. - g_ascii_strtoll() accepts "\v" as a space (just like `man 3 isspace` comments that "\v" is a space in C and POSIX locale. For some reasons (unknown to me) g_ascii_isspace() does not treat "\v" as space. And neither does NM_ASCII_SPACES and nm_str_skip_leading_spaces(). We should be consistent about what we consider spaces and what not. It's already odd to accept '\n' as spaces here, but well, lets do it for the sake of consistency (so that it matches with our understanding of ASCII spaces, albeit not POSIX's). - don't use bogus error domains in "g_set_error (error, 1, 0, ..." That is a bug and we have NM_UTILS_ERROR exactly for error instances with unspecified domain and code. - as before, accept a trailing ":" with omitted minor number. - reject all unexpected characters. strtoll() accepts '+' / '-' and a "0x" prefix of the numbers (and leading POSIX spaces). Be strict here and only accepts NM_ASCII_SPACES, ':', and hexdigits. In particular, don't accept the "0x" prefix. This parsing would be significantly simpler to implement, if we could just strdup() the string, split the string at the colon delimiter and use _nm_utils_ascii_str_to_int64() which gets leading/trailing spaces right. But let's save the "overhead" of an additional alloc.
2019-05-01 10:27:32 +02:00
_parse_tc_handle_inval ("0xFFFF");
_parse_tc_handle_inval ("10000");
_parse_tc_handle_valid ("\t\n\f\r FFFF", 0xFFFF, 0);
libnm: cleanup _nm_utils_parse_tc_handle() - g_ascii_strtoll() accepts leading spaces, but it leaves the end pointer at the first space after the digit. That means, we accepted "1: 0" but not "1 :0". We should either consistently accept spaces around the digits/colon or reject it. - g_ascii_strtoll() accepts "\v" as a space (just like `man 3 isspace` comments that "\v" is a space in C and POSIX locale. For some reasons (unknown to me) g_ascii_isspace() does not treat "\v" as space. And neither does NM_ASCII_SPACES and nm_str_skip_leading_spaces(). We should be consistent about what we consider spaces and what not. It's already odd to accept '\n' as spaces here, but well, lets do it for the sake of consistency (so that it matches with our understanding of ASCII spaces, albeit not POSIX's). - don't use bogus error domains in "g_set_error (error, 1, 0, ..." That is a bug and we have NM_UTILS_ERROR exactly for error instances with unspecified domain and code. - as before, accept a trailing ":" with omitted minor number. - reject all unexpected characters. strtoll() accepts '+' / '-' and a "0x" prefix of the numbers (and leading POSIX spaces). Be strict here and only accepts NM_ASCII_SPACES, ':', and hexdigits. In particular, don't accept the "0x" prefix. This parsing would be significantly simpler to implement, if we could just strdup() the string, split the string at the colon delimiter and use _nm_utils_ascii_str_to_int64() which gets leading/trailing spaces right. But let's save the "overhead" of an additional alloc.
2019-05-01 10:27:32 +02:00
_parse_tc_handle_inval ("\t\n\f\r \vFFFF");
}
/*****************************************************************************/
NMTST_DEFINE ();
int
main (int argc, char **argv)
{
nmtst_init (&argc, &argv, TRUE);
g_test_add_data_func ("/libnm/setting-8021x/key-and-cert",
"test_key_and_cert.pem, test",
test_8021x);
g_test_add_data_func ("/libnm/setting-8021x/key-only",
"test-key-only.pem, test",
test_8021x);
g_test_add_data_func ("/libnm/setting-8021x/pkcs8-enc-key",
"pkcs8-enc-key.pem, 1234567890",
test_8021x);
g_test_add_data_func ("/libnm/setting-8021x/pkcs12",
"test-cert.p12, test",
test_8021x);
g_test_add_func ("/libnm/settings/bond/verify", test_bond_verify);
g_test_add_func ("/libnm/settings/bond/compare", test_bond_compare);
g_test_add_func ("/libnm/settings/bond/normalize", test_bond_normalize);
g_test_add_func ("/libnm/settings/dcb/flags-valid", test_dcb_flags_valid);
g_test_add_func ("/libnm/settings/dcb/flags-invalid", test_dcb_flags_invalid);
g_test_add_func ("/libnm/settings/dcb/app-priorities", test_dcb_app_priorities);
g_test_add_func ("/libnm/settings/dcb/priorities", test_dcb_priorities_valid);
g_test_add_func ("/libnm/settings/dcb/bandwidth-sums", test_dcb_bandwidth_sums);
g_test_add_func ("/libnm/settings/ethtool/1", test_ethtool_1);
g_test_add_func ("/libnm/settings/sriov/vf", test_sriov_vf);
g_test_add_func ("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup);
g_test_add_func ("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan);
g_test_add_func ("/libnm/settings/sriov/setting", test_sriov_setting);
g_test_add_func ("/libnm/settings/sriov/vlans", test_sriov_parse_vlans);
g_test_add_func ("/libnm/settings/tc_config/qdisc", test_tc_config_qdisc);
g_test_add_func ("/libnm/settings/tc_config/action", test_tc_config_action);
g_test_add_func ("/libnm/settings/tc_config/tfilter", test_tc_config_tfilter);
g_test_add_func ("/libnm/settings/tc_config/setting/valid", test_tc_config_setting_valid);
g_test_add_func ("/libnm/settings/tc_config/setting/duplicates", test_tc_config_setting_duplicates);
g_test_add_func ("/libnm/settings/tc_config/dbus", test_tc_config_dbus);
g_test_add_func ("/libnm/settings/bridge/vlans", test_bridge_vlans);
#if WITH_JSON_VALIDATION
g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin",
test_runner_roundrobin_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_runner_from_config_broadcast",
test_runner_broadcast_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_runner_from_config_random",
test_runner_random_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_runner_from_config_activebackup",
test_runner_activebackup_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_runner_from_config_loadbalance",
test_runner_loadbalance_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_runner_from_config_lacp",
test_runner_lacp_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_ethtool",
test_watcher_ethtool_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_nsna_ping",
test_watcher_nsna_ping_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_arp_ping",
test_watcher_arp_ping_sync_from_config);
g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_all",
test_multiple_watchers_sync_from_config);
g_test_add_func ("/libnm/settings/team-port/sync_from_config_defaults", test_team_port_default);
g_test_add_func ("/libnm/settings/team-port/sync_from_config_queue_id", test_team_port_queue_id);
g_test_add_func ("/libnm/settings/team-port/sync_from_config_prio", test_team_port_prio);
g_test_add_func ("/libnm/settings/team-port/sync_from_config_sticky", test_team_port_sticky);
g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_prio", test_team_port_lacp_prio);
g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_key", test_team_port_lacp_key);
g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config);
#endif
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/0", GINT_TO_POINTER (0), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/1", GINT_TO_POINTER (1), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/2", GINT_TO_POINTER (2), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/3", GINT_TO_POINTER (3), test_roundtrip_conversion);
g_test_add_data_func ("/libnm/settings/routing-rule/1", GINT_TO_POINTER (0), test_routing_rule);
g_test_add_func ("/libnm/parse-tc-handle", test_parse_tc_handle);
return g_test_run ();
}