diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2018-05-25 12:05:24 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2018-07-11 16:16:22 +0200 |
commit | a9b4532fa77d75f2dd40cbbd2a5184df6ec0d387 (patch) | |
tree | 79f09f47a61403d671df1cab07b62cdad4184ee3 | |
parent | d6483592b8a69b8f5e78e455f40b75b912037f34 (diff) | |
download | NetworkManager-a9b4532fa77d75f2dd40cbbd2a5184df6ec0d387.tar.gz |
libnm-core: add SR-IOV setting
Add a setting containing SR-IOV parameters.
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | clients/common/settings-docs.h.in | 4 | ||||
-rw-r--r-- | docs/libnm/libnm-docs.xml | 1 | ||||
-rw-r--r-- | libnm-core/meson.build | 2 | ||||
-rw-r--r-- | libnm-core/nm-connection.c | 29 | ||||
-rw-r--r-- | libnm-core/nm-connection.h | 2 | ||||
-rw-r--r-- | libnm-core/nm-core-internal.h | 7 | ||||
-rw-r--r-- | libnm-core/nm-core-types.h | 1 | ||||
-rw-r--r-- | libnm-core/nm-keyfile.c | 83 | ||||
-rw-r--r-- | libnm-core/nm-setting-sriov.c | 1331 | ||||
-rw-r--r-- | libnm-core/nm-setting-sriov.h | 123 | ||||
-rw-r--r-- | libnm-core/nm-setting.c | 1 | ||||
-rw-r--r-- | libnm-core/nm-utils-private.h | 1 | ||||
-rw-r--r-- | libnm-core/nm-utils.c | 243 | ||||
-rw-r--r-- | libnm-core/nm-utils.h | 11 | ||||
-rw-r--r-- | libnm-core/tests/test-setting.c | 236 | ||||
-rw-r--r-- | libnm/NetworkManager.h | 1 | ||||
-rw-r--r-- | libnm/libnm.ver | 31 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
19 files changed, 2109 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 53235df7e1..ab9f649a85 100644 --- a/Makefile.am +++ b/Makefile.am @@ -432,6 +432,7 @@ libnm_core_lib_h_pub_real = \ libnm-core/nm-setting-pppoe.h \ libnm-core/nm-setting-proxy.h \ libnm-core/nm-setting-serial.h \ + libnm-core/nm-setting-sriov.h \ libnm-core/nm-setting-tc-config.h \ libnm-core/nm-setting-team-port.h \ libnm-core/nm-setting-team.h \ @@ -502,6 +503,7 @@ libnm_core_lib_c_settings_real = \ libnm-core/nm-setting-pppoe.c \ libnm-core/nm-setting-proxy.c \ libnm-core/nm-setting-serial.c \ + libnm-core/nm-setting-sriov.c \ libnm-core/nm-setting-tc-config.c \ libnm-core/nm-setting-team-port.c \ libnm-core/nm-setting-team.c \ diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index 30b48a9cf8..886c8f0c70 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -323,6 +323,10 @@ #define DESCRIBE_DOC_NM_SETTING_SERIAL_PARITY N_("Parity setting of the serial port.") #define DESCRIBE_DOC_NM_SETTING_SERIAL_SEND_DELAY N_("Time to delay between each byte sent to the modem, in microseconds.") #define DESCRIBE_DOC_NM_SETTING_SERIAL_STOPBITS N_("Number of stop bits for communication on the serial port. Either 1 or 2. The 1 in \"8n1\" for example.") +#define DESCRIBE_DOC_NM_SETTING_SRIOV_AUTOPROBE_DRIVERS N_("Whether to autoprobe virtual functions by a compatible driver. If set to NM_TERNARY_TRUE (1), the kernel will try to bind VFs to a compatible driver and if this succeeds a new network interface will be instantiated for each VF. If set to NM_TERNARY_FALSE (0), VFs will not be claimed and no network interfaces will be created for them. When set to NM_TERNARY_DEFAULT (-1), the global default is used; in case the global default is unspecified it is assumed to be NM_TERNARY_TRUE (1).") +#define DESCRIBE_DOC_NM_SETTING_SRIOV_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") +#define DESCRIBE_DOC_NM_SETTING_SRIOV_TOTAL_VFS N_("The total number of virtual functions to create.") +#define DESCRIBE_DOC_NM_SETTING_SRIOV_VFS N_("Array of virtual function descriptors. Each VF descriptor is a dictionary mapping attribute names to GVariant values. The 'index' entry is mandatory for each VF. When represented as string a VF is in the form: \"INDEX [ATTR=VALUE[ ATTR=VALUE]...]\". for example: \"2 mac=00:11:22:33:44:55 spoof-check=true\". The \"vlans\" attribute is represented as a semicolor-separated list of VLAN descriptors, where each descriptor has the form \"ID[.PRIORITY[.PROTO]]\". PROTO can be either 'q' for 802.1Q (the default) or 'ad' for 802.1ad.") #define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_NAME N_("The setting's name, which uniquely identifies the setting within the connection. Each setting type has a name unique to that type, for example \"ppp\" or \"wireless\" or \"wired\".") #define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS N_("Array of TC queueing disciplines.") #define DESCRIBE_DOC_NM_SETTING_TC_CONFIG_TFILTERS N_("Array of TC traffic filters.") diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml index 14d9636e26..bdb3c2daec 100644 --- a/docs/libnm/libnm-docs.xml +++ b/docs/libnm/libnm-docs.xml @@ -220,6 +220,7 @@ print ("NetworkManager version " + client.get_version())]]></programlisting></in <xi:include href="xml/nm-setting-ppp.xml"/> <xi:include href="xml/nm-setting-proxy.xml"/> <xi:include href="xml/nm-setting-serial.xml"/> + <xi:include href="xml/nm-setting-sriov.xml"/> <xi:include href="xml/nm-setting-tc-config.xml"/> <xi:include href="xml/nm-setting-team-port.xml"/> <xi:include href="xml/nm-setting-team.xml"/> diff --git a/libnm-core/meson.build b/libnm-core/meson.build index 39ea608c44..edee801ab5 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -34,6 +34,7 @@ libnm_core_headers = files( 'nm-setting-pppoe.h', 'nm-setting-proxy.h', 'nm-setting-serial.h', + 'nm-setting-sriov.h', 'nm-setting-tc-config.h', 'nm-setting-team-port.h', 'nm-setting-team.h', @@ -86,6 +87,7 @@ libnm_core_settings_sources = files( 'nm-setting-pppoe.c', 'nm-setting-proxy.c', 'nm-setting-serial.c', + 'nm-setting-sriov.c', 'nm-setting-tc-config.c', 'nm-setting-team-port.c', 'nm-setting-team.c', diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index 07b72c169c..2f8cb4e905 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -1178,6 +1178,18 @@ _normalize_ip_tunnel_wired_setting (NMConnection *self, GHashTable *parameters) } static gboolean +_normalize_sriov_vf_order (NMConnection *self, GHashTable *parameters) +{ + NMSettingSriov *s_sriov; + + s_sriov = nm_connection_get_setting_sriov (self); + if (!s_sriov) + return FALSE; + + return _nm_setting_sriov_sort_vfs (s_sriov); +} + +static gboolean _normalize_required_settings (NMConnection *self, GHashTable *parameters) { NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self); @@ -1523,6 +1535,7 @@ nm_connection_normalize (NMConnection *connection, was_modified |= _normalize_bluetooth_type (connection, parameters); was_modified |= _normalize_ovs_interface_type (connection, parameters); was_modified |= _normalize_ip_tunnel_wired_setting (connection, parameters); + was_modified |= _normalize_sriov_vf_order (connection, parameters); /* Verify anew. */ success = _nm_connection_verify (connection, error); @@ -2627,6 +2640,22 @@ nm_connection_get_setting_serial (NMConnection *connection) } /** + * nm_connection_get_setting_sriov: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingSriov the connection might contain. + * + * Returns: (transfer none): an #NMSettingSriov if the connection contains one, otherwise %NULL + * + * Since: 1.14 + **/ +NMSettingSriov * +nm_connection_get_setting_sriov (NMConnection *connection) +{ + return _connection_get_setting_check (connection, NM_TYPE_SETTING_SRIOV); +} + +/** * nm_connection_get_setting_tc_config: * @connection: the #NMConnection * diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h index 8f65e9fce9..312760f157 100644 --- a/libnm-core/nm-connection.h +++ b/libnm-core/nm-connection.h @@ -231,6 +231,8 @@ NMSettingPppoe * nm_connection_get_setting_pppoe (NMConnec NM_AVAILABLE_IN_1_6 NMSettingProxy * nm_connection_get_setting_proxy (NMConnection *connection); NMSettingSerial * nm_connection_get_setting_serial (NMConnection *connection); +NM_AVAILABLE_IN_1_14 +NMSettingSriov * nm_connection_get_setting_sriov (NMConnection *connection); NM_AVAILABLE_IN_1_12 NMSettingTCConfig * nm_connection_get_setting_tc_config (NMConnection *connection); NMSettingTun * nm_connection_get_setting_tun (NMConnection *connection); diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 8806e2ba53..3808a9e61b 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -65,6 +65,7 @@ #include "nm-setting-ppp.h" #include "nm-setting-pppoe.h" #include "nm-setting-serial.h" +#include "nm-setting-sriov.h" #include "nm-setting-tc-config.h" #include "nm-setting-team-port.h" #include "nm-setting-team.h" @@ -226,6 +227,9 @@ gboolean _nm_ip_route_attribute_validate_all (const NMIPRoute *route); const char **_nm_ip_route_get_attribute_names (const NMIPRoute *route, gboolean sorted, guint *out_length); GHashTable *_nm_ip_route_get_attributes_direct (NMIPRoute *route); +NMSriovVF *_nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError **error); +gboolean _nm_sriov_vf_attribute_validate_all (const NMSriovVF *vf, GError **error); + static inline void _nm_auto_ip_route_unref (NMIPRoute **v) { @@ -513,4 +517,7 @@ _nm_connection_type_is_master (const char *type) gboolean _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin); /*****************************************************************************/ + +gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting); + #endif diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h index 73ba579489..622b104f60 100644 --- a/libnm-core/nm-core-types.h +++ b/libnm-core/nm-core-types.h @@ -58,6 +58,7 @@ typedef struct _NMSettingOvsPort NMSettingOvsPort; typedef struct _NMSettingPpp NMSettingPpp; typedef struct _NMSettingPppoe NMSettingPppoe; typedef struct _NMSettingSerial NMSettingSerial; +typedef struct _NMSettingSriov NMSettingSriov; typedef struct _NMSettingTCConfig NMSettingTCConfig; typedef struct _NMSettingTeam NMSettingTeam; typedef struct _NMSettingTeamPort NMSettingTeamPort; diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c index 17741f5f14..dbc0b187f1 100644 --- a/libnm-core/nm-keyfile.c +++ b/libnm-core/nm-keyfile.c @@ -119,6 +119,49 @@ setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *k } static void +sriov_vfs_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + const char *setting_name = nm_setting_get_name (setting); + gs_unref_ptrarray GPtrArray *vfs = NULL; + gs_strfreev char **keys = NULL; + gsize n_keys = 0; + int i; + + keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); + if (!keys || n_keys == 0) + return; + + vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref); + + for (i = 0; i < n_keys; i++) { + gs_free char *value = NULL; + NMSriovVF *vf; + const char *rest; + + if (!g_str_has_prefix (keys[i], "vf.")) + continue; + + rest = &keys[i][3]; + + if (!NM_STRCHAR_ALL (rest, ch, g_ascii_isdigit (ch))) + continue; + + value = nm_keyfile_plugin_kf_get_string (info->keyfile, + setting_name, + keys[i], + NULL); + + vf = _nm_utils_sriov_vf_from_strparts (rest, value, NULL); + if (vf) + g_ptr_array_add (vfs, vf); + } + + g_object_set (G_OBJECT (setting), + key, vfs, + NULL); +} + +static void read_array_of_uint (GKeyFile *file, NMSetting *setting, const char *key) @@ -1510,6 +1553,37 @@ setting_alias_writer (KeyfileWriterInfo *info, } static void +sriov_vfs_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + GPtrArray *vfs; + guint i; + + vfs = g_value_get_boxed (value); + if (!vfs) + return; + + for (i = 0; i < vfs->len; i++) { + const NMSriovVF *vf = vfs->pdata[i]; + gs_free char *kf_value = NULL; + char kf_key[32]; + + kf_value = nm_utils_sriov_vf_to_str (vf, TRUE, NULL); + if (!kf_value) + continue; + + nm_sprintf_buf (kf_key, "vf.%u", nm_sriov_vf_get_index (vf)); + + nm_keyfile_plugin_kf_set_string (info->keyfile, + nm_setting_get_name (setting), + kf_key, + kf_value); + } +} + +static void write_array_of_uint (GKeyFile *file, NMSetting *setting, const char *key, @@ -2189,6 +2263,15 @@ static const ParseInfoSetting parse_infos[] = { ), ), ), + PARSE_INFO_SETTING (NM_SETTING_SRIOV_SETTING_NAME, + PARSE_INFO_PROPERTIES ( + PARSE_INFO_PROPERTY (NM_SETTING_SRIOV_VFS, + .parser_no_check_key = TRUE, + .parser = sriov_vfs_parser, + .writer = sriov_vfs_writer, + ), + ), + ), PARSE_INFO_SETTING (NM_SETTING_TC_CONFIG_SETTING_NAME, PARSE_INFO_PROPERTIES ( PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_QDISCS, diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c new file mode 100644 index 0000000000..45608f9dc8 --- /dev/null +++ b/libnm-core/nm-setting-sriov.c @@ -0,0 +1,1331 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-setting-sriov.h" +#include "nm-setting-private.h" +#include "nm-utils-private.h" + +/** + * SECTION:nm-setting-sriov + * @short_description: Describes SR-IOV connection properties + * @include: nm-setting-sriov.h + **/ + +/** + * NMSettingSriov: + * + * SR-IOV settings. + * + * Since: 1.14 + */ +struct _NMSettingSriov { + NMSetting parent; + GPtrArray *vfs; + guint total_vfs; + NMTernary autoprobe_drivers; +}; + +struct _NMSettingSriovClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE_WITH_CODE (NMSettingSriov, nm_setting_sriov, NM_TYPE_SETTING, + _nm_register_setting (SRIOV, NM_SETTING_PRIORITY_HW_AUX)) + +enum { + PROP_0, + PROP_TOTAL_VFS, + PROP_VFS, + PROP_AUTOPROBE_DRIVERS, + + LAST_PROP +}; + +/*****************************************************************************/ + +G_DEFINE_BOXED_TYPE (NMSriovVF, nm_sriov_vf, nm_sriov_vf_dup, nm_sriov_vf_unref) + +struct _NMSriovVF { + guint refcount; + guint index; + GHashTable *attributes; + GHashTable *vlans; + guint *vlan_ids; +}; + +typedef struct { + guint id; + guint qos; + NMSriovVFVlanProtocol protocol; +} VFVlan; + +static guint +_vf_vlan_hash (gconstpointer ptr) +{ + return nm_hash_val (1348254767u, *((guint *) ptr)); +} + +static gboolean +_vf_vlan_equal (gconstpointer a, gconstpointer b) +{ + return *((guint *) a) == *((guint *) b); +} + +static GHashTable * +_vf_vlan_create_hash (void) +{ + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (VFVlan, id) == 0); + return g_hash_table_new_full (_vf_vlan_hash, + _vf_vlan_equal, + NULL, + nm_g_slice_free_fcn (VFVlan)); +} + +/** + * nm_srio_vf_new: + * @index: the VF index + * + * Creates a new #NMSriovVF object. + * + * Returns: (transfer full): the new #NMSriovVF object. + * + * Since: 1.14 + **/ +NMSriovVF * +nm_sriov_vf_new (guint index) +{ + NMSriovVF *vf; + + vf = g_slice_new0 (NMSriovVF); + vf->refcount = 1; + vf->index = index; + vf->attributes = g_hash_table_new_full (nm_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) g_variant_unref); + return vf; +} + +/** + * nm_sriov_vf_ref: + * @vf: the #NMSriovVF + * + * Increases the reference count of the object. + * + * Since: 1.14 + **/ +void +nm_sriov_vf_ref (NMSriovVF *vf) +{ + g_return_if_fail (vf); + g_return_if_fail (vf->refcount > 0); + + vf->refcount++; +} + +/** + * nm_sriov_vf_unref: + * @vf: the #NMSriovVF + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.14 + **/ +void +nm_sriov_vf_unref (NMSriovVF *vf) +{ + g_return_if_fail (vf); + g_return_if_fail (vf->refcount > 0); + + vf->refcount--; + if (vf->refcount == 0) { + g_hash_table_unref (vf->attributes); + if (vf->vlans) + g_hash_table_unref (vf->vlans); + g_free (vf->vlan_ids); + g_slice_free (NMSriovVF, vf); + } +} + +/** + * nm_sriov_vf_equal: + * @vf: the #NMSriovVF + * @other: the #NMSriovVF to compare @vf to. + * + * Determines if two #NMSriovVF objects have the same index, + * attributes and VLANs. + * + * Returns: %TRUE if the objects contain the same values, %FALSE + * if they do not. + * + * Since: 1.14 + **/ +gboolean +nm_sriov_vf_equal (const NMSriovVF *vf, const NMSriovVF *other) +{ + GHashTableIter iter; + const char *key; + GVariant *value, *value2; + VFVlan *vlan, *vlan2; + guint n_vlans; + + g_return_val_if_fail (vf, FALSE); + g_return_val_if_fail (vf->refcount > 0, FALSE); + g_return_val_if_fail (other, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if (vf == other) + return TRUE; + + if (vf->index != other->index) + return FALSE; + + if (g_hash_table_size (vf->attributes) != g_hash_table_size (other->attributes)) + return FALSE; + g_hash_table_iter_init (&iter, vf->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; + } + + n_vlans = vf->vlans ? g_hash_table_size (vf->vlans) : 0u; + if (n_vlans != (other->vlans ? g_hash_table_size (other->vlans) : 0u)) + return FALSE; + if (n_vlans > 0) { + g_hash_table_iter_init (&iter, vf->vlans); + while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL)) { + vlan2 = g_hash_table_lookup (other->vlans, vlan); + if (!vlan2) + return FALSE; + if ( vlan->qos != vlan2->qos + || vlan->protocol != vlan2->protocol) + return FALSE; + } + } + + return TRUE; +} + +static void +vf_add_vlan (NMSriovVF *vf, + guint vlan_id, + guint qos, + NMSriovVFVlanProtocol protocol) +{ + VFVlan *vlan; + + vlan = g_slice_new0 (VFVlan); + vlan->id = vlan_id; + vlan->qos = qos; + vlan->protocol = protocol; + + if (!vf->vlans) + vf->vlans = _vf_vlan_create_hash (); + + g_hash_table_add (vf->vlans, vlan); + g_clear_pointer (&vf->vlan_ids, g_free); +} + +/** + * nm_sriov_vf_dup: + * @vf: the #NMSriovVF + * + * Creates a copy of @vf. + * + * Returns: (transfer full): a copy of @vf + * + * Since: 1.14 + **/ +NMSriovVF * +nm_sriov_vf_dup (const NMSriovVF *vf) +{ + NMSriovVF *copy; + GHashTableIter iter; + const char *name; + GVariant *variant; + VFVlan *vlan; + + g_return_val_if_fail (vf, NULL); + g_return_val_if_fail (vf->refcount > 0, NULL); + + copy = nm_sriov_vf_new (vf->index); + + g_hash_table_iter_init (&iter, vf->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) + nm_sriov_vf_set_attribute (copy, name, variant); + + if (vf->vlans) { + g_hash_table_iter_init (&iter, vf->vlans); + while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL)) + vf_add_vlan (copy, vlan->id, vlan->qos, vlan->protocol); + } + + return copy; +} + +/** + * nm_sriov_vf_get_index: + * @vf: the #NMSriovVF + * + * Gets the index property of this VF object. + * + * Returns: the VF index + * + * Since: 1.14 + **/ +guint +nm_sriov_vf_get_index (const NMSriovVF *vf) +{ + g_return_val_if_fail (vf, 0); + g_return_val_if_fail (vf->refcount > 0, 0); + + return vf->index; +} + +/** + * nm_sriov_vf_set_attribute: + * @vf: the #NMSriovVF + * @name: the name of a route attribute + * @value: (transfer none) (allow-none): the value + * + * Sets the named attribute on @vf to the given value. + * + * Since: 1.14 + **/ +void +nm_sriov_vf_set_attribute (NMSriovVF *vf, const char *name, GVariant *value) +{ + g_return_if_fail (vf); + g_return_if_fail (vf->refcount > 0); + g_return_if_fail (name && *name != '\0'); + g_return_if_fail (!nm_streq (name, "index")); + + if (value) { + g_hash_table_insert (vf->attributes, + g_strdup (name), + g_variant_ref_sink (value)); + } else + g_hash_table_remove (vf->attributes, name); +} + +/** + * nm_sriov_vf_get_attribute_names: + * @vf: the #NMSriovVF + * + * Gets an array of attribute names defined on @vf. + * + * Returns: (transfer container): a %NULL-terminated array of attribute names + * + * Since: 1.14 + **/ +const char ** +nm_sriov_vf_get_attribute_names (const NMSriovVF *vf) +{ + g_return_val_if_fail (vf, NULL); + g_return_val_if_fail (vf->refcount > 0, NULL); + + return nm_utils_strdict_get_keys (vf->attributes, TRUE, NULL); +} + +/** + * nm_sriov_vf_get_attribute: + * @vf: the #NMSriovVF + * @name: the name of a VF attribute + * + * Gets the value of the attribute with name @name on @vf + * + * Returns: (transfer none): the value of the attribute with name @name on + * @vf, or %NULL if @vf has no such attribute. + * + * Since: 1.14 + **/ +GVariant * +nm_sriov_vf_get_attribute (const NMSriovVF *vf, const char *name) +{ + g_return_val_if_fail (vf, NULL); + g_return_val_if_fail (vf->refcount > 0, NULL); + g_return_val_if_fail (name && *name != '\0', NULL); + + return g_hash_table_lookup (vf->attributes, name); +} + +#define SRIOV_ATTR_SPEC_PTR(name, type, str_type) \ + &(NMVariantAttributeSpec) { name, type, FALSE, FALSE, FALSE, FALSE, str_type } + +const NMVariantAttributeSpec * const _nm_sriov_vf_attribute_spec[] = { + SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MAC, G_VARIANT_TYPE_STRING, 'm'), + SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, G_VARIANT_TYPE_BOOLEAN, 0), + SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_TRUST, G_VARIANT_TYPE_BOOLEAN, 0), + SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, G_VARIANT_TYPE_UINT32, 0), + SRIOV_ATTR_SPEC_PTR (NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, G_VARIANT_TYPE_UINT32, 0), + /* D-Bus only, synthetic attributes */ + SRIOV_ATTR_SPEC_PTR ("vlans", G_VARIANT_TYPE_STRING, 'd'), + NULL, +}; + +/** + * nm_sriov_vf_attribute_validate: + * @name: the attribute name + * @value: the attribute value + * @known: (out): on return, whether the attribute name is a known one + * @error: (allow-none): return location for a #GError, or %NULL + * + * Validates a VF attribute, i.e. checks that the attribute is a known one, + * the value is of the correct type and well-formed. + * + * Returns: %TRUE if the attribute is valid, %FALSE otherwise + * + * Since: 1.14 + */ +gboolean +nm_sriov_vf_attribute_validate (const char *name, + GVariant *value, + gboolean *known, + GError **error) +{ + const NMVariantAttributeSpec *const *iter; + const NMVariantAttributeSpec *spec = NULL; + + g_return_val_if_fail (name, FALSE); + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + for (iter = _nm_sriov_vf_attribute_spec; *iter; iter++) { + if (nm_streq (name, (*iter)->name)) { + spec = *iter; + break; + } + } + + if (!spec || spec->str_type == 'd') { + NM_SET_OUT (known, FALSE); + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("unknown attribute")); + return FALSE; + } + + NM_SET_OUT (known, TRUE); + + if (!g_variant_is_of_type (value, spec->type)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid attribute type '%s'"), + g_variant_get_type_string (value)); + return FALSE; + } + + if (spec->type == G_VARIANT_TYPE_STRING) { + const char *string; + + switch (spec->str_type) { + case 'm': /* MAC address */ + string = g_variant_get_string (value, NULL); + if (!nm_utils_hwaddr_valid (string, -1)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("'%s' is not a valid MAC address"), + string); + return FALSE; + } + break; + default: + break; + } + } + + return TRUE; +} + +gboolean +_nm_sriov_vf_attribute_validate_all (const NMSriovVF *vf, GError **error) +{ + GHashTableIter iter; + const char *name; + GVariant *variant; + GVariant *min, *max; + + g_return_val_if_fail (vf, FALSE); + g_return_val_if_fail (vf->refcount > 0, FALSE); + + g_hash_table_iter_init (&iter, vf->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &variant)) { + if (!nm_sriov_vf_attribute_validate (name, variant, NULL, error)) { + g_prefix_error (error, "attribute '%s':", name); + return FALSE; + } + } + + min = g_hash_table_lookup (vf->attributes, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE); + max = g_hash_table_lookup (vf->attributes, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE); + if ( min + && max + && g_variant_get_uint32 (min) > g_variant_get_uint32 (max)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "min_tx_rate is greater than max_tx_rate"); + return FALSE; + } + + return TRUE; +} + +/** + * nm_sriov_vf_add_vlan: + * @vf: the #NMSriovVF + * @vlan_id: the VLAN id + * + * Adds a VLAN to the VF. + * + * Returns: %TRUE if the VLAN was added; %FALSE if it already existed + * + * Since: 1.14 + **/ +gboolean +nm_sriov_vf_add_vlan (NMSriovVF *vf, guint vlan_id) +{ + g_return_val_if_fail (vf, FALSE); + g_return_val_if_fail (vf->refcount > 0, FALSE); + + if ( vf->vlans + && g_hash_table_contains (vf->vlans, &vlan_id)) + return FALSE; + + vf_add_vlan (vf, vlan_id, 0, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + + return TRUE; +} + +/** + * nm_sriov_vf_remove_vlan: + * @vf: the #NMSriovVF + * @vlan_id: the VLAN id + * + * Removes a VLAN from a VF. + * + * Returns: %TRUE if the VLAN was removed, %FALSE if the VLAN @vlan_id + * did not belong to the VF. + * + * Since: 1.14 + */ +gboolean +nm_sriov_vf_remove_vlan (NMSriovVF *vf, guint vlan_id) +{ + g_return_val_if_fail (vf, FALSE); + g_return_val_if_fail (vf->refcount > 0, FALSE); + + if ( !vf->vlans + || !g_hash_table_remove (vf->vlans, &vlan_id)) + return FALSE; + + g_clear_pointer (&vf->vlan_ids, g_free); + return TRUE; +} + +static int +vlan_id_compare (gconstpointer a, gconstpointer b, gpointer user_data) +{ + guint id_a = *(guint *) a; + guint id_b = *(guint *) b; + + if (id_a < id_b) + return -1; + else if (id_a > id_b) + return 1; + else return 0; +} + +/** + * nm_sriov_vf_get_vlan_ids: + * @vf: the #NMSriovVF + * @length: (out) (allow-none): on return, the number of VLANs configured + * + * Returns the VLANs currently configured on the VF. + * + * Returns: (transfer none): a list of VLAN ids configured on the VF. + * + * Since: 1.14 + */ +const guint * +nm_sriov_vf_get_vlan_ids (const NMSriovVF *vf, guint *length) +{ + GHashTableIter iter; + VFVlan *vlan; + guint num, i; + + g_return_val_if_fail (vf, NULL); + g_return_val_if_fail (vf->refcount > 0, NULL); + + num = vf->vlans ? g_hash_table_size (vf->vlans) : 0u; + NM_SET_OUT (length, num); + + if (vf->vlan_ids) + return vf->vlan_ids; + if (num == 0) + return NULL; + + /* vf is const, however, vlan_ids is a mutable field caching the + * result ("mutable" in C++ terminology) */ + ((NMSriovVF *) vf)->vlan_ids = g_new0 (guint, num); + + i = 0; + g_hash_table_iter_init (&iter, vf->vlans); + while (g_hash_table_iter_next (&iter, (gpointer *) &vlan, NULL)) + vf->vlan_ids[i++] = vlan->id; + + nm_assert (num == i); + + g_qsort_with_data (vf->vlan_ids, num, sizeof (guint), vlan_id_compare, NULL); + + return vf->vlan_ids; +} + +/** + * nm_sriov_vf_set_vlan_qos: + * @vf: the #NMSriovVF + * @vlan_id: the VLAN id + * @qos: a QoS (priority) value + * + * Sets a QoS value for the given VLAN. + * + * Since: 1.14 + */ +void +nm_sriov_vf_set_vlan_qos (NMSriovVF *vf, guint vlan_id, guint32 qos) +{ + VFVlan *vlan; + + g_return_if_fail (vf); + g_return_if_fail (vf->refcount > 0); + + if ( !vf->vlans + || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id))) + g_return_if_reached (); + + vlan->qos = qos; +} + +/** + * nm_sriov_vf_set_vlan_protocol: + * @vf: the #NMSriovVF + * @vlan_id: the VLAN id + * @protocol: the VLAN protocol + * + * Sets the protocol for the given VLAN. + * + * Since: 1.14 + */ +void +nm_sriov_vf_set_vlan_protocol (NMSriovVF *vf, guint vlan_id, NMSriovVFVlanProtocol protocol) +{ + VFVlan *vlan; + + g_return_if_fail (vf); + g_return_if_fail (vf->refcount > 0); + + if ( !vf->vlans + || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id))) + g_return_if_reached (); + + vlan->protocol = protocol; +} + +/** + * nm_sriov_vf_get_vlan_qos: + * @vf: the #NMSriovVF + * @vlan_id: the VLAN id + * + * Returns the QoS value for the given VLAN. + * + * Returns: the QoS value + * + * Since: 1.14 + */ +guint32 +nm_sriov_vf_get_vlan_qos (const NMSriovVF *vf, guint vlan_id) +{ + VFVlan *vlan; + + g_return_val_if_fail (vf, 0); + g_return_val_if_fail (vf->refcount > 0, 0); + + if ( !vf->vlans + || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id))) + g_return_val_if_reached (0); + + return vlan->qos; +} + +/* + * nm_sriov_vf_get_vlan_protocol: + * @vf: the #NMSriovVF + * @vlan_id: the VLAN id + * + * Returns the configured protocol for the given VLAN. + * + * Returns: the configured protocol + * + * Since: 1.14 + */ +NMSriovVFVlanProtocol +nm_sriov_vf_get_vlan_protocol (const NMSriovVF *vf, guint vlan_id) +{ + VFVlan *vlan; + + g_return_val_if_fail (vf, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + g_return_val_if_fail (vf->refcount > 0, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + + if ( !vf->vlans + || !(vlan = g_hash_table_lookup (vf->vlans, &vlan_id))) + g_return_val_if_reached (NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + + return vlan->protocol; +} + +/*****************************************************************************/ + +/** + * nm_setting_sriov_new: + * + * Creates a new #NMSettingSriov object with default values. + * + * Returns: (transfer full): the new empty #NMSettingSriov object + * + * Since: 1.14 + **/ +NMSetting * +nm_setting_sriov_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_SRIOV, NULL); +} + +/** + * nm_setting_sriov_get_total_vfs: + * @setting: the #NMSettingSriov + * + * Returns the value contained in the #NMSettingSriov:total-vfs + * property. + * + * Returns: the total number of SR-IOV virtual functions to create + * + * Since: 1.14 + **/ +guint +nm_setting_sriov_get_total_vfs (NMSettingSriov *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), 0); + + return setting->total_vfs; +} + +/** + * nm_setting_sriov_get_num_vfs: + * @setting: the #NMSettingSriov + * + * Returns: the number of configured VFs + * + * Since: 1.14 + **/ +guint +nm_setting_sriov_get_num_vfs (NMSettingSriov *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), 0); + + return setting->vfs->len; +} + +/** + * nm_setting_sriov_get_vf: + * @setting: the #NMSettingSriov + * @idx: index number of the VF to return + * + * Returns: (transfer none): the VF at index @idx + * + * Since: 1.14 + **/ +NMSriovVF * +nm_setting_sriov_get_vf (NMSettingSriov *setting, guint idx) +{ + g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), NULL); + g_return_val_if_fail (idx < setting->vfs->len, NULL); + + return setting->vfs->pdata[idx]; +} + +/** + * nm_setting_sriov_add_vf: + * @setting: the #NMSettingSriov + * @vf: the VF to add + * + * Appends a new VF and associated information to the setting. The + * given VF is duplicated internally and is not changed by this function. + * + * Since: 1.14 + **/ +void +nm_setting_sriov_add_vf (NMSettingSriov *setting, NMSriovVF *vf) +{ + g_return_if_fail (NM_IS_SETTING_SRIOV (setting)); + g_return_if_fail (vf); + g_return_if_fail (vf->refcount > 0); + + g_ptr_array_add (setting->vfs, nm_sriov_vf_dup (vf)); + g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS); +} + +/** + * nm_setting_sriov_remove_vf: + * @setting: the #NMSettingSriov + * @idx: index number of the VF + * + * Removes the VF at index @idx. + * + * Since: 1.14 + **/ +void +nm_setting_sriov_remove_vf (NMSettingSriov *setting, guint idx) +{ + g_return_if_fail (NM_IS_SETTING_SRIOV (setting)); + g_return_if_fail (idx < setting->vfs->len); + + g_ptr_array_remove_index (setting->vfs, idx); + g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS); +} + +/** + * nm_setting_sriov_remove_vf_by_index: + * @setting: the #NMSettingSriov + * @index: the VF index of the VF to remove + * + * Removes the VF with VF index @index. + * + * Returns: %TRUE if the VF was found and removed; %FALSE if it was not + * + * Since: 1.14 + **/ +gboolean +nm_setting_sriov_remove_vf_by_index (NMSettingSriov *setting, + guint index) +{ + guint i; + + g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), FALSE); + + for (i = 0; i < setting->vfs->len; i++) { + if (nm_sriov_vf_get_index (setting->vfs->pdata[i]) == index) { + g_ptr_array_remove_index (setting->vfs, i); + g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_sriov_clear_vfs: + * @setting: the #NMSettingSriov + * + * Removes all configured VFs. + * + * Since: 1.14 + **/ +void +nm_setting_sriov_clear_vfs (NMSettingSriov *setting) +{ + g_return_if_fail (NM_IS_SETTING_SRIOV (setting)); + + if (setting->vfs->len != 0) { + g_ptr_array_set_size (setting->vfs, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS); + } +} + +/** + * nm_setting_sriov_get_autoprobe_drivers: + * @setting: the #NMSettingSriov + * + * Returns the value contained in the #NMSettingSriov:autoprobe-drivers + * property. + * + * Returns: the autoprobe-drivers property value + * + * Since: 1.14 + **/ +NMTernary +nm_setting_sriov_get_autoprobe_drivers (NMSettingSriov *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_SRIOV (setting), NM_TERNARY_DEFAULT); + + return setting->autoprobe_drivers; +} + +static gint +vf_index_compare (gconstpointer a, gconstpointer b) +{ + NMSriovVF *vf_a = *(NMSriovVF **) a; + NMSriovVF *vf_b = *(NMSriovVF **) b; + + if (vf_a->index < vf_b->index) + return -1; + else if (vf_a->index > vf_b->index) + return 1; + else + return 0; +} + +gboolean +_nm_setting_sriov_sort_vfs (NMSettingSriov *setting) +{ + gboolean need_sort = FALSE; + guint i; + + for (i = 1; i < setting->vfs->len; i++) { + NMSriovVF *vf_prev = setting->vfs->pdata[i - 1]; + NMSriovVF *vf = setting->vfs->pdata[i]; + + if (vf->index <= vf_prev->index) { + need_sort = TRUE; + break; + } + } + + if (need_sort) + g_ptr_array_sort (setting->vfs, vf_index_compare); + + return need_sort; +} + +/*****************************************************************************/ + +static GVariant * +vfs_to_dbus (NMSetting *setting, const char *property) +{ + gs_unref_ptrarray GPtrArray *vfs = NULL; + GVariantBuilder builder; + guint i; + + g_object_get (setting, NM_SETTING_SRIOV_VFS, &vfs, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + if (vfs) { + for (i = 0; i < vfs->len; i++) { + gs_free const char **attr_names = NULL; + NMSriovVF *vf = vfs->pdata[i]; + GVariantBuilder vf_builder; + const guint *vlan_ids; + const char **name; + guint num_vlans; + + g_variant_builder_init (&vf_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&vf_builder, "{sv}", "index", + g_variant_new_uint32 (nm_sriov_vf_get_index (vf))); + + attr_names = nm_utils_strdict_get_keys (vf->attributes, TRUE, NULL); + if (attr_names) { + for (name = attr_names; *name; name++) { + g_variant_builder_add (&vf_builder, + "{sv}", + *name, + nm_sriov_vf_get_attribute (vf, *name)); + } + } + + /* VLANs are translated into an array of maps, where each map has + * keys 'id', 'qos' and 'proto'. This guarantees enough flexibility + * to accomodate any future new option. */ + vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans); + if (num_vlans) { + GVariantBuilder vlans_builder; + guint j; + + g_variant_builder_init (&vlans_builder, G_VARIANT_TYPE ("aa{sv}")); + for (j = 0; j < num_vlans; j++) { + GVariantBuilder vlan_builder; + + g_variant_builder_init (&vlan_builder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&vlan_builder, + "{sv}", "id", + g_variant_new_uint32 (vlan_ids[j])); + g_variant_builder_add (&vlan_builder, + "{sv}", "qos", + g_variant_new_uint32 (nm_sriov_vf_get_vlan_qos (vf, + vlan_ids[j]))); + g_variant_builder_add (&vlan_builder, + "{sv}", "protocol", + g_variant_new_uint32 (nm_sriov_vf_get_vlan_protocol (vf, + vlan_ids[j]))); + g_variant_builder_add (&vlans_builder, + "a{sv}", + &vlan_builder); + } + g_variant_builder_add (&vf_builder , "{sv}", "vlans", g_variant_builder_end (&vlans_builder)); + } + g_variant_builder_add (&builder, "a{sv}", &vf_builder); + } + } + + return g_variant_builder_end (&builder); +} + +static gboolean +vfs_from_dbus (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + GPtrArray *vfs; + GVariantIter vf_iter; + GVariant *vf_var; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), FALSE); + + vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref); + g_variant_iter_init (&vf_iter, value); + while (g_variant_iter_next (&vf_iter, "@a{sv}", &vf_var)) { + NMSriovVF *vf; + guint32 index; + GVariantIter attr_iter; + const char *attr_name; + GVariant *attr_var, *vlans_var; + + if (!g_variant_lookup (vf_var, "index", "u", &index)) + goto next; + + vf = nm_sriov_vf_new (index); + + g_variant_iter_init (&attr_iter, vf_var); + while (g_variant_iter_next (&attr_iter, "{&sv}", &attr_name, &attr_var)) { + if (!NM_IN_STRSET (attr_name, "index", "vlans")) + nm_sriov_vf_set_attribute (vf, attr_name, attr_var); + g_variant_unref (attr_var); + } + + if (g_variant_lookup (vf_var, "vlans", "@aa{sv}", &vlans_var)) { + GVariantIter vlan_iter; + GVariant *vlan_var; + + g_variant_iter_init (&vlan_iter, vlans_var); + while (g_variant_iter_next (&vlan_iter, "@a{sv}", &vlan_var)) { + NMSriovVFVlanProtocol proto = NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q; + gint64 vlan_id = -1; + guint qos = 0; + + g_variant_iter_init (&attr_iter, vlan_var); + while (g_variant_iter_next (&attr_iter, "{&sv}", &attr_name, &attr_var)) { + if ( nm_streq (attr_name, "id") + && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32)) + vlan_id = g_variant_get_uint32 (attr_var); + else if ( nm_streq (attr_name, "qos") + && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32)) + qos = g_variant_get_uint32 (attr_var); + else if ( nm_streq (attr_name, "protocol") + && g_variant_is_of_type (attr_var, G_VARIANT_TYPE_UINT32)) + proto = g_variant_get_uint32 (attr_var); + g_variant_unref (attr_var); + } + if (vlan_id != -1) + vf_add_vlan (vf, vlan_id, qos, proto); + g_variant_unref (vlan_var); + } + g_variant_unref (vlans_var); + } + + g_ptr_array_add (vfs, vf); +next: + g_variant_unref (vf_var); + } + + g_object_set (setting, NM_SETTING_SRIOV_VFS, vfs, NULL); + g_ptr_array_unref (vfs); + + return TRUE; +} + +/*****************************************************************************/ + +static gboolean +verify (NMSetting *setting, NMConnection *connection, GError **error) +{ + NMSettingSriov *self = NM_SETTING_SRIOV (setting); + guint i; + + if (self->vfs->len) { + gs_unref_hashtable GHashTable *h = NULL; + + h = g_hash_table_new (nm_direct_hash, NULL); + for (i = 0; i < self->vfs->len; i++) { + NMSriovVF *vf = self->vfs->pdata[i]; + gs_free_error GError *local = NULL; + + if (vf->index >= self->total_vfs) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("VF with index %u, but the total number of VFs is %u"), + vf->index, self->total_vfs); + g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME, + NM_SETTING_SRIOV_VFS); + return FALSE; + } + + if (!_nm_sriov_vf_attribute_validate_all (vf, &local)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid VF %u: %s"), + vf->index, + local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME, + NM_SETTING_SRIOV_VFS); + return FALSE; + } + + if (g_hash_table_contains (h, GUINT_TO_POINTER (vf->index))) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("duplicate VF index %u"), vf->index); + g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME, + NM_SETTING_SRIOV_VFS); + return FALSE; + } + + g_hash_table_add (h, GUINT_TO_POINTER (vf->index)); + } + } + + /* Failures from here on are NORMALIZABLE... */ + + if (self->vfs->len) { + for (i = 1; i < self->vfs->len; i++) { + NMSriovVF *vf_prev = self->vfs->pdata[i - 1]; + NMSriovVF *vf = self->vfs->pdata[i]; + + if (vf->index <= vf_prev->index) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("VFs %d and %d are not sorted by ascending index"), + vf_prev->index, vf->index); + g_prefix_error (error, "%s.%s: ", NM_SETTING_SRIOV_SETTING_NAME, + NM_SETTING_SRIOV_VFS); + return NM_SETTING_VERIFY_NORMALIZABLE; + } + } + } + + return TRUE; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingSriov *self = NM_SETTING_SRIOV (object); + + switch (prop_id) { + case PROP_TOTAL_VFS: + self->total_vfs = g_value_get_uint (value); + break; + case PROP_VFS: + g_ptr_array_unref (self->vfs); + self->vfs = _nm_utils_copy_array (g_value_get_boxed (value), + (NMUtilsCopyFunc) nm_sriov_vf_dup, + (GDestroyNotify) nm_sriov_vf_unref); + break; + case PROP_AUTOPROBE_DRIVERS: + self->autoprobe_drivers = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingSriov *self = NM_SETTING_SRIOV (object); + + switch (prop_id) { + case PROP_TOTAL_VFS: + g_value_set_uint (value, self->total_vfs); + break; + case PROP_VFS: + g_value_take_boxed (value, _nm_utils_copy_array (self->vfs, + (NMUtilsCopyFunc) nm_sriov_vf_dup, + (GDestroyNotify) nm_sriov_vf_unref)); + break; + case PROP_AUTOPROBE_DRIVERS: + g_value_set_enum (value, self->autoprobe_drivers); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingSriov *a = NM_SETTING_SRIOV (setting); + NMSettingSriov *b = NM_SETTING_SRIOV (other); + NMSettingClass *parent_class; + guint i; + + if (nm_streq (prop_spec->name, NM_SETTING_SRIOV_VFS)) { + if (a->vfs->len != b->vfs->len) + return FALSE; + for (i = 0; i < a->vfs->len; i++) { + if (!nm_sriov_vf_equal (a->vfs->pdata[i], b->vfs->pdata[i])) + return FALSE; + } + return TRUE; + } + + /* Otherwise chain up to parent to handle generic compare */ + parent_class = NM_SETTING_CLASS (nm_setting_sriov_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +static void +nm_setting_sriov_init (NMSettingSriov *setting) +{ + setting->vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref); +} + +static void +finalize (GObject *object) +{ + NMSettingSriov *self = NM_SETTING_SRIOV (object); + + g_ptr_array_unref (self->vfs); + + G_OBJECT_CLASS (nm_setting_sriov_parent_class)->finalize (object); +} + +static void +nm_setting_sriov_class_init (NMSettingSriovClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + parent_class->compare_property = compare_property; + parent_class->verify = verify; + + /** + * NMSettingSriov:total-vfs + * + * The total number of virtual functions to create. + * + * Since: 1.14 + **/ + g_object_class_install_property + (object_class, PROP_TOTAL_VFS, + g_param_spec_uint (NM_SETTING_SRIOV_TOTAL_VFS, "", "", + 0, G_MAXUINT32, 0, + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingSriov:vfs: (type GPtrArray(NMSriovVF)) + * + * Array of virtual function descriptors. + * + * Each VF descriptor is a dictionary mapping attribute names + * to GVariant values. The 'index' entry is mandatory for + * each VF. + * + * When represented as string a VF is in the form: + * + * "INDEX [ATTR=VALUE[ ATTR=VALUE]...]". + * + * for example: + * + * "2 mac=00:11:22:33:44:55 spoof-check=true". + * + * The "vlans" attribute is represented as a semicolor-separated + * list of VLAN descriptors, where each descriptor has the form + * + * "ID[.PRIORITY[.PROTO]]". + * + * PROTO can be either 'q' for 802.1Q (the default) or 'ad' for + * 802.1ad. + * + * Since: 1.14 + **/ + g_object_class_install_property + (object_class, PROP_VFS, + g_param_spec_boxed (NM_SETTING_SRIOV_VFS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + _nm_setting_class_override_property (parent_class, + NM_SETTING_SRIOV_VFS, + G_VARIANT_TYPE ("aa{sv}"), + vfs_to_dbus, + vfs_from_dbus, + NULL); + + /** + * NMSettingSriov:autoprobe-drivers + * + * Whether to autoprobe virtual functions by a compatible driver. + * + * If set to %NM_TERNARY_TRUE, the kernel will try to bind VFs to + * a compatible driver and if this succeeds a new network + * interface will be instantiated for each VF. + * + * If set to %NM_TERNARY_FALSE, VFs will not be claimed and no + * network interfaces will be created for them. + * + * When set to %NM_TERNARY_DEFAULT, the global default is used; in + * case the global default is unspecified it is assumed to be + * %NM_TERNARY_TRUE. + * + * Since: 1.14 + **/ + g_object_class_install_property + (object_class, PROP_AUTOPROBE_DRIVERS, + g_param_spec_enum (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, "", "", + nm_ternary_get_type (), + NM_TERNARY_DEFAULT, + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); +} diff --git a/libnm-core/nm-setting-sriov.h b/libnm-core/nm-setting-sriov.h new file mode 100644 index 0000000000..2e209964d4 --- /dev/null +++ b/libnm-core/nm-setting-sriov.h @@ -0,0 +1,123 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef NM_SETTING_SRIOV_H +#define NM_SETTING_SRIOV_H + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only <NetworkManager.h> can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_SRIOV (nm_setting_sriov_get_type ()) +#define NM_SETTING_SRIOV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_SRIOV, NMSettingSriov)) +#define NM_SETTING_SRIOV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_SRIOV, NMSettingSriovClass)) +#define NM_IS_SETTING_SRIOV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_SRIOV)) +#define NM_IS_SETTING_SRIOV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_SRIOV)) +#define NM_SETTING_SRIOV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_SRIOV, NMSettingSriovClass)) + +#define NM_SETTING_SRIOV_SETTING_NAME "sriov" + +#define NM_SETTING_SRIOV_TOTAL_VFS "total-vfs" +#define NM_SETTING_SRIOV_VFS "vfs" +#define NM_SETTING_SRIOV_AUTOPROBE_DRIVERS "autoprobe-drivers" + +#define NM_SRIOV_VF_ATTRIBUTE_MAC "mac" +#define NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK "spoof-check" +#define NM_SRIOV_VF_ATTRIBUTE_TRUST "trust" +#define NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE "min-tx-rate" +#define NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE "max-tx-rate" + +typedef struct _NMSettingSriovClass NMSettingSriovClass; +typedef struct _NMSriovVF NMSriovVF; + +/** + * NMSriovVFVlanProtocol: + * @NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q: use 802.1Q + * @NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: use 802.1ad + * + * #NMSriovVFVlanProtocol indicates the VLAN protocol to use. + * + * Since: 1.14 + */ +typedef enum { + NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q = 0, + NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD = 1, +} NMSriovVFVlanProtocol; + +NM_AVAILABLE_IN_1_14 +GType nm_setting_sriov_get_type (void); +NM_AVAILABLE_IN_1_14 +NMSetting *nm_setting_sriov_new (void); +NM_AVAILABLE_IN_1_14 +guint nm_setting_sriov_get_total_vfs (NMSettingSriov *setting); +NM_AVAILABLE_IN_1_14 +guint nm_setting_sriov_get_num_vfs (NMSettingSriov *setting); +NM_AVAILABLE_IN_1_14 +NMSriovVF *nm_setting_sriov_get_vf (NMSettingSriov *setting, guint idx); +NM_AVAILABLE_IN_1_14 +void nm_setting_sriov_add_vf (NMSettingSriov *setting, NMSriovVF *vf); +NM_AVAILABLE_IN_1_14 +void nm_setting_sriov_remove_vf (NMSettingSriov *setting, guint idx); +NM_AVAILABLE_IN_1_14 +gboolean nm_setting_sriov_remove_vf_by_index (NMSettingSriov *setting, guint index); +NM_AVAILABLE_IN_1_14 +void nm_setting_sriov_clear_vfs (NMSettingSriov *setting); +NM_AVAILABLE_IN_1_14 +NMTernary nm_setting_sriov_get_autoprobe_drivers (NMSettingSriov *setting); + +NM_AVAILABLE_IN_1_14 +gboolean nm_sriov_vf_add_vlan (NMSriovVF *vf, guint vlan_id); +NM_AVAILABLE_IN_1_14 +gboolean nm_sriov_vf_remove_vlan (NMSriovVF *vf, guint vlan_id); +NM_AVAILABLE_IN_1_14 +const guint *nm_sriov_vf_get_vlan_ids (const NMSriovVF *vf, guint *length); +NM_AVAILABLE_IN_1_14 +void nm_sriov_vf_set_vlan_qos (NMSriovVF *vf, guint vlan_id, guint32 qos); +NM_AVAILABLE_IN_1_14 +void nm_sriov_vf_set_vlan_protocol (NMSriovVF *vf, guint vlan_id, NMSriovVFVlanProtocol protocol); +NM_AVAILABLE_IN_1_14 +guint32 nm_sriov_vf_get_vlan_qos (const NMSriovVF *vf, guint vlan_id); +NM_AVAILABLE_IN_1_14 +NMSriovVFVlanProtocol nm_sriov_vf_get_vlan_protocol (const NMSriovVF *vf, guint vlan_id); + +NM_AVAILABLE_IN_1_14 +GType nm_sriov_vf_get_type (void); +NM_AVAILABLE_IN_1_14 +NMSriovVF *nm_sriov_vf_new (guint index); +NM_AVAILABLE_IN_1_14 +void nm_sriov_vf_ref (NMSriovVF *vf); +NM_AVAILABLE_IN_1_14 +void nm_sriov_vf_unref (NMSriovVF *vf); +NM_AVAILABLE_IN_1_14 +gboolean nm_sriov_vf_equal (const NMSriovVF *vf, const NMSriovVF *other); +NM_AVAILABLE_IN_1_14 +NMSriovVF *nm_sriov_vf_dup (const NMSriovVF *vf); +NM_AVAILABLE_IN_1_14 +guint nm_sriov_vf_get_index (const NMSriovVF *vf); +NM_AVAILABLE_IN_1_14 +void nm_sriov_vf_set_attribute (NMSriovVF *vf, const char *name, GVariant *value); +NM_AVAILABLE_IN_1_14 +const char **nm_sriov_vf_get_attribute_names (const NMSriovVF *vf); +NM_AVAILABLE_IN_1_14 +GVariant *nm_sriov_vf_get_attribute (const NMSriovVF *vf, const char *name); +NM_AVAILABLE_IN_1_14 +gboolean nm_sriov_vf_attribute_validate (const char *name, GVariant *value, gboolean *known, GError **error); + +G_END_DECLS + +#endif /* NM_SETTING_SRIOV_H */ diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c index fa1f3fdfba..575305e220 100644 --- a/libnm-core/nm-setting.c +++ b/libnm-core/nm-setting.c @@ -132,6 +132,7 @@ _register_settings_ensure_types (void) ENSURE_TYPE (nm_setting_pppoe_get_type); ENSURE_TYPE (nm_setting_proxy_get_type); ENSURE_TYPE (nm_setting_serial_get_type); + ENSURE_TYPE (nm_setting_sriov_get_type); ENSURE_TYPE (nm_setting_tc_config_get_type); ENSURE_TYPE (nm_setting_team_get_type); ENSURE_TYPE (nm_setting_team_port_get_type); diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 58539cb301..0605d83fa1 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -88,6 +88,7 @@ void _nm_utils_format_variant_attributes_full (GString *str, guint num_values, char attr_separator, char key_value_separator); +gboolean _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error); /* JSON to GValue conversion macros */ diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 9247d7f8a0..bea6b427b0 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2657,6 +2657,249 @@ nm_utils_tc_tfilter_from_str (const char *str, GError **error) /*****************************************************************************/ +extern const NMVariantAttributeSpec *const _nm_sriov_vf_attribute_spec[]; + +/** + * nm_utils_sriov_vf_to_str: + * @vf: the %NMSriovVF + * @omit_index: if %TRUE, the VF index will be omitted from output string + * @error: (out) (allow-none): location to store the error on failure + * + * Converts a SR-IOV virtual function object to its string representation. + * + * Returns: a newly allocated string or %NULL on error + * + * Since: 1.14 + */ +char * +nm_utils_sriov_vf_to_str (const NMSriovVF *vf, gboolean omit_index, GError **error) +{ + gs_free NMUtilsNamedValue *values = NULL; + gs_free const char **names = NULL; + const guint *vlan_ids; + guint num_vlans, num_attrs; + guint i; + GString *str; + + str = g_string_new (""); + if (!omit_index) + g_string_append_printf (str, "%u", nm_sriov_vf_get_index (vf)); + + names = nm_sriov_vf_get_attribute_names (vf); + num_attrs = names ? g_strv_length ((char **) names) : 0; + values = g_new0 (NMUtilsNamedValue, num_attrs); + + for (i = 0; i < num_attrs; i++) { + values[i].name = names[i]; + values[i].value_ptr = nm_sriov_vf_get_attribute (vf, names[i]); + } + + if (num_attrs > 0) { + if (!omit_index) + g_string_append_c (str, ' '); + _nm_utils_format_variant_attributes_full (str, values, num_attrs, ' ', '='); + } + + vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans); + if (num_vlans != 0) { + g_string_append (str, " vlans"); + for (i = 0; i < num_vlans; i++) { + guint32 qos; + NMSriovVFVlanProtocol protocol; + + qos = nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]); + protocol = nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]); + + g_string_append_c (str, i == 0 ? '=' : ';'); + + g_string_append_printf (str, "%u", vlan_ids[i]); + + if ( qos != 0 + || protocol != NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q) { + g_string_append_printf (str, + ".%u%s", + (unsigned) qos, + protocol == NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q ? "" : ".ad"); + } + } + } + + return g_string_free (str, FALSE); +} + +gboolean +_nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error) +{ + gs_free const char **vlans = NULL; + guint i; + + vlans = nm_utils_strsplit_set (str, ";"); + if (!vlans) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "empty VF VLAN"); + return FALSE; + } + + for (i = 0; vlans[i]; i++) { + gs_strfreev char **params = NULL; + guint id = G_MAXUINT; + gint64 qos = -1; + + /* we accept leading/trailing whitespace around vlans[1]. Hence + * the nm_str_skip_leading_spaces() and g_strchomp() below. + * + * However, we don't accept any whitespace inside the specifier. + * Hence the NM_STRCHAR_ALL() checks. */ + + params = g_strsplit (nm_str_skip_leading_spaces (vlans[i]), ".", 3); + if (!params || !params[0] || *params[0] == '\0') { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "empty VF VLAN"); + return FALSE; + } + + if (!params[1]) + g_strchomp (params[0]); + if (NM_STRCHAR_ALL (params[0], ch, ch == 'x' || g_ascii_isdigit (ch))) + id = _nm_utils_ascii_str_to_int64 (params[0], 0, 0, 4095, G_MAXUINT); + if (id == G_MAXUINT) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid VF VLAN id '%s'", + params[0]); + return FALSE; + } + if (!nm_sriov_vf_add_vlan (vf, id)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "duplicate VLAN id %u", + id); + return FALSE; + } + + if (!params[1]) + continue; + + if (!params[2]) + g_strchomp (params[1]); + if (NM_STRCHAR_ALL (params[1], ch, ch == 'x' || g_ascii_isdigit (ch))) + qos = _nm_utils_ascii_str_to_int64 (params[1], 0, 0, G_MAXUINT32, -1); + if (qos == -1) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid VF VLAN QoS '%s'", + params[1]); + return FALSE; + } + nm_sriov_vf_set_vlan_qos (vf, id, qos); + + if (!params[2]) + continue; + + g_strchomp (params[2]); + + if (nm_streq (params[2], "ad")) + nm_sriov_vf_set_vlan_protocol (vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD); + else if (nm_streq (params[2], "q")) + nm_sriov_vf_set_vlan_protocol (vf, id, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + else { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid VF VLAN protocol '%s'", + params[2]); + return FALSE; + } + } + + return TRUE; +} + +/** + * nm_utils_sriov_vf_from_str: + * @str: the input string + * @error: (out) (allow-none): location to store the error on failure + * + * Converts a string to a SR-IOV virtual function object. + * + * Returns: (transfer full): the virtual function object + * + * Since: 1.14 + */ +NMSriovVF * +nm_utils_sriov_vf_from_str (const char *str, GError **error) +{ + gs_free char *index_free = NULL; + const char *detail; + + g_return_val_if_fail (str, NULL); + g_return_val_if_fail (!error || !*error, NULL); + + while (*str == ' ') + str++; + + detail = strchr (str, ' '); + if (detail) { + index_free = g_strndup (str, detail - str); + str = index_free; + detail++; + } + + return _nm_utils_sriov_vf_from_strparts (str, detail, error); +} + +NMSriovVF * +_nm_utils_sriov_vf_from_strparts (const char *index, const char *detail, GError **error) +{ + NMSriovVF *vf; + guint32 n_index; + GHashTableIter iter; + char *key; + GVariant *variant; + gs_unref_hashtable GHashTable *ht = NULL; + + n_index = _nm_utils_ascii_str_to_int64 (index, 10, 0, G_MAXUINT32, 0); + if (errno) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + "invalid index"); + return NULL; + } + + vf = nm_sriov_vf_new (n_index); + if (detail) { + ht = nm_utils_parse_variant_attributes (detail, ' ', '=', TRUE, _nm_sriov_vf_attribute_spec, error); + if (!ht) { + nm_sriov_vf_unref (vf); + return NULL; + } + + if ((variant = g_hash_table_lookup (ht, "vlans"))) { + if (!_nm_sriov_vf_parse_vlans (vf, g_variant_get_string (variant, NULL), error)) { + nm_sriov_vf_unref (vf); + return NULL; + } + g_hash_table_remove (ht, "vlans"); + } + + g_hash_table_iter_init (&iter, ht); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &variant)) + nm_sriov_vf_set_attribute (vf, key, g_variant_ref_sink (variant)); + } + + return vf; +} + +/*****************************************************************************/ + /** * nm_utils_uuid_generate_buf_: * @buf: input buffer, must contain at least 37 bytes diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h index 87463ac3cb..cf9572b907 100644 --- a/libnm-core/nm-utils.h +++ b/libnm-core/nm-utils.h @@ -34,8 +34,9 @@ #include <linux/if_infiniband.h> #include "nm-core-enum-types.h" -#include "nm-setting-wireless-security.h" +#include "nm-setting-sriov.h" #include "nm-setting-tc-config.h" +#include "nm-setting-wireless-security.h" G_BEGIN_DECLS @@ -250,9 +251,17 @@ char *nm_utils_tc_tfilter_to_str (NMTCTfilter *tfilter, GError **error /*****************************************************************************/ +NM_AVAILABLE_IN_1_14 +char *nm_utils_sriov_vf_to_str (const NMSriovVF *vf, gboolean omit_index, GError **error); +NM_AVAILABLE_IN_1_14 +NMSriovVF *nm_utils_sriov_vf_from_str (const char *str, GError **error); + +/*****************************************************************************/ + NM_AVAILABLE_IN_1_12 gint64 nm_utils_get_timestamp_msec (void); + G_END_DECLS #endif /* __NM_UTILS_H__ */ diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c index e71d6b7cc2..cb02d1c4a5 100644 --- a/libnm-core/tests/test-setting.c +++ b/libnm-core/tests/test-setting.c @@ -23,6 +23,8 @@ #include <string.h> #include "nm-utils.h" +#include "nm-utils-private.h" +#include "nm-core-internal.h" #include "nm-setting-8021x.h" #include "nm-setting-bond.h" #include "nm-setting-dcb.h" @@ -1263,6 +1265,234 @@ test_team_port_full_config (void) /*****************************************************************************/ static void +test_sriov_vf (void) +{ + NMSriovVF *vf1, *vf2; + GError *error = NULL; + char *str; + + vf1 = nm_sriov_vf_new (1); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22:33:44:55")); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE)); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE)); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (100)); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (500)); + + str = nm_utils_sriov_vf_to_str (vf1, FALSE, &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, "1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100 spoof-check=true trust=false"); + g_free (str); + + vf2 = nm_utils_sriov_vf_from_str (" 1 mac=00:11:22:33:44:55 max-tx-rate=500 min-tx-rate=100", &error); + nmtst_assert_success (vf2, error); + nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (FALSE)); + nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK, g_variant_new_boolean (TRUE)); + nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (TRUE)); + nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, NULL); + nm_sriov_vf_set_attribute (vf2, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE)); + + g_assert (nm_sriov_vf_equal (vf1, vf2)); + + nm_sriov_vf_unref (vf1); + nm_sriov_vf_unref (vf2); +} + +static void +test_sriov_vf_dup (void) +{ + NMSriovVF *vf1, *vf2; + + vf1 = nm_sriov_vf_new (1); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("foobar")); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_TRUST, g_variant_new_boolean (FALSE)); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE, g_variant_new_uint32 (10)); + nm_sriov_vf_set_attribute (vf1, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE, g_variant_new_uint32 (1000)); + nm_sriov_vf_add_vlan (vf1, 80); + nm_sriov_vf_set_vlan_qos (vf1, 80, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD); + + vf2 = nm_sriov_vf_dup (vf1); + g_assert (nm_sriov_vf_equal (vf1, vf2)); + + nm_sriov_vf_unref (vf1); + nm_sriov_vf_unref (vf2); +} + +static void +test_sriov_vf_vlan (void) +{ + NMSriovVF *vf; + const guint *vlan_ids; + guint num; + GError *error = NULL; + gs_free char *str = NULL; + + vf = nm_sriov_vf_new (19); + nm_sriov_vf_set_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC, g_variant_new_string ("00:11:22")); + g_assert (nm_sriov_vf_add_vlan (vf, 80)); + g_assert (!nm_sriov_vf_add_vlan (vf, 80)); + g_assert (nm_sriov_vf_add_vlan (vf, 82)); + g_assert (nm_sriov_vf_add_vlan (vf, 83)); + g_assert (nm_sriov_vf_add_vlan (vf, 81)); + g_assert (!nm_sriov_vf_remove_vlan (vf, 100)); + g_assert (nm_sriov_vf_remove_vlan (vf, 82)); + nm_sriov_vf_set_vlan_qos (vf, 81, 0xabba); + nm_sriov_vf_set_vlan_protocol (vf, 81, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD); + + vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num); + g_assert (vlan_ids); + g_assert_cmpint (num, ==, 3); + g_assert_cmpint (vlan_ids[0], ==, 80); + g_assert_cmpint (vlan_ids[1], ==, 81); + g_assert_cmpint (vlan_ids[2], ==, 83); + g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 80), ==, 0x0); + g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 80), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 0xabba); + g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD); + + nm_sriov_vf_unref (vf); + + vf = nm_utils_sriov_vf_from_str ("20 spoof-check=false vlans=85.0.q;4000.0x20.ad;81.10;83", &error); + nmtst_assert_success (vf, error); + vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num); + g_assert (vlan_ids); + g_assert_cmpint (num, ==, 4); + g_assert_cmpint (vlan_ids[0], ==, 81); + g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 81), ==, 10); + g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 81), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + g_assert_cmpint (vlan_ids[1], ==, 83); + g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 83), ==, 0); + g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 83), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + g_assert_cmpint (vlan_ids[2], ==, 85); + g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 85), ==, 0); + g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 85), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + g_assert_cmpint (vlan_ids[3], ==, 4000); + g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, 4000), ==, 0x20); + g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, 4000), ==, NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD); + + str = nm_utils_sriov_vf_to_str (vf, FALSE, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "20 spoof-check=false vlans=81.10;83;85;4000.32.ad"); + + nm_sriov_vf_unref (vf); +} + +static void +test_sriov_setting (void) +{ + gs_unref_object NMConnection *con = NULL; + NMSettingConnection *s_con; + NMSettingSriov *s_sriov = NULL; + NMSriovVF *vf1, *vf2, *vf3; + GError *error = NULL; + gboolean success; + + con = nm_simple_connection_new (); + + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (con, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test SR-IOV connection", + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0", + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + nm_connection_add_setting (con, nm_setting_wired_new ()); + + s_sriov = (NMSettingSriov *) nm_setting_sriov_new (); + nm_connection_add_setting (con, NM_SETTING (s_sriov)); + + g_object_set (s_sriov, NM_SETTING_SRIOV_TOTAL_VFS, 16, NULL); + nm_setting_sriov_add_vf (s_sriov, (vf1 = nm_sriov_vf_new (0))); + nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (4))); + nm_setting_sriov_add_vf (s_sriov, (vf3 = nm_sriov_vf_new (10))); + g_assert (nm_setting_sriov_remove_vf_by_index (s_sriov, 4)); + nm_sriov_vf_unref (vf2); + nm_setting_sriov_add_vf (s_sriov, (vf2 = nm_sriov_vf_new (2))); + + nmtst_assert_connection_verifies_and_normalizable (con); + nmtst_connection_normalize (con); + success = nm_setting_verify ((NMSetting *) s_sriov, con, &error); + nmtst_assert_success (success, error); + + g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3); + g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 0)), ==, 0); + g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 1)), ==, 2); + g_assert_cmpint (nm_sriov_vf_get_index (nm_setting_sriov_get_vf (s_sriov, 2)), ==, 10); + + nm_sriov_vf_unref (vf1); + nm_sriov_vf_unref (vf2); + nm_sriov_vf_unref (vf3); +} + +typedef struct { + guint id; + guint qos; + bool proto_ad; +} VlanData; + +static void +_test_sriov_parse_vlan_one (const char *string, gboolean exp_res, VlanData *data, guint data_length) +{ + NMSriovVF *vf; + gboolean res; + guint i, num_vlans; + const guint *vlan_ids; + + vf = nm_sriov_vf_new (1); + g_assert (vf); + + res = _nm_sriov_vf_parse_vlans (vf, string, NULL); + g_assert_cmpint (res, ==, exp_res); + + if (exp_res) { + vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans); + g_assert_cmpint (num_vlans, ==, data_length); + for (i = 0; i < num_vlans; i++) { + g_assert_cmpint (vlan_ids[i], ==, data[i].id); + g_assert_cmpint (nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]), ==, data[i].qos); + g_assert_cmpint (nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]), + ==, + data[i].proto_ad ? NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD: NM_SRIOV_VF_VLAN_PROTOCOL_802_1Q); + } + } + + nm_sriov_vf_unref (vf); +} + +#define test_sriov_parse_vlan_one(string, result, ...) \ + { \ + VlanData _data[] = { __VA_ARGS__ }; \ + guint _length = G_N_ELEMENTS (_data); \ + \ + _test_sriov_parse_vlan_one (string, result, _data, _length); \ + } + +static void +test_sriov_parse_vlans (void) +{ + test_sriov_parse_vlan_one ("", FALSE, {}); + test_sriov_parse_vlan_one ("1", TRUE, {1, 0, 0}); + test_sriov_parse_vlan_one ("1;2", TRUE, {1, 0, 0}, {2, 0, 0}); + test_sriov_parse_vlan_one ("4095;;2", TRUE, {2, 0, 0}, {4095, 0, 0}); + test_sriov_parse_vlan_one ("1 2", FALSE, {}); + test_sriov_parse_vlan_one ("4096", FALSE, {}); + test_sriov_parse_vlan_one ("1.10", TRUE, {1, 10, 0}); + test_sriov_parse_vlan_one ("1.20.ad", TRUE, {1, 20, 1}); + test_sriov_parse_vlan_one ("1.21.q", TRUE, {1, 21, 0}); + test_sriov_parse_vlan_one ("9.20.foo", FALSE, {}); + test_sriov_parse_vlan_one ("1.20.ad.12", FALSE, {}); + test_sriov_parse_vlan_one ("1;1.10", FALSE, {}); + test_sriov_parse_vlan_one ("1..1;2", FALSE, {}); + test_sriov_parse_vlan_one ("1..ad;2", FALSE, {}); + test_sriov_parse_vlan_one ("1.2.ad;2.0.q;5;3", TRUE, {1, 2, 1}, {2, 0, 0}, {3, 0, 0}, {5, 0, 0}); +} + +/*****************************************************************************/ + +static void test_tc_config_qdisc (void) { NMTCQdisc *qdisc1, *qdisc2; @@ -1669,6 +1899,12 @@ main (int argc, char **argv) g_test_add_func ("/libnm/settings/dcb/priorities", test_dcb_priorities_valid); g_test_add_func ("/libnm/settings/dcb/bandwidth-sums", test_dcb_bandwidth_sums); + g_test_add_func ("/libnm/settings/sriov/vf", test_sriov_vf); + g_test_add_func ("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup); + g_test_add_func ("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan); + g_test_add_func ("/libnm/settings/sriov/setting", test_sriov_setting); + g_test_add_func ("/libnm/settings/sriov/vlans", test_sriov_parse_vlans); + g_test_add_func ("/libnm/settings/tc_config/qdisc", test_tc_config_qdisc); g_test_add_func ("/libnm/settings/tc_config/action", test_tc_config_action); g_test_add_func ("/libnm/settings/tc_config/tfilter", test_tc_config_tfilter); diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h index 73186f74ea..9cecef3acb 100644 --- a/libnm/NetworkManager.h +++ b/libnm/NetworkManager.h @@ -89,6 +89,7 @@ #include "nm-setting-pppoe.h" #include "nm-setting-proxy.h" #include "nm-setting-serial.h" +#include "nm-setting-sriov.h" #include "nm-setting-tc-config.h" #include "nm-setting-team.h" #include "nm-setting-team-port.h" diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 89a9d0da62..d10bf54191 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1381,10 +1381,41 @@ libnm_1_12_2 { libnm_1_14_0 { global: nm_connection_get_setting_6lowpan; + nm_connection_get_setting_sriov; nm_connection_get_setting_wpan; nm_device_6lowpan_get_type; nm_device_wpan_get_type; nm_setting_6lowpan_get_type; + nm_setting_sriov_add_vf; + nm_setting_sriov_clear_vfs; + nm_setting_sriov_get_autoprobe_drivers; + nm_setting_sriov_get_num_vfs; + nm_setting_sriov_get_total_vfs; + nm_setting_sriov_get_type; + nm_setting_sriov_get_vf; + nm_setting_sriov_new; + nm_setting_sriov_remove_vf; + nm_setting_sriov_remove_vf_by_index; nm_setting_wpan_get_type; + nm_sriov_vf_add_vlan; + nm_sriov_vf_dup; + nm_sriov_vf_equal; + nm_sriov_vf_get_attribute; + nm_sriov_vf_get_attribute_names; + nm_sriov_vf_get_index; + nm_sriov_vf_get_type; + nm_sriov_vf_get_vlan_ids; + nm_sriov_vf_get_vlan_protocol; + nm_sriov_vf_get_vlan_qos; + nm_sriov_vf_new; + nm_sriov_vf_ref; + nm_sriov_vf_remove_vlan; + nm_sriov_vf_set_attribute; + nm_sriov_vf_set_vlan_protocol; + nm_sriov_vf_set_vlan_qos; + nm_sriov_vf_unref; + nm_sriov_vf_vlan_protocol_get_type; nm_ternary_get_type; + nm_utils_sriov_vf_from_str; + nm_utils_sriov_vf_to_str; } libnm_1_12_0; diff --git a/po/POTFILES.in b/po/POTFILES.in index f432d5f0d2..fafc4225d2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -83,6 +83,7 @@ libnm-core/nm-setting-ovs-bridge.c libnm-core/nm-setting-ppp.c libnm-core/nm-setting-pppoe.c libnm-core/nm-setting-proxy.c +libnm-core/nm-setting-sriov.c libnm-core/nm-setting-tc-config.c libnm-core/nm-setting-team.c libnm-core/nm-setting-team-port.c |