summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2018-05-25 12:05:24 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2018-07-11 16:16:22 +0200
commita9b4532fa77d75f2dd40cbbd2a5184df6ec0d387 (patch)
tree79f09f47a61403d671df1cab07b62cdad4184ee3
parentd6483592b8a69b8f5e78e455f40b75b912037f34 (diff)
downloadNetworkManager-a9b4532fa77d75f2dd40cbbd2a5184df6ec0d387.tar.gz
libnm-core: add SR-IOV setting
Add a setting containing SR-IOV parameters.
-rw-r--r--Makefile.am2
-rw-r--r--clients/common/settings-docs.h.in4
-rw-r--r--docs/libnm/libnm-docs.xml1
-rw-r--r--libnm-core/meson.build2
-rw-r--r--libnm-core/nm-connection.c29
-rw-r--r--libnm-core/nm-connection.h2
-rw-r--r--libnm-core/nm-core-internal.h7
-rw-r--r--libnm-core/nm-core-types.h1
-rw-r--r--libnm-core/nm-keyfile.c83
-rw-r--r--libnm-core/nm-setting-sriov.c1331
-rw-r--r--libnm-core/nm-setting-sriov.h123
-rw-r--r--libnm-core/nm-setting.c1
-rw-r--r--libnm-core/nm-utils-private.h1
-rw-r--r--libnm-core/nm-utils.c243
-rw-r--r--libnm-core/nm-utils.h11
-rw-r--r--libnm-core/tests/test-setting.c236
-rw-r--r--libnm/NetworkManager.h1
-rw-r--r--libnm/libnm.ver31
-rw-r--r--po/POTFILES.in1
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