libnm-core: reject tc configurations with duplicate elements

A configuration with duplicate tc qdiscs and tfilters is not valid;
reject it in verify(). Note that nm_setting_tc_config_add_qdisc() and
nm_setting_tc_config_add_tfilter() can't add duplicate entries and so
the only way to achieve an invalid configuration is setting the
properties directly.

https://github.com/NetworkManager/NetworkManager/pull/95
This commit is contained in:
Beniamino Galvani 2018-06-21 15:56:29 +02:00
parent 5e8773ee63
commit 2576e3a8e8
2 changed files with 162 additions and 2 deletions

View file

@ -154,6 +154,19 @@ nm_tc_qdisc_equal (NMTCQdisc *qdisc, NMTCQdisc *other)
return TRUE;
}
static guint
_nm_tc_qdisc_hash (NMTCQdisc *qdisc)
{
NMHashState h;
nm_hash_init (&h, 43869703);
nm_hash_update_vals (&h,
qdisc->handle,
qdisc->parent);
nm_hash_update_str0 (&h, qdisc->kind);
return nm_hash_complete (&h);
}
/**
* nm_tc_qdisc_dup:
* @qdisc: the #NMTCQdisc
@ -626,6 +639,38 @@ nm_tc_tfilter_equal (NMTCTfilter *tfilter, NMTCTfilter *other)
return TRUE;
}
static guint
_nm_tc_tfilter_hash (NMTCTfilter *tfilter)
{
gs_free const char **names = NULL;
guint i, attr_hash;
GVariant *variant;
NMHashState h;
guint length;
nm_hash_init (&h, 63624437);
nm_hash_update_vals (&h,
tfilter->handle,
tfilter->parent);
nm_hash_update_str0 (&h, tfilter->kind);
if (tfilter->action) {
nm_hash_update_str0 (&h, tfilter->action->kind);
names = nm_utils_strdict_get_keys (tfilter->action->attributes, TRUE, &length);
for (i = 0; i < length; i++) {
nm_hash_update_str (&h, names[i]);
variant = g_hash_table_lookup (tfilter->action->attributes, names[i]);
if (g_variant_type_is_basic (g_variant_get_type (variant))) {
/* g_variant_hash() works only for basic types, thus
* we ignore any non-basic attribute. Actions differing
* only for non-basic attributes will collide. */
attr_hash = g_variant_hash (variant);
nm_hash_update_val (&h, attr_hash);
}
}
}
return nm_hash_complete (&h);
}
/**
* nm_tc_tfilter_dup:
* @tfilter: the #NMTCTfilter
@ -1137,6 +1182,55 @@ finalize (GObject *object)
G_OBJECT_CLASS (nm_setting_tc_config_parent_class)->finalize (object);
}
static gboolean
verify (NMSetting *setting, NMConnection *connection, GError **error)
{
NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (setting);
guint i;
if (self->qdiscs->len != 0) {
gs_unref_hashtable GHashTable *ht = NULL;
ht = g_hash_table_new ((GHashFunc) _nm_tc_qdisc_hash,
(GEqualFunc) nm_tc_qdisc_equal);
for (i = 0; i < self->qdiscs->len; i++) {
if (!g_hash_table_add (ht, self->qdiscs->pdata[i])) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("there are duplicate TC qdiscs"));
g_prefix_error (error,
"%s.%s: ",
NM_SETTING_TC_CONFIG_SETTING_NAME,
NM_SETTING_TC_CONFIG_QDISCS);
return FALSE;
}
}
}
if (self->tfilters->len != 0) {
gs_unref_hashtable GHashTable *ht = NULL;
ht = g_hash_table_new ((GHashFunc) _nm_tc_tfilter_hash,
(GEqualFunc) nm_tc_tfilter_equal);
for (i = 0; i < self->tfilters->len; i++) {
if (!g_hash_table_add (ht, self->tfilters->pdata[i])) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("there are duplicate TC filters"));
g_prefix_error (error,
"%s.%s: ",
NM_SETTING_TC_CONFIG_SETTING_NAME,
NM_SETTING_TC_CONFIG_TFILTERS);
return FALSE;
}
}
}
return TRUE;
}
static gboolean
compare_property (NMSetting *setting,
NMSetting *other,
@ -1502,6 +1596,7 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
object_class->get_property = get_property;
object_class->finalize = finalize;
parent_class->compare_property = compare_property;
parent_class->verify = verify;
/* Properties */

View file

@ -1440,7 +1440,7 @@ test_tc_config_tfilter (void)
}
static void
test_tc_config_setting (void)
test_tc_config_setting_valid (void)
{
gs_unref_object NMSettingTCConfig *s_tc = NULL;
NMTCQdisc *qdisc1, *qdisc2;
@ -1473,6 +1473,70 @@ test_tc_config_setting (void)
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)
{
@ -1608,7 +1672,8 @@ main (int argc, char **argv)
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", test_tc_config_setting);
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);
#if WITH_JSON_VALIDATION