From 6100b52e5ccc80b9c11e35907c0537d930f50da6 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 2 Nov 2020 17:29:26 +0100 Subject: [PATCH] libnm: add NMSettingOvsExternalIDs --- Makefile.am | 2 + .../generate-docs-nm-settings-nmcli.xml.in | 2 + clients/common/nm-meta-setting-desc.c | 2 + clients/common/settings-docs.h.in | 1 + docs/libnm/libnm-docs.xml | 1 + libnm-core/meson.build | 6 +- libnm-core/nm-core-internal.h | 4 + libnm-core/nm-core-types.h | 1 + libnm-core/nm-keyfile/nm-keyfile.c | 107 +++- .../nm-libnm-core-utils.h | 4 + libnm-core/nm-setting-ovs-external-ids.c | 536 ++++++++++++++++++ libnm-core/nm-setting-ovs-external-ids.h | 68 +++ libnm/libnm.ver | 7 + po/POTFILES.in | 1 + shared/nm-meta-setting.c | 8 + shared/nm-meta-setting.h | 1 + 16 files changed, 747 insertions(+), 4 deletions(-) create mode 100644 libnm-core/nm-setting-ovs-external-ids.c create mode 100644 libnm-core/nm-setting-ovs-external-ids.h diff --git a/Makefile.am b/Makefile.am index 473a763d74..dce16fe8c8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -954,6 +954,7 @@ libnm_core_lib_h_pub_real = \ libnm-core/nm-setting-olpc-mesh.h \ libnm-core/nm-setting-ovs-bridge.h \ libnm-core/nm-setting-ovs-dpdk.h \ + libnm-core/nm-setting-ovs-external-ids.h \ libnm-core/nm-setting-ovs-interface.h \ libnm-core/nm-setting-ovs-patch.h \ libnm-core/nm-setting-ovs-port.h \ @@ -1027,6 +1028,7 @@ libnm_core_lib_c_settings_real = \ libnm-core/nm-setting-olpc-mesh.c \ libnm-core/nm-setting-ovs-bridge.c \ libnm-core/nm-setting-ovs-dpdk.c \ + libnm-core/nm-setting-ovs-external-ids.c \ libnm-core/nm-setting-ovs-interface.c \ libnm-core/nm-setting-ovs-patch.c \ libnm-core/nm-setting-ovs-port.c \ diff --git a/clients/cli/generate-docs-nm-settings-nmcli.xml.in b/clients/cli/generate-docs-nm-settings-nmcli.xml.in index 25675a1ec7..f2f589fe8d 100644 --- a/clients/cli/generate-docs-nm-settings-nmcli.xml.in +++ b/clients/cli/generate-docs-nm-settings-nmcli.xml.in @@ -784,6 +784,8 @@ + + diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 2609c706fe..86b555fe5f 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -7946,6 +7946,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN) #define SETTING_PRETTY_NAME_OLPC_MESH N_("OLPC Mesh connection") #define SETTING_PRETTY_NAME_OVS_BRIDGE N_("Open vSwitch bridge settings") #define SETTING_PRETTY_NAME_OVS_DPDK N_("Open vSwitch DPDK interface settings") +#define SETTING_PRETTY_NAME_OVS_EXTERNAL_IDS N_("OVS External IDs") #define SETTING_PRETTY_NAME_OVS_INTERFACE N_("Open vSwitch interface settings") #define SETTING_PRETTY_NAME_OVS_PATCH N_("Open vSwitch patch interface settings") #define SETTING_PRETTY_NAME_OVS_PORT N_("Open vSwitch port settings") @@ -8129,6 +8130,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { ), ), SETTING_INFO (OVS_DPDK), + SETTING_INFO_EMPTY (OVS_EXTERNAL_IDS), SETTING_INFO (OVS_INTERFACE, .valid_parts = NM_META_SETTING_VALID_PARTS ( NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 5b93627245..78198ec973 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -294,6 +294,7 @@ #define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_RSTP_ENABLE N_("Enable or disable RSTP.") #define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_STP_ENABLE N_("Enable or disable STP.") #define DESCRIBE_DOC_NM_SETTING_OVS_DPDK_DEVARGS N_("Open vSwitch DPDK device arguments.") +#define DESCRIBE_DOC_NM_SETTING_OVS_EXTERNAL_IDS_DATA N_("A dictionary of key/value pairs with exernal-ids for OVS.") #define DESCRIBE_DOC_NM_SETTING_OVS_INTERFACE_TYPE N_("The interface type. Either \"internal\", \"system\", \"patch\", \"dpdk\", or empty.") #define DESCRIBE_DOC_NM_SETTING_OVS_PATCH_PEER N_("Specifies the name of the interface for the other side of the patch. The patch on the other side must also set this interface as peer.") #define DESCRIBE_DOC_NM_SETTING_OVS_PORT_BOND_DOWNDELAY N_("The time port must be inactive in order to be considered down.") diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml index 47b1fa1c08..d0be0eb475 100644 --- a/docs/libnm/libnm-docs.xml +++ b/docs/libnm/libnm-docs.xml @@ -338,6 +338,7 @@ print ("NetworkManager version " + client.get_version())]]> + diff --git a/libnm-core/meson.build b/libnm-core/meson.build index aa8823c991..7b59a8c204 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -43,8 +43,9 @@ libnm_core_headers = files( 'nm-setting-match.h', 'nm-setting-olpc-mesh.h', 'nm-setting-ovs-bridge.h', - 'nm-setting-ovs-interface.h', 'nm-setting-ovs-dpdk.h', + 'nm-setting-ovs-external-ids.h', + 'nm-setting-ovs-interface.h', 'nm-setting-ovs-patch.h', 'nm-setting-ovs-port.h', 'nm-setting-ppp.h', @@ -143,8 +144,9 @@ libnm_core_settings_sources = files( 'nm-setting-match.c', 'nm-setting-olpc-mesh.c', 'nm-setting-ovs-bridge.c', - 'nm-setting-ovs-interface.c', 'nm-setting-ovs-dpdk.c', + 'nm-setting-ovs-external-ids.c', + 'nm-setting-ovs-interface.c', 'nm-setting-ovs-patch.c', 'nm-setting-ovs-port.c', 'nm-setting-ppp.c', diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index f296a77fa6..fcc38565d3 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -476,6 +476,10 @@ GSList *_nm_vpn_plugin_info_list_load_dir(const char * dirname, /*****************************************************************************/ +GHashTable *_nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *self); + +/*****************************************************************************/ + typedef struct { const char *name; gboolean numeric; diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h index c2ac48e618..64d6464e2b 100644 --- a/libnm-core/nm-core-types.h +++ b/libnm-core/nm-core-types.h @@ -39,6 +39,7 @@ typedef struct _NMSettingMatch NMSettingMatch; typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh; typedef struct _NMSettingOvsBridge NMSettingOvsBridge; typedef struct _NMSettingOvsDpdk NMSettingOvsDpdk; +typedef struct _NMSettingOvsExternalIDs NMSettingOvsExternalIDs; typedef struct _NMSettingOvsInterface NMSettingOvsInterface; typedef struct _NMSettingOvsPatch NMSettingOvsPatch; typedef struct _NMSettingOvsPort NMSettingOvsPort; diff --git a/libnm-core/nm-keyfile/nm-keyfile.c b/libnm-core/nm-keyfile/nm-keyfile.c index 1042d2d604..d3a1357281 100644 --- a/libnm-core/nm-keyfile/nm-keyfile.c +++ b/libnm-core/nm-keyfile/nm-keyfile.c @@ -24,9 +24,14 @@ #include "nm-core-internal.h" #include "nm-keyfile.h" #include "nm-setting-user.h" +#include "nm-setting-ovs-external-ids.h" #include "nm-keyfile-utils.h" +#define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options" + +#define OVS_EXTERNAL_IDS_DATA_PREFIX "data." + /*****************************************************************************/ typedef struct _ParseInfoProperty ParseInfoProperty; @@ -989,6 +994,44 @@ ip_routing_rule_parser_full(KeyfileReaderInfo * info, } } +static void +_parser_full_ovs_external_ids_data(KeyfileReaderInfo * info, + const NMMetaSettingInfo * setting_info, + const NMSettInfoProperty *property_info, + const ParseInfoProperty * pip, + NMSetting * setting) +{ + const char * setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME; + gs_strfreev char **keys = NULL; + gsize n_keys; + gsize i; + + nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)); + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + nm_assert(nm_streq(setting_name, setting_info->setting_name)); + nm_assert(nm_streq(setting_name, nm_setting_get_name(setting))); + + keys = nm_keyfile_plugin_kf_get_keys(info->keyfile, setting_name, &n_keys, NULL); + + for (i = 0; i < n_keys; i++) { + const char * key = keys[i]; + gs_free char *name_to_free = NULL; + gs_free char *value = NULL; + const char * name; + + if (!NM_STR_HAS_PREFIX(key, OVS_EXTERNAL_IDS_DATA_PREFIX)) + continue; + + value = nm_keyfile_plugin_kf_get_string(info->keyfile, setting_name, key, NULL); + if (!value) + continue; + + name = &key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)]; + name = nm_keyfile_key_decode(name, &name_to_free); + nm_setting_ovs_external_ids_set_data(NM_SETTING_OVS_EXTERNAL_IDS(setting), name, value); + } +} + static void ip_dns_parser(KeyfileReaderInfo *info, NMSetting *setting, const char *key) { @@ -2214,8 +2257,6 @@ bridge_vlan_writer(KeyfileWriterInfo *info, } } -#define ETHERNET_S390_OPTIONS_GROUP_NAME "ethernet-s390-options" - static void wired_s390_options_parser_full(KeyfileReaderInfo * info, const NMMetaSettingInfo * setting_info, @@ -2360,6 +2401,60 @@ tfilter_writer(KeyfileWriterInfo *info, NMSetting *setting, const char *key, con } } +static void +_writer_full_ovs_external_ids_data(KeyfileWriterInfo * info, + const NMMetaSettingInfo * setting_info, + const NMSettInfoProperty *property_info, + const ParseInfoProperty * pip, + NMSetting * setting) +{ + GHashTable * hash; + NMUtilsNamedValue data_static[300u / sizeof(NMUtilsNamedValue)]; + gs_free NMUtilsNamedValue *data_free = NULL; + const NMUtilsNamedValue * data; + guint data_len; + char full_key_static[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + 300u]; + guint i; + + nm_assert(NM_IS_SETTING_OVS_EXTERNAL_IDS(setting)); + nm_assert(nm_streq(property_info->name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)); + + hash = _nm_setting_ovs_external_ids_get_data(NM_SETTING_OVS_EXTERNAL_IDS(setting)); + if (!hash) + return; + + data = nm_utils_named_values_from_strdict(hash, &data_len, data_static, &data_free); + if (data_len == 0) + return; + + memcpy(full_key_static, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)); + + for (i = 0; i < data_len; i++) { + const char * key = data[i].name; + const char * val = data[i].value_str; + gs_free char *escaped_key_to_free = NULL; + const char * escaped_key; + gsize len; + gs_free char *full_key_free = NULL; + char * full_key = full_key_static; + + escaped_key = nm_keyfile_key_encode(key, &escaped_key_to_free); + + len = strlen(escaped_key) + 1u; + if (len >= G_N_ELEMENTS(full_key_static) - NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)) { + full_key_free = g_new(char, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX) + len); + full_key = full_key_free; + memcpy(full_key, OVS_EXTERNAL_IDS_DATA_PREFIX, NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)); + } + memcpy(&full_key[NM_STRLEN(OVS_EXTERNAL_IDS_DATA_PREFIX)], escaped_key, len); + + nm_keyfile_plugin_kf_set_string(info->keyfile, + NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME, + full_key, + val); + } +} + static void write_hash_of_string(GKeyFile *file, NMSetting *setting, const char *key, const GValue *value) { @@ -2799,6 +2894,14 @@ static const ParseInfoSetting *const parse_infos[_NM_META_SETTING_TYPE_NUM] = { .writer_full = ip_routing_rule_writer_full, .has_parser_full = TRUE, .has_writer_full = TRUE, ), ), ), + PARSE_INFO_SETTING( + NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_OVS_EXTERNAL_IDS_DATA, + .parser_no_check_key = TRUE, + .parser_full = _parser_full_ovs_external_ids_data, + .writer_full = _writer_full_ovs_external_ids_data, + .has_parser_full = TRUE, + .has_writer_full = TRUE, ), ), ), PARSE_INFO_SETTING(NM_META_SETTING_TYPE_SERIAL, PARSE_INFO_PROPERTIES(PARSE_INFO_PROPERTY(NM_SETTING_SERIAL_PARITY, .parser = parity_parser, ), ), ), diff --git a/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h b/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h index ca29eade24..2361bf8c30 100644 --- a/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h +++ b/libnm-core/nm-libnm-core-intern/nm-libnm-core-utils.h @@ -98,6 +98,10 @@ gboolean nm_utils_vlan_priority_map_parse_str(NMVlanPriorityMap map_type, /*****************************************************************************/ +#define NM_OVS_EXTERNAL_ID_NM_PREFIX "NM." + +/*****************************************************************************/ + static inline int nm_setting_ip_config_get_addr_family(NMSettingIPConfig *s_ip) { diff --git a/libnm-core/nm-setting-ovs-external-ids.c b/libnm-core/nm-setting-ovs-external-ids.c new file mode 100644 index 0000000000..90fda48317 --- /dev/null +++ b/libnm-core/nm-setting-ovs-external-ids.c @@ -0,0 +1,536 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (C) 2017 - 2020 Red Hat, Inc. + */ + +#include "nm-default.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) { + const char *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)) { + 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")); + 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) +{ + 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 + */ +gpointer +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); +} diff --git a/libnm-core/nm-setting-ovs-external-ids.h b/libnm-core/nm-setting-ovs-external-ids.h new file mode 100644 index 0000000000..0f2442b48f --- /dev/null +++ b/libnm-core/nm-setting-ovs-external-ids.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/* + * Copyright (C) 2017 - 2020 Red Hat, Inc. + */ + +#ifndef __NM_SETTING_OVS_EXTERNAL_IDS_H__ +#define __NM_SETTING_OVS_EXTERNAL_IDS_H__ + +#if !defined(__NETWORKMANAGER_H_INSIDE__) && !defined(NETWORKMANAGER_COMPILATION) + #error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_OVS_EXTERNAL_IDS (nm_setting_ovs_external_ids_get_type()) +#define NM_SETTING_OVS_EXTERNAL_IDS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NM_TYPE_SETTING_OVS_EXTERNAL_IDS, NMSettingOvsExternalIDs)) +#define NM_SETTING_OVS_EXTERNAL_IDS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + NM_TYPE_SETTING_OVS_EXTERNAL_IDS, \ + NMSettingOvsExternalIDsClass)) +#define NM_IS_SETTING_OVS_EXTERNAL_IDS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), NM_TYPE_SETTING_OVS_EXTERNAL_IDS)) +#define NM_IS_SETTING_OVS_EXTERNAL_IDS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), NM_TYPE_SETTING_OVS_EXTERNAL_IDS)) +#define NM_SETTING_OVS_EXTERNAL_IDS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), \ + NM_TYPE_SETTING_OVS_EXTERNAL_IDS, \ + NMSettingOvsExternalIDsClass)) + +#define NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME "ovs-external-ids" + +#define NM_SETTING_OVS_EXTERNAL_IDS_DATA "data" + +typedef struct _NMSettingOvsExternalIDsClass NMSettingOvsExternalIDsClass; + +NM_AVAILABLE_IN_1_30 +GType nm_setting_ovs_external_ids_get_type(void); + +NM_AVAILABLE_IN_1_30 +void *nm_setting_ovs_external_ids_new(void); + +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_30 +const char *const *nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting, + guint * out_len); + +NM_AVAILABLE_IN_1_30 +const char *nm_setting_ovs_external_ids_get_data(NMSettingOvsExternalIDs *setting, const char *key); + +NM_AVAILABLE_IN_1_30 +void nm_setting_ovs_external_ids_set_data(NMSettingOvsExternalIDs *setting, + const char * key, + const char * val); + +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_30 +gboolean nm_setting_ovs_external_ids_check_key(const char *key, GError **error); +NM_AVAILABLE_IN_1_30 +gboolean nm_setting_ovs_external_ids_check_val(const char *val, GError **error); + +G_END_DECLS + +#endif /* __NM_SETTING_OVS_EXTERNAL_IDS_H__ */ diff --git a/libnm/libnm.ver b/libnm/libnm.ver index baf1e06f86..0086fc01d2 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1766,5 +1766,12 @@ global: nm_keyfile_read; nm_keyfile_warn_severity_get_type; nm_keyfile_write; + nm_setting_ovs_external_ids_check_key; + nm_setting_ovs_external_ids_check_val; + nm_setting_ovs_external_ids_get_data; + nm_setting_ovs_external_ids_get_data_keys; + nm_setting_ovs_external_ids_get_type; + nm_setting_ovs_external_ids_new; + nm_setting_ovs_external_ids_set_data; nm_utils_print; } libnm_1_28_0; diff --git a/po/POTFILES.in b/po/POTFILES.in index ea2eafa3f3..f2718ac648 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -82,6 +82,7 @@ libnm-core/nm-setting-macvlan.c libnm-core/nm-setting-match.c libnm-core/nm-setting-olpc-mesh.c libnm-core/nm-setting-ovs-bridge.c +libnm-core/nm-setting-ovs-external-ids.c libnm-core/nm-setting-ovs-interface.c libnm-core/nm-setting-ovs-patch.c libnm-core/nm-setting-ovs-port.c diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index 51b90e6f05..5356b9a5de 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -33,6 +33,7 @@ #include "nm-setting-ovs-bridge.h" #include "nm-setting-ovs-interface.h" #include "nm-setting-ovs-dpdk.h" +#include "nm-setting-ovs-external-ids.h" #include "nm-setting-ovs-patch.h" #include "nm-setting-ovs-port.h" #include "nm-setting-ppp.h" @@ -312,6 +313,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_OVS_DPDK_SETTING_NAME, .get_setting_gtype = nm_setting_ovs_dpdk_get_type, }, + [NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS] = + { + .meta_type = NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_OVS_EXTERNAL_IDS_SETTING_NAME, + .get_setting_gtype = nm_setting_ovs_external_ids_get_type, + }, [NM_META_SETTING_TYPE_OVS_INTERFACE] = { .meta_type = NM_META_SETTING_TYPE_OVS_INTERFACE, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index dadf2f7217..9d268e0ec2 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -128,6 +128,7 @@ typedef enum { NM_META_SETTING_TYPE_MATCH, NM_META_SETTING_TYPE_OVS_BRIDGE, NM_META_SETTING_TYPE_OVS_DPDK, + NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS, NM_META_SETTING_TYPE_OVS_INTERFACE, NM_META_SETTING_TYPE_OVS_PATCH, NM_META_SETTING_TYPE_OVS_PORT,