From fe07d6a404c05cea7d5e13fca8751ac82a7cff0f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 28 Jun 2018 17:12:54 +0200 Subject: [PATCH 01/14] shared: add nm_hash_val() macro A helper macro, to combine the steps for hashing one value. --- shared/nm-utils/nm-hash-utils.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h index d0bb6cf9b8..7d9620b96c 100644 --- a/shared/nm-utils/nm-hash-utils.h +++ b/shared/nm-utils/nm-hash-utils.h @@ -209,6 +209,15 @@ guint nm_direct_hash (gconstpointer str); guint nm_hash_str (const char *str); guint nm_str_hash (gconstpointer str); +#define nm_hash_val(static_seed, val) \ + ({ \ + NMHashState _h; \ + \ + nm_hash_init (&_h, static_seed); \ + nm_hash_update_val (&_h, val); \ + nm_hash_complete (&_h); \ + }) + /*****************************************************************************/ /* nm_pstr_*() are for hashing keys that are pointers to strings, From 634b2d1ce2687d724add893836f06beacd5a3700 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 11 Jul 2018 14:06:28 +0200 Subject: [PATCH 02/14] shared: add nm_auto_vfree macro This really is the same as gs_strfreev / g_strfreev(). However, the difference is, that the former has the notion of freeing strv arrays (char **), while this in general frees an array of pointers. Implementation-wise, they are the same. --- shared/nm-utils/nm-macros-internal.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index e60bacebdc..5de0536796 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -161,6 +161,23 @@ _nm_auto_unref_gsource (GSource **ptr) } #define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource) +static inline void +_nm_auto_freev (gpointer ptr) +{ + gpointer **p = ptr; + gpointer *_ptr; + + if (*p) { + for (_ptr = *p; *_ptr; _ptr++) + g_free (*_ptr); + g_free (*p); + } +} +/* g_free a NULL terminated array of pointers, with also freeing each + * pointer with g_free(). It essentially does the same as + * gs_strfreev / g_strfreev(), but not restricted to strv arrays. */ +#define nm_auto_freev nm_auto(_nm_auto_freev) + /*****************************************************************************/ /* http://stackoverflow.com/a/11172679 */ From d7382fbe93c60dfe133e14f6289eb82c1303f76b Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 18 Jun 2018 18:46:52 +0200 Subject: [PATCH 03/14] nm-utils: add _nm_utils_format_variant_attributes_full() --- libnm-core/nm-utils-private.h | 6 +++ libnm-core/nm-utils.c | 80 ++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 5d0f83868e..58539cb301 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -83,6 +83,12 @@ char * _nm_utils_hwaddr_canonical_or_invalid (const char *mac, gssize lengt GPtrArray * _nm_utils_team_link_watchers_from_variant (GVariant *value); GVariant * _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers); +void _nm_utils_format_variant_attributes_full (GString *str, + const NMUtilsNamedValue *values, + guint num_values, + char attr_separator, + char key_value_separator); + /* JSON to GValue conversion macros */ typedef struct { diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 62da974f19..9247d7f8a0 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -6156,44 +6156,21 @@ next: return g_steal_pointer (&ht); } -/* - * nm_utils_format_variant_attributes: - * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values - * @attr_separator: the attribute separator character - * @key_value_separator: character separating key and values - * - * Format attributes to a string. - * - * Returns: (transfer full): the string representing attributes, or %NULL - * in case there are no attributes - * - * Since: 1.8 - */ -char * -nm_utils_format_variant_attributes (GHashTable *attributes, - char attr_separator, - char key_value_separator) +void +_nm_utils_format_variant_attributes_full (GString *str, + const NMUtilsNamedValue *values, + guint num_values, + char attr_separator, + char key_value_separator) { - GString *str = NULL; - GVariant *variant; - char sep = 0; const char *name, *value; + GVariant *variant; char *escaped; char buf[64]; - gs_free NMUtilsNamedValue *values = NULL; - guint i, len; + char sep = 0; + guint i; - g_return_val_if_fail (attr_separator, NULL); - g_return_val_if_fail (key_value_separator, NULL); - - if (!attributes || !g_hash_table_size (attributes)) - return NULL; - - values = nm_utils_named_values_from_str_dict (attributes, &len); - - str = g_string_new (""); - - for (i = 0; i < len; i++) { + for (i = 0; i < num_values; i++) { name = values[i].name; variant = (GVariant *) values[i].value_ptr; value = NULL; @@ -6226,7 +6203,44 @@ nm_utils_format_variant_attributes (GHashTable *attributes, sep = attr_separator; } +} +/* + * nm_utils_format_variant_attributes: + * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values + * @attr_separator: the attribute separator character + * @key_value_separator: character separating key and values + * + * Format attributes to a string. + * + * Returns: (transfer full): the string representing attributes, or %NULL + * in case there are no attributes + * + * Since: 1.8 + */ +char * +nm_utils_format_variant_attributes (GHashTable *attributes, + char attr_separator, + char key_value_separator) +{ + GString *str = NULL; + gs_free NMUtilsNamedValue *values = NULL; + guint len; + + g_return_val_if_fail (attr_separator, NULL); + g_return_val_if_fail (key_value_separator, NULL); + + if (!attributes || !g_hash_table_size (attributes)) + return NULL; + + values = nm_utils_named_values_from_str_dict (attributes, &len); + + str = g_string_new (""); + _nm_utils_format_variant_attributes_full (str, + values, + len, + attr_separator, + key_value_separator); return g_string_free (str, FALSE); } From 347e0d8b5a0f172c80fc230b930117001956459e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 4 Jun 2018 15:11:57 +0200 Subject: [PATCH 04/14] ifcfg-rh: add @match_key_type argument to svGetKeys() Add a @match_key_type to svGetKeys() to filter the keys to be returned. --- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 5 +- src/settings/plugins/ifcfg-rh/shvar.c | 107 ++++++++++-------- src/settings/plugins/ifcfg-rh/shvar.h | 19 ++-- 3 files changed, 68 insertions(+), 63 deletions(-) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 57394791a6..882932b11a 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1178,7 +1178,7 @@ make_user_setting (shvarFile *ifcfg) const char *key; nm_auto_free_gstring GString *str = NULL; - keys = svGetKeys (ifcfg); + keys = svGetKeys (ifcfg, SV_KEY_TYPE_USER); if (!keys) return NULL; @@ -1187,9 +1187,6 @@ make_user_setting (shvarFile *ifcfg) const char *value; gs_free char *value_to_free = NULL; - if (!g_str_has_prefix (key, "NM_USER_")) - continue; - value = svGetValue (ifcfg, key, &value_to_free); if (!value) diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index c739b19dec..e8d028094b 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -870,8 +870,57 @@ svCreateFile (const char *name) /*****************************************************************************/ +static gboolean +_is_all_digits (const char *str) +{ + return str[0] + && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch)); +} + +#define IS_NUMBERED_TAG(key, tab_name) \ + ({ \ + const char *_key = (key); \ + \ + ( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \ + && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \ + }) + +static gboolean +_svKeyMatchesType (const char *key, SvKeyType match_key_type) +{ + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY)) + return TRUE; + + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) { + if ( IS_NUMBERED_TAG (key, "ADDRESS") + || IS_NUMBERED_TAG (key, "NETMASK") + || IS_NUMBERED_TAG (key, "GATEWAY") + || IS_NUMBERED_TAG (key, "METRIC") + || IS_NUMBERED_TAG (key, "OPTIONS")) + return TRUE; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) { + if ( IS_NUMBERED_TAG (key, "IPADDR") + || IS_NUMBERED_TAG (key, "PREFIX") + || IS_NUMBERED_TAG (key, "NETMASK") + || IS_NUMBERED_TAG (key, "GATEWAY")) + return TRUE; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) { + if (g_str_has_prefix (key, "NM_USER_")) + return TRUE; + } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) { + if ( IS_NUMBERED_TAG (key, "QDISC") + || IS_NUMBERED_TAG (key, "FILTER")) + return TRUE; + } + + return FALSE; +} + GHashTable * -svGetKeys (shvarFile *s) +svGetKeys (shvarFile *s, SvKeyType match_key_type) { GHashTable *keys = NULL; CList *current; @@ -881,7 +930,9 @@ svGetKeys (shvarFile *s) c_list_for_each (current, &s->lst_head) { line = c_list_entry (current, shvarLine, lst); - if (line->key && line->line) { + if ( line->key + && line->line + && _svKeyMatchesType (line->key, match_key_type)) { /* we don't clone the keys. The keys are only valid * until @s gets modified. */ if (!keys) @@ -1120,21 +1171,6 @@ svGetValueEnum (shvarFile *s, const char *key, /*****************************************************************************/ -static gboolean -_is_all_digits (const char *str) -{ - return str[0] - && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch)); -} - -#define IS_NUMBERED_TAG(key, tab_name) \ - ({ \ - const char *_key = (key); \ - \ - ( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \ - && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \ - }) - gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type) { @@ -1150,38 +1186,11 @@ svUnsetAll (shvarFile *s, SvKeyType match_key_type) if (!line->key) continue; - if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY)) - goto do_clear; - if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) { - if ( IS_NUMBERED_TAG (line->key, "ADDRESS") - || IS_NUMBERED_TAG (line->key, "NETMASK") - || IS_NUMBERED_TAG (line->key, "GATEWAY") - || IS_NUMBERED_TAG (line->key, "METRIC") - || IS_NUMBERED_TAG (line->key, "OPTIONS")) - goto do_clear; - } - if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) { - if ( IS_NUMBERED_TAG (line->key, "IPADDR") - || IS_NUMBERED_TAG (line->key, "PREFIX") - || IS_NUMBERED_TAG (line->key, "NETMASK") - || IS_NUMBERED_TAG (line->key, "GATEWAY")) - goto do_clear; - } - if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) { - if (g_str_has_prefix (line->key, "NM_USER_")) - goto do_clear; - } - if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) { - if ( IS_NUMBERED_TAG (line->key, "QDISC") - || IS_NUMBERED_TAG (line->key, "FILTER")) - goto do_clear; - } - - continue; -do_clear: - if (nm_clear_g_free (&line->line)) { - ASSERT_shvarLine (line); - changed = TRUE; + if (_svKeyMatchesType (line->key, match_key_type)) { + if (nm_clear_g_free (&line->line)) { + ASSERT_shvarLine (line); + changed = TRUE; + } } } diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 838c79cc16..bef5f47743 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -33,6 +33,14 @@ typedef struct _shvarFile shvarFile; +typedef enum { + SV_KEY_TYPE_ANY = (1LL << 0), + SV_KEY_TYPE_ROUTE_SVFORMAT = (1LL << 1), + SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2), + SV_KEY_TYPE_TC = (1LL << 3), + SV_KEY_TYPE_USER = (1LL << 4), +} SvKeyType; + const char *svFileGetName (const shvarFile *s); void _nmtst_svFileSetName (shvarFile *s, const char *fileName); @@ -58,7 +66,7 @@ char *svGetValueStr_cp (shvarFile *s, const char *key); int svParseBoolean (const char *value, int def); -GHashTable *svGetKeys (shvarFile *s); +GHashTable *svGetKeys (shvarFile *s, SvKeyType match_key_type); /* return TRUE if resolves to any truth value (e.g. "yes", "y", "true") * return FALSE if resolves to any non-truth value (e.g. "no", "n", "false") @@ -85,15 +93,6 @@ gboolean svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, g gboolean svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value); gboolean svUnsetValue (shvarFile *s, const char *key); - -typedef enum { - SV_KEY_TYPE_ANY = (1LL << 0), - SV_KEY_TYPE_ROUTE_SVFORMAT = (1LL << 1), - SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2), - SV_KEY_TYPE_TC = (1LL << 3), - SV_KEY_TYPE_USER = (1LL << 4), -} SvKeyType; - gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type); /* Write the current contents iff modified. Returns FALSE on error From a2846bd735ee6c1e7af73e1ef7dd32749b46a184 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 28 May 2018 15:31:11 +0200 Subject: [PATCH 05/14] libnm-core: don't emit signal when clearing lists already empty If the property is a list and it is already empty, we should not emit a signal when it gets cleared. --- libnm-core/nm-setting-ip-config.c | 26 ++++++++++++++++++-------- libnm-core/nm-setting-tc-config.c | 12 ++++++++---- libnm-core/nm-setting-team-port.c | 6 ++++-- libnm-core/nm-setting-team.c | 6 ++++-- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index 8e2df0c321..fa5aa60f1a 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -1589,8 +1589,11 @@ nm_setting_ip_config_clear_dns (NMSettingIPConfig *setting) g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting)); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting); - g_ptr_array_set_size (priv->dns, 0); - g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS); + + if (priv->dns->len != 0) { + g_ptr_array_set_size (priv->dns, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS); + } } /** @@ -1727,8 +1730,11 @@ nm_setting_ip_config_clear_dns_searches (NMSettingIPConfig *setting) g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting)); priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting); - g_ptr_array_set_size (priv->dns_search, 0); - g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH); + + if (priv->dns_search->len != 0) { + g_ptr_array_set_size (priv->dns_search, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH); + } } /** @@ -2107,8 +2113,10 @@ nm_setting_ip_config_clear_addresses (NMSettingIPConfig *setting) g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting)); - g_ptr_array_set_size (priv->addresses, 0); - g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES); + if (priv->addresses->len != 0) { + g_ptr_array_set_size (priv->addresses, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES); + } } /** @@ -2264,8 +2272,10 @@ nm_setting_ip_config_clear_routes (NMSettingIPConfig *setting) g_return_if_fail (NM_IS_SETTING_IP_CONFIG (setting)); - g_ptr_array_set_size (priv->routes, 0); - g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES); + if (priv->routes->len != 0) { + g_ptr_array_set_size (priv->routes, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES); + } } /** diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c index 04e2f2d510..b05bb38ebb 100644 --- a/libnm-core/nm-setting-tc-config.c +++ b/libnm-core/nm-setting-tc-config.c @@ -982,8 +982,10 @@ nm_setting_tc_config_clear_qdiscs (NMSettingTCConfig *self) { g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self)); - g_ptr_array_set_size (self->qdiscs, 0); - g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS); + if (self->qdiscs->len != 0) { + g_ptr_array_set_size (self->qdiscs, 0); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS); + } } /*****************************************************************************/ @@ -1116,8 +1118,10 @@ nm_setting_tc_config_clear_tfilters (NMSettingTCConfig *self) { g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self)); - g_ptr_array_set_size (self->tfilters, 0); - g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS); + if (self->tfilters->len != 0) { + g_ptr_array_set_size (self->tfilters, 0); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS); + } } /*****************************************************************************/ diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c index e0e5dacaa7..d6412224e2 100644 --- a/libnm-core/nm-setting-team-port.c +++ b/libnm-core/nm-setting-team-port.c @@ -322,8 +322,10 @@ nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting) g_return_if_fail (NM_IS_SETTING_TEAM_PORT (setting)); - g_ptr_array_set_size (priv->link_watchers, 0); - g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); + if (priv->link_watchers->len != 0) { + g_ptr_array_set_size (priv->link_watchers, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); + } } static GVariant * diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c index b413adaec2..1718b3e55f 100644 --- a/libnm-core/nm-setting-team.c +++ b/libnm-core/nm-setting-team.c @@ -1120,8 +1120,10 @@ nm_setting_team_clear_link_watchers (NMSettingTeam *setting) { g_return_if_fail (NM_IS_SETTING_TEAM (setting)); - g_ptr_array_set_size (priv->link_watchers, 0); - g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); + if (priv->link_watchers->len != 0) { + g_ptr_array_set_size (priv->link_watchers, 0); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); + } } static GVariant * From d3db846d95b11d75eb8bba45f503a888ac26233f Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 28 May 2018 18:13:42 +0200 Subject: [PATCH 06/14] ifcfg-rh: add missing NM-only annotations Add the (+) annotation to NM-only variables. --- libnm-core/nm-setting-ip4-config.c | 2 +- libnm-core/nm-setting-ip6-config.c | 2 +- libnm-core/nm-setting-tc-config.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c index c7d80a9fdc..a88a3f2b9c 100644 --- a/libnm-core/nm-setting-ip4-config.c +++ b/libnm-core/nm-setting-ip4-config.c @@ -746,7 +746,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class) /* ---ifcfg-rh--- * property: dad-timeout - * variable: ACD_TIMEOUT, ARPING_WAIT + * variable: ACD_TIMEOUT(+), ARPING_WAIT * default: missing variable means global default (config override or zero) * description: Timeout (in milliseconds for ACD_TIMEOUT or in seconds * for ARPING_WAIT) for address conflict detection before configuring diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c index d483d1921a..76a68aa085 100644 --- a/libnm-core/nm-setting-ip6-config.c +++ b/libnm-core/nm-setting-ip6-config.c @@ -589,7 +589,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class) /* ---ifcfg-rh--- * property: dns-search - * variable: IPV6_DOMAIN + * variable: IPV6_DOMAIN(+) * format: string (space-separated domains) * description: List of DNS search domains. * ---end--- diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c index b05bb38ebb..41db056794 100644 --- a/libnm-core/nm-setting-tc-config.c +++ b/libnm-core/nm-setting-tc-config.c @@ -1610,7 +1610,7 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class) **/ /* ---ifcfg-rh--- * property: qdiscs - * variable: QDISC1, QDISC2, ... + * variable: QDISC1(+), QDISC2(+), ... * description: Queueing disciplines * example: QDISC1=ingress, QDISC2="root handle 1234: fq_codel" * ---end--- @@ -1637,7 +1637,7 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class) **/ /* ---ifcfg-rh--- * property: qdiscs - * variable: FILTER1, FILTER2, ... + * variable: FILTER1(+), FILTER2(+), ... * description: Traffic filters * example: FILTER1="parent ffff: matchall action simple sdata Input", ... * ---end--- From 507d078635f580a3cb9b33311fd93ba7408c080c Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 16 May 2018 09:41:45 +0200 Subject: [PATCH 07/14] libnm-core: tc: fix indentation and typo --- clients/common/settings-docs.h.in | 2 +- libnm-core/nm-setting-tc-config.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index bde1d1900a..30b48a9cf8 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -324,7 +324,7 @@ #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_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 queuening disciplines.") +#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.") #define DESCRIBE_DOC_NM_SETTING_TEAM_CONFIG N_("The JSON configuration for the team network interface. The property should contain raw JSON configuration data suitable for teamd, because the value is passed directly to teamd. If not specified, the default configuration is used. See man teamd.conf for the format details.") #define DESCRIBE_DOC_NM_SETTING_TEAM_LINK_WATCHERS N_("Link watchers configuration for the connection: each link watcher is defined by a dictionary, whose keys depend upon the selected link watcher. Available link watchers are 'ethtool', 'nsna_ping' and 'arp_ping' and it is specified in the dictionary with the key 'name'. Available keys are: ethtool: 'delay-up', 'delay-down', 'init-wait'; nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host'; arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active', 'validate-incative', 'send-always'. See teamd.conf man for more details.") diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c index 41db056794..588446e5dd 100644 --- a/libnm-core/nm-setting-tc-config.c +++ b/libnm-core/nm-setting-tc-config.c @@ -820,18 +820,18 @@ enum { /** * NMSettingTCConfig: * - * Linux Traffic Contril Settings. + * Linux Traffic Control Settings. * * Since: 1.12 */ struct _NMSettingTCConfig { - NMSetting parent; + NMSetting parent; GPtrArray *qdiscs; GPtrArray *tfilters; }; struct _NMSettingTCConfigClass { - NMSettingClass parent; + NMSettingClass parent; }; G_DEFINE_TYPE_WITH_CODE (NMSettingTCConfig, nm_setting_tc_config, NM_TYPE_SETTING, @@ -1606,7 +1606,7 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class) /** * NMSettingTCConfig:qdiscs: (type GPtrArray(NMTCQdisc)) * - * Array of TC queuening disciplines. + * Array of TC queueing disciplines. **/ /* ---ifcfg-rh--- * property: qdiscs From d6483592b8a69b8f5e78e455f40b75b912037f34 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 24 May 2018 13:43:32 +0200 Subject: [PATCH 08/14] libnm: introduce NMTernary enum Add a new enum that can be used where we need a boolean value that can be overridden globally. --- libnm-core/nm-dbus-interface.h | 16 ++++++++++++++++ libnm/libnm.ver | 1 + 2 files changed, 17 insertions(+) diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 94b4c28d75..0b451d8446 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -991,4 +991,20 @@ typedef enum { /*< flags >*/ NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT = (1LL << 5), } NMSettingsUpdate2Flags; +/** + * NMTernary: + * @NM_TERNARY_DEFAULT: use the globally-configured default value. + * @NM_TERNARY_FALSE: the option is disabled. + * @NM_TERNARY_TRUE: the option is enabled. + * + * An boolean value that can be overridden by a default. + * + * Since: 1.14 + **/ +typedef enum { + NM_TERNARY_DEFAULT = -1, + NM_TERNARY_FALSE = 0, + NM_TERNARY_TRUE = 1, +} NMTernary; + #endif /* __NM_DBUS_INTERFACE_H__ */ diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 06592f3ab9..89a9d0da62 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1386,4 +1386,5 @@ global: nm_device_wpan_get_type; nm_setting_6lowpan_get_type; nm_setting_wpan_get_type; + nm_ternary_get_type; } libnm_1_12_0; From a9b4532fa77d75f2dd40cbbd2a5184df6ec0d387 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 25 May 2018 12:05:24 +0200 Subject: [PATCH 09/14] libnm-core: add SR-IOV setting Add a setting containing SR-IOV parameters. --- Makefile.am | 2 + clients/common/settings-docs.h.in | 4 + docs/libnm/libnm-docs.xml | 1 + libnm-core/meson.build | 2 + libnm-core/nm-connection.c | 29 + libnm-core/nm-connection.h | 2 + libnm-core/nm-core-internal.h | 7 + libnm-core/nm-core-types.h | 1 + libnm-core/nm-keyfile.c | 83 ++ libnm-core/nm-setting-sriov.c | 1331 +++++++++++++++++++++++++++++ libnm-core/nm-setting-sriov.h | 123 +++ libnm-core/nm-setting.c | 1 + libnm-core/nm-utils-private.h | 1 + libnm-core/nm-utils.c | 243 ++++++ libnm-core/nm-utils.h | 11 +- libnm-core/tests/test-setting.c | 236 +++++ libnm/NetworkManager.h | 1 + libnm/libnm.ver | 31 + po/POTFILES.in | 1 + 19 files changed, 2109 insertions(+), 1 deletion(-) create mode 100644 libnm-core/nm-setting-sriov.c create mode 100644 libnm-core/nm-setting-sriov.h 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())]]> + 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 @@ -1177,6 +1177,18 @@ _normalize_ip_tunnel_wired_setting (NMConnection *self, GHashTable *parameters) return FALSE; } +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) { @@ -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); @@ -2626,6 +2639,22 @@ nm_connection_get_setting_serial (NMConnection *connection) return _connection_get_setting_check (connection, NM_TYPE_SETTING_SERIAL); } +/** + * 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 @@ -118,6 +118,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, @@ -1509,6 +1552,37 @@ setting_alias_writer (KeyfileWriterInfo *info, alias ?: str); } +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, @@ -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 + * . + * + * 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 + * . + * + * 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 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 #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 #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" @@ -1262,6 +1264,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) { @@ -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 From a2f12994b7cd516adafc5b80fef574d28e9fa28e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 24 May 2018 17:48:37 +0200 Subject: [PATCH 10/14] cli: add support for configuring SR-IOV --- clients/cli/connections.c | 3 +- clients/common/nm-meta-setting-desc.c | 104 ++++++++++++++++++++++++++ shared/nm-meta-setting.c | 5 ++ shared/nm-meta-setting.h | 1 + 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index c8af47c0f6..9e6859dad4 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -769,7 +769,8 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO NM_SETTING_WPAN_SETTING_NAME","\ NM_SETTING_6LOWPAN_SETTING_NAME","\ NM_SETTING_PROXY_SETTING_NAME"," \ - NM_SETTING_TC_CONFIG_SETTING_NAME + NM_SETTING_TC_CONFIG_SETTING_NAME"," \ + NM_SETTING_SRIOV_SETTING_NAME // NM_SETTING_DUMMY_SETTING_NAME // NM_SETTING_WIMAX_SETTING_NAME diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 519f3c856c..a52fe3ee38 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -3774,6 +3774,37 @@ _validate_fcn_proxy_pac_script (const char *value, char **out_to_free, GError ** RETURN_STR_TO_FREE (script); } +static gconstpointer +_get_fcn_sriov_vfs (ARGS_GET_FCN) +{ + NMSettingSriov *s_sriov = NM_SETTING_SRIOV (setting); + GString *printable; + guint num_vfs, i; + NMSriovVF *vf; + char *str; + + RETURN_UNSUPPORTED_GET_TYPE (); + + printable = g_string_new (NULL); + + num_vfs = nm_setting_sriov_get_num_vfs (s_sriov); + for (i = 0; i < num_vfs; i++) { + vf = nm_setting_sriov_get_vf (s_sriov, i); + + if (printable->len > 0) + g_string_append (printable, ", "); + + str = nm_utils_sriov_vf_to_str (vf, FALSE, NULL); + if (str) { + g_string_append (printable, str); + g_free (str); + } + } + + NM_SET_OUT (out_is_default, num_vfs == 0); + RETURN_STR_TO_FREE (g_string_free (printable, FALSE)); +} + static gconstpointer _get_fcn_tc_config_qdiscs (ARGS_GET_FCN) { @@ -3805,6 +3836,28 @@ _get_fcn_tc_config_qdiscs (ARGS_GET_FCN) RETURN_STR_TO_FREE (g_string_free (printable, FALSE)); } +static gboolean +_set_fcn_sriov_vfs (ARGS_SET_FCN) +{ + gs_free const char **strv = NULL; + const char *const*iter; + NMSriovVF *vf; + GError *local = NULL; + + strv = nm_utils_strsplit_set (value, ","); + for (iter = strv; strv && *iter; iter++) { + vf = nm_utils_sriov_vf_from_str (*iter, &local); + if (!vf) { + g_set_error (error, 1, 0, "%s. %s", local->message, + _("The valid syntax is: vf [attribute=value]... [,vf [attribute=value]...]")); + return FALSE; + } + nm_setting_sriov_add_vf (NM_SETTING_SRIOV (setting), vf); + nm_sriov_vf_unref (vf); + } + return TRUE; +} + static gboolean _set_fcn_tc_config_qdiscs (ARGS_SET_FCN) { @@ -3827,6 +3880,34 @@ _set_fcn_tc_config_qdiscs (ARGS_SET_FCN) return TRUE; } +static gboolean +_validate_and_remove_sriov_vf (NMSettingSriov *setting, + const char *value, + GError **error) +{ + NMSriovVF *vf; + gboolean ret; + + vf = nm_utils_sriov_vf_from_str (value, error); + if (!vf) + return FALSE; + + ret = nm_setting_sriov_remove_vf_by_index (setting, nm_sriov_vf_get_index (vf)); + if (!ret) { + g_set_error (error, 1, 0, + _("the property doesn't contain vf with index %u"), + nm_sriov_vf_get_index (vf)); + } + nm_sriov_vf_unref (vf); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_sriov_vfs, + NM_SETTING_SRIOV, + nm_setting_sriov_get_num_vfs, + nm_setting_sriov_remove_vf, + _validate_and_remove_sriov_vf) + + static gboolean _validate_and_remove_tc_qdisc (NMSettingTCConfig *setting, const char *value, @@ -6831,6 +6912,25 @@ static const NMMetaPropertyInfo *const property_infos_SERIAL[] = { NULL }; +#undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_SRIOV +static const NMMetaPropertyInfo *const property_infos_SRIOV[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_TOTAL_VFS, + .property_type = &_pt_gobject_int, + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_VFS, + .property_type = DEFINE_PROPERTY_TYPE ( + .get_fcn = _get_fcn_sriov_vfs, + .set_fcn = _set_fcn_sriov_vfs, + .remove_fcn = _remove_fcn_sriov_vfs, + ), + ), + PROPERTY_INFO_WITH_DESC (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, + .property_type = &_pt_gobject_enum, + ), + NULL +}; + #undef _CURRENT_NM_META_SETTING_TYPE #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TUN static const NMMetaPropertyInfo *const property_infos_TUN[] = { @@ -7649,6 +7749,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN) #define SETTING_PRETTY_NAME_PPPOE N_("PPPoE") #define SETTING_PRETTY_NAME_PROXY N_("Proxy") #define SETTING_PRETTY_NAME_SERIAL N_("Serial settings") +#define SETTING_PRETTY_NAME_SRIOV N_("SR-IOV settings") #define SETTING_PRETTY_NAME_TC_CONFIG N_("Traffic controls") #define SETTING_PRETTY_NAME_TEAM N_("Team device") #define SETTING_PRETTY_NAME_TEAM_PORT N_("Team port") @@ -7765,6 +7866,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { .valid_parts = NM_META_SETTING_VALID_PARTS ( NM_META_SETTING_VALID_PART_ITEM (CONNECTION, TRUE), NM_META_SETTING_VALID_PART_ITEM (INFINIBAND, TRUE), + NM_META_SETTING_VALID_PART_ITEM (SRIOV, FALSE), ), .setting_init_fcn = _setting_init_fcn_infiniband, ), @@ -7844,6 +7946,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { .setting_init_fcn = _setting_init_fcn_proxy, ), SETTING_INFO (SERIAL), + SETTING_INFO (SRIOV), SETTING_INFO (TC_CONFIG), SETTING_INFO (TEAM, .valid_parts = NM_META_SETTING_VALID_PARTS ( @@ -7896,6 +7999,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (WIRED, TRUE), NM_META_SETTING_VALID_PART_ITEM (802_1X, FALSE), NM_META_SETTING_VALID_PART_ITEM (DCB, FALSE), + NM_META_SETTING_VALID_PART_ITEM (SRIOV, FALSE), ), ), SETTING_INFO (WIRELESS, diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index b984a1ff52..148f3753f9 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -287,6 +287,11 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_SERIAL_SETTING_NAME, .get_setting_gtype = nm_setting_serial_get_type, }, + [NM_META_SETTING_TYPE_SRIOV] = { + .meta_type = NM_META_SETTING_TYPE_SRIOV, + .setting_name = NM_SETTING_SRIOV_SETTING_NAME, + .get_setting_gtype = nm_setting_sriov_get_type, + }, [NM_META_SETTING_TYPE_TC_CONFIG] = { .meta_type = NM_META_SETTING_TYPE_TC_CONFIG, .setting_name = NM_SETTING_TC_CONFIG_SETTING_NAME, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index e8d4db9c92..dd4780c5d9 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -84,6 +84,7 @@ typedef enum { NM_META_SETTING_TYPE_PPPOE, NM_META_SETTING_TYPE_PROXY, NM_META_SETTING_TYPE_SERIAL, + NM_META_SETTING_TYPE_SRIOV, NM_META_SETTING_TYPE_TC_CONFIG, NM_META_SETTING_TYPE_TEAM, NM_META_SETTING_TYPE_TEAM_PORT, From 7df33338797fc335c245fed65ac1186284afc357 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 23 May 2018 14:11:14 +0200 Subject: [PATCH 11/14] platform: allow setting drivers-autoprobe on SR-IOV PFs It is possible to tell kernel not to automatically autoprobe drivers for VFs. This is useful, for example, if the VF must be used by a VM. --- src/devices/nm-device.c | 4 +- src/platform/nm-fake-platform.c | 7 --- src/platform/nm-linux-platform.c | 73 +++++++++++++++++++++----------- src/platform/nm-platform.c | 19 +++++++-- src/platform/nm-platform.h | 4 +- 5 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index aa2a389a5d..8844672ab5 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -3951,8 +3951,8 @@ device_init_sriov_num_vfs (NMDevice *self) NULL); num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1); if (num_vfs >= 0) { - nm_platform_link_set_sriov_num_vfs (nm_device_get_platform (self), - priv->ifindex, num_vfs); + nm_platform_link_set_sriov_params (nm_device_get_platform (self), + priv->ifindex, num_vfs, -1); } } } diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index f8f4627ed0..d8d192c712 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -602,12 +602,6 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) return NM_PLATFORM_ERROR_SUCCESS; } -static gboolean -link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs) -{ - return TRUE; -} - static const char * link_get_udi (NMPlatform *platform, int ifindex) { @@ -1422,7 +1416,6 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_set_address = link_set_address; platform_class->link_set_mtu = link_set_mtu; - platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs; platform_class->link_get_driver_info = link_get_driver_info; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index e02269f6eb..19821a87c3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -5528,16 +5528,18 @@ nla_put_failure: } static gboolean -link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs) +link_set_sriov_params (NMPlatform *platform, + int ifindex, + guint num_vfs, + int autoprobe) { nm_auto_pop_netns NMPNetns *netns = NULL; nm_auto_close int dirfd = -1; - int total, current; + gboolean current_autoprobe; + guint total, current_num; char ifname[IFNAMSIZ]; char buf[64]; - _LOGD ("link: change %d: num VFs: %u", ifindex, num_vfs); - if (!nm_platform_netns_push (platform, &netns)) return FALSE; @@ -5545,46 +5547,69 @@ link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs) if (!dirfd) return FALSE; - total = nm_platform_sysctl_get_int32 (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_totalvfs"), - -1); - if (total < 1) + total = nm_platform_sysctl_get_int_checked (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, + ifname, + "device/sriov_totalvfs"), + 10, 0, G_MAXUINT, 0); + if (errno) return FALSE; if (num_vfs > total) { _LOGW ("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs); num_vfs = total; } - current = nm_platform_sysctl_get_int32 (platform, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - -1); - if (current == num_vfs) + /* + * Take special care when setting new values: + * - don't touch anything if the right values are already set + * - to change the number of VFs or autoprobe we need to destroy existing VFs + * - the autoprobe setting is irrelevant when numvfs is zero + */ + current_num = nm_platform_sysctl_get_int_checked (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, + ifname, + "device/sriov_numvfs"), + 10, 0, G_MAXUINT, 0); + current_autoprobe = nm_platform_sysctl_get_int_checked (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, + ifname, + "device/sriov_drivers_autoprobe"), + 10, 0, G_MAXUINT, 0); + if ( current_num == num_vfs + && (autoprobe == -1 || current_autoprobe == autoprobe)) return TRUE; - if (current != 0) { - /* We need to destroy all other VFs before changing the value */ + if (current_num != 0) { + /* We need to destroy all other VFs before changing any value */ if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), "0")) { - _LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", 0, strerror (errno)); + _LOGW ("link: couldn't reset SR-IOV num_vfs: %s", strerror (errno)); return FALSE; } - if (num_vfs == 0) - return TRUE; } - /* Finally, set the desired value */ + if (num_vfs == 0) + return TRUE; + + if ( autoprobe >= 0 + && current_autoprobe != autoprobe + && !nm_platform_sysctl_set (NM_PLATFORM_GET, + NMP_SYSCTL_PATHID_NETDIR (dirfd, + ifname, + "device/sriov_drivers_autoprobe"), + nm_sprintf_buf (buf, "%d", autoprobe))) { + _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", autoprobe, strerror (errno)); + return FALSE; + } + if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), - nm_sprintf_buf (buf, "%d", num_vfs))) { + nm_sprintf_buf (buf, "%u", num_vfs))) { _LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, strerror (errno)); return FALSE; } @@ -7735,7 +7760,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; platform_class->link_set_name = link_set_name; - platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs; + platform_class->link_set_sriov_params = link_set_sriov_params; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 96465e3dd5..1146a07309 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1463,18 +1463,31 @@ nm_platform_link_supports_sriov (NMPlatform *self, int ifindex) return klass->link_supports_sriov (self, ifindex); } +/** + * nm_platform_link_set_sriov_params: + * @self: platform instance + * @ifindex: the index of the interface to change + * @num_vfs: the number of VFs to create + * @autoprobe: -1 to keep the current autoprobe-drivers value, + * or {0,1} to set a new value + */ gboolean -nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs) +nm_platform_link_set_sriov_params (NMPlatform *self, + int ifindex, + guint num_vfs, + int autoprobe) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (NM_IN_SET (autoprobe, -1, 0, 1), FALSE); - _LOGD ("link: setting %u VFs for %s (%d)", + _LOGD ("link: setting %u total VFs and autoprobe %d for %s (%d)", num_vfs, + autoprobe, nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex); - return klass->link_set_sriov_num_vfs (self, ifindex, num_vfs); + return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe); } /** diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 00df7fa31d..01e13f2178 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -802,7 +802,7 @@ typedef struct { NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); NMPlatformError (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); - gboolean (*link_set_sriov_num_vfs) (NMPlatform *, int ifindex, guint num_vfs); + gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); guint (*link_get_dev_id) (NMPlatform *, int ifindex); @@ -1193,7 +1193,7 @@ gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); -gboolean nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs); +gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe); char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex); From 8720dd3df1d9970684fca07bc62f3341988a77bd Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 23 May 2018 14:33:24 +0200 Subject: [PATCH 12/14] platform: add support for changing VF attributes --- src/platform/nm-linux-platform.c | 136 +++++++++++++++++++++++++++++++ src/platform/nm-platform.c | 71 ++++++++++++++++ src/platform/nm-platform.h | 23 ++++++ 3 files changed, 230 insertions(+) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 19821a87c3..641f7d68c0 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -215,6 +215,46 @@ G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +/* Redefine VF enums and structures that are not available on older kernels. */ + +#define IFLA_VF_UNSPEC 0 +#define IFLA_VF_MAC 1 +#define IFLA_VF_VLAN 2 +#define IFLA_VF_TX_RATE 3 +#define IFLA_VF_SPOOFCHK 4 +#define IFLA_VF_LINK_STATE 5 +#define IFLA_VF_RATE 6 +#define IFLA_VF_RSS_QUERY_EN 7 +#define IFLA_VF_STATS 8 +#define IFLA_VF_TRUST 9 +#define IFLA_VF_IB_NODE_GUID 10 +#define IFLA_VF_IB_PORT_GUID 11 +#define IFLA_VF_VLAN_LIST 12 + +#define IFLA_VF_VLAN_INFO_UNSPEC 0 +#define IFLA_VF_VLAN_INFO 1 + +/* valid for TRUST, SPOOFCHK, LINK_STATE, RSS_QUERY_EN */ +struct _ifla_vf_setting { + guint32 vf; + guint32 setting; +}; + +struct _ifla_vf_rate { + guint32 vf; + guint32 min_tx_rate; + guint32 max_tx_rate; +}; + +struct _ifla_vf_vlan_info { + guint32 vf; + guint32 vlan; /* 0 - 4095, 0 disables VLAN filter */ + guint32 qos; + guint16 vlan_proto; /* VLAN protocol, either 802.1Q or 802.1ad */ +}; + +/*****************************************************************************/ + #define _NMLOG_PREFIX_NAME "platform-linux" #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG2_DOMAIN LOGD_PLATFORM @@ -5617,6 +5657,101 @@ link_set_sriov_params (NMPlatform *platform, return TRUE; } +static gboolean +link_set_sriov_vfs (NMPlatform *platform, int ifindex, const NMPlatformVF *const *vfs) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *list, *info, *vlan_list; + guint i; + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + + if (!(list = nla_nest_start (nlmsg, IFLA_VFINFO_LIST))) + goto nla_put_failure; + + for (i = 0; vfs[i]; i++) { + const NMPlatformVF *vf = vfs[i]; + + if (!(info = nla_nest_start (nlmsg, IFLA_VF_INFO))) + goto nla_put_failure; + + if (vf->spoofchk >= 0) { + struct _ifla_vf_setting ivs = { 0 }; + + ivs.vf = vf->index; + ivs.setting = vf->spoofchk; + NLA_PUT (nlmsg, IFLA_VF_SPOOFCHK, sizeof (ivs), &ivs); + } + + if (vf->trust >= 0) { + struct _ifla_vf_setting ivs = { 0 }; + + ivs.vf = vf->index; + ivs.setting = vf->trust; + NLA_PUT (nlmsg, IFLA_VF_TRUST, sizeof (ivs), &ivs); + } + + if (vf->mac.len) { + struct ifla_vf_mac ivm = { 0 }; + + ivm.vf = vf->index; + memcpy (ivm.mac, vf->mac.data, vf->mac.len); + NLA_PUT (nlmsg, IFLA_VF_MAC, sizeof (ivm), &ivm); + } + + if (vf->min_tx_rate || vf->max_tx_rate) { + struct _ifla_vf_rate ivr = { 0 }; + + ivr.vf = vf->index; + ivr.min_tx_rate = vf->min_tx_rate; + ivr.max_tx_rate = vf->max_tx_rate; + NLA_PUT (nlmsg, IFLA_VF_RATE, sizeof (ivr), &ivr); + } + + /* Kernel only supports one VLAN per VF now. If this + * changes in the future, we need to figure out how to + * clear existing VLANs and set new ones in one message + * with the new API.*/ + if (vf->num_vlans > 1) { + _LOGW ("multiple VLANs per VF are not supported at the moment"); + return FALSE; + } else { + struct _ifla_vf_vlan_info ivvi = { 0 }; + + if (!(vlan_list = nla_nest_start (nlmsg, IFLA_VF_VLAN_LIST))) + goto nla_put_failure; + + ivvi.vf = vf->index; + if (vf->num_vlans == 1) { + ivvi.vlan = vf->vlans[0].id; + ivvi.qos = vf->vlans[0].qos; + ivvi.vlan_proto = htons (vf->vlans[0].proto_ad ? ETH_P_8021AD : ETH_P_8021Q); + } else { + /* Clear existing VLAN */ + ivvi.vlan = 0; + ivvi.qos = 0; + ivvi.vlan_proto = htons (ETH_P_8021Q); + } + + NLA_PUT (nlmsg, IFLA_VF_VLAN_INFO, sizeof (ivvi), &ivvi); + nla_nest_end (nlmsg, vlan_list); + } + nla_nest_end (nlmsg, info); + } + nla_nest_end (nlmsg, list); + + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); +} + static char * link_get_physical_port_id (NMPlatform *platform, int ifindex) { @@ -7761,6 +7896,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_mtu = link_set_mtu; platform_class->link_set_name = link_set_name; platform_class->link_set_sriov_params = link_set_sriov_params; + platform_class->link_set_sriov_vfs = link_set_sriov_vfs; platform_class->link_get_physical_port_id = link_get_physical_port_id; platform_class->link_get_dev_id = link_get_dev_id; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 1146a07309..174d863637 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1490,6 +1490,27 @@ nm_platform_link_set_sriov_params (NMPlatform *self, return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe); } +gboolean +nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs) +{ + guint i; + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (ifindex > 0, FALSE); + + _LOGD ("link: setting VFs for \"%s\" (%d):", + nm_platform_link_get_name (self, ifindex), + ifindex); + + for (i = 0; vfs[i]; i++) { + const NMPlatformVF *vf = vfs[i]; + + _LOGD ("link: VF %s", nm_platform_vf_to_string (vf, NULL, 0)); + } + + return klass->link_set_sriov_vfs (self, ifindex, vfs); +} + /** * nm_platform_link_set_up: * @self: platform instance @@ -6055,6 +6076,56 @@ nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b) return 0; } +const char * +nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len) +{ + char str_mac[128], mac[128]; + char str_spoof_check[64]; + char str_trust[64]; + char str_min_tx_rate[64]; + char str_max_tx_rate[64]; + nm_auto_free_gstring GString *gstr_vlans = NULL; + guint i; + + if (!nm_utils_to_string_buffer_init_null (vf, &buf, &len)) + return buf; + + if (vf->mac.len) { + nm_utils_hwaddr_ntoa_buf (vf->mac.data, vf->mac.len, TRUE, mac, sizeof (mac)); + nm_sprintf_buf (str_mac, " mac %s", mac); + } else + str_mac[0] = '\0'; + + if (vf->num_vlans) { + gstr_vlans = g_string_new (""); + for (i = 0; i < vf->num_vlans; i++) { + g_string_append_printf (gstr_vlans, " vlan %u", (unsigned) vf->vlans[i].id); + if (vf->vlans[i].qos) + g_string_append_printf (gstr_vlans, " qos %u", (unsigned) vf->vlans[i].qos); + if (vf->vlans[i].proto_ad) + g_string_append (gstr_vlans, " proto 802.1ad"); + } + } + + g_snprintf (buf, len, + "%u" /* index */ + "%s" /* MAC */ + "%s" /* spoof check */ + "%s" /* trust */ + "%s" /* min tx rate */ + "%s" /* max tx rate */ + "%s", /* VLANs */ + vf->index, + str_mac, + vf->spoofchk >= 0 ? nm_sprintf_buf (str_spoof_check, " spoofchk %d", vf->spoofchk) : "", + vf->trust >= 0 ? nm_sprintf_buf (str_trust, " trust %d", vf->trust) : "", + vf->min_tx_rate ? nm_sprintf_buf (str_min_tx_rate, " min_tx_rate %u", (unsigned) vf->min_tx_rate) : "", + vf->max_tx_rate ? nm_sprintf_buf (str_max_tx_rate, " max_tx_rate %u", (unsigned) vf->max_tx_rate) : "", + gstr_vlans ? gstr_vlans->str : ""); + + return buf; +} + void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) { diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 01e13f2178..7e49e064a9 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -607,6 +607,26 @@ typedef struct { extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; +typedef struct { + guint16 id; + guint32 qos; + bool proto_ad:1; +} NMPlatformVFVlan; + +typedef struct { + guint32 index; + guint32 min_tx_rate; + guint32 max_tx_rate; + guint num_vlans; + NMPlatformVFVlan *vlans; + struct { + guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */ + guint8 len; + } mac; + gint8 spoofchk; + gint8 trust; +} NMPlatformVF; + typedef struct { in_addr_t local; in_addr_t remote; @@ -803,6 +823,7 @@ typedef struct { NMPlatformError (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe); + gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); guint (*link_get_dev_id) (NMPlatform *, int ifindex); @@ -1194,6 +1215,7 @@ NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, con NMPlatformError nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe); +gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); char *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex); guint nm_platform_link_get_dev_id (NMPlatform *self, int ifindex); @@ -1434,6 +1456,7 @@ const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, ch const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len); const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len); const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len); +const char *nm_platform_vf_to_string (const NMPlatformVF *vf, char *buf, gsize len); const char *nm_platform_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, From 53c2951f61fa9a11efeb36bbebff88e62297ea15 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 22 May 2018 10:48:11 +0200 Subject: [PATCH 13/14] device: configure SR-IOV --- clients/common/nm-client-utils.c | 1 + libnm-core/nm-dbus-interface.h | 2 + man/NetworkManager.conf.xml | 4 + src/devices/nm-device.c | 138 ++++++++++++++++++++++++++++++- 4 files changed, 142 insertions(+), 3 deletions(-) diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c index 058ddae39a..2e3d45ca38 100644 --- a/clients/common/nm-client-utils.c +++ b/clients/common/nm-client-utils.c @@ -351,6 +351,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (nmc_device_reason_to_string, NMDeviceStateReason, NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, N_("OpenVSwitch database connection failed")), NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE, N_("A duplicate IP address was detected")), NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, N_("The selected IP method is not supported")), + NM_UTILS_LOOKUP_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, N_("Failed to configure SR-IOV parameters")), ) NM_UTILS_LOOKUP_STR_DEFINE (nm_active_connection_state_reason_to_string, NMActiveConnectionStateReason, diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 0b451d8446..388898dd96 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -559,6 +559,7 @@ typedef enum { * @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with Open vSwitch database * @NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE: a duplicate IP address was detected * @NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED: The selected IP method is not supported + * @NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED: configuration of SR-IOV parameters failed * * Device state change reason codes */ @@ -629,6 +630,7 @@ typedef enum { NM_DEVICE_STATE_REASON_OVSDB_FAILED = 63, NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE = 64, NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED = 65, + NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED = 66, } NMDeviceStateReason; /** diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 17bc42f34b..34ab6999d3 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -727,6 +727,10 @@ ipv6.ip6-privacy=0 removes extraneous routes from the tables. + + sriov.autoprobe-drivers + If left unspecified, drivers are autoprobed when the SR-IOV VF gets created. + vpn.timeout If left unspecified, default value of 60 seconds is used. diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 8844672ab5..2117985e1b 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -724,6 +724,7 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_device_state_reason_to_str, NMDeviceStateReason, NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_OVSDB_FAILED, "ovsdb-failed"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE, "ip-address-duplicate"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, "ip-method-unsupported"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, "sriov-configuration-failed"), ); #define reason_to_string(reason) \ @@ -3937,7 +3938,7 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink } static void -device_init_sriov_num_vfs (NMDevice *self) +device_init_static_sriov_num_vfs (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gs_free char *value = NULL; @@ -3971,7 +3972,7 @@ config_changed (NMConfig *config, priv->ignore_carrier = nm_config_data_get_ignore_carrier (config_data, self); if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_VALUES)) - device_init_sriov_num_vfs (self); + device_init_static_sriov_num_vfs (self); } static void @@ -4115,7 +4116,7 @@ realize_start_setup (NMDevice *self, nm_device_set_carrier_from_platform (self); - device_init_sriov_num_vfs (self); + device_init_static_sriov_num_vfs (self); nm_assert (!priv->stats.timeout_id); real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms); @@ -5859,9 +5860,140 @@ lldp_rx_enabled (NMDevice *self) return lldp == NM_SETTING_CONNECTION_LLDP_ENABLE_RX; } +static NMPlatformVF * +sriov_vf_config_to_platform (NMDevice *self, + NMSriovVF *vf, + GError **error) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gs_free NMPlatformVF *plat_vf = NULL; + const guint *vlan_ids; + GVariant *variant; + guint i, num_vlans; + gsize length; + + g_return_val_if_fail (!error || !*error, FALSE); + + vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans); + plat_vf = g_malloc0 ( sizeof (NMPlatformVF) + + sizeof (NMPlatformVFVlan) * num_vlans); + + plat_vf->index = nm_sriov_vf_get_index (vf); + + variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK); + if (variant) + plat_vf->spoofchk = g_variant_get_boolean (variant); + else + plat_vf->spoofchk = -1; + + variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_TRUST); + if (variant) + plat_vf->trust = g_variant_get_boolean (variant); + else + plat_vf->trust = -1; + + variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC); + if (variant) { + if (!_nm_utils_hwaddr_aton (g_variant_get_string (variant, NULL), + plat_vf->mac.data, + sizeof (plat_vf->mac.data), + &length)) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "invalid MAC %s", + g_variant_get_string (variant, NULL)); + return NULL; + } + if (length != priv->hw_addr_len) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + "wrong MAC length %" G_GSIZE_FORMAT ", should be %u", + length, priv->hw_addr_len); + return NULL; + } + plat_vf->mac.len = length; + } + + variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MIN_TX_RATE); + if (variant) + plat_vf->min_tx_rate = g_variant_get_uint32 (variant); + + variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAX_TX_RATE); + if (variant) + plat_vf->max_tx_rate = g_variant_get_uint32 (variant); + + plat_vf->num_vlans = num_vlans; + plat_vf->vlans = (NMPlatformVFVlan *) (&plat_vf[1]); + for (i = 0; i < num_vlans; i++) { + plat_vf->vlans[i].id = vlan_ids[i]; + plat_vf->vlans[i].qos = nm_sriov_vf_get_vlan_qos (vf, vlan_ids[i]); + plat_vf->vlans[i].proto_ad = nm_sriov_vf_get_vlan_protocol (vf, vlan_ids[i]) == NM_SRIOV_VF_VLAN_PROTOCOL_802_1AD; + } + + return g_steal_pointer (&plat_vf); +} + static NMActStageReturn act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) { + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMSettingSriov *s_sriov; + guint i, num; + + if ( priv->ifindex > 0 + && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV) + && (s_sriov = (NMSettingSriov *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) { + nm_auto_freev NMPlatformVF **plat_vfs = NULL; + gs_free_error GError *error = NULL; + gs_free const char *str = NULL; + NMSriovVF *vf; + int autoprobe; + + autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov); + if (autoprobe == NM_TERNARY_DEFAULT) { + str = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, + "sriov.autoprobe-drivers", self); + autoprobe = _nm_utils_ascii_str_to_int64 (str, 10, + NM_TERNARY_FALSE, + NM_TERNARY_TRUE, + NM_TERNARY_TRUE); + } + + num = nm_setting_sriov_get_num_vfs (s_sriov); + plat_vfs = g_new0 (NMPlatformVF *, num + 1); + for (i = 0; i < num; i++) { + vf = nm_setting_sriov_get_vf (s_sriov, i); + plat_vfs[i] = sriov_vf_config_to_platform (self, vf, &error); + if (!plat_vfs[i]) { + _LOGE (LOGD_DEVICE, + "failed to apply SR-IOV VF '%s': %s", + nm_utils_sriov_vf_to_str (vf, FALSE, NULL), + error->message); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + } + + if (!nm_platform_link_set_sriov_params (nm_device_get_platform (self), + priv->ifindex, + nm_setting_sriov_get_total_vfs (s_sriov), + autoprobe)) { + _LOGE (LOGD_DEVICE, "failed to apply SR-IOV parameters"); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + if (!nm_platform_link_set_sriov_vfs (nm_device_get_platform (self), + priv->ifindex, + (const NMPlatformVF *const *) plat_vfs)) { + _LOGE (LOGD_DEVICE, "failed to apply SR-IOV VFs"); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + } + return NM_ACT_STAGE_RETURN_SUCCESS; } From c02d1c488f69ed6183cb86c80a771c902ea5e397 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 24 May 2018 17:32:37 +0200 Subject: [PATCH 14/14] ifcfg-rh: SR-IOV support --- Makefile.am | 2 + libnm-core/nm-setting-sriov.c | 22 +++ .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 67 ++++++++ .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 41 +++++ src/settings/plugins/ifcfg-rh/shvar.c | 4 + src/settings/plugins/ifcfg-rh/shvar.h | 1 + .../tests/network-scripts/ifcfg-test-sriov | 19 +++ .../ifcfg-test-sriov-write.cexpected | 18 +++ .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 150 ++++++++++++++++++ 9 files changed, 324 insertions(+) create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected diff --git a/Makefile.am b/Makefile.am index ab9f649a85..45074fa1e7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2303,6 +2303,8 @@ EXTRA_DIST += \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-permissions \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-read-proxy-basic \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sit-ignore \ + src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov \ + src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-tc \ diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c index 45608f9dc8..29161c8cd9 100644 --- a/libnm-core/nm-setting-sriov.c +++ b/libnm-core/nm-setting-sriov.c @@ -1250,6 +1250,13 @@ nm_setting_sriov_class_init (NMSettingSriovClass *setting_class) * * Since: 1.14 **/ + /* ---ifcfg-rh--- + * property: total-vfs + * variable: SRIOV_TOTAL_VFS(+) + * description: The total number of virtual functions to create + * example: SRIOV_TOTAL_VFS=16 + * ---end--- + */ g_object_class_install_property (object_class, PROP_TOTAL_VFS, g_param_spec_uint (NM_SETTING_SRIOV_TOTAL_VFS, "", "", @@ -1286,6 +1293,13 @@ nm_setting_sriov_class_init (NMSettingSriovClass *setting_class) * * Since: 1.14 **/ + /* ---ifcfg-rh--- + * property: vfs + * variable: SRIOV_VF1(+), SRIOV_VF2(+), ... + * description: SR-IOV virtual function descriptors + * example: SRIOV_VF10="mac=00:11:22:33:44:55", ... + * ---end--- + */ g_object_class_install_property (object_class, PROP_VFS, g_param_spec_boxed (NM_SETTING_SRIOV_VFS, "", "", @@ -1319,6 +1333,14 @@ nm_setting_sriov_class_init (NMSettingSriovClass *setting_class) * * Since: 1.14 **/ + /* ---ifcfg-rh--- + * property: autoprobe-drivers + * variable: SRIOV_AUTOPROBE_DRIVERS(+) + * default: missing variable means global default + * description: Whether to autoprobe virtual functions by a compatible driver + * example: SRIOV_AUTOPROBE_DRIVERS=0,1 + * ---end--- + */ g_object_class_install_property (object_class, PROP_AUTOPROBE_DRIVERS, g_param_spec_enum (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, "", "", diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 882932b11a..5cea628013 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1978,6 +1978,68 @@ error: return NULL; } +static NMSetting * +make_sriov_setting (shvarFile *ifcfg) +{ + gs_unref_hashtable GHashTable *keys = NULL; + gs_unref_ptrarray GPtrArray *vfs = NULL; + NMTernary autoprobe_drivers; + NMSettingSriov *s_sriov; + int total_vfs; + + total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXINT32, 0); + if (!total_vfs) + return NULL; + + autoprobe_drivers = svGetValueInt64 (ifcfg, + "SRIOV_AUTOPROBE_DRIVERS", + 10, + NM_TERNARY_FALSE, + NM_TERNARY_TRUE, + NM_TERNARY_DEFAULT); + + keys = svGetKeys (ifcfg, SV_KEY_TYPE_SRIOV_VF); + if (keys) { + GHashTableIter iter; + const char *key; + + g_hash_table_iter_init (&iter, keys); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) { + gs_free_error GError *error = NULL; + gs_free char *value_to_free = NULL; + const char *value; + NMSriovVF *vf; + + nm_assert (g_str_has_prefix (key, "SRIOV_VF")); + + value = svGetValue (ifcfg, key, &value_to_free); + if (!value) + continue; + + key += NM_STRLEN ("SRIOV_VF"); + + vf = _nm_utils_sriov_vf_from_strparts (key, value, &error); + if (!vf) { + PARSE_WARNING ("ignoring invalid SR-IOV VF '%s %s': %s", + key, value, error->message); + continue; + } + if (!vfs) + vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref); + g_ptr_array_add (vfs, vf); + } + } + + s_sriov = (NMSettingSriov *) nm_setting_sriov_new (); + g_object_set (s_sriov, + NM_SETTING_SRIOV_TOTAL_VFS, total_vfs, + NM_SETTING_SRIOV_VFS, vfs, + NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, (int) autoprobe_drivers, + NULL); + + return (NMSetting *) s_sriov; +} + static NMSetting * make_tc_setting (shvarFile *ifcfg) { @@ -5324,6 +5386,7 @@ connection_from_file_full (const char *filename, gs_free char *type = NULL; char *devtype, *bootproto; NMSetting *s_ip4, *s_ip6, *s_tc, *s_proxy, *s_port, *s_dcb = NULL, *s_user; + NMSetting *s_sriov; const char *ifcfg_name = NULL; gboolean has_ip4_defroute = FALSE; gboolean has_complex_routes_v4; @@ -5579,6 +5642,10 @@ connection_from_file_full (const char *filename, nm_connection_add_setting (connection, s_ip4); } + s_sriov = make_sriov_setting (parsed); + if (s_sriov) + nm_connection_add_setting (connection, s_sriov); + s_tc = make_tc_setting (parsed); if (s_tc) nm_connection_add_setting (connection, s_tc); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 8921646597..ab1938f180 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -2135,6 +2135,45 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) return TRUE; } +static void +write_sriov_setting (NMConnection *connection, shvarFile *ifcfg) +{ + NMSettingSriov *s_sriov; + guint i, num = 0; + NMTernary b; + NMSriovVF *vf; + char key[32]; + char *str; + + svUnsetAll (ifcfg, SV_KEY_TYPE_SRIOV_VF); + + s_sriov = nm_connection_get_setting_sriov (connection); + if (s_sriov) + num = nm_setting_sriov_get_total_vfs (s_sriov); + if (num == 0) { + svUnsetValue (ifcfg, "SRIOV_TOTAL_VFS"); + svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS"); + return; + } + + svSetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", num); + + b = nm_setting_sriov_get_autoprobe_drivers (s_sriov); + if (b != NM_TERNARY_DEFAULT) + svSetValueInt64 (ifcfg, "SRIOV_AUTOPROBE_DRIVERS", b); + else + svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS"); + + num = nm_setting_sriov_get_num_vfs (s_sriov); + for (i = 0; i < num; i++) { + vf = nm_setting_sriov_get_vf (s_sriov, i); + nm_sprintf_buf (key, "SRIOV_VF%u", nm_sriov_vf_get_index (vf)); + str = nm_utils_sriov_vf_to_str (vf, TRUE, NULL); + svSetValueStr (ifcfg, key, str); + g_free (str); + } +} + static gboolean write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { @@ -2921,6 +2960,8 @@ do_write_construct (NMConnection *connection, if (!write_user_setting (connection, ifcfg, error)) return FALSE; + write_sriov_setting (connection, ifcfg); + if (!write_tc_setting (connection, ifcfg, error)) return FALSE; diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index e8d028094b..f09bc2472f 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -915,6 +915,10 @@ _svKeyMatchesType (const char *key, SvKeyType match_key_type) || IS_NUMBERED_TAG (key, "FILTER")) return TRUE; } + if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_SRIOV_VF)) { + if (IS_NUMBERED_TAG (key, "SRIOV_VF")) + return TRUE; + } return FALSE; } diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index bef5f47743..622bb474b1 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -39,6 +39,7 @@ typedef enum { SV_KEY_TYPE_IP4_ADDRESS = (1LL << 2), SV_KEY_TYPE_TC = (1LL << 3), SV_KEY_TYPE_USER = (1LL << 4), + SV_KEY_TYPE_SRIOV_VF = (1LL << 5), } SvKeyType; const char *svFileGetName (const shvarFile *s); diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov new file mode 100644 index 0000000000..142f56e434 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov @@ -0,0 +1,19 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:55 +BOOTPROTO=none +ONBOOT=yes +DNS1=4.2.2.1 +DNS2=4.2.2.2 +IPADDR=192.168.1.5 +PREFIX=24 +NETMASK=255.255.255.0 +GATEWAY=192.168.1.1 +IPV6INIT=no +NAME=ethernet-sriov +UUID=acc703b8-e751-44ce-b456-1550bdf2057e +SRIOV_TOTAL_VFS=16 +SRIOV_AUTOPROBE_DRIVERS=0 +SRIOV_VF15="max-tx-rate=200 mac=01:23:45:67:89:ab vlans=2" +SRIOV_VF12="trust=false min-tx-rate=100 vlans=1.200.ad" +SRIOV_VF3="mac=55:44:33:22:11:00 spoof-check=true" diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected new file mode 100644 index 0000000000..c882c4796e --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected @@ -0,0 +1,18 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +SRIOV_TOTAL_VFS=64 +SRIOV_AUTOPROBE_DRIVERS=1 +SRIOV_VF2="mac=55:55:55:55:55:55 vlans=3.10.ad;10" +SRIOV_VF19=spoof-check=true +BOOTPROTO=none +IPADDR=1.1.1.3 +PREFIX=24 +GATEWAY=1.1.1.1 +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write SR-IOV config" +UUID=${UUID} +DEVICE=eth0 +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 1331391dc2..8d4efbbfd1 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -9632,6 +9632,153 @@ test_utils_ignore (void) do_test_utils_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE); } +/*****************************************************************************/ + +static void +test_sriov_read (void) +{ + gs_unref_object NMConnection *connection = NULL; + NMSettingSriov *s_sriov; + NMSriovVF *vf; + GVariant *variant; + GError *error = NULL; + char *str; + + connection = _connection_from_file (TEST_IFCFG_DIR "/ifcfg-test-sriov", + NULL, TYPE_ETHERNET,NULL); + + g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "eth0"); + + s_sriov = nm_connection_get_setting_sriov (connection); + g_assert (s_sriov); + + g_assert_cmpint (nm_setting_sriov_get_total_vfs (s_sriov), ==, 16); + g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3); + g_assert_cmpint (nm_setting_sriov_get_autoprobe_drivers (s_sriov), ==, NM_TERNARY_FALSE); + + /* VF 3 */ + vf = nm_setting_sriov_get_vf (s_sriov, 0); + g_assert (vf); + g_assert_cmpint (nm_sriov_vf_get_index (vf), ==, 3); + + variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "55:44:33:22:11:00"); + + variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)); + g_assert_cmpint (g_variant_get_boolean (variant), ==, TRUE); + + /* VF 12 */ + vf = nm_setting_sriov_get_vf (s_sriov, 1); + str = nm_utils_sriov_vf_to_str (vf, FALSE, &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, "12 min-tx-rate=100 trust=false vlans=1.200.ad"); + g_free (str); + + /* VF 15 */ + vf = nm_setting_sriov_get_vf (s_sriov, 2); + str = nm_utils_sriov_vf_to_str (vf, FALSE, &error); + g_assert_no_error (error); + g_assert_cmpstr (str, ==, "15 mac=01:23:45:67:89:ab max-tx-rate=200 vlans=2"); + g_free (str); +} + +static void +test_sriov_write (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + NMSettingWired *s_wired; + NMSettingSriov *s_sriov; + NMSriovVF *vf; + gs_unref_ptrarray GPtrArray *vfs = NULL; + NMIPAddress *addr; + GError *error = NULL; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write SR-IOV config", + 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); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, + NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1", + NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, + NULL); + + addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error); + g_assert_no_error (error); + nm_setting_ip_config_add_address (s_ip4, addr); + nm_ip_address_unref (addr); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + /* SRIOV setting */ + s_sriov = (NMSettingSriov *) nm_setting_sriov_new (); + nm_connection_add_setting (connection, NM_SETTING (s_sriov)); + + vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref); + + vf = nm_utils_sriov_vf_from_str ("2 mac=55:55:55:55:55:55 vlans=3.10.ad;10", &error); + nmtst_assert_success (vf, error); + g_ptr_array_add (vfs, vf); + + vf = nm_utils_sriov_vf_from_str ("19 spoof-check=true", &error); + nmtst_assert_success (vf, error); + g_ptr_array_add (vfs, vf); + + g_object_set (s_sriov, + NM_SETTING_SRIOV_TOTAL_VFS, 64, + NM_SETTING_SRIOV_VFS, vfs, + NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, NM_TERNARY_TRUE, + NULL); + + nm_connection_add_setting (connection, nm_setting_proxy_new ()); + + nmtst_assert_connection_verifies_without_normalization (connection); + + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR "/ifcfg-test-sriov-write.cexpected", + &testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + +/*****************************************************************************/ + static void test_tc_read (void) { @@ -10033,6 +10180,9 @@ int main (int argc, char **argv) g_test_add_func (TPATH "utils/path", test_utils_path); g_test_add_func (TPATH "utils/ignore", test_utils_ignore); + g_test_add_func (TPATH "sriov/read", test_sriov_read); + g_test_add_func (TPATH "sriov/write", test_sriov_write); + g_test_add_func (TPATH "tc/read", test_tc_read); g_test_add_func (TPATH "tc/write", test_tc_write);