mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-03 06:38:05 +02:00
"libnm-core/" is rather complicated. It provides a static library that
is linked into libnm.so and NetworkManager. It also contains public
headers (like "nm-setting.h") which are part of public libnm API.
Then we have helper libraries ("libnm-core/nm-libnm-core-*/") which
only rely on public API of libnm-core, but are themself static
libraries that can be used by anybody who uses libnm-core. And
"libnm-core/nm-libnm-core-intern" is used by libnm-core itself.
Move "libnm-core/" to "src/". But also split it in different
directories so that they have a clearer purpose.
The goal is to have a flat directory hierarchy. The "src/libnm-core*/"
directories correspond to the different modules (static libraries and set
of headers that we have). We have different kinds of such modules because
of how we combine various code together. The directory layout now reflects
this.
551 lines
18 KiB
C
551 lines
18 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
/*
|
|
* Copyright (C) 2017 - 2020 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "libnm-core-impl/nm-default-libnm-core.h"
|
|
|
|
#include "nm-setting-ovs-external-ids.h"
|
|
|
|
#include "nm-setting-private.h"
|
|
#include "nm-utils-private.h"
|
|
#include "nm-connection-private.h"
|
|
|
|
#define MAX_NUM_KEYS 256
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* SECTION:nm-setting-ovs-external-ids
|
|
* @short_description: External-IDs for OVS database
|
|
*
|
|
* The #NMSettingOvsExternalIDs object is a #NMSetting subclass that allow to
|
|
* configure external ids for OVS.
|
|
**/
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_GOBJECT_PROPERTIES_DEFINE(NMSettingOvsExternalIDs, PROP_DATA, );
|
|
|
|
typedef struct {
|
|
GHashTable * data;
|
|
const char **data_keys;
|
|
} NMSettingOvsExternalIDsPrivate;
|
|
|
|
/**
|
|
* NMSettingOvsExternalIDs:
|
|
*
|
|
* OVS External IDs Settings
|
|
*/
|
|
struct _NMSettingOvsExternalIDs {
|
|
NMSetting parent;
|
|
NMSettingOvsExternalIDsPrivate _priv;
|
|
};
|
|
|
|
struct _NMSettingOvsExternalIDsClass {
|
|
NMSettingClass parent;
|
|
};
|
|
|
|
G_DEFINE_TYPE(NMSettingOvsExternalIDs, nm_setting_ovs_external_ids, NM_TYPE_SETTING)
|
|
|
|
#define NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self) \
|
|
_NM_GET_PRIVATE(self, NMSettingOvsExternalIDs, NM_IS_SETTING_OVS_EXTERNAL_IDS)
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_exid_key_char_is_regular(char ch)
|
|
{
|
|
/* allow words of printable characters, plus some
|
|
* special characters, for example to support base64 encoding. */
|
|
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
|
|
|| NM_IN_SET(ch, '-', '_', '+', '/', '=', '.');
|
|
}
|
|
|
|
/**
|
|
* nm_setting_ovs_external_ids_check_key:
|
|
* @key: (allow-none): the key to check
|
|
* @error: a #GError, %NULL to ignore.
|
|
*
|
|
* Checks whether @key is a valid key for OVS' external-ids.
|
|
* This means, the key cannot be %NULL, not too large and valid ASCII.
|
|
* Also, only digits and numbers are allowed with a few special
|
|
* characters. They key must also not start with "NM.".
|
|
*
|
|
* Since: 1.30
|
|
*
|
|
* Returns: %TRUE if @key is a valid user data key.
|
|
*/
|
|
gboolean
|
|
nm_setting_ovs_external_ids_check_key(const char *key, GError **error)
|
|
{
|
|
gsize len;
|
|
|
|
g_return_val_if_fail(!error || !*error, FALSE);
|
|
|
|
if (!key || !key[0]) {
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("missing key"));
|
|
return FALSE;
|
|
}
|
|
len = strlen(key);
|
|
if (len > 255u) {
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("key is too long"));
|
|
return FALSE;
|
|
}
|
|
if (!g_utf8_validate(key, len, NULL)) {
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("key must be UTF8"));
|
|
return FALSE;
|
|
}
|
|
if (!NM_STRCHAR_ALL(key, ch, _exid_key_char_is_regular(ch))) {
|
|
/* Probably OVS is more forgiving about what makes a valid key for
|
|
* an external-id. However, we are strict (at least, for now). */
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("key contains invalid characters"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (NM_STR_HAS_PREFIX(key, NM_OVS_EXTERNAL_ID_NM_PREFIX)) {
|
|
/* these keys are reserved. */
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("key cannot start with \"NM.\""));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_setting_ovs_external_ids_check_val:
|
|
* @val: (allow-none): the value to check
|
|
* @error: a #GError, %NULL to ignore.
|
|
*
|
|
* Checks whether @val is a valid user data value. This means,
|
|
* value is not %NULL, not too large and valid UTF-8.
|
|
*
|
|
* Since: 1.30
|
|
*
|
|
* Returns: %TRUE if @val is a valid user data value.
|
|
*/
|
|
gboolean
|
|
nm_setting_ovs_external_ids_check_val(const char *val, GError **error)
|
|
{
|
|
gsize len;
|
|
|
|
g_return_val_if_fail(!error || !*error, FALSE);
|
|
|
|
if (!val) {
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("value is missing"));
|
|
return FALSE;
|
|
}
|
|
|
|
len = strlen(val);
|
|
if (len > (8u * 1024u)) {
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("value is too large"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!g_utf8_validate(val, len, NULL)) {
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("value is not valid UTF8"));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static GHashTable *
|
|
_create_data_hash(void)
|
|
{
|
|
return g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
|
|
}
|
|
|
|
GHashTable *
|
|
_nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *self)
|
|
{
|
|
return NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self)->data;
|
|
}
|
|
|
|
/**
|
|
* nm_setting_ovs_external_ids_get_data_keys:
|
|
* @setting: the #NMSettingOvsExternalIDs
|
|
* @out_len: (out): the length of the returned array
|
|
*
|
|
* Returns: (array length=out_len) (transfer none): a
|
|
* %NULL-terminated array containing each key from the table.
|
|
**/
|
|
const char *const *
|
|
nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting, guint *out_len)
|
|
{
|
|
NMSettingOvsExternalIDs * self = setting;
|
|
NMSettingOvsExternalIDsPrivate *priv;
|
|
|
|
g_return_val_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self), NULL);
|
|
|
|
priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
|
|
|
|
if (priv->data_keys) {
|
|
NM_SET_OUT(out_len, g_hash_table_size(priv->data));
|
|
return priv->data_keys;
|
|
}
|
|
|
|
priv->data_keys = nm_utils_strdict_get_keys(priv->data, TRUE, out_len);
|
|
|
|
/* don't return %NULL, but hijack the @data_keys fields as a pseudo
|
|
* empty strv array. */
|
|
return priv->data_keys ?: ((const char **) &priv->data_keys);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_setting_ovs_external_ids_get_data:
|
|
* @setting: the #NMSettingOvsExternalIDs instance
|
|
* @key: the external-id to lookup
|
|
*
|
|
* Since: 1.30
|
|
*
|
|
* Returns: (transfer none): the value associated with @key or %NULL if no such
|
|
* value exists.
|
|
*/
|
|
const char *
|
|
nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *setting, const char *key)
|
|
{
|
|
NMSettingOvsExternalIDs * self = setting;
|
|
NMSettingOvsExternalIDsPrivate *priv;
|
|
|
|
g_return_val_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self), NULL);
|
|
g_return_val_if_fail(key, NULL);
|
|
|
|
priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
|
|
|
|
if (!priv->data)
|
|
return NULL;
|
|
|
|
return g_hash_table_lookup(priv->data, key);
|
|
}
|
|
|
|
/**
|
|
* nm_setting_ovs_external_ids_set_data:
|
|
* @setting: the #NMSettingOvsExternalIDs instance
|
|
* @key: the key to set
|
|
* @val: (allow-none): the value to set or %NULL to clear a key.
|
|
*
|
|
* Since: 1.30
|
|
*/
|
|
void
|
|
nm_setting_ovs_external_ids_set_data(NMSettingOvsExternalIDs *setting,
|
|
const char * key,
|
|
const char * val)
|
|
{
|
|
NMSettingOvsExternalIDs * self = setting;
|
|
NMSettingOvsExternalIDsPrivate *priv;
|
|
|
|
g_return_if_fail(NM_IS_SETTING_OVS_EXTERNAL_IDS(self));
|
|
|
|
priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
|
|
|
|
if (!val) {
|
|
if (priv->data && g_hash_table_remove(priv->data, key))
|
|
goto out_changed;
|
|
return;
|
|
}
|
|
|
|
if (priv->data) {
|
|
const char *val2;
|
|
|
|
if (g_hash_table_lookup_extended(priv->data, key, NULL, (gpointer *) &val2)) {
|
|
if (nm_streq(val, val2))
|
|
return;
|
|
}
|
|
} else
|
|
priv->data = _create_data_hash();
|
|
|
|
g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val));
|
|
|
|
out_changed:
|
|
nm_clear_g_free(&priv->data_keys);
|
|
_notify(self, PROP_DATA);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
verify(NMSetting *setting, NMConnection *connection, GError **error)
|
|
{
|
|
NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(setting);
|
|
NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
|
|
|
|
if (priv->data) {
|
|
gs_free_error GError *local = NULL;
|
|
GHashTableIter iter;
|
|
const char * key;
|
|
const char * val;
|
|
|
|
g_hash_table_iter_init(&iter, priv->data);
|
|
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val)) {
|
|
if (!nm_setting_ovs_external_ids_check_key(key, &local)) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_FAILED,
|
|
_("invalid key \"%s\": %s"),
|
|
key,
|
|
local->message);
|
|
} else if (!nm_setting_ovs_external_ids_check_val(val, &local)) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_FAILED,
|
|
_("invalid value for \"%s\": %s"),
|
|
key,
|
|
local->message);
|
|
} else
|
|
continue;
|
|
g_prefix_error(error,
|
|
"%s.%s: ",
|
|
NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
|
|
NM_SETTING_OVS_EXTERNAL_IDS_DATA);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (priv->data && g_hash_table_size(priv->data) > MAX_NUM_KEYS) {
|
|
g_set_error(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("maximum number of user data entries reached (%u instead of %u)"),
|
|
g_hash_table_size(priv->data),
|
|
(unsigned) MAX_NUM_KEYS);
|
|
g_prefix_error(error,
|
|
"%s.%s: ",
|
|
NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME,
|
|
NM_SETTING_OVS_EXTERNAL_IDS_DATA);
|
|
return FALSE;
|
|
}
|
|
|
|
if (connection) {
|
|
NMSettingConnection *s_con;
|
|
const char * type;
|
|
const char * slave_type;
|
|
|
|
type = nm_connection_get_connection_type(connection);
|
|
if (!type) {
|
|
NMSetting *s_base;
|
|
|
|
s_base = _nm_connection_find_base_type_setting(connection);
|
|
if (s_base)
|
|
type = nm_setting_get_name(s_base);
|
|
}
|
|
if (NM_IN_STRSET(type,
|
|
NM_SETTING_OVS_BRIDGE_SETTING_NAME,
|
|
NM_SETTING_OVS_PORT_SETTING_NAME,
|
|
NM_SETTING_OVS_INTERFACE_SETTING_NAME))
|
|
goto connection_type_is_good;
|
|
|
|
if ((s_con = nm_connection_get_setting_connection(connection))
|
|
&& _nm_connection_detect_slave_type_full(s_con,
|
|
connection,
|
|
&slave_type,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL)
|
|
&& nm_streq0(slave_type, NM_SETTING_OVS_PORT_SETTING_NAME))
|
|
goto connection_type_is_good;
|
|
|
|
g_set_error_literal(error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("OVS external IDs can only be added to a profile of type OVS "
|
|
"bridge/port/interface or to OVS system interface"));
|
|
return FALSE;
|
|
}
|
|
connection_type_is_good:
|
|
|
|
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)
|
|
{
|
|
NMSettingOvsExternalIDsPrivate *priv;
|
|
NMSettingOvsExternalIDsPrivate *pri2;
|
|
|
|
if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)) {
|
|
if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
|
|
return NM_TERNARY_DEFAULT;
|
|
|
|
if (!set_b)
|
|
return TRUE;
|
|
|
|
priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_a));
|
|
pri2 = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_b));
|
|
return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal);
|
|
}
|
|
|
|
return NM_SETTING_CLASS(nm_setting_ovs_external_ids_parent_class)
|
|
->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(object);
|
|
NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
|
|
GHashTableIter iter;
|
|
GHashTable * data;
|
|
const char * key;
|
|
const char * val;
|
|
|
|
switch (prop_id) {
|
|
case PROP_DATA:
|
|
data = _create_data_hash();
|
|
if (priv->data) {
|
|
g_hash_table_iter_init(&iter, priv->data);
|
|
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val))
|
|
g_hash_table_insert(data, g_strdup(key), g_strdup(val));
|
|
}
|
|
g_value_take_boxed(value, data);
|
|
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)
|
|
{
|
|
NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(object);
|
|
NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DATA:
|
|
{
|
|
gs_unref_hashtable GHashTable *old = NULL;
|
|
GHashTableIter iter;
|
|
GHashTable * data;
|
|
const char * key;
|
|
const char * val;
|
|
|
|
nm_clear_g_free(&priv->data_keys);
|
|
|
|
old = g_steal_pointer(&priv->data);
|
|
|
|
data = g_value_get_boxed(value);
|
|
if (nm_g_hash_table_size(data) <= 0)
|
|
return;
|
|
|
|
priv->data = _create_data_hash();
|
|
g_hash_table_iter_init(&iter, data);
|
|
while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &val))
|
|
g_hash_table_insert(priv->data, g_strdup(key), g_strdup(val));
|
|
break;
|
|
}
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_setting_ovs_external_ids_init(NMSettingOvsExternalIDs *self)
|
|
{}
|
|
|
|
/**
|
|
* nm_setting_ovs_external_ids_new:
|
|
*
|
|
* Creates a new #NMSettingOvsExternalIDs object with default values.
|
|
*
|
|
* Returns: (transfer full) (type NMSettingOvsExternalIDs): the new empty
|
|
* #NMSettingOvsExternalIDs object
|
|
*
|
|
* Since: 1.30
|
|
*/
|
|
NMSetting *
|
|
nm_setting_ovs_external_ids_new(void)
|
|
{
|
|
return g_object_new(NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NULL);
|
|
}
|
|
|
|
static void
|
|
finalize(GObject *object)
|
|
{
|
|
NMSettingOvsExternalIDs * self = NM_SETTING_OVS_EXTERNAL_IDS(object);
|
|
NMSettingOvsExternalIDsPrivate *priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(self);
|
|
|
|
g_free(priv->data_keys);
|
|
if (priv->data)
|
|
g_hash_table_unref(priv->data);
|
|
|
|
G_OBJECT_CLASS(nm_setting_ovs_external_ids_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
nm_setting_ovs_external_ids_class_init(NMSettingOvsExternalIDsClass *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;
|
|
|
|
/**
|
|
* NMSettingOvsExternalIDs:data: (type GHashTable(utf8,utf8))
|
|
*
|
|
* A dictionary of key/value pairs with exernal-ids for OVS.
|
|
*
|
|
* Since: 1.30
|
|
**/
|
|
obj_properties[PROP_DATA] = g_param_spec_boxed(NM_SETTING_OVS_EXTERNAL_IDS_DATA,
|
|
"",
|
|
"",
|
|
G_TYPE_HASH_TABLE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
_nm_properties_override_gobj(properties_override,
|
|
obj_properties[PROP_DATA],
|
|
&nm_sett_info_propert_type_strdict);
|
|
|
|
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
|
|
|
|
_nm_setting_class_commit_full(setting_class,
|
|
NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
|
|
NULL,
|
|
properties_override);
|
|
}
|