/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (C) 2017 Red Hat, Inc.
*/
#include "nm-default.h"
#include "nm-setting-tc-config.h"
#include <linux/pkt_sched.h>
#include "nm-setting-private.h"
/**
* SECTION:nm-setting-tc-config
* @short_description: Describes connection properties for the Linux Traffic Control
* @include: nm-setting-tc-config.h
**/
/*****************************************************************************/
G_DEFINE_BOXED_TYPE(NMTCQdisc, nm_tc_qdisc, nm_tc_qdisc_dup, nm_tc_qdisc_unref)
struct NMTCQdisc {
guint refcount;
char * kind;
guint32 handle;
guint32 parent;
GHashTable *attributes;
};
/**
* nm_tc_qdisc_new:
* @kind: name of the queueing discipline
* @parent: the parent queueing discipline
* @error: location to store error, or %NULL
*
* Creates a new #NMTCQdisc object.
*
* Returns: (transfer full): the new #NMTCQdisc object, or %NULL on error
*
* Since: 1.12
**/
NMTCQdisc *
nm_tc_qdisc_new(const char *kind, guint32 parent, GError **error)
{
NMTCQdisc *qdisc;
if (!kind || !*kind) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("kind is missing"));
return NULL;
}
if (strchr(kind, ' ') || strchr(kind, '\t')) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid kind"),
kind);
return NULL;
}
if (!parent) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("parent handle missing"));
return NULL;
}
qdisc = g_slice_new0(NMTCQdisc);
qdisc->refcount = 1;
qdisc->kind = g_strdup(kind);
qdisc->parent = parent;
return qdisc;
}
/**
* nm_tc_qdisc_ref:
* @qdisc: the #NMTCQdisc
*
* Increases the reference count of the object.
*
* Since: 1.12
**/
void
nm_tc_qdisc_ref(NMTCQdisc *qdisc)
{
g_return_if_fail(qdisc != NULL);
g_return_if_fail(qdisc->refcount > 0);
qdisc->refcount++;
}
/**
* nm_tc_qdisc_unref:
* @qdisc: the #NMTCQdisc
*
* Decreases the reference count of the object. If the reference count
* reaches zero, the object will be destroyed.
*
* Since: 1.12
**/
void
nm_tc_qdisc_unref(NMTCQdisc *qdisc)
{
g_return_if_fail(qdisc != NULL);
g_return_if_fail(qdisc->refcount > 0);
qdisc->refcount--;
if (qdisc->refcount == 0) {
g_free(qdisc->kind);
if (qdisc->attributes)
g_hash_table_unref(qdisc->attributes);
g_slice_free(NMTCQdisc, qdisc);
}
}
/**
* nm_tc_qdisc_equal:
* @qdisc: the #NMTCQdisc
* @other: the #NMTCQdisc to compare @qdisc to.
*
* Determines if two #NMTCQdisc objects contain the same kind, * handle
* and parent.
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
*
* Since: 1.12
**/
gboolean
nm_tc_qdisc_equal(NMTCQdisc *qdisc, NMTCQdisc *other)
{
GHashTableIter iter;
const char * key;
GVariant * value, *value2;
guint n;
g_return_val_if_fail(qdisc != NULL, FALSE);
g_return_val_if_fail(qdisc->refcount > 0, FALSE);
g_return_val_if_fail(other != NULL, FALSE);
g_return_val_if_fail(other->refcount > 0, FALSE);
if (qdisc->handle != other->handle || qdisc->parent != other->parent
|| g_strcmp0(qdisc->kind, other->kind) != 0)
return FALSE;
n = qdisc->attributes ? g_hash_table_size(qdisc->attributes) : 0;
if (n != (other->attributes ? g_hash_table_size(other->attributes) : 0))
return FALSE;
if (n) {
g_hash_table_iter_init(&iter, qdisc->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
value2 = g_hash_table_lookup(other->attributes, key);
if (!value2)
return FALSE;
if (!g_variant_equal(value, value2))
return FALSE;
}
}
return TRUE;
}
static guint
_nm_tc_qdisc_hash(NMTCQdisc *qdisc)
{
NMUtilsNamedValue attrs_static[30];
gs_free NMUtilsNamedValue *attrs_free = NULL;
const NMUtilsNamedValue * attrs;
NMHashState h;
guint length;
guint i;
attrs =
nm_utils_named_values_from_strdict(qdisc->attributes, &length, attrs_static, &attrs_free);
nm_hash_init(&h, 43869703);
nm_hash_update_vals(&h, qdisc->handle, qdisc->parent, length);
nm_hash_update_str0(&h, qdisc->kind);
for (i = 0; i < length; i++) {
const char * key = attrs[i].name;
GVariant * variant = attrs[i].value_ptr;
const GVariantType *vtype;
vtype = g_variant_get_type(variant);
nm_hash_update_str(&h, key);
nm_hash_update_str(&h, (const char *) vtype);
if (g_variant_type_is_basic(vtype))
nm_hash_update_val(&h, g_variant_hash(variant));
}
return nm_hash_complete(&h);
}
/**
* nm_tc_qdisc_dup:
* @qdisc: the #NMTCQdisc
*
* Creates a copy of @qdisc
*
* Returns: (transfer full): a copy of @qdisc
*
* Since: 1.12
**/
NMTCQdisc *
nm_tc_qdisc_dup(NMTCQdisc *qdisc)
{
NMTCQdisc *copy;
g_return_val_if_fail(qdisc != NULL, NULL);
g_return_val_if_fail(qdisc->refcount > 0, NULL);
copy = nm_tc_qdisc_new(qdisc->kind, qdisc->parent, NULL);
nm_tc_qdisc_set_handle(copy, qdisc->handle);
if (qdisc->attributes) {
GHashTableIter iter;
const char * key;
GVariant * value;
g_hash_table_iter_init(&iter, qdisc->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value))
nm_tc_qdisc_set_attribute(copy, key, value);
}
return copy;
}
/**
* nm_tc_qdisc_get_kind:
* @qdisc: the #NMTCQdisc
*
* Returns:
*
* Since: 1.12
**/
const char *
nm_tc_qdisc_get_kind(NMTCQdisc *qdisc)
{
g_return_val_if_fail(qdisc != NULL, NULL);
g_return_val_if_fail(qdisc->refcount > 0, NULL);
return qdisc->kind;
}
/**
* nm_tc_qdisc_get_handle:
* @qdisc: the #NMTCQdisc
*
* Returns: the queueing discipline handle
*
* Since: 1.12
**/
guint32
nm_tc_qdisc_get_handle(NMTCQdisc *qdisc)
{
g_return_val_if_fail(qdisc != NULL, TC_H_UNSPEC);
g_return_val_if_fail(qdisc->refcount > 0, TC_H_UNSPEC);
return qdisc->handle;
}
/**
* nm_tc_qdisc_set_handle:
* @qdisc: the #NMTCQdisc
* @handle: the queueing discipline handle
*
* Sets the queueing discipline handle.
*
* Since: 1.12
**/
void
nm_tc_qdisc_set_handle(NMTCQdisc *qdisc, guint32 handle)
{
g_return_if_fail(qdisc != NULL);
g_return_if_fail(qdisc->refcount > 0);
qdisc->handle = handle;
}
/**
* nm_tc_qdisc_get_parent:
* @qdisc: the #NMTCQdisc
*
* Returns: the parent class
*
* Since: 1.12
**/
guint32
nm_tc_qdisc_get_parent(NMTCQdisc *qdisc)
{
g_return_val_if_fail(qdisc != NULL, TC_H_UNSPEC);
g_return_val_if_fail(qdisc->refcount > 0, TC_H_UNSPEC);
return qdisc->parent;
}
/**
* nm_tc_qdisc_get_attribute_names:
* @qdisc: the #NMTCQdisc
*
* Gets an array of attribute names defined on @qdisc.
*
* Returns: (transfer container): a %NULL-terminated array of attribute names
* or %NULL if no attributes are set.
*
* Since: 1.18
**/
const char **
nm_tc_qdisc_get_attribute_names(NMTCQdisc *qdisc)
{
g_return_val_if_fail(qdisc, NULL);
return nm_utils_strdict_get_keys(qdisc->attributes, TRUE, NULL);
}
GHashTable *
_nm_tc_qdisc_get_attributes(NMTCQdisc *qdisc)
{
nm_assert(qdisc);
return qdisc->attributes;
}
/**
* nm_tc_qdisc_get_attribute:
* @qdisc: the #NMTCQdisc
* @name: the name of an qdisc attribute
*
* Gets the value of the attribute with name @name on @qdisc
*
* Returns: (transfer none): the value of the attribute with name @name on
* @qdisc, or %NULL if @qdisc has no such attribute.
*
* Since: 1.18
**/
GVariant *
nm_tc_qdisc_get_attribute(NMTCQdisc *qdisc, const char *name)
{
g_return_val_if_fail(qdisc != NULL, NULL);
g_return_val_if_fail(name != NULL && *name != '\0', NULL);
if (qdisc->attributes)
return g_hash_table_lookup(qdisc->attributes, name);
else
return NULL;
}
/**
* nm_tc_qdisc_set_attribute:
* @qdisc: the #NMTCQdisc
* @name: the name of an qdisc attribute
* @value: (transfer none) (allow-none): the value
*
* Sets or clears the named attribute on @qdisc to the given value.
*
* Since: 1.18
**/
void
nm_tc_qdisc_set_attribute(NMTCQdisc *qdisc, const char *name, GVariant *value)
{
g_return_if_fail(qdisc != NULL);
g_return_if_fail(name != NULL && *name != '\0');
g_return_if_fail(strcmp(name, "kind") != 0);
if (!qdisc->attributes) {
qdisc->attributes = g_hash_table_new_full(nm_str_hash,
g_str_equal,
g_free,
(GDestroyNotify) g_variant_unref);
}
if (value)
g_hash_table_insert(qdisc->attributes, g_strdup(name), g_variant_ref_sink(value));
else
g_hash_table_remove(qdisc->attributes, name);
}
/*****************************************************************************/
G_DEFINE_BOXED_TYPE(NMTCAction, nm_tc_action, nm_tc_action_dup, nm_tc_action_unref)
struct NMTCAction {
guint refcount;
char *kind;
GHashTable *attributes;
};
/**
* nm_tc_action_new:
* @kind: name of the queueing discipline
* @error: location to store error, or %NULL
*
* Creates a new #NMTCAction object.
*
* Returns: (transfer full): the new #NMTCAction object, or %NULL on error
*
* Since: 1.12
**/
NMTCAction *
nm_tc_action_new(const char *kind, GError **error)
{
NMTCAction *action;
if (!kind || !*kind) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("kind is missing"));
return NULL;
}
if (strchr(kind, ' ') || strchr(kind, '\t')) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid kind"),
kind);
return NULL;
}
action = g_slice_new0(NMTCAction);
action->refcount = 1;
action->kind = g_strdup(kind);
return action;
}
/**
* nm_tc_action_ref:
* @action: the #NMTCAction
*
* Increases the reference count of the object.
*
* Since: 1.12
**/
void
nm_tc_action_ref(NMTCAction *action)
{
g_return_if_fail(action != NULL);
g_return_if_fail(action->refcount > 0);
action->refcount++;
}
/**
* nm_tc_action_unref:
* @action: the #NMTCAction
*
* Decreases the reference count of the object. If the reference count
* reaches zero, the object will be destroyed.
*
* Since: 1.12
**/
void
nm_tc_action_unref(NMTCAction *action)
{
g_return_if_fail(action != NULL);
g_return_if_fail(action->refcount > 0);
action->refcount--;
if (action->refcount == 0) {
g_free(action->kind);
if (action->attributes)
g_hash_table_unref(action->attributes);
g_slice_free(NMTCAction, action);
}
}
/**
* nm_tc_action_equal:
* @action: the #NMTCAction
* @other: the #NMTCAction to compare @action to.
*
* Determines if two #NMTCAction objects contain the same kind, family,
* handle, parent and info.
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
*
* Since: 1.12
**/
gboolean
nm_tc_action_equal(NMTCAction *action, NMTCAction *other)
{
GHashTableIter iter;
const char * key;
GVariant * value, *value2;
guint n;
g_return_val_if_fail(!action || action->refcount > 0, FALSE);
g_return_val_if_fail(!other || other->refcount > 0, FALSE);
if (action == other)
return TRUE;
if (!action || !other)
return FALSE;
if (g_strcmp0(action->kind, other->kind) != 0)
return FALSE;
n = action->attributes ? g_hash_table_size(action->attributes) : 0;
if (n != (other->attributes ? g_hash_table_size(other->attributes) : 0))
return FALSE;
if (n) {
g_hash_table_iter_init(&iter, action->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
value2 = g_hash_table_lookup(other->attributes, key);
if (!value2)
return FALSE;
if (!g_variant_equal(value, value2))
return FALSE;
}
}
return TRUE;
}
/**
* nm_tc_action_dup:
* @action: the #NMTCAction
*
* Creates a copy of @action
*
* Returns: (transfer full): a copy of @action
*
* Since: 1.12
**/
NMTCAction *
nm_tc_action_dup(NMTCAction *action)
{
NMTCAction *copy;
g_return_val_if_fail(action != NULL, NULL);
g_return_val_if_fail(action->refcount > 0, NULL);
copy = nm_tc_action_new(action->kind, NULL);
if (action->attributes) {
GHashTableIter iter;
const char * key;
GVariant * value;
g_hash_table_iter_init(&iter, action->attributes);
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value))
nm_tc_action_set_attribute(copy, key, value);
}
return copy;
}
/**
* nm_tc_action_get_kind:
* @action: the #NMTCAction
*
* Returns:
*
* Since: 1.12
**/
const char *
nm_tc_action_get_kind(NMTCAction *action)
{
g_return_val_if_fail(action != NULL, NULL);
g_return_val_if_fail(action->refcount > 0, NULL);
return action->kind;
}
/**
* nm_tc_action_get_attribute_names:
* @action: the #NMTCAction
*
* Gets an array of attribute names defined on @action.
*
* Returns: (transfer full): a %NULL-terminated array of attribute names,
*
* Since: 1.12
**/
char **
nm_tc_action_get_attribute_names(NMTCAction *action)
{
const char **names;
g_return_val_if_fail(action, NULL);
names = nm_utils_strdict_get_keys(action->attributes, TRUE, NULL);
return nm_utils_strv_make_deep_copied_nonnull(names);
}
GHashTable *
_nm_tc_action_get_attributes(NMTCAction *action)
{
nm_assert(action);
return action->attributes;
}
/**
* nm_tc_action_get_attribute:
* @action: the #NMTCAction
* @name: the name of an action attribute
*
* Gets the value of the attribute with name @name on @action
*
* Returns: (transfer none): the value of the attribute with name @name on
* @action, or %NULL if @action has no such attribute.
*
* Since: 1.12
**/
GVariant *
nm_tc_action_get_attribute(NMTCAction *action, const char *name)
{
g_return_val_if_fail(action != NULL, NULL);
g_return_val_if_fail(name != NULL && *name != '\0', NULL);
if (action->attributes)
return g_hash_table_lookup(action->attributes, name);
else
return NULL;
}
/**
* nm_tc_action_set_attribute:
* @action: the #NMTCAction
* @name: the name of an action attribute
* @value: (transfer none) (allow-none): the value
*
* Sets or clears the named attribute on @action to the given value.
*
* Since: 1.12
**/
void
nm_tc_action_set_attribute(NMTCAction *action, const char *name, GVariant *value)
{
g_return_if_fail(action != NULL);
g_return_if_fail(name != NULL && *name != '\0');
g_return_if_fail(strcmp(name, "kind") != 0);
if (!action->attributes) {
action->attributes = g_hash_table_new_full(nm_str_hash,
g_str_equal,
g_free,
(GDestroyNotify) g_variant_unref);
}
if (value)
g_hash_table_insert(action->attributes, g_strdup(name), g_variant_ref_sink(value));
else
g_hash_table_remove(action->attributes, name);
}
/*****************************************************************************/
G_DEFINE_BOXED_TYPE(NMTCTfilter, nm_tc_tfilter, nm_tc_tfilter_dup, nm_tc_tfilter_unref)
struct NMTCTfilter {
guint refcount;
char * kind;
guint32 handle;
guint32 parent;
NMTCAction *action;
};
/**
* nm_tc_tfilter_new:
* @kind: name of the queueing discipline
* @parent: the parent queueing discipline
* @error: location to store error, or %NULL
*
* Creates a new #NMTCTfilter object.
*
* Returns: (transfer full): the new #NMTCTfilter object, or %NULL on error
*
* Since: 1.12
**/
NMTCTfilter *
nm_tc_tfilter_new(const char *kind, guint32 parent, GError **error)
{
NMTCTfilter *tfilter;
if (!kind || !*kind) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("kind is missing"));
return NULL;
}
if (strchr(kind, ' ') || strchr(kind, '\t')) {
g_set_error(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("'%s' is not a valid kind"),
kind);
return NULL;
}
if (!parent) {
g_set_error_literal(error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("parent handle missing"));
return NULL;
}
tfilter = g_slice_new0(NMTCTfilter);
tfilter->refcount = 1;
tfilter->kind = g_strdup(kind);
tfilter->parent = parent;
return tfilter;
}
/**
* nm_tc_tfilter_ref:
* @tfilter: the #NMTCTfilter
*
* Increases the reference count of the object.
*
* Since: 1.12
**/
void
nm_tc_tfilter_ref(NMTCTfilter *tfilter)
{
g_return_if_fail(tfilter != NULL);
g_return_if_fail(tfilter->refcount > 0);
tfilter->refcount++;
}
/**
* nm_tc_tfilter_unref:
* @tfilter: the #NMTCTfilter
*
* Decreases the reference count of the object. If the reference count
* reaches zero, the object will be destroyed.
*
* Since: 1.12
**/
void
nm_tc_tfilter_unref(NMTCTfilter *tfilter)
{
g_return_if_fail(tfilter != NULL);
g_return_if_fail(tfilter->refcount > 0);
tfilter->refcount--;
if (tfilter->refcount == 0) {
g_free(tfilter->kind);
if (tfilter->action)
nm_tc_action_unref(tfilter->action);
g_slice_free(NMTCTfilter, tfilter);
}
}
/**
* nm_tc_tfilter_equal:
* @tfilter: the #NMTCTfilter
* @other: the #NMTCTfilter to compare @tfilter to.
*
* Determines if two #NMTCTfilter objects contain the same kind, family,
* handle, parent and info.
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
*
* Since: 1.12
**/
gboolean
nm_tc_tfilter_equal(NMTCTfilter *tfilter, NMTCTfilter *other)
{
g_return_val_if_fail(tfilter != NULL, FALSE);
g_return_val_if_fail(tfilter->refcount > 0, FALSE);
g_return_val_if_fail(other != NULL, FALSE);
g_return_val_if_fail(other->refcount > 0, FALSE);
if (tfilter->handle != other->handle || tfilter->parent != other->parent
|| g_strcmp0(tfilter->kind, other->kind) != 0
|| !nm_tc_action_equal(tfilter->action, other->action))
return FALSE;
return TRUE;
}
static guint
_nm_tc_tfilter_hash(NMTCTfilter *tfilter)
{
NMHashState h;
nm_hash_init(&h, 63624437);
nm_hash_update_vals(&h, tfilter->handle, tfilter->parent);
nm_hash_update_str0(&h, tfilter->kind);
if (tfilter->action) {
gs_free NMUtilsNamedValue *attrs_free = NULL;
NMUtilsNamedValue attrs_static[30];
const NMUtilsNamedValue * attrs;
guint length;
guint i;
nm_hash_update_str0(&h, tfilter->action->kind);
attrs = nm_utils_named_values_from_strdict(tfilter->action->attributes,
&length,
attrs_static,
&attrs_free);
for (i = 0; i < length; i++) {
GVariant *variant = attrs[i].value_ptr;
nm_hash_update_str(&h, attrs[i].name);
if (g_variant_type_is_basic(g_variant_get_type(variant))) {
guint attr_hash;
/* 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
*
* Creates a copy of @tfilter
*
* Returns: (transfer full): a copy of @tfilter
*
* Since: 1.12
**/
NMTCTfilter *
nm_tc_tfilter_dup(NMTCTfilter *tfilter)
{
NMTCTfilter *copy;
g_return_val_if_fail(tfilter != NULL, NULL);
g_return_val_if_fail(tfilter->refcount > 0, NULL);
copy = nm_tc_tfilter_new(tfilter->kind, tfilter->parent, NULL);
nm_tc_tfilter_set_handle(copy, tfilter->handle);
nm_tc_tfilter_set_action(copy, tfilter->action);
return copy;
}
/**
* nm_tc_tfilter_get_kind:
* @tfilter: the #NMTCTfilter
*
* Returns:
*
* Since: 1.12
**/
const char *
nm_tc_tfilter_get_kind(NMTCTfilter *tfilter)
{
g_return_val_if_fail(tfilter != NULL, NULL);
g_return_val_if_fail(tfilter->refcount > 0, NULL);
return tfilter->kind;
}
/**
* nm_tc_tfilter_get_handle:
* @tfilter: the #NMTCTfilter
*
* Returns: the queueing discipline handle
*
* Since: 1.12
**/
guint32
nm_tc_tfilter_get_handle(NMTCTfilter *tfilter)
{
g_return_val_if_fail(tfilter != NULL, TC_H_UNSPEC);
g_return_val_if_fail(tfilter->refcount > 0, TC_H_UNSPEC);
return tfilter->handle;
}
/**
* nm_tc_tfilter_set_handle:
* @tfilter: the #NMTCTfilter
* @handle: the queueing discipline handle
*
* Sets the queueing discipline handle.
*
* Since: 1.12
**/
void
nm_tc_tfilter_set_handle(NMTCTfilter *tfilter, guint32 handle)
{
g_return_if_fail(tfilter != NULL);
g_return_if_fail(tfilter->refcount > 0);
tfilter->handle = handle;
}
/**
* nm_tc_tfilter_get_parent:
* @tfilter: the #NMTCTfilter
*
* Returns: the parent class
*
* Since: 1.12
**/
guint32
nm_tc_tfilter_get_parent(NMTCTfilter *tfilter)
{
g_return_val_if_fail(tfilter != NULL, TC_H_UNSPEC);
g_return_val_if_fail(tfilter->refcount > 0, TC_H_UNSPEC);
return tfilter->parent;
}
/**
* nm_tc_tfilter_get_action:
* @tfilter: the #NMTCTfilter
*
* Returns: the action associated with a traffic filter.
*
* Since: 1.12
**/
NMTCAction *
nm_tc_tfilter_get_action(NMTCTfilter *tfilter)
{
g_return_val_if_fail(tfilter != NULL, TC_H_UNSPEC);
g_return_val_if_fail(tfilter->refcount > 0, TC_H_UNSPEC);
if (tfilter->action == NULL)
return NULL;
return tfilter->action;
}
/**
* nm_tc_tfilter_set_action:
* @tfilter: the #NMTCTfilter
* @action: the action object
*
* Sets the action associated with a traffic filter.
*
* Since: 1.12
**/
void
nm_tc_tfilter_set_action(NMTCTfilter *tfilter, NMTCAction *action)
{
g_return_if_fail(tfilter != NULL);
g_return_if_fail(tfilter->refcount > 0);
if (action)
nm_tc_action_ref(action);
if (tfilter->action)
nm_tc_action_unref(tfilter->action);
tfilter->action = action;
}
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingTCConfig, PROP_QDISCS, PROP_TFILTERS, );
/**
* NMSettingTCConfig:
*
* Linux Traffic Control Settings
*
* Since: 1.12
*/
struct _NMSettingTCConfig {
NMSetting parent;
GPtrArray *qdiscs;
GPtrArray *tfilters;
};
struct _NMSettingTCConfigClass {
NMSettingClass parent;
};
G_DEFINE_TYPE(NMSettingTCConfig, nm_setting_tc_config, NM_TYPE_SETTING)
/*****************************************************************************/
/**
* nm_setting_tc_config_get_num_qdiscs:
* @setting: the #NMSettingTCConfig
*
* Returns: the number of configured queueing disciplines
*
* Since: 1.12
**/
guint
nm_setting_tc_config_get_num_qdiscs(NMSettingTCConfig *self)
{
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), 0);
return self->qdiscs->len;
}
/**
* nm_setting_tc_config_get_qdisc:
* @setting: the #NMSettingTCConfig
* @idx: index number of the qdisc to return
*
* Returns: (transfer none): the qdisc at index @idx
*
* Since: 1.12
**/
NMTCQdisc *
nm_setting_tc_config_get_qdisc(NMSettingTCConfig *self, guint idx)
{
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), NULL);
g_return_val_if_fail(idx < self->qdiscs->len, NULL);
return self->qdiscs->pdata[idx];
}
/**
* nm_setting_tc_config_add_qdisc:
* @setting: the #NMSettingTCConfig
* @qdisc: the qdisc to add
*
* Appends a new qdisc and associated information to the setting. The
* given qdisc is duplicated internally and is not changed by this function.
* If an identical qdisc (considering attributes as well) already exists, the
* qdisc is not added and the function returns %FALSE.
*
* Returns: %TRUE if the qdisc was added; %FALSE if the qdisc was already known.
*
* Since: 1.12
**/
gboolean
nm_setting_tc_config_add_qdisc(NMSettingTCConfig *self, NMTCQdisc *qdisc)
{
guint i;
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), FALSE);
g_return_val_if_fail(qdisc != NULL, FALSE);
for (i = 0; i < self->qdiscs->len; i++) {
if (nm_tc_qdisc_equal(self->qdiscs->pdata[i], qdisc))
return FALSE;
}
g_ptr_array_add(self->qdiscs, nm_tc_qdisc_dup(qdisc));
_notify(self, PROP_QDISCS);
return TRUE;
}
/**
* nm_setting_tc_config_remove_qdisc:
* @setting: the #NMSettingTCConfig
* @idx: index number of the qdisc
*
* Removes the qdisc at index @idx.
*
* Since: 1.12
**/
void
nm_setting_tc_config_remove_qdisc(NMSettingTCConfig *self, guint idx)
{
g_return_if_fail(NM_IS_SETTING_TC_CONFIG(self));
g_return_if_fail(idx < self->qdiscs->len);
g_ptr_array_remove_index(self->qdiscs, idx);
_notify(self, PROP_QDISCS);
}
/**
* nm_setting_tc_config_remove_qdisc_by_value:
* @setting: the #NMSettingTCConfig
* @qdisc: the qdisc to remove
*
* Removes the first matching qdisc that matches @qdisc.
*
* Returns: %TRUE if the qdisc was found and removed; %FALSE if it was not.
*
* Since: 1.12
**/
gboolean
nm_setting_tc_config_remove_qdisc_by_value(NMSettingTCConfig *self, NMTCQdisc *qdisc)
{
guint i;
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), FALSE);
g_return_val_if_fail(qdisc != NULL, FALSE);
for (i = 0; i < self->qdiscs->len; i++) {
if (nm_tc_qdisc_equal(self->qdiscs->pdata[i], qdisc)) {
g_ptr_array_remove_index(self->qdiscs, i);
_notify(self, PROP_QDISCS);
return TRUE;
}
}
return FALSE;
}
/**
* nm_setting_tc_config_clear_qdiscs:
* @setting: the #NMSettingTCConfig
*
* Removes all configured queueing disciplines.
*
* Since: 1.12
**/
void
nm_setting_tc_config_clear_qdiscs(NMSettingTCConfig *self)
{
g_return_if_fail(NM_IS_SETTING_TC_CONFIG(self));
if (self->qdiscs->len != 0) {
g_ptr_array_set_size(self->qdiscs, 0);
_notify(self, PROP_QDISCS);
}
}
/*****************************************************************************/
/**
* nm_setting_tc_config_get_num_tfilters:
* @setting: the #NMSettingTCConfig
*
* Returns: the number of configured queueing disciplines
*
* Since: 1.12
**/
guint
nm_setting_tc_config_get_num_tfilters(NMSettingTCConfig *self)
{
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), 0);
return self->tfilters->len;
}
/**
* nm_setting_tc_config_get_tfilter:
* @setting: the #NMSettingTCConfig
* @idx: index number of the tfilter to return
*
* Returns: (transfer none): the tfilter at index @idx
*
* Since: 1.12
**/
NMTCTfilter *
nm_setting_tc_config_get_tfilter(NMSettingTCConfig *self, guint idx)
{
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), NULL);
g_return_val_if_fail(idx < self->tfilters->len, NULL);
return self->tfilters->pdata[idx];
}
/**
* nm_setting_tc_config_add_tfilter:
* @setting: the #NMSettingTCConfig
* @tfilter: the tfilter to add
*
* Appends a new tfilter and associated information to the setting. The
* given tfilter is duplicated internally and is not changed by this function.
* If an identical tfilter (considering attributes as well) already exists, the
* tfilter is not added and the function returns %FALSE.
*
* Returns: %TRUE if the tfilter was added; %FALSE if the tfilter was already known.
*
* Since: 1.12
**/
gboolean
nm_setting_tc_config_add_tfilter(NMSettingTCConfig *self, NMTCTfilter *tfilter)
{
guint i;
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), FALSE);
g_return_val_if_fail(tfilter != NULL, FALSE);
for (i = 0; i < self->tfilters->len; i++) {
if (nm_tc_tfilter_equal(self->tfilters->pdata[i], tfilter))
return FALSE;
}
g_ptr_array_add(self->tfilters, nm_tc_tfilter_dup(tfilter));
_notify(self, PROP_TFILTERS);
return TRUE;
}
/**
* nm_setting_tc_config_remove_tfilter:
* @setting: the #NMSettingTCConfig
* @idx: index number of the tfilter
*
* Removes the tfilter at index @idx.
*
* Since: 1.12
**/
void
nm_setting_tc_config_remove_tfilter(NMSettingTCConfig *self, guint idx)
{
g_return_if_fail(NM_IS_SETTING_TC_CONFIG(self));
g_return_if_fail(idx < self->tfilters->len);
g_ptr_array_remove_index(self->tfilters, idx);
_notify(self, PROP_TFILTERS);
}
/**
* nm_setting_tc_config_remove_tfilter_by_value:
* @setting: the #NMSettingTCConfig
* @tfilter: the tfilter to remove
*
* Removes the first matching tfilter that matches @tfilter.
*
* Returns: %TRUE if the tfilter was found and removed; %FALSE if it was not.
*
* Since: 1.12
**/
gboolean
nm_setting_tc_config_remove_tfilter_by_value(NMSettingTCConfig *self, NMTCTfilter *tfilter)
{
guint i;
g_return_val_if_fail(NM_IS_SETTING_TC_CONFIG(self), FALSE);
g_return_val_if_fail(tfilter != NULL, FALSE);
for (i = 0; i < self->tfilters->len; i++) {
if (nm_tc_tfilter_equal(self->tfilters->pdata[i], tfilter)) {
g_ptr_array_remove_index(self->tfilters, i);
_notify(self, PROP_TFILTERS);
return TRUE;
}
}
return FALSE;
}
/**
* nm_setting_tc_config_clear_tfilters:
* @setting: the #NMSettingTCConfig
*
* Removes all configured queueing disciplines.
*
* Since: 1.12
**/
void
nm_setting_tc_config_clear_tfilters(NMSettingTCConfig *self)
{
g_return_if_fail(NM_IS_SETTING_TC_CONFIG(self));
if (self->tfilters->len != 0) {
g_ptr_array_set_size(self->tfilters, 0);
_notify(self, PROP_TFILTERS);
}
}
/*****************************************************************************/
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 NMTernary
compare_property(const NMSettInfoSetting *sett_info,
guint property_idx,
NMConnection * con_a,
NMSetting * set_a,
NMConnection * con_b,
NMSetting * set_b,
NMSettingCompareFlags flags)
{
NMSettingTCConfig *a_tc_config = NM_SETTING_TC_CONFIG(set_a);
NMSettingTCConfig *b_tc_config = NM_SETTING_TC_CONFIG(set_b);
guint i;
if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_TC_CONFIG_QDISCS)) {
if (set_b) {
if (a_tc_config->qdiscs->len != b_tc_config->qdiscs->len)
return FALSE;
for (i = 0; i < a_tc_config->qdiscs->len; i++) {
if (!nm_tc_qdisc_equal(a_tc_config->qdiscs->pdata[i],
b_tc_config->qdiscs->pdata[i]))
return FALSE;
}
}
return TRUE;
}
if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_TC_CONFIG_TFILTERS)) {
if (set_b) {
if (a_tc_config->tfilters->len != b_tc_config->tfilters->len)
return FALSE;
for (i = 0; i < a_tc_config->tfilters->len; i++) {
if (!nm_tc_tfilter_equal(a_tc_config->tfilters->pdata[i],
b_tc_config->tfilters->pdata[i]))
return FALSE;
}
}
return TRUE;
}
return NM_SETTING_CLASS(nm_setting_tc_config_parent_class)
->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
}
/**
* _qdiscs_to_variant:
* @qdiscs: (element-type NMTCQdisc): an array of #NMTCQdisc objects
*
* Utility function to convert a #GPtrArray of #NMTCQdisc objects representing
* TC qdiscs into a #GVariant of type 'aa{sv}' representing an array
* of NetworkManager TC qdiscs.
*
* Returns: (transfer none): a new floating #GVariant representing @qdiscs.
**/
static GVariant *
_qdiscs_to_variant(GPtrArray *qdiscs)
{
GVariantBuilder builder;
int i;
g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
if (qdiscs) {
for (i = 0; i < qdiscs->len; i++) {
NMUtilsNamedValue attrs_static[30];
gs_free NMUtilsNamedValue *attrs_free = NULL;
const NMUtilsNamedValue * attrs;
NMTCQdisc * qdisc = qdiscs->pdata[i];
guint length;
GVariantBuilder qdisc_builder;
guint y;
g_variant_builder_init(&qdisc_builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(&qdisc_builder,
"{sv}",
"kind",
g_variant_new_string(nm_tc_qdisc_get_kind(qdisc)));
g_variant_builder_add(&qdisc_builder,
"{sv}",
"handle",
g_variant_new_uint32(nm_tc_qdisc_get_handle(qdisc)));
g_variant_builder_add(&qdisc_builder,
"{sv}",
"parent",
g_variant_new_uint32(nm_tc_qdisc_get_parent(qdisc)));
attrs = nm_utils_named_values_from_strdict(qdisc->attributes,
&length,
attrs_static,
&attrs_free);
for (y = 0; y < length; y++) {
g_variant_builder_add(&qdisc_builder, "{sv}", attrs[y].name, attrs[y].value_ptr);
}
g_variant_builder_add(&builder, "a{sv}", &qdisc_builder);
}
}
return g_variant_builder_end(&builder);
}
/**
* _qdiscs_from_variant:
* @value: a #GVariant of type 'aa{sv}'
*
* Utility function to convert a #GVariant representing a list of TC qdiscs
* into a #GPtrArray of * #NMTCQdisc objects.
*
* Returns: (transfer full) (element-type NMTCQdisc): a newly allocated
* #GPtrArray of #NMTCQdisc objects
**/
static GPtrArray *
_qdiscs_from_variant(GVariant *value)
{
GPtrArray * qdiscs;
GVariant * qdisc_var;
GVariantIter iter;
GError * error = NULL;
g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), NULL);
g_variant_iter_init(&iter, value);
qdiscs = g_ptr_array_new_with_free_func((GDestroyNotify) nm_tc_qdisc_unref);
while (g_variant_iter_next(&iter, "@a{sv}", &qdisc_var)) {
const char * kind;
guint32 parent;
NMTCQdisc * qdisc;
GVariantIter qdisc_iter;
const char * key;
GVariant * attr_value;
if (!g_variant_lookup(qdisc_var, "kind", "&s", &kind)
|| !g_variant_lookup(qdisc_var, "parent", "u", &parent)) {
//g_warning ("Ignoring invalid qdisc");
goto next;
}
qdisc = nm_tc_qdisc_new(kind, parent, &error);
if (!qdisc) {
//g_warning ("Ignoring invalid qdisc: %s", error->message);
g_clear_error(&error);
goto next;
}
g_variant_iter_init(&qdisc_iter, qdisc_var);
while (g_variant_iter_next(&qdisc_iter, "{&sv}", &key, &attr_value)) {
if (strcmp(key, "kind") == 0 || strcmp(key, "parent") == 0) {
/* Already processed above */
} else if (strcmp(key, "handle") == 0) {
nm_tc_qdisc_set_handle(qdisc, g_variant_get_uint32(attr_value));
} else {
nm_tc_qdisc_set_attribute(qdisc, key, attr_value);
}
g_variant_unref(attr_value);
}
g_ptr_array_add(qdiscs, qdisc);
next:
g_variant_unref(qdisc_var);
}
return qdiscs;
}
static GVariant *
tc_qdiscs_get(const NMSettInfoSetting * sett_info,
guint property_idx,
NMConnection * connection,
NMSetting * setting,
NMConnectionSerializationFlags flags,
const NMConnectionSerializationOptions *options)
{
gs_unref_ptrarray GPtrArray *qdiscs = NULL;
g_object_get(setting, NM_SETTING_TC_CONFIG_QDISCS, &qdiscs, NULL);
return _qdiscs_to_variant(qdiscs);
}
static gboolean
tc_qdiscs_set(NMSetting * setting,
GVariant * connection_dict,
const char * property,
GVariant * value,
NMSettingParseFlags parse_flags,
GError ** error)
{
GPtrArray *qdiscs;
qdiscs = _qdiscs_from_variant(value);
g_object_set(setting, NM_SETTING_TC_CONFIG_QDISCS, qdiscs, NULL);
g_ptr_array_unref(qdiscs);
return TRUE;
}
static GVariant *
_action_to_variant(NMTCAction *action)
{
GVariantBuilder builder;
gs_strfreev char **attrs = nm_tc_action_get_attribute_names(action);
int i;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(&builder,
"{sv}",
"kind",
g_variant_new_string(nm_tc_action_get_kind(action)));
for (i = 0; attrs[i]; i++) {
g_variant_builder_add(&builder,
"{sv}",
attrs[i],
nm_tc_action_get_attribute(action, attrs[i]));
}
return g_variant_builder_end(&builder);
}
/**
* _tfilters_to_variant:
* @tfilters: (element-type NMTCTfilter): an array of #NMTCTfilter objects
*
* Utility function to convert a #GPtrArray of #NMTCTfilter objects representing
* TC tfilters into a #GVariant of type 'aa{sv}' representing an array
* of NetworkManager TC tfilters.
*
* Returns: (transfer none): a new floating #GVariant representing @tfilters.
**/
static GVariant *
_tfilters_to_variant(GPtrArray *tfilters)
{
GVariantBuilder builder;
int i;
g_variant_builder_init(&builder, G_VARIANT_TYPE("aa{sv}"));
if (tfilters) {
for (i = 0; i < tfilters->len; i++) {
NMTCTfilter * tfilter = tfilters->pdata[i];
NMTCAction * action = nm_tc_tfilter_get_action(tfilter);
GVariantBuilder tfilter_builder;
g_variant_builder_init(&tfilter_builder, G_VARIANT_TYPE("a{sv}"));
g_variant_builder_add(&tfilter_builder,
"{sv}",
"kind",
g_variant_new_string(nm_tc_tfilter_get_kind(tfilter)));
g_variant_builder_add(&tfilter_builder,
"{sv}",
"handle",
g_variant_new_uint32(nm_tc_tfilter_get_handle(tfilter)));
g_variant_builder_add(&tfilter_builder,
"{sv}",
"parent",
g_variant_new_uint32(nm_tc_tfilter_get_parent(tfilter)));
if (action) {
g_variant_builder_add(&tfilter_builder,
"{sv}",
"action",
_action_to_variant(action));
}
g_variant_builder_add(&builder, "a{sv}", &tfilter_builder);
}
}
return g_variant_builder_end(&builder);
}
/**
* _tfilters_from_variant:
* @value: a #GVariant of type 'aa{sv}'
*
* Utility function to convert a #GVariant representing a list of TC tfilters
* into a #GPtrArray of * #NMTCTfilter objects.
*
* Returns: (transfer full) (element-type NMTCTfilter): a newly allocated
* #GPtrArray of #NMTCTfilter objects
**/
static GPtrArray *
_tfilters_from_variant(GVariant *value)
{
GPtrArray * tfilters;
GVariant * tfilter_var;
GVariantIter iter;
GError * error = NULL;
g_return_val_if_fail(g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")), NULL);
g_variant_iter_init(&iter, value);
tfilters = g_ptr_array_new_with_free_func((GDestroyNotify) nm_tc_tfilter_unref);
while (g_variant_iter_next(&iter, "@a{sv}", &tfilter_var)) {
NMTCTfilter *tfilter = NULL;
const char * kind;
guint32 handle;
guint32 parent;
NMTCAction * action;
const char * action_kind = NULL;
char * action_key;
GVariantIter action_iter;
GVariant * action_var = NULL;
GVariant * action_val;
if (!g_variant_lookup(tfilter_var, "kind", "&s", &kind)
|| !g_variant_lookup(tfilter_var, "parent", "u", &parent)) {
//g_warning ("Ignoring invalid tfilter");
goto next;
}
tfilter = nm_tc_tfilter_new(kind, parent, &error);
if (!tfilter) {
//g_warning ("Ignoring invalid tfilter: %s", error->message);
g_clear_error(&error);
goto next;
}
if (g_variant_lookup(tfilter_var, "handle", "u", &handle))
nm_tc_tfilter_set_handle(tfilter, handle);
action_var = g_variant_lookup_value(tfilter_var, "action", G_VARIANT_TYPE_VARDICT);
if (action_var) {
if (!g_variant_lookup(action_var, "kind", "&s", &action_kind)) {
//g_warning ("Ignoring tfilter with invalid action");
goto next;
}
action = nm_tc_action_new(action_kind, &error);
if (!action) {
//g_warning ("Ignoring tfilter with invalid action: %s", error->message);
g_clear_error(&error);
goto next;
}
g_variant_iter_init(&action_iter, action_var);
while (g_variant_iter_next(&action_iter, "{&sv}", &action_key, &action_val)) {
if (strcmp(action_key, "kind") != 0)
nm_tc_action_set_attribute(action, action_key, action_val);
g_variant_unref(action_val);
}
nm_tc_tfilter_set_action(tfilter, action);
nm_tc_action_unref(action);
}
nm_tc_tfilter_ref(tfilter);
g_ptr_array_add(tfilters, tfilter);
next:
if (tfilter)
nm_tc_tfilter_unref(tfilter);
if (action_var)
g_variant_unref(action_var);
g_variant_unref(tfilter_var);
}
return tfilters;
}
static GVariant *
tc_tfilters_get(const NMSettInfoSetting * sett_info,
guint property_idx,
NMConnection * connection,
NMSetting * setting,
NMConnectionSerializationFlags flags,
const NMConnectionSerializationOptions *options)
{
gs_unref_ptrarray GPtrArray *tfilters = NULL;
g_object_get(setting, NM_SETTING_TC_CONFIG_TFILTERS, &tfilters, NULL);
return _tfilters_to_variant(tfilters);
}
static gboolean
tc_tfilters_set(NMSetting * setting,
GVariant * connection_dict,
const char * property,
GVariant * value,
NMSettingParseFlags parse_flags,
GError ** error)
{
gs_unref_ptrarray GPtrArray *tfilters = NULL;
tfilters = _tfilters_from_variant(value);
g_object_set(setting, NM_SETTING_TC_CONFIG_TFILTERS, tfilters, NULL);
return TRUE;
}
/*****************************************************************************/
static void
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
NMSettingTCConfig *self = NM_SETTING_TC_CONFIG(object);
switch (prop_id) {
case PROP_QDISCS:
g_value_take_boxed(value,
_nm_utils_copy_array(self->qdiscs,
(NMUtilsCopyFunc) nm_tc_qdisc_dup,
(GDestroyNotify) nm_tc_qdisc_unref));
break;
case PROP_TFILTERS:
g_value_take_boxed(value,
_nm_utils_copy_array(self->tfilters,
(NMUtilsCopyFunc) nm_tc_tfilter_dup,
(GDestroyNotify) nm_tc_tfilter_unref));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static void
set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
NMSettingTCConfig *self = NM_SETTING_TC_CONFIG(object);
switch (prop_id) {
case PROP_QDISCS:
g_ptr_array_unref(self->qdiscs);
self->qdiscs = _nm_utils_copy_array(g_value_get_boxed(value),
(NMUtilsCopyFunc) nm_tc_qdisc_dup,
(GDestroyNotify) nm_tc_qdisc_unref);
break;
case PROP_TFILTERS:
g_ptr_array_unref(self->tfilters);
self->tfilters = _nm_utils_copy_array(g_value_get_boxed(value),
(NMUtilsCopyFunc) nm_tc_tfilter_dup,
(GDestroyNotify) nm_tc_tfilter_unref);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
nm_setting_tc_config_init(NMSettingTCConfig *self)
{
self->qdiscs = g_ptr_array_new_with_free_func((GDestroyNotify) nm_tc_qdisc_unref);
self->tfilters = g_ptr_array_new_with_free_func((GDestroyNotify) nm_tc_tfilter_unref);
}
/**
* nm_setting_tc_config_new:
*
* Creates a new #NMSettingTCConfig object with default values.
*
* Returns: (transfer full): the new empty #NMSettingTCConfig object
*
* Since: 1.12
**/
NMSetting *
nm_setting_tc_config_new(void)
{
return g_object_new(NM_TYPE_SETTING_TC_CONFIG, NULL);
}
static void
finalize(GObject *object)
{
NMSettingTCConfig *self = NM_SETTING_TC_CONFIG(object);
g_ptr_array_unref(self->qdiscs);
g_ptr_array_unref(self->tfilters);
G_OBJECT_CLASS(nm_setting_tc_config_parent_class)->finalize(object);
}
static void
nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
{
GObjectClass * object_class = G_OBJECT_CLASS(klass);
NMSettingClass *setting_class = NM_SETTING_CLASS(klass);
GArray * properties_override = _nm_sett_info_property_override_create_array();
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->finalize = finalize;
setting_class->compare_property = compare_property;
setting_class->verify = verify;
/**
* NMSettingTCConfig:qdiscs: (type GPtrArray(NMTCQdisc))
*
* Array of TC queueing disciplines.
**/
/* ---ifcfg-rh---
* property: qdiscs
* variable: QDISC1(+), QDISC2(+), ...
* description: Queueing disciplines
* example: QDISC1=ingress, QDISC2="root handle 1234: fq_codel"
* ---end---
*/
obj_properties[PROP_QDISCS] = g_param_spec_boxed(NM_SETTING_TC_CONFIG_QDISCS,
"",
"",
G_TYPE_PTR_ARRAY,
G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
| G_PARAM_STATIC_STRINGS);
_nm_properties_override_gobj(properties_override,
obj_properties[PROP_QDISCS],
NM_SETT_INFO_PROPERT_TYPE(.dbus_type = NM_G_VARIANT_TYPE("aa{sv}"),
.to_dbus_fcn = tc_qdiscs_get,
.from_dbus_fcn = tc_qdiscs_set, ));
/**
* NMSettingTCConfig:tfilters: (type GPtrArray(NMTCTfilter))
*
* Array of TC traffic filters.
**/
/* ---ifcfg-rh---
* property: qdiscs
* variable: FILTER1(+), FILTER2(+), ...
* description: Traffic filters
* example: FILTER1="parent ffff: matchall action simple sdata Input", ...
* ---end---
*/
obj_properties[PROP_TFILTERS] = g_param_spec_boxed(
NM_SETTING_TC_CONFIG_TFILTERS,
"",
"",
G_TYPE_PTR_ARRAY,
G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
_nm_properties_override_gobj(properties_override,
obj_properties[PROP_TFILTERS],
NM_SETT_INFO_PROPERT_TYPE(.dbus_type = NM_G_VARIANT_TYPE("aa{sv}"),
.to_dbus_fcn = tc_tfilters_get,
.from_dbus_fcn = tc_tfilters_set, ));
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit_full(setting_class,
NM_META_SETTING_TYPE_TC_CONFIG,
NULL,
properties_override);
}