From afcd268ea7b1149fbfb66bce4eca659b675da0a2 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Tue, 12 Dec 2017 15:53:07 +0100 Subject: New upstream version 1.10.2 --- libnm-core/nm-connection.c | 18 +- libnm-core/nm-connection.h | 4 +- libnm-core/nm-core-internal.h | 50 +- libnm-core/nm-core-types.h | 3 +- libnm-core/nm-dbus-interface.h | 44 + libnm-core/nm-dbus-types.xml | 55 + libnm-core/nm-errors.h | 2 + libnm-core/nm-keyfile-reader.c | 122 +- libnm-core/nm-keyfile-utils.c | 2 +- libnm-core/nm-keyfile-writer.c | 146 ++- libnm-core/nm-property-compare.c | 34 + libnm-core/nm-setting-bond.c | 150 ++- libnm-core/nm-setting-bridge.c | 42 +- libnm-core/nm-setting-ip-config.c | 7 +- libnm-core/nm-setting-ip-config.h | 1 + libnm-core/nm-setting-ip4-config.c | 8 + libnm-core/nm-setting-ip6-config.c | 10 +- libnm-core/nm-setting-tc-config.c | 1576 ++++++++++++++++++++++ libnm-core/nm-setting-tc-config.h | 191 +++ libnm-core/nm-setting-team-port.c | 471 ++++++- libnm-core/nm-setting-team-port.h | 41 +- libnm-core/nm-setting-team.c | 1635 ++++++++++++++++++++++- libnm-core/nm-setting-team.h | 167 ++- libnm-core/nm-setting-wireless-security.c | 5 +- libnm-core/nm-utils-private.h | 120 +- libnm-core/nm-utils.c | 2035 ++++++++++++++++++++++++----- libnm-core/nm-utils.h | 20 +- libnm-core/nm-version.h | 6 + libnm-core/tests/test-general.c | 2 +- libnm-core/tests/test-keyfile.c | 5 + libnm-core/tests/test-setting-8021x.c | 404 ------ libnm-core/tests/test-setting-bond.c | 247 ---- libnm-core/tests/test-setting-dcb.c | 321 ----- libnm-core/tests/test-setting.c | 1632 +++++++++++++++++++++++ 34 files changed, 8168 insertions(+), 1408 deletions(-) create mode 100644 libnm-core/nm-setting-tc-config.c create mode 100644 libnm-core/nm-setting-tc-config.h delete mode 100644 libnm-core/tests/test-setting-8021x.c delete mode 100644 libnm-core/tests/test-setting-bond.c delete mode 100644 libnm-core/tests/test-setting-dcb.c create mode 100644 libnm-core/tests/test-setting.c (limited to 'libnm-core') diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index d5e28f7b..9c88cfd6 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -16,7 +16,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2017 Red Hat, Inc. * Copyright 2007 - 2008 Novell, Inc. */ @@ -2530,6 +2530,22 @@ nm_connection_get_setting_serial (NMConnection *connection) return _connection_get_setting_check (connection, NM_TYPE_SETTING_SERIAL); } +/** + * nm_connection_get_setting_tc_config: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingTCConfig the connection might contain. + * + * Returns: (transfer none): an #NMSettingTCConfig if the connection contains one, otherwise %NULL + * + * Since: 1.10.2 + **/ +NMSettingTCConfig * +nm_connection_get_setting_tc_config (NMConnection *connection) +{ + return _connection_get_setting_check (connection, NM_TYPE_SETTING_TC_CONFIG); +} + /** * nm_connection_get_setting_tun: * @connection: the #NMConnection diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h index c6b23a00..95e11f1f 100644 --- a/libnm-core/nm-connection.h +++ b/libnm-core/nm-connection.h @@ -16,7 +16,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2007 - 2013 Red Hat, Inc. + * Copyright 2007 - 2017 Red Hat, Inc. * Copyright 2007 - 2008 Novell, Inc. */ @@ -229,6 +229,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_10_2 +NMSettingTCConfig * nm_connection_get_setting_tc_config (NMConnection *connection); NMSettingTun * nm_connection_get_setting_tun (NMConnection *connection); NMSettingVpn * nm_connection_get_setting_vpn (NMConnection *connection); NMSettingWimax * nm_connection_get_setting_wimax (NMConnection *connection); diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 59ebfcb1..5a27d438 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -1,5 +1,4 @@ /* -*- 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 @@ -16,7 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * (C) Copyright 2014 Red Hat, Inc. + * (C) Copyright 2014 - 2017 Red Hat, Inc. */ #ifndef NM_CORE_NM_INTERNAL_H @@ -63,6 +62,7 @@ #include "nm-setting-ppp.h" #include "nm-setting-pppoe.h" #include "nm-setting-serial.h" +#include "nm-setting-tc-config.h" #include "nm-setting-team-port.h" #include "nm-setting-team.h" #include "nm-setting-tun.h" @@ -80,6 +80,20 @@ #include "nm-core-types-internal.h" #include "nm-vpn-editor-plugin.h" +/* IEEE 802.1D-1998 timer values */ +#define NM_BR_MIN_HELLO_TIME 1 +#define NM_BR_MAX_HELLO_TIME 10 + +#define NM_BR_MIN_FORWARD_DELAY 2 +#define NM_BR_MAX_FORWARD_DELAY 30 + +#define NM_BR_MIN_MAX_AGE 6 +#define NM_BR_MAX_MAX_AGE 40 + +/* IEEE 802.1D-1998 Table 7.4 */ +#define NM_BR_MIN_AGEING_TIME 0 +#define NM_BR_MAX_AGEING_TIME 1000000 + /* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated * connection can be replaced by a already-defined connection. This flag only * takes into account properties marked with the %NM_SETTING_PARAM_INFERRABLE @@ -230,10 +244,6 @@ gssize _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpoi gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data); gssize _nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data); -char ** _nm_utils_strsplit_set (const char *str, - const char *delimiters, - int max_tokens); - GSList * _nm_utils_strv_to_slist (char **strv, gboolean deep_copy); char ** _nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy); @@ -429,11 +439,24 @@ NMSettingBluetooth *_nm_connection_get_setting_bluetooth_for_nap (NMConnection * /*****************************************************************************/ +const char *nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst); + gboolean _nm_utils_inet6_is_token (const struct in6_addr *in6addr); /*****************************************************************************/ -gboolean _nm_utils_team_config_equal (const char *conf1, const char *conf2, gboolean port); +gboolean _nm_utils_team_config_equal (const char *conf1, const char *conf2, gboolean port); +GValue *_nm_utils_team_config_get (const char *conf, + const char *key, + const char *key2, + const char *key3, + gboolean port_config); + +gboolean _nm_utils_team_config_set (char **conf, + const char *key, + const char *key2, + const char *key3, + const GValue *value); /*****************************************************************************/ @@ -449,4 +472,17 @@ nm_setting_ip_config_get_addr_family (NMSettingIPConfig *s_ip) /*****************************************************************************/ +guint32 _nm_utils_parse_tc_handle (const char *str, + GError **error); +void _nm_utils_string_append_tc_parent (GString *string, + const char *prefix, + guint32 parent); +void _nm_utils_string_append_tc_qdisc_rest (GString *string, + NMTCQdisc *qdisc); +gboolean _nm_utils_string_append_tc_tfilter_rest (GString *string, + NMTCTfilter *tfilter, + GError **error); + +/*****************************************************************************/ + #endif diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h index ae680b7a..6a274333 100644 --- a/libnm-core/nm-core-types.h +++ b/libnm-core/nm-core-types.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2014 Red Hat, Inc. + * Copyright 2014 - 2017 Red Hat, Inc. */ #ifndef __NM_CORE_TYPES_H__ @@ -57,6 +57,7 @@ typedef struct _NMSettingOvsPort NMSettingOvsPort; typedef struct _NMSettingPpp NMSettingPpp; typedef struct _NMSettingPppoe NMSettingPppoe; typedef struct _NMSettingSerial NMSettingSerial; +typedef struct _NMSettingTCConfig NMSettingTCConfig; typedef struct _NMSettingTeam NMSettingTeam; typedef struct _NMSettingTeamPort NMSettingTeamPort; typedef struct _NMSettingTun NMSettingTun; diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 7f75c849..98c94154 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -893,4 +893,48 @@ typedef enum { /*< flags >*/ NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES = (1LL << 5), } NMActivationStateFlags; +/** + * NMSettingsUpdate2Flags: + * @NM_SETTINGS_UPDATE2_FLAG_NONE: an alias for numeric zero, no flags set. + * @NM_SETTINGS_UPDATE2_FLAG_TO_DISK: to persist the connection to disk. + * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY: to make the connection in-memory only. + * If the connection was previously persistent, the corresponding file on disk + * is not deleted but merely the connection is decoupled from the file + * on disk. If you later delete an in-memory connection, the connection + * on disk will be deleted as well. + * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED: this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, + * but if the connection has a corresponding file on disk, the association between + * the connection and the file is forgotten but the file is not modified. + * The difference to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY is if you later + * save the connection again to disk, a new file name will be chosen without + * overwriting the remaining file on disk. Also, if you delete the connection + * later, the file on disk will not be deleted. + * @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY: this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, + * but if the connection has a corresponding file on disk, the file on + * disk will be deleted. + * @NM_SETTINGS_UPDATE2_FLAG_VOLATILE: This can be specified with either + * %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED or %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY. + * After making the connection in-memory only, the connection is marked + * as volatile. That means, if the connection is currently not active + * it will be deleted right away. Otherwise, it is marked to for deletion + * once the connection deactivates. A volatile connection cannot autoactivate + * again (because it's about to be deleted), but a manual activation will + * clear the volatile flag. + * @NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT: usually, when the connection + * has autoconnect enabled and is modified, it becomes elegible to autoconnect + * right away. Setting this flag, disables autoconnect until the connection + * is manually activated. + * + * Since: 1.10.2 + */ +typedef enum { /*< flags >*/ + NM_SETTINGS_UPDATE2_FLAG_NONE = 0, + NM_SETTINGS_UPDATE2_FLAG_TO_DISK = (1LL << 0), + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY = (1LL << 1), + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED = (1LL << 2), + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY = (1LL << 3), + NM_SETTINGS_UPDATE2_FLAG_VOLATILE = (1LL << 4), + NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT = (1LL << 5), +} NMSettingsUpdate2Flags; + #endif /* __NM_DBUS_INTERFACE_H__ */ diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml index ba58a615..bc0c023b 100644 --- a/libnm-core/nm-dbus-types.xml +++ b/libnm-core/nm-dbus-types.xml @@ -1603,4 +1603,59 @@ + + enum NMSettingsUpdate2Flags + + NMSettingsUpdate2Flags + + Since: 1.10.2 + + Values + + + + + + + + NM_SETTINGS_UPDATE2_FLAG_NONE + = 0 + an alias for numeric zero, no flags set. + + + NM_SETTINGS_UPDATE2_FLAG_TO_DISK + = (1LL + to persist the connection to disk. + + + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY + = (1LL + to make the connection in-memory only. If the connection was previously persistent, the corresponding file on disk is not deleted but merely the connection is decoupled from the file on disk. If you later delete an in-memory connection, the connection on disk will be deleted as well. + + + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED + = (1LL + this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, but if the connection has a corresponding file on disk, the association between the connection and the file is forgotten but the file is not modified. The difference to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY is if you later save the connection again to disk, a new file name will be chosen without overwriting the remaining file on disk. Also, if you delete the connection later, the file on disk will not be deleted. + + + NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY + = (1LL + this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, but if the connection has a corresponding file on disk, the file on disk will be deleted. + + + NM_SETTINGS_UPDATE2_FLAG_VOLATILE + = (1LL + This can be specified with either %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED or %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY. After making the connection in-memory only, the connection is marked as volatile. That means, if the connection is currently not active it will be deleted right away. Otherwise, it is marked to for deletion once the connection deactivates. A volatile connection cannot autoactivate again (because it's about to be deleted), but a manual activation will clear the volatile flag. + + + NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT + = (1LL + usually, when the connection has autoconnect enabled and is modified, it becomes elegible to autoconnect right away. Setting this flag, disables autoconnect until the connection is manually activated. + + + + + + + diff --git a/libnm-core/nm-errors.h b/libnm-core/nm-errors.h index 6bacc8da..bdd6c223 100644 --- a/libnm-core/nm-errors.h +++ b/libnm-core/nm-errors.h @@ -261,6 +261,7 @@ GQuark nm_secret_agent_error_quark (void); * @NM_SETTINGS_ERROR_READ_ONLY_CONNECTION: attempted to modify a read-only connection * @NM_SETTINGS_ERROR_UUID_EXISTS: a connection with that UUID already exists * @NM_SETTINGS_ERROR_INVALID_HOSTNAME: attempted to set an invalid hostname + * @NM_SETTINGS_ERROR_INVALID_ARGUMENTS: invalid arguments * * Errors related to the settings/persistent configuration interface of * NetworkManager. @@ -277,6 +278,7 @@ typedef enum { NM_SETTINGS_ERROR_READ_ONLY_CONNECTION, /*< nick=ReadOnlyConnection >*/ NM_SETTINGS_ERROR_UUID_EXISTS, /*< nick=UuidExists >*/ NM_SETTINGS_ERROR_INVALID_HOSTNAME, /*< nick=InvalidHostname >*/ + NM_SETTINGS_ERROR_INVALID_ARGUMENTS, /*< nick=InvalidArguments >*/ } NMSettingsError; GQuark nm_settings_error_quark (void); diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index 0ac417cd..20b5a3fc 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2008 - 2015 Red Hat, Inc. + * Copyright (C) 2008 - 2017 Red Hat, Inc. */ #include "nm-default.h" @@ -30,6 +30,7 @@ #include #include #include +#include #include "nm-common-macros.h" #include "nm-core-internal.h" @@ -1313,6 +1314,102 @@ team_config_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key g_object_set (G_OBJECT (setting), key, conf, NULL); } +static void +qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + const char *setting_name = nm_setting_get_name (setting); + GPtrArray *qdiscs; + gs_strfreev gchar **keys = NULL; + gsize n_keys = 0; + int i; + + qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref); + + keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); + if (!keys || n_keys == 0) + return; + + for (i = 0; i < n_keys; i++) { + NMTCQdisc *qdisc; + const char *qdisc_parent; + gs_free char *qdisc_rest = NULL; + gs_free char *qdisc_str = NULL; + gs_free_error GError *err = NULL; + + if (!g_str_has_prefix (keys[i], "qdisc.")) + continue; + + qdisc_parent = keys[i] + sizeof ("qdisc.") - 1; + qdisc_rest = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, keys[i], NULL); + qdisc_str = g_strdup_printf ("%s%s %s", + _nm_utils_parse_tc_handle (qdisc_parent, NULL) != TC_H_UNSPEC ? "parent " : "", + qdisc_parent, + qdisc_rest); + + qdisc = nm_utils_tc_qdisc_from_str (qdisc_str, &err); + if (!qdisc) { + handle_warn (info, keys[i], NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid qdisc: %s"), + err->message); + } else { + g_ptr_array_add (qdiscs, qdisc); + } + } + + if (qdiscs->len >= 1) + g_object_set (setting, key, qdiscs, NULL); + + g_ptr_array_unref (qdiscs); +} + +static void +tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + const char *setting_name = nm_setting_get_name (setting); + GPtrArray *tfilters; + gs_strfreev gchar **keys = NULL; + gsize n_keys = 0; + int i; + + tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref); + + keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL); + if (!keys || n_keys == 0) + return; + + for (i = 0; i < n_keys; i++) { + NMTCTfilter *tfilter; + const char *tfilter_parent; + gs_free char *tfilter_rest = NULL; + gs_free char *tfilter_str = NULL; + gs_free_error GError *err = NULL; + + if (!g_str_has_prefix (keys[i], "tfilter.")) + continue; + + tfilter_parent = keys[i] + sizeof ("tfilter.") - 1; + tfilter_rest = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, keys[i], NULL); + tfilter_str = g_strdup_printf ("%s%s %s", + _nm_utils_parse_tc_handle (tfilter_parent, NULL) != TC_H_UNSPEC ? "parent " : "", + tfilter_parent, + tfilter_rest); + + tfilter = nm_utils_tc_tfilter_from_str (tfilter_str, &err); + if (!tfilter) { + handle_warn (info, keys[i], NM_KEYFILE_WARN_SEVERITY_WARN, + _("invalid tfilter: %s"), + err->message); + } else { + g_ptr_array_add (tfilters, tfilter); + } + } + + if (tfilters->len >= 1) + g_object_set (setting, key, tfilters, NULL); + + g_ptr_array_unref (tfilters); +} + typedef struct { const char *setting_name; const char *key; @@ -1439,6 +1536,14 @@ static KeyParser key_parsers[] = { NM_SETTING_TEAM_CONFIG, TRUE, team_config_parser }, + { NM_SETTING_TC_CONFIG_SETTING_NAME, + NM_SETTING_TC_CONFIG_QDISCS, + FALSE, + qdisc_parser }, + { NM_SETTING_TC_CONFIG_SETTING_NAME, + NM_SETTING_TC_CONFIG_TFILTERS, + FALSE, + tfilter_parser }, { NULL, NULL, FALSE } }; @@ -1480,6 +1585,13 @@ read_one_setting_value (NMSetting *setting, && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY)) return; + if ( ( NM_IS_SETTING_TEAM (setting) + || NM_IS_SETTING_TEAM_PORT (setting)) + && !NM_IN_STRSET (key, NM_SETTING_TEAM_CONFIG)) { + /* silently ignore all team properties (except "config"). */ + return; + } + setting_name = nm_setting_get_name (setting); /* Look through the list of handlers for non-standard format key values */ @@ -1491,15 +1603,11 @@ read_one_setting_value (NMSetting *setting, parser++; } - /* VPN properties don't have the exact key name */ if (NM_IS_SETTING_VPN (setting)) check_for_key = FALSE; - - if (NM_IS_SETTING_USER (setting)) + else if (NM_IS_SETTING_USER (setting)) check_for_key = FALSE; - - /* Bonding 'options' don't have the exact key name. The options are right under [bond] group. */ - if (NM_IS_SETTING_BOND (setting)) + else if (NM_IS_SETTING_BOND (setting)) check_for_key = FALSE; /* Check for the exact key in the GKeyFile if required. Most setting diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c index 1b7860d8..88583319 100644 --- a/libnm-core/nm-keyfile-utils.c +++ b/libnm-core/nm-keyfile-utils.c @@ -442,7 +442,7 @@ _keyfile_key_encode (const char *name, && g_ascii_isxdigit (name[i + 2])) || ( ch == ' ' && name[i + 1] == '\0')) - g_string_append_printf (str, "\\%2X", ch); + g_string_append_printf (str, "\\%02X", ch); else g_string_append_c (str, (char) ch); } diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index 30d519c4..0333b644 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -16,7 +16,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright (C) 2008 Novell, Inc. - * Copyright (C) 2008 - 2015 Red Hat, Inc. + * Copyright (C) 2008 - 2017 Red Hat, Inc. */ #include "nm-default.h" @@ -253,6 +253,72 @@ route_writer (KeyfileWriterInfo *info, write_ip_values (info->keyfile, setting_name, array, NULL, TRUE); } +static void +qdisc_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + gsize i; + GPtrArray *array; + + array = (GPtrArray *) g_value_get_boxed (value); + if (!array || !array->len) + return; + + for (i = 0; i < array->len; i++) { + NMTCQdisc *qdisc = array->pdata[i]; + GString *key_name = g_string_sized_new (16); + GString *value_str = g_string_sized_new (60); + + g_string_append (key_name, "qdisc."); + _nm_utils_string_append_tc_parent (key_name, NULL, + nm_tc_qdisc_get_parent (qdisc)); + _nm_utils_string_append_tc_qdisc_rest (value_str, qdisc); + + nm_keyfile_plugin_kf_set_string (info->keyfile, + NM_SETTING_TC_CONFIG_SETTING_NAME, + key_name->str, + value_str->str); + + g_string_free (key_name, TRUE); + g_string_free (value_str, TRUE); + } +} + +static void +tfilter_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + gsize i; + GPtrArray *array; + + array = (GPtrArray *) g_value_get_boxed (value); + if (!array || !array->len) + return; + + for (i = 0; i < array->len; i++) { + NMTCTfilter *tfilter = array->pdata[i]; + GString *key_name = g_string_sized_new (16); + GString *value_str = g_string_sized_new (60); + + g_string_append (key_name, "tfilter."); + _nm_utils_string_append_tc_parent (key_name, NULL, + nm_tc_tfilter_get_parent (tfilter)); + _nm_utils_string_append_tc_tfilter_rest (value_str, tfilter, NULL); + + nm_keyfile_plugin_kf_set_string (info->keyfile, + NM_SETTING_TC_CONFIG_SETTING_NAME, + key_name->str, + value_str->str); + + g_string_free (key_name, TRUE); + g_string_free (value_str, TRUE); + } +} + static void write_hash_of_string (GKeyFile *file, NMSetting *setting, @@ -501,6 +567,15 @@ cert_writer (KeyfileWriterInfo *info, cert_writer_default (info->connection, info->keyfile, &type_data); } +static void +null_writer (KeyfileWriterInfo *info, + NMSetting *setting, + const char *key, + const GValue *value) +{ + /* skip */ +} + /*****************************************************************************/ typedef struct { @@ -576,6 +651,75 @@ static KeyWriter key_writers[] = { { NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, cert_writer }, + { NM_SETTING_TC_CONFIG_SETTING_NAME, + NM_SETTING_TC_CONFIG_QDISCS, + qdisc_writer }, + { NM_SETTING_TC_CONFIG_SETTING_NAME, + NM_SETTING_TC_CONFIG_TFILTERS, + tfilter_writer }, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_MCAST_REJOIN_COUNT, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_TX_HASH, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_TX_BALANCER, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_ACTIVE, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_FAST_RATE, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_SYS_PRIO, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_MIN_PORTS, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, + null_writer}, + { NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_TEAM_LINK_WATCHERS, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_QUEUE_ID, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_PRIO, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_STICKY, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_LACP_PRIO, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_LACP_KEY, + null_writer}, + { NM_SETTING_TEAM_PORT_SETTING_NAME, + NM_SETTING_TEAM_PORT_LINK_WATCHERS, + null_writer}, { NULL, NULL, NULL } }; diff --git a/libnm-core/nm-property-compare.c b/libnm-core/nm-property-compare.c index d13d019f..64ed663c 100644 --- a/libnm-core/nm-property-compare.c +++ b/libnm-core/nm-property-compare.c @@ -55,6 +55,38 @@ _nm_property_compare_collection (GVariant *value1, GVariant *value2) return 0; } +static gint +_nm_property_compare_vardict (GVariant *value1, GVariant *value2) +{ + GVariantIter iter; + int len1, len2; + const char *key; + GVariant *val1, *val2; + + len1 = g_variant_n_children (value1); + len2 = g_variant_n_children (value2); + + if (len1 != len2) + return len1 < len2 ? -1 : 1; + + g_variant_iter_init (&iter, value1); + while (g_variant_iter_next (&iter, "{&sv}", &key, &val1)) { + if (!g_variant_lookup (value2, key, "v", &val2)) { + g_variant_unref (val1); + return -1; + } + if (!g_variant_equal (val1, val2)) { + g_variant_unref (val1); + g_variant_unref (val2); + return -1; + } + g_variant_unref (val1); + g_variant_unref (val2); + } + + return 0; +} + static gint _nm_property_compare_strdict (GVariant *value1, GVariant *value2) { @@ -106,6 +138,8 @@ nm_property_compare (GVariant *value1, GVariant *value2) ret = g_variant_compare (value1, value2); else if (g_variant_is_of_type (value1, G_VARIANT_TYPE ("a{ss}"))) ret = _nm_property_compare_strdict (value1, value2); + else if (g_variant_is_of_type (value1, G_VARIANT_TYPE ("a{sv}"))) + ret = _nm_property_compare_vardict (value1, value2); else if (g_variant_type_is_array (type1)) ret = _nm_property_compare_collection (value1, value2); else if (g_variant_type_is_tuple (type1)) diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c index d12940f6..a44972cf 100644 --- a/libnm-core/nm-setting-bond.c +++ b/libnm-core/nm-setting-bond.c @@ -34,6 +34,8 @@ #include "nm-setting-infiniband.h" #include "nm-core-internal.h" +/*****************************************************************************/ + /** * SECTION:nm-setting-bond * @short_description: Describes connection properties for bonds @@ -42,15 +44,7 @@ * necessary for bond connections. **/ -G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING, - _nm_register_setting (BOND, NM_SETTING_PRIORITY_HW_BASE)) -NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND) - -#define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate)) - -typedef struct { - GHashTable *options; -} NMSettingBondPrivate; +/*****************************************************************************/ enum { PROP_0, @@ -58,6 +52,19 @@ enum { LAST_PROP }; +typedef struct { + GHashTable *options; + NMUtilsNamedValue *options_idx_cache; +} NMSettingBondPrivate; + +G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING, + _nm_register_setting (BOND, NM_SETTING_PRIORITY_HW_BASE)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND) + +#define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate)) + +/*****************************************************************************/ + typedef struct { const char *opt; const char *val; @@ -104,18 +111,7 @@ static const BondDefault defaults[] = { { NM_SETTING_BOND_OPTION_LP_INTERVAL, "1", NM_BOND_OPTION_TYPE_INT, 1, G_MAXINT }, }; -/** - * nm_setting_bond_new: - * - * Creates a new #NMSettingBond object with default values. - * - * Returns: (transfer full): the new empty #NMSettingBond object - **/ -NMSetting * -nm_setting_bond_new (void) -{ - return (NMSetting *) g_object_new (NM_TYPE_SETTING_BOND, NULL); -} +/*****************************************************************************/ /** * nm_setting_bond_get_num_options: @@ -163,26 +159,38 @@ nm_setting_bond_get_option (NMSettingBond *setting, const char **out_value) { NMSettingBondPrivate *priv; - GList *keys; - const char *_key = NULL, *_value = NULL; + guint i, len; + GHashTableIter iter; + const char *key, *value; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); priv = NM_SETTING_BOND_GET_PRIVATE (setting); - if (idx >= nm_setting_bond_get_num_options (setting)) + len = g_hash_table_size (priv->options); + if (idx >= len) return FALSE; - keys = g_hash_table_get_keys (priv->options); - _key = g_list_nth_data (keys, idx); - _value = g_hash_table_lookup (priv->options, _key); + if (!G_UNLIKELY (priv->options_idx_cache)) { + NMUtilsNamedValue *options; + + i = 0; + options = g_new (NMUtilsNamedValue, len); + g_hash_table_iter_init (&iter, priv->options); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + options[i].name = key; + options[i].value_str = value; + i++; + } + nm_assert (i == len); - if (out_name) - *out_name = _key; - if (out_value) - *out_value = _value; + g_qsort_with_data (options, len, sizeof (options[0]), + nm_utils_named_entry_cmp_with_data, NULL); + priv->options_idx_cache = options; + } - g_list_free (keys); + NM_SET_OUT (out_name, priv->options_idx_cache[idx].name); + NM_SET_OUT (out_value, priv->options_idx_cache[idx].value_str); return TRUE; } @@ -362,6 +370,7 @@ nm_setting_bond_add_option (NMSettingBond *setting, priv = NM_SETTING_BOND_GET_PRIVATE (setting); + nm_clear_g_free (&priv->options_idx_cache); g_hash_table_insert (priv->options, g_strdup (name), g_strdup (value)); if ( !strcmp (name, NM_SETTING_BOND_OPTION_MIIMON) @@ -395,6 +404,7 @@ gboolean nm_setting_bond_remove_option (NMSettingBond *setting, const char *name) { + NMSettingBondPrivate *priv; gboolean found; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); @@ -402,7 +412,10 @@ nm_setting_bond_remove_option (NMSettingBond *setting, if (!nm_setting_bond_validate_option (name, NULL)) return FALSE; - found = g_hash_table_remove (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name); + priv = NM_SETTING_BOND_GET_PRIVATE (setting); + + nm_clear_g_free (&priv->options_idx_cache); + found = g_hash_table_remove (priv->options, name); if (found) g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS); return found; @@ -514,6 +527,8 @@ _nm_setting_bond_mode_from_string (const char *str) return NM_BOND_MODE_UNKNOWN; } +/*****************************************************************************/ + #define BIT(x) (1 << (x)) static const struct { @@ -812,6 +827,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } +/*****************************************************************************/ + static gboolean options_hash_match (NMSettingBond *s_bond, GHashTable *options1, @@ -887,25 +904,22 @@ compare_property (NMSetting *setting, return parent_class->compare_property (setting, other, prop_spec, flags); } -static void -nm_setting_bond_init (NMSettingBond *setting) -{ - NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); - - priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - /* Default values: */ - nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr"); -} +/*****************************************************************************/ static void -finalize (GObject *object) +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) { NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); - g_hash_table_destroy (priv->options); - - G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object); + switch (prop_id) { + case PROP_OPTIONS: + g_value_take_boxed (value, _nm_utils_copy_strdict (priv->options)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } static void @@ -916,6 +930,7 @@ set_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_OPTIONS: + nm_clear_g_free (&priv->options_idx_cache); g_hash_table_unref (priv->options); priv->options = _nm_utils_copy_strdict (g_value_get_boxed (value)); break; @@ -925,20 +940,41 @@ set_property (GObject *object, guint prop_id, } } +/*****************************************************************************/ + static void -get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) +nm_setting_bond_init (NMSettingBond *setting) +{ + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); + + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* Default values: */ + nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr"); +} + +/** + * nm_setting_bond_new: + * + * Creates a new #NMSettingBond object with default values. + * + * Returns: (transfer full): the new empty #NMSettingBond object + **/ +NMSetting * +nm_setting_bond_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_BOND, NULL); +} + +static void +finalize (GObject *object) { NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); - switch (prop_id) { - case PROP_OPTIONS: - g_value_take_boxed (value, _nm_utils_copy_strdict (priv->options)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } + nm_clear_g_free (&priv->options_idx_cache); + g_hash_table_destroy (priv->options); + + G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object); } static void diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c index 53300384..152cdf65 100644 --- a/libnm-core/nm-setting-bridge.c +++ b/libnm-core/nm-setting-bridge.c @@ -213,20 +213,6 @@ nm_setting_bridge_get_multicast_snooping (NMSettingBridge *setting) return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->multicast_snooping; } -/* IEEE 802.1D-1998 timer values */ -#define BR_MIN_HELLO_TIME 1 -#define BR_MAX_HELLO_TIME 10 - -#define BR_MIN_FORWARD_DELAY 2 -#define BR_MAX_FORWARD_DELAY 30 - -#define BR_MIN_MAX_AGE 6 -#define BR_MAX_MAX_AGE 40 - -/* IEEE 802.1D-1998 Table 7.4 */ -#define BR_MIN_AGEING_TIME 0 -#define BR_MAX_AGEING_TIME 1000000 - static inline gboolean check_range (guint32 val, guint32 min, @@ -265,32 +251,32 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } if (!check_range (priv->forward_delay, - BR_MIN_FORWARD_DELAY, - BR_MAX_FORWARD_DELAY, + NM_BR_MIN_FORWARD_DELAY, + NM_BR_MAX_FORWARD_DELAY, !priv->stp, NM_SETTING_BRIDGE_FORWARD_DELAY, error)) return FALSE; if (!check_range (priv->hello_time, - BR_MIN_HELLO_TIME, - BR_MAX_HELLO_TIME, + NM_BR_MIN_HELLO_TIME, + NM_BR_MAX_HELLO_TIME, !priv->stp, NM_SETTING_BRIDGE_HELLO_TIME, error)) return FALSE; if (!check_range (priv->max_age, - BR_MIN_MAX_AGE, - BR_MAX_MAX_AGE, + NM_BR_MIN_MAX_AGE, + NM_BR_MAX_MAX_AGE, !priv->stp, NM_SETTING_BRIDGE_MAX_AGE, error)) return FALSE; if (!check_range (priv->ageing_time, - BR_MIN_AGEING_TIME, - BR_MAX_AGEING_TIME, + NM_BR_MIN_AGEING_TIME, + NM_BR_MAX_AGEING_TIME, !priv->stp, NM_SETTING_BRIDGE_AGEING_TIME, error)) @@ -443,10 +429,10 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class) * ---end--- * ---ifcfg-rh--- * property: mac-address - * variable: MACADDR(+) + * variable: BRIDGE_MACADDR(+) * description: MAC address of the bridge. Note that this requires a recent * kernel support, originally introduced in 3.15 upstream kernel) - * MACADDR for bridges is an NM extension. + * BRIDGE_MACADDR for bridges is an NM extension. * ---end--- */ g_object_class_install_property @@ -522,7 +508,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class) g_object_class_install_property (object_class, PROP_FORWARD_DELAY, g_param_spec_uint (NM_SETTING_BRIDGE_FORWARD_DELAY, "", "", - 0, BR_MAX_FORWARD_DELAY, 15, + 0, NM_BR_MAX_FORWARD_DELAY, 15, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE | @@ -544,7 +530,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class) g_object_class_install_property (object_class, PROP_HELLO_TIME, g_param_spec_uint (NM_SETTING_BRIDGE_HELLO_TIME, "", "", - 0, BR_MAX_HELLO_TIME, 2, + 0, NM_BR_MAX_HELLO_TIME, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE | @@ -566,7 +552,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class) g_object_class_install_property (object_class, PROP_MAX_AGE, g_param_spec_uint (NM_SETTING_BRIDGE_MAX_AGE, "", "", - 0, BR_MAX_MAX_AGE, 20, + 0, NM_BR_MAX_MAX_AGE, 20, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE | @@ -588,7 +574,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class) g_object_class_install_property (object_class, PROP_AGEING_TIME, g_param_spec_uint (NM_SETTING_BRIDGE_AGEING_TIME, "", "", - 0, BR_MAX_AGEING_TIME, 300, + NM_BR_MIN_AGEING_TIME, NM_BR_MAX_AGEING_TIME, 300, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE | diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index 7265fdc5..779d4428 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -16,7 +16,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2007 - 2014 Red Hat, Inc. + * Copyright 2007 - 2017 Red Hat, Inc. * Copyright 2007 - 2008 Novell, Inc. */ @@ -1238,13 +1238,14 @@ nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value) } #define ATTR_SPEC_PTR(name, type, v4, v6, str_type) \ - &(NMVariantAttributeSpec) { name, type, v4, v6, str_type } + &(NMVariantAttributeSpec) { name, type, v4, v6, FALSE, FALSE, str_type } static const NMVariantAttributeSpec * const ip_route_attribute_spec[] = { ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TABLE, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_SRC, G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a'), ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_FROM, G_VARIANT_TYPE_STRING, FALSE, TRUE, 'p'), ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TOS, G_VARIANT_TYPE_BYTE, TRUE, FALSE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK, G_VARIANT_TYPE_BOOLEAN, TRUE, FALSE, 0 ), ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_CWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), @@ -1790,7 +1791,7 @@ nm_setting_ip_config_get_num_dns_options (NMSettingIPConfig *setting) * or the options are left undefined. The latter means to use * a default configuration, while the former explicitly means "no-options". * - * Returns: whether DNS options are initalized or left unset (the default). + * Returns: whether DNS options are initialized or left unset (the default). **/ gboolean nm_setting_ip_config_has_dns_options (NMSettingIPConfig *setting) diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h index 84ce8465..96e61bb5 100644 --- a/libnm-core/nm-setting-ip-config.h +++ b/libnm-core/nm-setting-ip-config.h @@ -146,6 +146,7 @@ gboolean nm_ip_route_attribute_validate (const char *name, #define NM_IP_ROUTE_ATTRIBUTE_SRC "src" #define NM_IP_ROUTE_ATTRIBUTE_FROM "from" #define NM_IP_ROUTE_ATTRIBUTE_TOS "tos" +#define NM_IP_ROUTE_ATTRIBUTE_ONLINK "onlink" #define NM_IP_ROUTE_ATTRIBUTE_WINDOW "window" #define NM_IP_ROUTE_ATTRIBUTE_CWND "cwnd" #define NM_IP_ROUTE_ATTRIBUTE_INITCWND "initcwnd" diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c index db3a12be..26e7f505 100644 --- a/libnm-core/nm-setting-ip4-config.c +++ b/libnm-core/nm-setting-ip4-config.c @@ -688,6 +688,14 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class) * ---end--- */ + /* ---ifcfg-rh--- + * property: dns-options + * variable: RES_OPTIONS(+) + * description: List of DNS options to be added to /etc/resolv.conf + * example: RES_OPTIONS=ndots:2 timeout:3 + * ---end--- + */ + /* ---ifcfg-rh--- * property: dns-priority * variable: IPV4_DNS_PRIORITY(+) diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c index cc6381f0..00e9391d 100644 --- a/libnm-core/nm-setting-ip6-config.c +++ b/libnm-core/nm-setting-ip6-config.c @@ -550,7 +550,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class) /* ---ifcfg-rh--- * property: dns-search - * variable: DOMAIN + * variable: IPV6_DOMAIN * format: string (space-separated domains) * description: List of DNS search domains. * ---end--- @@ -671,6 +671,14 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class) * ---end--- */ + /* ---ifcfg-rh--- + * property: dns-options + * variable: IPV6_RES_OPTIONS(+) + * description: List of DNS options to be added to /etc/resolv.conf + * example: IPV6_RES_OPTIONS=ndots:2 timeout:3 + * ---end--- + */ + /** * NMSettingIP6Config:ip6-privacy: * diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c new file mode 100644 index 00000000..a4960038 --- /dev/null +++ b/libnm-core/nm-setting-tc-config.c @@ -0,0 +1,1576 @@ +/* + * 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. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include + +#include "nm-setting-tc-config.h" +#include "nm-setting-private.h" + +/** + * SECTION:nm-setting-tc-config + * @short_description: Describes connection properties for the Linux Traffic Control + * @include: nm-setting-tc-config.h + **/ + +/*****************************************************************************/ + +G_DEFINE_BOXED_TYPE (NMTCQdisc, nm_tc_qdisc, nm_tc_qdisc_dup, nm_tc_qdisc_unref) + +struct NMTCQdisc { + guint refcount; + + char *kind; + guint32 handle; + guint32 parent; +}; + +/** + * nm_tc_qdisc_new: + * @kind: name of the queueing discipline + * @parent: the parent queueing discipline + * @error: location to store error, or %NULL + * + * Creates a new #NMTCQdisc object. + * + * Returns: (transfer full): the new #NMTCQdisc object, or %NULL on error + * + * Since: 1.10.2 + **/ +NMTCQdisc * +nm_tc_qdisc_new (const char *kind, + guint32 parent, + GError **error) +{ + NMTCQdisc *qdisc; + + if (!kind || !*kind || strchr (kind, ' ') || strchr (kind, '\t')) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid kind"), kind); + return NULL; + } + + if (!parent) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("parent handle missing")); + return NULL; + } + + qdisc = g_slice_new0 (NMTCQdisc); + qdisc->refcount = 1; + + qdisc->kind = g_strdup (kind); + qdisc->parent = parent; + + return qdisc; +} + +/** + * nm_tc_qdisc_ref: + * @qdisc: the #NMTCQdisc + * + * Increases the reference count of the object. + * + * Since: 1.10.2 + **/ +void +nm_tc_qdisc_ref (NMTCQdisc *qdisc) +{ + g_return_if_fail (qdisc != NULL); + g_return_if_fail (qdisc->refcount > 0); + + qdisc->refcount++; +} + +/** + * nm_tc_qdisc_unref: + * @qdisc: the #NMTCQdisc + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.10.2 + **/ +void +nm_tc_qdisc_unref (NMTCQdisc *qdisc) +{ + g_return_if_fail (qdisc != NULL); + g_return_if_fail (qdisc->refcount > 0); + + qdisc->refcount--; + if (qdisc->refcount == 0) { + g_free (qdisc->kind); + g_slice_free (NMTCQdisc, qdisc); + } +} + +/** + * nm_tc_qdisc_equal: + * @qdisc: the #NMTCQdisc + * @other: the #NMTCQdisc to compare @qdisc to. + * + * Determines if two #NMTCQdisc objects contain the same kind, * handle + * and parent. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + * + * Since: 1.10.2 + **/ +gboolean +nm_tc_qdisc_equal (NMTCQdisc *qdisc, NMTCQdisc *other) +{ + g_return_val_if_fail (qdisc != NULL, FALSE); + g_return_val_if_fail (qdisc->refcount > 0, FALSE); + + g_return_val_if_fail (other != NULL, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if ( qdisc->handle != other->handle + || qdisc->parent != other->parent + || g_strcmp0 (qdisc->kind, other->kind) != 0) + return FALSE; + + return TRUE; +} + +/** + * nm_tc_qdisc_dup: + * @qdisc: the #NMTCQdisc + * + * Creates a copy of @qdisc + * + * Returns: (transfer full): a copy of @qdisc + * + * Since: 1.10.2 + **/ +NMTCQdisc * +nm_tc_qdisc_dup (NMTCQdisc *qdisc) +{ + NMTCQdisc *copy; + + g_return_val_if_fail (qdisc != NULL, NULL); + g_return_val_if_fail (qdisc->refcount > 0, NULL); + + copy = nm_tc_qdisc_new (qdisc->kind, qdisc->parent, NULL); + nm_tc_qdisc_set_handle (copy, qdisc->handle); + + return copy; +} + +/** + * nm_tc_qdisc_get_kind: + * @qdisc: the #NMTCQdisc + * + * Returns: + * + * Since: 1.10.2 + **/ +const char * +nm_tc_qdisc_get_kind (NMTCQdisc *qdisc) +{ + g_return_val_if_fail (qdisc != NULL, NULL); + g_return_val_if_fail (qdisc->refcount > 0, NULL); + + return qdisc->kind; +} + +/** + * nm_tc_qdisc_get_handle: + * @qdisc: the #NMTCQdisc + * + * Returns: the queueing discipline handle + * + * Since: 1.10.2 + **/ +guint32 +nm_tc_qdisc_get_handle (NMTCQdisc *qdisc) +{ + g_return_val_if_fail (qdisc != NULL, TC_H_UNSPEC); + g_return_val_if_fail (qdisc->refcount > 0, TC_H_UNSPEC); + + return qdisc->handle; +} + +/** + * nm_tc_qdisc_set_handle: + * @qdisc: the #NMTCQdisc + * @handle: the queueing discipline handle + * + * Sets the queueing discipline handle. + * + * Since: 1.10.2 + **/ +void +nm_tc_qdisc_set_handle (NMTCQdisc *qdisc, guint32 handle) +{ + g_return_if_fail (qdisc != NULL); + g_return_if_fail (qdisc->refcount > 0); + + qdisc->handle = handle; +} + +/** + * nm_tc_qdisc_get_parent: + * @qdisc: the #NMTCQdisc + * + * Returns: the parent class + * + * Since: 1.10.2 + **/ +guint32 +nm_tc_qdisc_get_parent (NMTCQdisc *qdisc) +{ + g_return_val_if_fail (qdisc != NULL, TC_H_UNSPEC); + g_return_val_if_fail (qdisc->refcount > 0, TC_H_UNSPEC); + + return qdisc->parent; +} + +/*****************************************************************************/ + +G_DEFINE_BOXED_TYPE (NMTCAction, nm_tc_action, nm_tc_action_dup, nm_tc_action_unref) + +struct NMTCAction { + guint refcount; + + char *kind; + + GHashTable *attributes; +}; + +/** + * nm_tc_action_new: + * @kind: name of the queueing discipline + * @error: location to store error, or %NULL + * + * Creates a new #NMTCAction object. + * + * Returns: (transfer full): the new #NMTCAction object, or %NULL on error + * + * Since: 1.10.2 + **/ +NMTCAction * +nm_tc_action_new (const char *kind, + GError **error) +{ + NMTCAction *action; + + if (!kind || !*kind || strchr (kind, ' ') || strchr (kind, '\t')) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid kind"), kind); + return NULL; + } + + action = g_slice_new0 (NMTCAction); + action->refcount = 1; + + action->kind = g_strdup (kind); + + return action; +} + +/** + * nm_tc_action_ref: + * @action: the #NMTCAction + * + * Increases the reference count of the object. + * + * Since: 1.10.2 + **/ +void +nm_tc_action_ref (NMTCAction *action) +{ + g_return_if_fail (action != NULL); + g_return_if_fail (action->refcount > 0); + + action->refcount++; +} + +/** + * nm_tc_action_unref: + * @action: the #NMTCAction + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.10.2 + **/ +void +nm_tc_action_unref (NMTCAction *action) +{ + g_return_if_fail (action != NULL); + g_return_if_fail (action->refcount > 0); + + action->refcount--; + if (action->refcount == 0) { + g_free (action->kind); + if (action->attributes) + g_hash_table_unref (action->attributes); + g_slice_free (NMTCAction, action); + } +} + +/** + * nm_tc_action_equal: + * @action: the #NMTCAction + * @other: the #NMTCAction to compare @action to. + * + * Determines if two #NMTCAction objects contain the same kind, family, + * handle, parent and info. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + * + * Since: 1.10.2 + **/ +gboolean +nm_tc_action_equal (NMTCAction *action, NMTCAction *other) +{ + GHashTableIter iter; + const char *key; + GVariant *value, *value2; + guint n; + + g_return_val_if_fail (!action || action->refcount > 0, FALSE); + g_return_val_if_fail (!other || other->refcount > 0, FALSE); + + if (action == other) + return TRUE; + if (!action || !other) + return FALSE; + + if (g_strcmp0 (action->kind, other->kind) != 0) + return FALSE; + + n = action->attributes ? g_hash_table_size (action->attributes) : 0; + if (n != (other->attributes ? g_hash_table_size (other->attributes) : 0)) + return FALSE; + if (n) { + g_hash_table_iter_init (&iter, action->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; + } + } + + return TRUE; +} + +/** + * nm_tc_action_dup: + * @action: the #NMTCAction + * + * Creates a copy of @action + * + * Returns: (transfer full): a copy of @action + * + * Since: 1.10.2 + **/ +NMTCAction * +nm_tc_action_dup (NMTCAction *action) +{ + NMTCAction *copy; + + g_return_val_if_fail (action != NULL, NULL); + g_return_val_if_fail (action->refcount > 0, NULL); + + copy = nm_tc_action_new (action->kind, NULL); + + if (action->attributes) { + GHashTableIter iter; + const char *key; + GVariant *value; + + g_hash_table_iter_init (&iter, action->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) + nm_tc_action_set_attribute (copy, key, value); + } + + return copy; +} + +/** + * nm_tc_action_get_kind: + * @action: the #NMTCAction + * + * Returns: + * + * Since: 1.10.2 + **/ +const char * +nm_tc_action_get_kind (NMTCAction *action) +{ + g_return_val_if_fail (action != NULL, NULL); + g_return_val_if_fail (action->refcount > 0, NULL); + + return action->kind; +} + +/** + * nm_tc_action_get_attribute_names: + * @action: the #NMTCAction + * + * Gets an array of attribute names defined on @action. + * + * Returns: (transfer full): a %NULL-terminated array of attribute names, + **/ +char ** +nm_tc_action_get_attribute_names (NMTCAction *action) +{ + GHashTableIter iter; + const char *key; + GPtrArray *names; + + g_return_val_if_fail (action != NULL, NULL); + + names = g_ptr_array_new (); + + if (action->attributes) { + g_hash_table_iter_init (&iter, action->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) + g_ptr_array_add (names, g_strdup (key)); + } + g_ptr_array_add (names, NULL); + + return (char **) g_ptr_array_free (names, FALSE); +} + +/** + * nm_tc_action_get_attribute: + * @action: the #NMTCAction + * @name: the name of an action attribute + * + * Gets the value of the attribute with name @name on @action + * + * Returns: (transfer none): the value of the attribute with name @name on + * @action, or %NULL if @action has no such attribute. + **/ +GVariant * +nm_tc_action_get_attribute (NMTCAction *action, const char *name) +{ + g_return_val_if_fail (action != NULL, NULL); + g_return_val_if_fail (name != NULL && *name != '\0', NULL); + + if (action->attributes) + return g_hash_table_lookup (action->attributes, name); + else + return NULL; +} + +/** + * nm_tc_action_set_attribute: + * @action: the #NMTCAction + * @name: the name of an action attribute + * @value: (transfer none) (allow-none): the value + * + * Sets or clears the named attribute on @action to the given value. + **/ +void +nm_tc_action_set_attribute (NMTCAction *action, const char *name, GVariant *value) +{ + g_return_if_fail (action != NULL); + g_return_if_fail (name != NULL && *name != '\0'); + g_return_if_fail (strcmp (name, "kind") != 0); + + if (!action->attributes) { + action->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_variant_unref); + } + + if (value) + g_hash_table_insert (action->attributes, g_strdup (name), g_variant_ref_sink (value)); + else + g_hash_table_remove (action->attributes, name); +} + +/*****************************************************************************/ + +G_DEFINE_BOXED_TYPE (NMTCTfilter, nm_tc_tfilter, nm_tc_tfilter_dup, nm_tc_tfilter_unref) + +struct NMTCTfilter { + guint refcount; + + char *kind; + guint32 handle; + guint32 parent; + NMTCAction *action; +}; + +/** + * nm_tc_tfilter_new: + * @kind: name of the queueing discipline + * @parent: the parent queueing discipline + * @error: location to store error, or %NULL + * + * Creates a new #NMTCTfilter object. + * + * Returns: (transfer full): the new #NMTCTfilter object, or %NULL on error + * + * Since: 1.10.2 + **/ +NMTCTfilter * +nm_tc_tfilter_new (const char *kind, + guint32 parent, + GError **error) +{ + NMTCTfilter *tfilter; + + if (!kind || !*kind || strchr (kind, ' ') || strchr (kind, '\t')) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid kind"), kind); + return NULL; + } + + if (!parent) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("parent handle missing")); + return NULL; + } + + tfilter = g_slice_new0 (NMTCTfilter); + tfilter->refcount = 1; + + tfilter->kind = g_strdup (kind); + tfilter->parent = parent; + + return tfilter; +} + +/** + * nm_tc_tfilter_ref: + * @tfilter: the #NMTCTfilter + * + * Increases the reference count of the object. + * + * Since: 1.10.2 + **/ +void +nm_tc_tfilter_ref (NMTCTfilter *tfilter) +{ + g_return_if_fail (tfilter != NULL); + g_return_if_fail (tfilter->refcount > 0); + + tfilter->refcount++; +} + +/** + * nm_tc_tfilter_unref: + * @tfilter: the #NMTCTfilter + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.10.2 + **/ +void +nm_tc_tfilter_unref (NMTCTfilter *tfilter) +{ + g_return_if_fail (tfilter != NULL); + g_return_if_fail (tfilter->refcount > 0); + + tfilter->refcount--; + if (tfilter->refcount == 0) { + g_free (tfilter->kind); + if (tfilter->action) + nm_tc_action_unref (tfilter->action); + g_slice_free (NMTCTfilter, tfilter); + } +} + +/** + * nm_tc_tfilter_equal: + * @tfilter: the #NMTCTfilter + * @other: the #NMTCTfilter to compare @tfilter to. + * + * Determines if two #NMTCTfilter objects contain the same kind, family, + * handle, parent and info. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + * + * Since: 1.10.2 + **/ +gboolean +nm_tc_tfilter_equal (NMTCTfilter *tfilter, NMTCTfilter *other) +{ + g_return_val_if_fail (tfilter != NULL, FALSE); + g_return_val_if_fail (tfilter->refcount > 0, FALSE); + + g_return_val_if_fail (other != NULL, FALSE); + g_return_val_if_fail (other->refcount > 0, FALSE); + + if ( tfilter->handle != other->handle + || tfilter->parent != other->parent + || g_strcmp0 (tfilter->kind, other->kind) != 0 + || !nm_tc_action_equal (tfilter->action, other->action)) + return FALSE; + + return TRUE; +} + +/** + * nm_tc_tfilter_dup: + * @tfilter: the #NMTCTfilter + * + * Creates a copy of @tfilter + * + * Returns: (transfer full): a copy of @tfilter + * + * Since: 1.10.2 + **/ +NMTCTfilter * +nm_tc_tfilter_dup (NMTCTfilter *tfilter) +{ + NMTCTfilter *copy; + + g_return_val_if_fail (tfilter != NULL, NULL); + g_return_val_if_fail (tfilter->refcount > 0, NULL); + + copy = nm_tc_tfilter_new (tfilter->kind, tfilter->parent, NULL); + nm_tc_tfilter_set_handle (copy, tfilter->handle); + nm_tc_tfilter_set_action (copy, tfilter->action); + + return copy; +} + +/** + * nm_tc_tfilter_get_kind: + * @tfilter: the #NMTCTfilter + * + * Returns: + * + * Since: 1.10.2 + **/ +const char * +nm_tc_tfilter_get_kind (NMTCTfilter *tfilter) +{ + g_return_val_if_fail (tfilter != NULL, NULL); + g_return_val_if_fail (tfilter->refcount > 0, NULL); + + return tfilter->kind; +} + +/** + * nm_tc_tfilter_get_handle: + * @tfilter: the #NMTCTfilter + * + * Returns: the queueing discipline handle + * + * Since: 1.10.2 + **/ +guint32 +nm_tc_tfilter_get_handle (NMTCTfilter *tfilter) +{ + g_return_val_if_fail (tfilter != NULL, TC_H_UNSPEC); + g_return_val_if_fail (tfilter->refcount > 0, TC_H_UNSPEC); + + return tfilter->handle; +} + +/** + * nm_tc_tfilter_set_handle: + * @tfilter: the #NMTCTfilter + * @handle: the queueing discipline handle + * + * Sets the queueing discipline handle. + * + * Since: 1.10.2 + **/ +void +nm_tc_tfilter_set_handle (NMTCTfilter *tfilter, guint32 handle) +{ + g_return_if_fail (tfilter != NULL); + g_return_if_fail (tfilter->refcount > 0); + + tfilter->handle = handle; +} + +/** + * nm_tc_tfilter_get_parent: + * @tfilter: the #NMTCTfilter + * + * Returns: the parent class + * + * Since: 1.10.2 + **/ +guint32 +nm_tc_tfilter_get_parent (NMTCTfilter *tfilter) +{ + g_return_val_if_fail (tfilter != NULL, TC_H_UNSPEC); + g_return_val_if_fail (tfilter->refcount > 0, TC_H_UNSPEC); + + return tfilter->parent; +} + +/** + * nm_tc_tfilter_get_action: + * @tfilter: the #NMTCTfilter + * + * Returns: the action associated with a traffic filter. + * + * Since: 1.10.2 + **/ +NMTCAction * +nm_tc_tfilter_get_action (NMTCTfilter *tfilter) +{ + g_return_val_if_fail (tfilter != NULL, TC_H_UNSPEC); + g_return_val_if_fail (tfilter->refcount > 0, TC_H_UNSPEC); + + if (tfilter->action == NULL) + return NULL; + + return tfilter->action; +} + +/** + * nm_tc_tfilter_set_action: + * @tfilter: the #NMTCTfilter + * @action: the action object + * + * Sets the action associated with a traffic filter. + * + * Since: 1.10.2 + **/ +void +nm_tc_tfilter_set_action (NMTCTfilter *tfilter, NMTCAction *action) +{ + g_return_if_fail (tfilter != NULL); + g_return_if_fail (tfilter->refcount > 0); + + if (action) + nm_tc_action_ref (action); + if (tfilter->action) + nm_tc_action_unref (tfilter->action); + tfilter->action = action; +} + +/*****************************************************************************/ + +enum { + PROP_0, + PROP_QDISCS, + PROP_TFILTERS, + + LAST_PROP +}; + +/** + * NMSettingTCConfig: + * + * Linux Traffic Contril Settings. + * + * Since: 1.10.2 + */ +struct _NMSettingTCConfig { + NMSetting parent; + GPtrArray *qdiscs; + GPtrArray *tfilters; +}; + +struct _NMSettingTCConfigClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE_WITH_CODE (NMSettingTCConfig, nm_setting_tc_config, NM_TYPE_SETTING, + _nm_register_setting (TC_CONFIG, NM_SETTING_PRIORITY_IP)) +NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TC_CONFIG) + +/** + * nm_setting_tc_config_new: + * + * Creates a new #NMSettingTCConfig object with default values. + * + * Returns: (transfer full): the new empty #NMSettingTCConfig object + * + * Since: 1.10.2 + **/ +NMSetting * +nm_setting_tc_config_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_TC_CONFIG, NULL); +} + +/** + * nm_setting_tc_config_get_num_qdiscs: + * @setting: the #NMSettingTCConfig + * + * Returns: the number of configured queueing disciplines + * + * Since: 1.10.2 + **/ +guint +nm_setting_tc_config_get_num_qdiscs (NMSettingTCConfig *self) +{ + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), 0); + + return self->qdiscs->len; +} + +/** + * nm_setting_tc_config_get_qdisc: + * @setting: the #NMSettingTCConfig + * @idx: index number of the qdisc to return + * + * Returns: (transfer none): the qdisc at index @idx + * + * Since: 1.10.2 + **/ +NMTCQdisc * +nm_setting_tc_config_get_qdisc (NMSettingTCConfig *self, guint idx) +{ + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), NULL); + g_return_val_if_fail (idx < self->qdiscs->len, NULL); + + return self->qdiscs->pdata[idx]; +} + +/** + * nm_setting_tc_config_add_qdisc: + * @setting: the #NMSettingTCConfig + * @qdisc: the qdisc to add + * + * Appends a new qdisc and associated information to the setting. The + * given qdisc is duplicated internally and is not changed by this function. + * If an identical qdisc (considering attributes as well) already exists, the + * qdisc is not added and the function returns %FALSE. + * + * Returns: %TRUE if the qdisc was added; %FALSE if the qdisc was already known. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_tc_config_add_qdisc (NMSettingTCConfig *self, + NMTCQdisc *qdisc) +{ + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), FALSE); + g_return_val_if_fail (qdisc != NULL, FALSE); + + for (i = 0; i < self->qdiscs->len; i++) { + if (nm_tc_qdisc_equal (self->qdiscs->pdata[i], qdisc)) + return FALSE; + } + + g_ptr_array_add (self->qdiscs, nm_tc_qdisc_dup (qdisc)); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS); + return TRUE; +} + +/** + * nm_setting_tc_config_remove_qdisc: + * @setting: the #NMSettingTCConfig + * @idx: index number of the qdisc + * + * Removes the qdisc at index @idx. + * + * Since: 1.10.2 + **/ +void +nm_setting_tc_config_remove_qdisc (NMSettingTCConfig *self, guint idx) +{ + g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self)); + + g_return_if_fail (idx < self->qdiscs->len); + + g_ptr_array_remove_index (self->qdiscs, idx); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS); +} + +/** + * nm_setting_tc_config_remove_qdisc_by_value: + * @setting: the #NMSettingTCConfig + * @qdisc: the qdisc to remove + * + * Removes the first matching qdisc that matches @qdisc. + * + * Returns: %TRUE if the qdisc was found and removed; %FALSE if it was not. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_tc_config_remove_qdisc_by_value (NMSettingTCConfig *self, + NMTCQdisc *qdisc) +{ + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), FALSE); + g_return_val_if_fail (qdisc != NULL, FALSE); + + for (i = 0; i < self->qdiscs->len; i++) { + if (nm_tc_qdisc_equal (self->qdiscs->pdata[i], qdisc)) { + g_ptr_array_remove_index (self->qdiscs, i); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_tc_config_clear_qdiscs: + * @setting: the #NMSettingTCConfig + * + * Removes all configured queueing disciplines. + * + * Since: 1.10.2 + **/ +void +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); +} + +/*****************************************************************************/ +/** + * nm_setting_tc_config_get_num_tfilters: + * @setting: the #NMSettingTCConfig + * + * Returns: the number of configured queueing disciplines + * + * Since: 1.10.2 + **/ +guint +nm_setting_tc_config_get_num_tfilters (NMSettingTCConfig *self) +{ + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), 0); + + return self->tfilters->len; +} + +/** + * nm_setting_tc_config_get_tfilter: + * @setting: the #NMSettingTCConfig + * @idx: index number of the tfilter to return + * + * Returns: (transfer none): the tfilter at index @idx + * + * Since: 1.10.2 + **/ +NMTCTfilter * +nm_setting_tc_config_get_tfilter (NMSettingTCConfig *self, guint idx) +{ + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), NULL); + g_return_val_if_fail (idx < self->tfilters->len, NULL); + + return self->tfilters->pdata[idx]; +} + +/** + * nm_setting_tc_config_add_tfilter: + * @setting: the #NMSettingTCConfig + * @tfilter: the tfilter to add + * + * Appends a new tfilter and associated information to the setting. The + * given tfilter is duplicated internally and is not changed by this function. + * If an identical tfilter (considering attributes as well) already exists, the + * tfilter is not added and the function returns %FALSE. + * + * Returns: %TRUE if the tfilter was added; %FALSE if the tfilter was already known. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_tc_config_add_tfilter (NMSettingTCConfig *self, + NMTCTfilter *tfilter) +{ + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), FALSE); + g_return_val_if_fail (tfilter != NULL, FALSE); + + for (i = 0; i < self->tfilters->len; i++) { + if (nm_tc_tfilter_equal (self->tfilters->pdata[i], tfilter)) + return FALSE; + } + + g_ptr_array_add (self->tfilters, nm_tc_tfilter_dup (tfilter)); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS); + return TRUE; +} + +/** + * nm_setting_tc_config_remove_tfilter: + * @setting: the #NMSettingTCConfig + * @idx: index number of the tfilter + * + * Removes the tfilter at index @idx. + * + * Since: 1.10.2 + **/ +void +nm_setting_tc_config_remove_tfilter (NMSettingTCConfig *self, guint idx) +{ + g_return_if_fail (NM_IS_SETTING_TC_CONFIG (self)); + g_return_if_fail (idx < self->tfilters->len); + + g_ptr_array_remove_index (self->tfilters, idx); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS); +} + +/** + * nm_setting_tc_config_remove_tfilter_by_value: + * @setting: the #NMSettingTCConfig + * @tfilter: the tfilter to remove + * + * Removes the first matching tfilter that matches @tfilter. + * + * Returns: %TRUE if the tfilter was found and removed; %FALSE if it was not. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_tc_config_remove_tfilter_by_value (NMSettingTCConfig *self, + NMTCTfilter *tfilter) +{ + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TC_CONFIG (self), FALSE); + g_return_val_if_fail (tfilter != NULL, FALSE); + + for (i = 0; i < self->tfilters->len; i++) { + if (nm_tc_tfilter_equal (self->tfilters->pdata[i], tfilter)) { + g_ptr_array_remove_index (self->tfilters, i); + g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_tc_config_clear_tfilters: + * @setting: the #NMSettingTCConfig + * + * Removes all configured queueing disciplines. + * + * Since: 1.10.2 + **/ +void +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); +} + +/*****************************************************************************/ + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object); + + switch (prop_id) { + case PROP_QDISCS: + g_ptr_array_unref (self->qdiscs); + self->qdiscs = _nm_utils_copy_array (g_value_get_boxed (value), + (NMUtilsCopyFunc) nm_tc_qdisc_dup, + (GDestroyNotify) nm_tc_qdisc_unref); + break; + case PROP_TFILTERS: + g_ptr_array_unref (self->tfilters); + self->tfilters = _nm_utils_copy_array (g_value_get_boxed (value), + (NMUtilsCopyFunc) nm_tc_tfilter_dup, + (GDestroyNotify) nm_tc_tfilter_unref); + 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) +{ + NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object); + + switch (prop_id) { + case PROP_QDISCS: + g_value_take_boxed (value, _nm_utils_copy_array (self->qdiscs, + (NMUtilsCopyFunc) nm_tc_qdisc_dup, + (GDestroyNotify) nm_tc_qdisc_unref)); + break; + case PROP_TFILTERS: + g_value_take_boxed (value, _nm_utils_copy_array (self->tfilters, + (NMUtilsCopyFunc) nm_tc_tfilter_dup, + (GDestroyNotify) nm_tc_tfilter_unref)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object); + + g_ptr_array_unref (self->qdiscs); + g_ptr_array_unref (self->tfilters); + + G_OBJECT_CLASS (nm_setting_tc_config_parent_class)->finalize (object); +} + +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingTCConfig *a_tc_config = NM_SETTING_TC_CONFIG (setting); + NMSettingTCConfig *b_tc_config = NM_SETTING_TC_CONFIG (other); + NMSettingClass *parent_class; + guint i; + + if (nm_streq (prop_spec->name, NM_SETTING_TC_CONFIG_QDISCS)) { + if (a_tc_config->qdiscs->len != b_tc_config->qdiscs->len) + return FALSE; + for (i = 0; i < a_tc_config->qdiscs->len; i++) { + if (!nm_tc_qdisc_equal (a_tc_config->qdiscs->pdata[i], b_tc_config->qdiscs->pdata[i])) + return FALSE; + } + return TRUE; + } + + if (nm_streq (prop_spec->name, NM_SETTING_TC_CONFIG_TFILTERS)) { + if (a_tc_config->tfilters->len != b_tc_config->tfilters->len) + return FALSE; + for (i = 0; i < a_tc_config->tfilters->len; i++) { + if (!nm_tc_tfilter_equal (a_tc_config->tfilters->pdata[i], b_tc_config->tfilters->pdata[i])) + return FALSE; + } + return TRUE; + } + + /* Otherwise chain up to parent to handle generic compare */ + parent_class = NM_SETTING_CLASS (nm_setting_tc_config_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +static void +nm_setting_tc_config_init (NMSettingTCConfig *self) +{ + self->qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref); + self->tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref); +} + +/** + * _qdiscs_to_variant: + * @qdiscs: (element-type NMTCQdisc): an array of #NMTCQdisc objects + * + * Utility function to convert a #GPtrArray of #NMTCQdisc objects representing + * TC qdiscs into a #GVariant of type 'aa{sv}' representing an array + * of NetworkManager TC qdiscs. + * + * Returns: (transfer none): a new floating #GVariant representing @qdiscs. + **/ +static GVariant * +_qdiscs_to_variant (GPtrArray *qdiscs) +{ + GVariantBuilder builder; + int i; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + if (qdiscs) { + for (i = 0; i < qdiscs->len; i++) { + NMTCQdisc *qdisc = qdiscs->pdata[i]; + GVariantBuilder qdisc_builder; + + g_variant_builder_init (&qdisc_builder, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add (&qdisc_builder, "{sv}", "kind", + g_variant_new_string (nm_tc_qdisc_get_kind (qdisc))); + + g_variant_builder_add (&qdisc_builder, "{sv}", "handle", + g_variant_new_uint32 (nm_tc_qdisc_get_handle (qdisc))); + + g_variant_builder_add (&qdisc_builder, "{sv}", "parent", + g_variant_new_uint32 (nm_tc_qdisc_get_parent (qdisc))); + + g_variant_builder_add (&builder, "a{sv}", &qdisc_builder); + } + } + + return g_variant_builder_end (&builder); +} + +/** + * _qdiscs_from_variant: + * @value: a #GVariant of type 'aa{sv}' + * + * Utility function to convert a #GVariant representing a list of TC qdiscs + * into a #GPtrArray of * #NMTCQdisc objects. + * + * Returns: (transfer full) (element-type NMTCQdisc): a newly allocated + * #GPtrArray of #NMTCQdisc objects + **/ +static GPtrArray * +_qdiscs_from_variant (GVariant *value) +{ + GPtrArray *qdiscs; + GVariant *qdisc_var; + GVariantIter iter; + GError *error = NULL; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), NULL); + + g_variant_iter_init (&iter, value); + qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref); + + while (g_variant_iter_next (&iter, "@a{sv}", &qdisc_var)) { + const char *kind; + guint32 handle; + guint32 parent; + NMTCQdisc *qdisc; + + if ( !g_variant_lookup (qdisc_var, "kind", "&s", &kind) + || !g_variant_lookup (qdisc_var, "parent", "u", &parent)) { + //g_warning ("Ignoring invalid qdisc"); + goto next; + } + + qdisc = nm_tc_qdisc_new (kind, parent, &error); + if (!qdisc) { + //g_warning ("Ignoring invalid qdisc: %s", error->message); + g_clear_error (&error); + goto next; + } + + if (g_variant_lookup (qdisc_var, "handle", "u", &handle)) + nm_tc_qdisc_set_handle (qdisc, handle); + + g_ptr_array_add (qdiscs, qdisc); +next: + g_variant_unref (qdisc_var); + } + + return qdiscs; +} + +static GVariant * +tc_qdiscs_get (NMSetting *setting, + const char *property) +{ + GPtrArray *qdiscs; + GVariant *ret; + + g_object_get (setting, NM_SETTING_TC_CONFIG_QDISCS, &qdiscs, NULL); + ret = _qdiscs_to_variant (qdiscs); + g_ptr_array_unref (qdiscs); + + return ret; +} + +static gboolean +tc_qdiscs_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + GPtrArray *qdiscs; + + qdiscs = _qdiscs_from_variant (value); + g_object_set (setting, NM_SETTING_TC_CONFIG_QDISCS, qdiscs, NULL); + g_ptr_array_unref (qdiscs); + + return TRUE; +} + +static GVariant * +_action_to_variant (NMTCAction *action) +{ + GVariantBuilder builder; + gs_strfreev char **attrs = nm_tc_action_get_attribute_names (action); + int i; + + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add (&builder, "{sv}", "kind", + g_variant_new_string (nm_tc_action_get_kind (action))); + + for (i = 0; attrs[i]; i++) { + g_variant_builder_add (&builder, "{sv}", attrs[i], + nm_tc_action_get_attribute (action, attrs[i])); + } + + return g_variant_builder_end (&builder); +} + +/** + * _tfilters_to_variant: + * @tfilters: (element-type NMTCTfilter): an array of #NMTCTfilter objects + * + * Utility function to convert a #GPtrArray of #NMTCTfilter objects representing + * TC tfilters into a #GVariant of type 'aa{sv}' representing an array + * of NetworkManager TC tfilters. + * + * Returns: (transfer none): a new floating #GVariant representing @tfilters. + **/ +static GVariant * +_tfilters_to_variant (GPtrArray *tfilters) +{ + GVariantBuilder builder; + int i; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + if (tfilters) { + for (i = 0; i < tfilters->len; i++) { + NMTCTfilter *tfilter = tfilters->pdata[i]; + NMTCAction *action = nm_tc_tfilter_get_action (tfilter); + GVariantBuilder tfilter_builder; + + g_variant_builder_init (&tfilter_builder, G_VARIANT_TYPE ("a{sv}")); + + g_variant_builder_add (&tfilter_builder, "{sv}", "kind", + g_variant_new_string (nm_tc_tfilter_get_kind (tfilter))); + g_variant_builder_add (&tfilter_builder, "{sv}", "handle", + g_variant_new_uint32 (nm_tc_tfilter_get_handle (tfilter))); + g_variant_builder_add (&tfilter_builder, "{sv}", "parent", + g_variant_new_uint32 (nm_tc_tfilter_get_parent (tfilter))); + + if (action) { + g_variant_builder_add (&tfilter_builder, "{sv}", "action", + _action_to_variant (action)); + } + + g_variant_builder_add (&builder, "a{sv}", &tfilter_builder); + } + } + + return g_variant_builder_end (&builder); +} + +/** + * _tfilters_from_variant: + * @value: a #GVariant of type 'aa{sv}' + * + * Utility function to convert a #GVariant representing a list of TC tfilters + * into a #GPtrArray of * #NMTCTfilter objects. + * + * Returns: (transfer full) (element-type NMTCTfilter): a newly allocated + * #GPtrArray of #NMTCTfilter objects + **/ +static GPtrArray * +_tfilters_from_variant (GVariant *value) +{ + GPtrArray *tfilters; + GVariant *tfilter_var; + GVariantIter iter; + GError *error = NULL; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), NULL); + + g_variant_iter_init (&iter, value); + tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref); + + while (g_variant_iter_next (&iter, "@a{sv}", &tfilter_var)) { + NMTCTfilter *tfilter = NULL; + const char *kind; + guint32 handle; + guint32 parent; + NMTCAction *action; + const char *action_kind = NULL; + char *action_key; + GVariantIter action_iter; + GVariant *action_var = NULL; + GVariant *action_val; + + if ( !g_variant_lookup (tfilter_var, "kind", "&s", &kind) + || !g_variant_lookup (tfilter_var, "parent", "u", &parent)) { + //g_warning ("Ignoring invalid tfilter"); + goto next; + } + + tfilter = nm_tc_tfilter_new (kind, parent, &error); + if (!tfilter) { + //g_warning ("Ignoring invalid tfilter: %s", error->message); + g_clear_error (&error); + goto next; + } + + if (g_variant_lookup (tfilter_var, "handle", "u", &handle)) + nm_tc_tfilter_set_handle (tfilter, handle); + + action_var = g_variant_lookup_value (tfilter_var, "action", G_VARIANT_TYPE_VARDICT); + + + if (action_var) { + if (!g_variant_lookup (action_var, "kind", "&s", &action_kind)) { + //g_warning ("Ignoring tfilter with invalid action"); + goto next; + } + + action = nm_tc_action_new (action_kind, &error); + if (!action) { + //g_warning ("Ignoring tfilter with invalid action: %s", error->message); + g_clear_error (&error); + goto next; + } + + g_variant_iter_init (&action_iter, action_var); + while (g_variant_iter_next (&action_iter, "{&sv}", &action_key, &action_val)) { + if (strcmp (action_key, "kind") != 0) + nm_tc_action_set_attribute (action, action_key, action_val); + g_variant_unref (action_val); + } + + nm_tc_tfilter_set_action (tfilter, action); + nm_tc_action_unref (action); + } + + nm_tc_tfilter_ref (tfilter); + g_ptr_array_add (tfilters, tfilter); +next: + if (tfilter) + nm_tc_tfilter_unref (tfilter); + if (action_var) + g_variant_unref (action_var); + g_variant_unref (tfilter_var); + } + + return tfilters; +} + +static GVariant * +tc_tfilters_get (NMSetting *setting, + const char *property) +{ + GPtrArray *tfilters; + GVariant *ret; + + g_object_get (setting, NM_SETTING_TC_CONFIG_TFILTERS, &tfilters, NULL); + ret = _tfilters_to_variant (tfilters); + g_ptr_array_unref (tfilters); + + return ret; +} + +static gboolean +tc_tfilters_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + GPtrArray *tfilters; + + tfilters = _tfilters_from_variant (value); + g_object_set (setting, NM_SETTING_TC_CONFIG_TFILTERS, tfilters, NULL); + g_ptr_array_unref (tfilters); + + return TRUE; +} + +static void +nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->compare_property = compare_property; + + /* Properties */ + + /** + * NMSettingTCConfig:qdiscs: + * + * Array of TC queuening disciplines. + * + * Element-Type: NMTCQdisc + **/ + /* ---ifcfg-rh--- + * property: qdiscs + * variable: QDISC1, QDISC2, ... + * description: Queueing disciplines + * example: QDISC1=ingress, QDISC2="root handle 1234: fq_codel" + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_QDISCS, + g_param_spec_boxed (NM_SETTING_TC_CONFIG_QDISCS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + _nm_setting_class_override_property (parent_class, + NM_SETTING_TC_CONFIG_QDISCS, + G_VARIANT_TYPE ("aa{sv}"), + tc_qdiscs_get, + tc_qdiscs_set, + NULL); + + /** + * NMSettingTCConfig:tfilters: + * + * Array of TC traffic filters. + * + * Element-Type: NMTCTfilter + **/ + /* ---ifcfg-rh--- + * property: qdiscs + * variable: FILTER1, FILTER2, ... + * description: Traffic filters + * example: FILTER1="parent ffff: matchall action simple sdata Input", ... + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_TFILTERS, + g_param_spec_boxed (NM_SETTING_TC_CONFIG_TFILTERS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + _nm_setting_class_override_property (parent_class, + NM_SETTING_TC_CONFIG_TFILTERS, + G_VARIANT_TYPE ("aa{sv}"), + tc_tfilters_get, + tc_tfilters_set, + NULL); +} diff --git a/libnm-core/nm-setting-tc-config.h b/libnm-core/nm-setting-tc-config.h new file mode 100644 index 00000000..736d5ad1 --- /dev/null +++ b/libnm-core/nm-setting-tc-config.h @@ -0,0 +1,191 @@ +/* + * 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. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2017 Red Hat, Inc. + */ + +#ifndef NM_SETTING_TC_CONFIG_H +#define NM_SETTING_TC_CONFIG_H + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +NM_AVAILABLE_IN_1_10_2 +typedef struct NMTCQdisc NMTCQdisc; + +NM_AVAILABLE_IN_1_10_2 +GType nm_tc_qdisc_get_type (void); + +NM_AVAILABLE_IN_1_10_2 +NMTCQdisc *nm_tc_qdisc_new (const char *kind, + guint32 parent, + GError **error); + +NM_AVAILABLE_IN_1_10_2 +void nm_tc_qdisc_ref (NMTCQdisc *qdisc); +NM_AVAILABLE_IN_1_10_2 +void nm_tc_qdisc_unref (NMTCQdisc *qdisc); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_tc_qdisc_equal (NMTCQdisc *qdisc, + NMTCQdisc *other); + +NM_AVAILABLE_IN_1_10_2 +NMTCQdisc *nm_tc_qdisc_dup (NMTCQdisc *qdisc); + + +NM_AVAILABLE_IN_1_10_2 +const char *nm_tc_qdisc_get_kind (NMTCQdisc *qdisc); +NM_AVAILABLE_IN_1_10_2 +guint32 nm_tc_qdisc_get_handle (NMTCQdisc *qdisc); +NM_AVAILABLE_IN_1_10_2 +void nm_tc_qdisc_set_handle (NMTCQdisc *qdisc, + guint32 handle); +NM_AVAILABLE_IN_1_10_2 +guint32 nm_tc_qdisc_get_parent (NMTCQdisc *qdisc); + +NM_AVAILABLE_IN_1_10_2 +typedef struct NMTCAction NMTCAction; + +NM_AVAILABLE_IN_1_10_2 +GType nm_tc_action_get_type (void); + +NM_AVAILABLE_IN_1_10_2 +NMTCAction *nm_tc_action_new (const char *kind, + GError **error); + +NM_AVAILABLE_IN_1_10_2 +void nm_tc_action_ref (NMTCAction *action); +NM_AVAILABLE_IN_1_10_2 +void nm_tc_action_unref (NMTCAction *action); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_tc_action_equal (NMTCAction *action, + NMTCAction *other); + +NM_AVAILABLE_IN_1_10_2 +NMTCAction *nm_tc_action_dup (NMTCAction *action); + + +NM_AVAILABLE_IN_1_10_2 +const char *nm_tc_action_get_kind (NMTCAction *action); + +NM_AVAILABLE_IN_1_10_2 +char **nm_tc_action_get_attribute_names (NMTCAction *action); +NM_AVAILABLE_IN_1_10_2 +GVariant *nm_tc_action_get_attribute (NMTCAction *action, + const char *name); +NM_AVAILABLE_IN_1_10_2 +void nm_tc_action_set_attribute (NMTCAction *action, + const char *name, + GVariant *value); + +NM_AVAILABLE_IN_1_10_2 +typedef struct NMTCTfilter NMTCTfilter; + +NM_AVAILABLE_IN_1_10_2 +GType nm_tc_tfilter_get_type (void); + +NM_AVAILABLE_IN_1_10_2 +NMTCTfilter *nm_tc_tfilter_new (const char *kind, + guint32 parent, + GError **error); + +NM_AVAILABLE_IN_1_10_2 +void nm_tc_tfilter_ref (NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +void nm_tc_tfilter_unref (NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_tc_tfilter_equal (NMTCTfilter *tfilter, + NMTCTfilter *other); + +NM_AVAILABLE_IN_1_10_2 +NMTCTfilter *nm_tc_tfilter_dup (NMTCTfilter *tfilter); + +NM_AVAILABLE_IN_1_10_2 +const char *nm_tc_tfilter_get_kind (NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +guint32 nm_tc_tfilter_get_handle (NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +void nm_tc_tfilter_set_handle (NMTCTfilter *tfilter, + guint32 handle); +NM_AVAILABLE_IN_1_10_2 +guint32 nm_tc_tfilter_get_parent (NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +NMTCAction *nm_tc_tfilter_get_action (NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +void nm_tc_tfilter_set_action (NMTCTfilter *tfilter, NMTCAction *action); + + +#define NM_TYPE_SETTING_TC_CONFIG (nm_setting_tc_config_get_type ()) +#define NM_SETTING_TC_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TC_CONFIG, NMSettingTCConfig)) +#define NM_SETTING_TC_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_TC_CONFIG, NMSettingTCConfigClass)) +#define NM_IS_SETTING_TC_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_TC_CONFIG)) +#define NM_IS_SETTING_TC_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_TC_CONFIG)) +#define NM_SETTING_TC_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_TC_CONFIG, NMSettingTCConfigClass)) + +#define NM_SETTING_TC_CONFIG_SETTING_NAME "tc" + +#define NM_SETTING_TC_CONFIG_QDISCS "qdiscs" +#define NM_SETTING_TC_CONFIG_TFILTERS "tfilters" + +typedef struct _NMSettingTCConfigClass NMSettingTCConfigClass; + +GType nm_setting_tc_config_get_type (void); + +NM_AVAILABLE_IN_1_10_2 +NMSetting *nm_setting_tc_config_new (void); + +NM_AVAILABLE_IN_1_10_2 +guint nm_setting_tc_config_get_num_qdiscs (NMSettingTCConfig *setting); +NM_AVAILABLE_IN_1_10_2 +NMTCQdisc *nm_setting_tc_config_get_qdisc (NMSettingTCConfig *setting, + guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_tc_config_add_qdisc (NMSettingTCConfig *setting, + NMTCQdisc *qdisc); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_tc_config_remove_qdisc (NMSettingTCConfig *setting, + guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_tc_config_remove_qdisc_by_value (NMSettingTCConfig *setting, + NMTCQdisc *qdisc); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_tc_config_clear_qdiscs (NMSettingTCConfig *setting); + +NM_AVAILABLE_IN_1_10_2 +guint nm_setting_tc_config_get_num_tfilters (NMSettingTCConfig *setting); +NM_AVAILABLE_IN_1_10_2 +NMTCTfilter *nm_setting_tc_config_get_tfilter (NMSettingTCConfig *setting, + guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_tc_config_add_tfilter (NMSettingTCConfig *setting, + NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_tc_config_remove_tfilter (NMSettingTCConfig *setting, + guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_tc_config_remove_tfilter_by_value (NMSettingTCConfig *setting, + NMTCTfilter *tfilter); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_tc_config_clear_tfilters (NMSettingTCConfig *setting); + +G_END_DECLS + +#endif /* NM_SETTING_TC_CONFIG_H */ diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c index c0309665..5ab8f691 100644 --- a/libnm-core/nm-setting-team-port.c +++ b/libnm-core/nm-setting-team-port.c @@ -15,6 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * + * Copyright 2017 Red Hat, Inc. * Copyright 2013 Jiri Pirko */ @@ -29,7 +30,6 @@ #include "nm-utils-private.h" #include "nm-connection-private.h" #include "nm-setting-connection.h" -#include "nm-setting-team.h" /** * SECTION:nm-setting-team-port @@ -47,14 +47,40 @@ NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM_PORT) typedef struct { char *config; + int queue_id; + int prio; + gboolean sticky; + int lacp_prio; + int lacp_key; + GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */ } NMSettingTeamPortPrivate; +/* Keep aligned with _prop_to_keys[] */ enum { PROP_0, PROP_CONFIG, + PROP_QUEUE_ID, + PROP_PRIO, + PROP_STICKY, + PROP_LACP_PRIO, + PROP_LACP_KEY, + PROP_LINK_WATCHERS, LAST_PROP }; +/* Keep aligned with team-port properties enum */ +static const _NMUtilsTeamPropertyKeys _prop_to_keys[LAST_PROP] = { + [PROP_0] = { NULL, NULL, NULL, 0 }, + [PROP_CONFIG] = { NULL, NULL, NULL, 0 }, + [PROP_QUEUE_ID] = { "queue_id", NULL, NULL, NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT }, + [PROP_PRIO] = { "prio", NULL, NULL, 0 }, + [PROP_STICKY] = { "sticky", NULL, NULL, 0 }, + [PROP_LACP_PRIO] = { "lacp_prio", NULL, NULL, NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT }, + [PROP_LACP_KEY] = { "lacp_key", NULL, NULL, 0 }, + [PROP_LINK_WATCHERS] = { "link_watch", NULL, NULL, 0 } +}; + + /** * nm_setting_team_port_new: * @@ -82,6 +108,239 @@ nm_setting_team_port_get_config (NMSettingTeamPort *setting) return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->config; } +/** + * nm_setting_team_port_get_queue_id: + * @setting: the #NMSettingTeamPort + * + * Returns: the #NMSettingTeamPort:queue_id property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), -1); + + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->queue_id; +} + +/** + * nm_setting_team_port_get_prio: + * @setting: the #NMSettingTeamPort + * + * Returns: the #NMSettingTeamPort:prio property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_port_get_prio (NMSettingTeamPort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); + + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->prio; +} + +/** + * nm_setting_team_port_get_sticky: + * @setting: the #NMSettingTeamPort + * + * Returns: the #NMSettingTeamPort:sticky property of the setting + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_port_get_sticky (NMSettingTeamPort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), FALSE); + + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->sticky; +} + +/** + * nm_setting_team_port_get_lacp_prio: + * @setting: the #NMSettingTeamPort + * + * Returns: the #NMSettingTeamPort:lacp-prio property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); + + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->lacp_prio; +} + +/** + * nm_setting_team_port_get_lacp_key: + * @setting: the #NMSettingTeamPort + * + * Returns: the #NMSettingTeamPort:lacp-key property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); + + return NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->lacp_key; +} + +/** + * nm_setting_team_port_get_num_link_watchers: + * @setting: the #NMSettingTeamPort + * + * Returns: the number of configured link watchers + * + * Since: 1.10.2 + **/ +guint +nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), 0); + + return priv->link_watchers->len; +} + +/** + * nm_setting_team_port_get_link_watcher: + * @setting: the #NMSettingTeamPort + * @idx: index number of the link watcher to return + * + * Returns: (transfer none): the link watcher at index @idx. + * + * Since: 1.10.2 + **/ +NMTeamLinkWatcher * +nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), NULL); + g_return_val_if_fail (idx < priv->link_watchers->len, NULL); + + return priv->link_watchers->pdata[idx]; +} + +/** + * nm_setting_team_port_add_link_watcher: + * @setting: the #NMSettingTeamPort + * @link_watcher: the link watcher to add + * + * Appends a new link watcher to the setting. + * + * Returns: %TRUE if the link watcher is added; %FALSE if an identical link + * watcher was already there. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting, + NMTeamLinkWatcher *link_watcher) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), FALSE); + g_return_val_if_fail (link_watcher != NULL, FALSE); + + for (i = 0; i < priv->link_watchers->len; i++) { + if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) + return FALSE; + } + + g_ptr_array_add (priv->link_watchers, nm_team_link_watcher_dup (link_watcher)); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); + return TRUE; +} + +/** + * nm_setting_team_port_remove_link_watcher: + * @setting: the #NMSettingTeamPort + * @idx: index number of the link watcher to remove + * + * Removes the link watcher at index #idx. + * + * Since: 1.10.2 + **/ +void +nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_TEAM_PORT (setting)); + g_return_if_fail (idx < priv->link_watchers->len); + + g_ptr_array_remove_index (priv->link_watchers, idx); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); +} + +/** + * nm_setting_team_port_remove_link_watcher_by_value: + * @setting: the #NMSettingTeamPort + * @link_watcher: the link watcher to remove + * + * Removes the link watcher entry matching link_watcher. + * + * Returns: %TRUE if the link watcher was found and removed, %FALSE otherwise. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting, + NMTeamLinkWatcher *link_watcher) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TEAM_PORT (setting), FALSE); + + for (i = 0; i < priv->link_watchers->len; i++) { + if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) { + g_ptr_array_remove_index (priv->link_watchers, i); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_team_port_clear_link_watchers: + * @setting: the #NMSettingTeamPort + * + * Removes all configured link watchers. + * + * Since: 1.10.2 + **/ +void +nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting) +{ + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (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); +} + +static GVariant * +team_link_watchers_to_dbus (const GValue *prop_value) +{ + return _nm_utils_team_link_watchers_to_variant (g_value_get_boxed (prop_value)); +} + +static void +team_link_watchers_from_dbus (GVariant *dbus_value, + GValue *prop_value) +{ + g_value_take_boxed (prop_value, _nm_utils_team_link_watchers_from_variant (dbus_value)); +} + static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) { @@ -150,6 +409,8 @@ compare_property (NMSetting *setting, NMSettingCompareFlags flags) { NMSettingClass *parent_class; + NMSettingTeamPortPrivate *a_priv, *b_priv; + guint i, j; /* If we are trying to match a connection in order to assume it (and thus * @flags contains INFERRABLE), use the "relaxed" matching for team @@ -162,6 +423,24 @@ compare_property (NMSetting *setting, NM_SETTING_TEAM_PORT_GET_PRIVATE (other)->config, TRUE); } + if (nm_streq0 (prop_spec->name, NM_SETTING_TEAM_PORT_LINK_WATCHERS)) { + a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (other); + + if (a_priv->link_watchers->len != b_priv->link_watchers->len) + return FALSE; + for (i = 0; i < a_priv->link_watchers->len; i++) { + for (j = 0; j < b_priv->link_watchers->len; j++) { + if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i], + b_priv->link_watchers->pdata[j])) { + break; + } + } + if (j == b_priv->link_watchers->len) + return FALSE; + } + return TRUE; + } /* Otherwise chain up to parent to handle generic compare */ parent_class = NM_SETTING_CLASS (nm_setting_team_port_parent_class); @@ -171,23 +450,92 @@ compare_property (NMSetting *setting, static void nm_setting_team_port_init (NMSettingTeamPort *setting) { + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); + + priv->queue_id = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT; + priv->lacp_prio = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT; + priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); } +#define JSON_TO_VAL(typ, id) _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], TRUE) + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (object); + const GValue *align_value = NULL; + gboolean align_config = FALSE; switch (prop_id) { case PROP_CONFIG: g_free (priv->config); priv->config = g_value_dup_string (value); + priv->queue_id = JSON_TO_VAL (int, PROP_QUEUE_ID); + priv->prio = JSON_TO_VAL (int, PROP_PRIO); + priv->sticky = JSON_TO_VAL (boolean, PROP_STICKY); + priv->lacp_prio = JSON_TO_VAL (int, PROP_LACP_PRIO); + priv->lacp_key = JSON_TO_VAL (int, PROP_LACP_KEY); + + g_ptr_array_unref (priv->link_watchers); + priv->link_watchers = JSON_TO_VAL (ptr_array, PROP_LINK_WATCHERS); + break; + case PROP_QUEUE_ID: + if (priv->queue_id == g_value_get_int (value)) + break; + priv->queue_id = g_value_get_int (value); + if (priv->queue_id != NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT) + align_value = value; + align_config = TRUE; + break; + case PROP_PRIO: + if (priv->prio == g_value_get_int (value)) + break; + priv->prio = g_value_get_int (value); + if (priv->prio) + align_value = value; + align_config = TRUE; + break; + case PROP_STICKY: + if (priv->sticky == g_value_get_boolean (value)) + break; + priv->sticky = g_value_get_boolean (value); + if (priv->sticky) + align_value = value; + align_config = TRUE; + break; + case PROP_LACP_PRIO: + if (priv->lacp_prio == g_value_get_int (value)) + break; + priv->lacp_prio = g_value_get_int (value); + /* from libteam sources: lacp_prio default value is 0xff */ + if (priv->lacp_prio != NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT) + align_value = value; + align_config = TRUE; + break; + case PROP_LACP_KEY: + if (priv->lacp_key == g_value_get_int (value)) + break; + priv->lacp_key = g_value_get_int (value); + if (priv->lacp_key) + align_value = value; + align_config = TRUE; + break; + case PROP_LINK_WATCHERS: + g_ptr_array_unref (priv->link_watchers); + priv->link_watchers = _nm_utils_copy_array (g_value_get_boxed (value), + (NMUtilsCopyFunc) nm_team_link_watcher_dup, + (GDestroyNotify) nm_team_link_watcher_unref); + if (priv->link_watchers->len) + align_value = value; + align_config = TRUE; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + if (align_config) + _nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], align_value); } static void @@ -195,11 +543,32 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMSettingTeamPort *setting = NM_SETTING_TEAM_PORT (object); + NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting); switch (prop_id) { case PROP_CONFIG: g_value_set_string (value, nm_setting_team_port_get_config (setting)); break; + case PROP_QUEUE_ID: + g_value_set_int (value, priv->queue_id); + break; + case PROP_PRIO: + g_value_set_int (value, priv->prio); + break; + case PROP_STICKY: + g_value_set_boolean (value, priv->sticky); + break; + case PROP_LACP_PRIO: + g_value_set_int (value, priv->lacp_prio); + break; + case PROP_LACP_KEY: + g_value_set_int (value, priv->lacp_key); + break; + case PROP_LINK_WATCHERS: + g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers, + (NMUtilsCopyFunc) nm_team_link_watcher_dup, + (GDestroyNotify) nm_team_link_watcher_unref)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -212,6 +581,7 @@ finalize (GObject *object) NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (object); g_free (priv->config); + g_ptr_array_unref (priv->link_watchers); G_OBJECT_CLASS (nm_setting_team_port_parent_class)->finalize (object); } @@ -253,4 +623,103 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeamPort:queue-id: + * + * Corresponds to the teamd ports.PORTIFNAME.queue_id. + * When set to -1 means the parameter is skipped from the json config. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_QUEUE_ID, + g_param_spec_int (NM_SETTING_TEAM_PORT_QUEUE_ID, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeamPort:prio: + * + * Corresponds to the teamd ports.PORTIFNAME.prio. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_PRIO, + g_param_spec_int (NM_SETTING_TEAM_PORT_PRIO, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeamPort:sticky: + * + * Corresponds to the teamd ports.PORTIFNAME.sticky. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_STICKY, + g_param_spec_boolean (NM_SETTING_TEAM_PORT_STICKY, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeamPort:lacp-prio: + * + * Corresponds to the teamd ports.PORTIFNAME.lacp_prio. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_LACP_PRIO, + g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_PRIO, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeamPort:lacp-key: + * + * Corresponds to the teamd ports.PORTIFNAME.lacp_key. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_LACP_KEY, + g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_KEY, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeamPort:link-watchers: + * + * 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. + * + * Element-Type: NMTeamLinkWatcher + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_LINK_WATCHERS, + g_param_spec_boxed (NM_SETTING_TEAM_PORT_LINK_WATCHERS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + _nm_setting_class_transform_property (parent_class, + NM_SETTING_TEAM_PORT_LINK_WATCHERS, + G_VARIANT_TYPE ("aa{sv}"), + team_link_watchers_to_dbus, + team_link_watchers_from_dbus); + } diff --git a/libnm-core/nm-setting-team-port.h b/libnm-core/nm-setting-team-port.h index 4fcc5108..26b2e098 100644 --- a/libnm-core/nm-setting-team-port.h +++ b/libnm-core/nm-setting-team-port.h @@ -26,6 +26,7 @@ #endif #include "nm-setting.h" +#include "nm-setting-team.h" G_BEGIN_DECLS @@ -38,7 +39,16 @@ G_BEGIN_DECLS #define NM_SETTING_TEAM_PORT_SETTING_NAME "team-port" -#define NM_SETTING_TEAM_PORT_CONFIG "config" +#define NM_SETTING_TEAM_PORT_CONFIG "config" +#define NM_SETTING_TEAM_PORT_QUEUE_ID "queue-id" +#define NM_SETTING_TEAM_PORT_PRIO "prio" +#define NM_SETTING_TEAM_PORT_STICKY "sticky" +#define NM_SETTING_TEAM_PORT_LACP_PRIO "lacp-prio" +#define NM_SETTING_TEAM_PORT_LACP_KEY "lacp-key" +#define NM_SETTING_TEAM_PORT_LINK_WATCHERS "link-watchers" + +#define NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT -1 +#define NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT 255 /** * NMSettingTeamPort: @@ -61,7 +71,34 @@ GType nm_setting_team_port_get_type (void); NMSetting * nm_setting_team_port_new (void); const char * nm_setting_team_port_get_config (NMSettingTeamPort *setting); - +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_port_get_prio (NMSettingTeamPort *setting); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_team_port_get_sticky (NMSettingTeamPort *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting); +NM_AVAILABLE_IN_1_10_2 +guint nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting); +NM_AVAILABLE_IN_1_10_2 +NMTeamLinkWatcher * +nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean +nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_10_2 +void +nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean +nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting); G_END_DECLS #endif /* __NM_SETTING_TEAM_PORT_H__ */ diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c index 0a09c4a6..d3688278 100644 --- a/libnm-core/nm-setting-team.c +++ b/libnm-core/nm-setting-team.c @@ -15,6 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * + * Copyright 2017 Red Hat, Inc. * Copyright 2013 Jiri Pirko */ @@ -36,53 +37,1112 @@ * necessary for team connections. **/ +/***************************************************************************** + * NMTeamLinkWatch + *****************************************************************************/ + +G_DEFINE_BOXED_TYPE (NMTeamLinkWatcher, nm_team_link_watcher, + nm_team_link_watcher_dup, nm_team_link_watcher_unref) + +enum LinkWatcherTypes { + LINK_WATCHER_ETHTOOL = 0, + LINK_WATCHER_NSNA_PING = 1, + LINK_WATCHER_ARP_PING = 2 +}; + +static const char* _link_watcher_name[] = { + [LINK_WATCHER_ETHTOOL] = NM_TEAM_LINK_WATCHER_ETHTOOL, + [LINK_WATCHER_NSNA_PING] = NM_TEAM_LINK_WATCHER_NSNA_PING, + [LINK_WATCHER_ARP_PING] = NM_TEAM_LINK_WATCHER_ARP_PING +}; + +struct NMTeamLinkWatcher { + guint refcount; + + guint8 type; /* LinkWatcherTypes */ + + /* + * The union is constructed in order to allow mapping the options of all the + * watchers on the arp_ping one: this would allow to manipulate all the watchers + * by using the arp_ping struct. See for instance the nm_team_link_watcher_unref() + * and nm_team_link_watcher_equal() functions. So, if you need to change the union + * be careful. + */ + union { + struct { + int delay_up; + int delay_down; + } ethtool; + struct { + int init_wait; + int interval; + int missed_max; + char *target_host; + } nsna_ping; + struct { + int init_wait; + int interval; + int missed_max; + char *target_host; + char *source_host; + NMTeamLinkWatcherArpPingFlags flags; + } arp_ping; + }; +}; + +#define _CHECK_WATCHER_VOID(watcher) \ + G_STMT_START { \ + g_return_if_fail (watcher != NULL); \ + g_return_if_fail (watcher->refcount > 0); \ + g_return_if_fail (watcher->type <= LINK_WATCHER_ARP_PING); \ + } G_STMT_END + +#define _CHECK_WATCHER(watcher, err_val) \ + G_STMT_START { \ + g_return_val_if_fail (watcher != NULL, err_val); \ + g_return_val_if_fail (watcher->refcount > 0, err_val); \ + g_return_val_if_fail (watcher->type <= LINK_WATCHER_ARP_PING, err_val); \ + } G_STMT_END + +/** + * nm_team_link_watcher_new_ethtool: + * @delay_up: delay_up value + * @delay_down: delay_down value + * @error: this call never fails, so this var is not used but kept for format + * consistency with the link_watcher constructors of other type + * + * Creates a new ethtool #NMTeamLinkWatcher object + * + * Returns: (transfer full): the new #NMTeamLinkWatcher object + * + * Since: 1.10.2 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_new_ethtool (gint delay_up, + gint delay_down, + GError **error) +{ + NMTeamLinkWatcher *watcher; + const char *val_fail = NULL; + + if (delay_up < 0 || delay_up > G_MAXINT32) + val_fail = "delay-up"; + if (delay_down < 0 || delay_down > G_MAXINT32) + val_fail = "delay-down"; + if (val_fail) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("%s is out of range [0, %d]"), val_fail, G_MAXINT32); + return NULL; + } + + watcher = g_slice_new0 (NMTeamLinkWatcher); + watcher->refcount = 1; + + watcher->type = LINK_WATCHER_ETHTOOL; + watcher->ethtool.delay_up = delay_up; + watcher->ethtool.delay_down = delay_down; + + return watcher; +} + +/** + * nm_team_link_watcher_new_nsna_ping: + * @init_wait: init_wait value + * @interval: interval value + * @missed_max: missed_max value + * @target_host: the host name or the ipv6 address that will be used as + * target address in the NS packet + * @error: (out) (allow-none): location to store the error on failure + * + * Creates a new nsna_ping #NMTeamLinkWatcher object + * + * Returns: (transfer full): the new #NMTeamLinkWatcher object, or %NULL on error + * + * Since: 1.10.2 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_new_nsna_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + GError **error) +{ + NMTeamLinkWatcher *watcher; + const char *val_fail = NULL; + + if (!target_host) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("Missing target-host in nsna_ping link watcher")); + return NULL; + } + + if (strpbrk (target_host, " \\/\t=\"\'")) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("target-host '%s' contains invalid characters"), target_host); + return NULL; + } + + if (init_wait < 0 || init_wait > G_MAXINT32) + val_fail = "init-wait"; + if (interval < 0 || interval > G_MAXINT32) + val_fail = "interval"; + if (missed_max < 0 || missed_max > G_MAXINT32) + val_fail = "missed-max"; + if (val_fail) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("%s is out of range [0, %d]"), val_fail, G_MAXINT32); + return NULL; + } + + watcher = g_slice_new0 (NMTeamLinkWatcher); + watcher->refcount = 1; + + watcher->type = LINK_WATCHER_NSNA_PING; + watcher->nsna_ping.init_wait = init_wait; + watcher->nsna_ping.interval = interval; + watcher->nsna_ping.missed_max = missed_max; + watcher->nsna_ping.target_host = g_strdup (target_host); + + return watcher; +} + +/** + * nm_team_link_watcher_new_arp_ping: + * @init_wait: init_wait value + * @interval: interval value + * @missed_max: missed_max value + * @target_host: the host name or the ip address that will be used as destination + * address in the arp request + * @source_host: the host name or the ip address that will be used as source + * address in the arp request + * @flags: the watcher #NMTeamLinkWatcherArpPingFlags + * @error: (out) (allow-none): location to store the error on failure + * + * Creates a new arp_ping #NMTeamLinkWatcher object + * + * Returns: (transfer full): the new #NMTeamLinkWatcher object, or %NULL on error + * + * Since: 1.10.2 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_new_arp_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + const char *source_host, + NMTeamLinkWatcherArpPingFlags flags, + GError **error) +{ + NMTeamLinkWatcher *watcher; + const char *val_fail = NULL; + + if (!target_host || !source_host) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("Missing %s in arp_ping link watcher"), + target_host ? "source-host" : "target-host"); + return NULL; + } + + if (strpbrk (target_host, " \\/\t=\"\'")) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("target-host '%s' contains invalid characters"), target_host); + return NULL; + } + + if (strpbrk (target_host, " \\/\t=\"\'")) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("source-host '%s' contains invalid characters"), source_host); + return NULL; + } + + if (init_wait < 0 || init_wait > G_MAXINT32) + val_fail = "init-wait"; + if (interval < 0 || interval > G_MAXINT32) + val_fail = "interval"; + if (missed_max < 0 || missed_max > G_MAXINT32) + val_fail = "missed-max"; + if (val_fail) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("%s is out of range [0, %d]"), val_fail, G_MAXINT32); + return NULL; + } + + watcher = g_slice_new0 (NMTeamLinkWatcher); + watcher->refcount = 1; + + watcher->type = LINK_WATCHER_ARP_PING; + watcher->arp_ping.init_wait = init_wait; + watcher->arp_ping.interval = interval; + watcher->arp_ping.missed_max = missed_max; + watcher->arp_ping.target_host = g_strdup (target_host); + watcher->arp_ping.source_host = g_strdup (source_host); + watcher->arp_ping.flags = flags; + + return watcher; +} + +/** + * nm_team_link_watcher_ref: + * @watcher: the #NMTeamLinkWatcher + * + * Increases the reference count of the object. + * + * Since: 1.10.2 + **/ +void +nm_team_link_watcher_ref (NMTeamLinkWatcher *watcher){ + _CHECK_WATCHER_VOID (watcher); + + watcher->refcount++; +} + +/** + * nm_team_link_watcher_unref: + * @watcher: the #NMTeamLinkWatcher + * + * Decreases the reference count of the object. If the reference count + * reaches zero, the object will be destroyed. + * + * Since: 1.10.2 + **/ +void +nm_team_link_watcher_unref (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER_VOID (watcher); + + watcher->refcount--; + if (watcher->refcount == 0) { + g_free (watcher->arp_ping.target_host); + g_free (watcher->arp_ping.source_host); + g_slice_free (NMTeamLinkWatcher, watcher); + } +} + +/** + * nm_team_link_watcher_equal: + * @watcher: the #NMTeamLinkWatcher + * @other: the #NMTeamLinkWatcher to compare @watcher to. + * + * Determines if two #NMTeamLinkWatcher objects contain the same values + * in all the properties. + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + * + * Since: 1.10.2 + **/ +gboolean +nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other) +{ + _CHECK_WATCHER (watcher, FALSE); + _CHECK_WATCHER (other, FALSE); + + if ( watcher->type != other->type + || !nm_streq0 (watcher->arp_ping.target_host, other->arp_ping.target_host) + || !nm_streq0 (watcher->arp_ping.source_host, other->arp_ping.source_host) + || watcher->arp_ping.init_wait != other->arp_ping.init_wait + || watcher->arp_ping.interval != other->arp_ping.interval + || watcher->arp_ping.missed_max != other->arp_ping.missed_max + || watcher->arp_ping.flags != other->arp_ping.flags) + return FALSE; + + return TRUE; +} + +/** + * nm_team_link_watcher_dup: + * @watcher: the #NMTeamLinkWatcher + * + * Creates a copy of @watcher + * + * Returns: (transfer full): a copy of @watcher + * + * Since: 1.10.2 + **/ +NMTeamLinkWatcher * +nm_team_link_watcher_dup (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + switch (watcher->type) { + case LINK_WATCHER_ETHTOOL: + return nm_team_link_watcher_new_ethtool (watcher->ethtool.delay_up, + watcher->ethtool.delay_down, + NULL); + break; + case LINK_WATCHER_NSNA_PING: + return nm_team_link_watcher_new_nsna_ping (watcher->nsna_ping.init_wait, + watcher->nsna_ping.interval, + watcher->nsna_ping.missed_max, + watcher->nsna_ping.target_host, + NULL); + break; + case LINK_WATCHER_ARP_PING: + return nm_team_link_watcher_new_arp_ping (watcher->arp_ping.init_wait, + watcher->arp_ping.interval, + watcher->arp_ping.missed_max, + watcher->arp_ping.target_host, + watcher->arp_ping.source_host, + watcher->arp_ping.flags, + NULL); + default: + g_assert_not_reached (); + return NULL; + } +} + +/** + * nm_team_link_watcher_get_name: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the name of the link watcher to be used. + * + * Since: 1.10.2 + **/ +const char * +nm_team_link_watcher_get_name (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + return _link_watcher_name[watcher->type]; +} + +/** + * nm_team_link_watcher_get_delay_up: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the delay_up interval (in milliseconds) that elapses between the link + * coming up and the runner beeing notified about it. + * + * Since: 1.10.2 + **/ +int +nm_team_link_watcher_get_delay_up (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (watcher->type != LINK_WATCHER_ETHTOOL) + return -1; + return watcher->ethtool.delay_up; +} + +/** + * nm_team_link_watcher_get_delay_down: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the delay_down interval (in milliseconds) that elapses between the link + * going down and the runner beeing notified about it. + * + * Since: 1.10.2 + **/ +int +nm_team_link_watcher_get_delay_down (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (watcher->type != LINK_WATCHER_ETHTOOL) + return -1; + return watcher->ethtool.delay_down; +} + +/** + * nm_team_link_watcher_get_init_wait: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the init_wait interval (in milliseconds) that the team slave should + * wait before sending the first packet to the target host. + * + * Since: 1.10.2 + **/ +int +nm_team_link_watcher_get_init_wait (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (!NM_IN_SET (watcher->type, + LINK_WATCHER_NSNA_PING, + LINK_WATCHER_ARP_PING)) + return -1; + return watcher->arp_ping.init_wait; +} + +/** + * nm_team_link_watcher_get_interval: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the interval (in milliseconds) that the team slave should wait between + * sending two check packets to the target host. + * + * Since: 1.10.2 + **/ +int +nm_team_link_watcher_get_interval (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (!NM_IN_SET (watcher->type, + LINK_WATCHER_NSNA_PING, + LINK_WATCHER_ARP_PING)) + return -1; + return watcher->arp_ping.interval; +} + +/** + * nm_team_link_watcher_get_missed_max: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the number of missed replies after which the link is considered down. + * + * Since: 1.10.2 + **/ +int +nm_team_link_watcher_get_missed_max (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + if (!NM_IN_SET (watcher->type, + LINK_WATCHER_NSNA_PING, + LINK_WATCHER_ARP_PING)) + return -1; + return watcher->arp_ping.missed_max; +} + +/** + * nm_team_link_watcher_get_target_host: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the host name/ip address to be used as destination for the link probing + * packets. + * + * Since: 1.10.2 + **/ +const char * +nm_team_link_watcher_get_target_host (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + return watcher->arp_ping.target_host; +} + +/** + * nm_team_link_watcher_get_source_host: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the ip address to be used as source for the link probing packets. + * + * Since: 1.10.2 + **/ +const char * +nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, NULL); + + return watcher->arp_ping.source_host; +} + +/** + * nm_team_link_watcher_get_flags: + * @watcher: the #NMTeamLinkWatcher + * + * Gets the arp ping watcher flags. + * + * Since: 1.10.2 + **/ +NMTeamLinkWatcherArpPingFlags +nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher) +{ + _CHECK_WATCHER (watcher, 0); + + return watcher->arp_ping.flags; +} + +/*****************************************************************************/ + G_DEFINE_TYPE_WITH_CODE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING, _nm_register_setting (TEAM, NM_SETTING_PRIORITY_HW_BASE)) NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TEAM) -#define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate)) +#define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate)) + +typedef struct { + char *config; + gint notify_peers_count; + gint notify_peers_interval; + gint mcast_rejoin_count; + gint mcast_rejoin_interval; + char *runner; + char *runner_hwaddr_policy; + GPtrArray *runner_tx_hash; + char *runner_tx_balancer; + gint runner_tx_balancer_interval; + gboolean runner_active; + gboolean runner_fast_rate; + gint runner_sys_prio; + gint runner_min_ports; + char *runner_agg_select_policy; + GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */ +} NMSettingTeamPrivate; + +/* Keep aligned with _prop_to_keys[] */ +enum { + PROP_0, + PROP_CONFIG, + PROP_NOTIFY_PEERS_COUNT, + PROP_NOTIFY_PEERS_INTERVAL, + PROP_MCAST_REJOIN_COUNT, + PROP_MCAST_REJOIN_INTERVAL, + PROP_RUNNER, + PROP_RUNNER_HWADDR_POLICY, + PROP_RUNNER_TX_HASH, + PROP_RUNNER_TX_BALANCER, + PROP_RUNNER_TX_BALANCER_INTERVAL, + PROP_RUNNER_ACTIVE, + PROP_RUNNER_FAST_RATE, + PROP_RUNNER_SYS_PRIO, + PROP_RUNNER_MIN_PORTS, + PROP_RUNNER_AGG_SELECT_POLICY, + PROP_LINK_WATCHERS, + LAST_PROP +}; + +/* Keep aligned with team properties enum */ +static const _NMUtilsTeamPropertyKeys _prop_to_keys[LAST_PROP] = { + [PROP_0] = { NULL, NULL, NULL, 0 }, + [PROP_CONFIG] = { NULL, NULL, NULL, 0 }, + [PROP_NOTIFY_PEERS_COUNT] = { "notify_peers", "count", NULL, 0 }, + [PROP_NOTIFY_PEERS_INTERVAL] = { "notify_peers", "interval", NULL, 0 }, + [PROP_MCAST_REJOIN_COUNT] = { "mcast_rejoin", "count", NULL, 0 }, + [PROP_MCAST_REJOIN_INTERVAL] = { "mcast_rejoin", "interval", NULL, 0 }, + [PROP_RUNNER] = { "runner", "name", NULL, + {.default_str = NM_SETTING_TEAM_RUNNER_DEFAULT} }, + [PROP_RUNNER_HWADDR_POLICY] = { "runner", "hwaddr_policy", NULL, 0 }, + [PROP_RUNNER_TX_HASH] = { "runner", "tx_hash", NULL, 0 }, + [PROP_RUNNER_TX_BALANCER] = { "runner", "tx_balancer", "name", 0 }, + [PROP_RUNNER_TX_BALANCER_INTERVAL] = { "runner", "tx_balancer", "balancing_interval", -1 }, + [PROP_RUNNER_ACTIVE] = { "runner", "active", NULL, 0 }, + [PROP_RUNNER_FAST_RATE] = { "runner", "fast_rate", NULL, 0 }, + [PROP_RUNNER_SYS_PRIO] = { "runner", "sys_prio", NULL, -1 }, + [PROP_RUNNER_MIN_PORTS] = { "runner", "min_ports", NULL, -1 }, + [PROP_RUNNER_AGG_SELECT_POLICY] = { "runner", "agg_select_policy", NULL, 0 }, + [PROP_LINK_WATCHERS] = { "link_watch", NULL, NULL, 0 } +}; + +/** + * nm_setting_team_new: + * + * Creates a new #NMSettingTeam object with default values. + * + * Returns: (transfer full): the new empty #NMSettingTeam object + **/ +NMSetting * +nm_setting_team_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM, NULL); +} + +/** + * nm_setting_team_get_config: + * @setting: the #NMSettingTeam + * + * Returns: the #NMSettingTeam:config property of the setting + **/ +const char * +nm_setting_team_get_config (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->config; +} + +/** + * nm_setting_team_get_notify_peers_count: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:notify-peers-count property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_get_notify_peers_count (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->notify_peers_count; +} + +/** + * nm_setting_team_get_notify_peers_interval: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:notify-peers-interval property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->notify_peers_interval; +} + +/** + * nm_setting_team_get_mcast_rejoin_count: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:mcast-rejoin-count property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->mcast_rejoin_count; +} + +/** + * nm_setting_team_get_mcast_rejoin_interval: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:mcast-rejoin-interval property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->mcast_rejoin_interval; +} + +/** + * nm_setting_team_get_runner: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner property of the setting + * + * Since: 1.10.2 + **/ +const char * +nm_setting_team_get_runner (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner; +} + +/** + * nm_setting_team_get_runner_hwaddr_policy: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner-hwaddr-policy property of the setting + * + * Since: 1.10.2 + **/ +const char * +nm_setting_team_get_runner_hwaddr_policy (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_hwaddr_policy; +} + +/** + * nm_setting_team_get_runner_tx_balancer: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner-tx-balancer property of the setting + * + * Since: 1.10.2 + **/ +const char * +nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_tx_balancer; +} + +/** + * nm_setting_team_get_runner_tx_balancer_interval: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner-tx-balancer_interval property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_tx_balancer_interval; +} + +/** + * nm_setting_team_get_runner_active: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner_active property of the setting + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_get_runner_active (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_active; +} + +/** + * nm_setting_team_get_runner_fast_rate: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner-fast-rate property of the setting + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_fast_rate; +} + +/** + * nm_setting_team_get_runner_sys_prio: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner-sys-prio property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_sys_prio; +} + +/** + * nm_setting_team_get_runner_min_ports: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner-min-ports property of the setting + * + * Since: 1.10.2 + **/ +gint +nm_setting_team_get_runner_min_ports (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_min_ports; +} + +/** + * nm_setting_team_get_runner_agg_select_policy: + * @setting: the #NMSettingTeam + * + * Returns: the ##NMSettingTeam:runner-agg-select-policy property of the setting + * + * Since: 1.10.2 + **/ +const char * +nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + + return NM_SETTING_TEAM_GET_PRIVATE (setting)->runner_agg_select_policy; +} + +/** + * nm_setting_team_remove_runner_tx_hash_by_value: + * @setting: the #NMSetetingTeam + * @txhash: the txhash element to remove + * + * Removes the txhash element #txhash + * + * Returns: %TRUE if the txhash element was found and removed; %FALSE if it was not. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting, + const char *txhash) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); + g_return_val_if_fail (txhash != NULL, FALSE); + g_return_val_if_fail (txhash[0] != '\0', FALSE); + + for (i = 0; i < priv->runner_tx_hash->len; i++) { + if (nm_streq (txhash, priv->runner_tx_hash->pdata[i])) { + g_ptr_array_remove_index (priv->runner_tx_hash, i); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_RUNNER_TX_HASH); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_team_get_num_runner_tx_hash: + * @setting: the #NMSettingTeam + * + * Returns: the number of elements in txhash + * + * Since: 1.10.2 + **/ +guint +nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); + + return priv->runner_tx_hash ? priv->runner_tx_hash->len : 0; +} + +/** + * nm_setting_team_get_runner_tx_hash + * @setting: the #NMSettingTeam + * @idx: index number of the txhash element to return + * + * Returns: the txhash element at index @idx + * + * Since: 1.10.2 + **/ +const char * +nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + g_return_val_if_fail (idx < priv->runner_tx_hash->len, NULL); + + return priv->runner_tx_hash->pdata[idx]; +} + +/** + * nm_setting_team_remove_runner_tx_hash: + * @setting: the #NMSettingTeam + * @idx: index number of the element to remove from txhash + * + * Removes the txhash element at index @idx. + * + * Since: 1.10.2 + **/ +void +nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_TEAM (setting)); + g_return_if_fail (idx < priv->runner_tx_hash->len); + + g_ptr_array_remove_index (priv->runner_tx_hash, idx); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_RUNNER_TX_HASH); +} + +/** + * nm_setting_team_add_runner_tx_hash: + * @setting: the #NMSettingTeam + * @txhash: the element to add to txhash + * + * Adds a new txhash element to the setting. + * + * Returns: %TRUE if the txhash element was added; %FALSE if the element + * was already knnown. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); + g_return_val_if_fail (txhash != NULL, FALSE); + g_return_val_if_fail (txhash[0] != '\0', FALSE); + + if (!priv->runner_tx_hash) + priv->runner_tx_hash = g_ptr_array_new_with_free_func (g_free); + for (i = 0; i < priv->runner_tx_hash->len; i++) { + if (nm_streq (txhash, priv->runner_tx_hash->pdata[i])) + return FALSE; + } + + g_ptr_array_add (priv->runner_tx_hash, g_strdup (txhash)); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_RUNNER_TX_HASH); + return TRUE; +} + +/** + * nm_setting_team_get_num_link_watchers: + * @setting: the #NMSettingTeam + * + * Returns: the number of configured link watchers + * + * Since: 1.10.2 + **/ +guint +nm_setting_team_get_num_link_watchers (NMSettingTeam *setting) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); -typedef struct { - char *config; -} NMSettingTeamPrivate; + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), 0); -enum { - PROP_0, - PROP_CONFIG, - LAST_PROP -}; + return priv->link_watchers->len; +} /** - * nm_setting_team_new: + * nm_setting_team_get_link_watcher: + * @setting: the #NMSettingTeam + * @idx: index number of the link watcher to return * - * Creates a new #NMSettingTeam object with default values. + * Returns: (transfer none): the link watcher at index @idx. * - * Returns: (transfer full): the new empty #NMSettingTeam object + * Since: 1.10.2 **/ -NMSetting * -nm_setting_team_new (void) +NMTeamLinkWatcher * +nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx) { - return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM, NULL); + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + g_return_val_if_fail (idx < priv->link_watchers->len, NULL); + + return priv->link_watchers->pdata[idx]; } /** - * nm_setting_team_get_config: + * nm_setting_team_add_link_watcher: * @setting: the #NMSettingTeam + * @link_watcher: the link watcher to add * - * Returns: the #NMSettingTeam:config property of the setting + * Appends a new link watcher to the setting. + * + * Returns: %TRUE if the link watcher is added; %FALSE if an identical link + * watcher was already there. + * + * Since: 1.10.2 **/ -const char * -nm_setting_team_get_config (NMSettingTeam *setting) +gboolean +nm_setting_team_add_link_watcher (NMSettingTeam *setting, + NMTeamLinkWatcher *link_watcher) { - g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), NULL); + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + guint i; - return NM_SETTING_TEAM_GET_PRIVATE (setting)->config; + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); + g_return_val_if_fail (link_watcher != NULL, FALSE); + + for (i = 0; i < priv->link_watchers->len; i++) { + if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) + return FALSE; + } + + g_ptr_array_add (priv->link_watchers, nm_team_link_watcher_dup (link_watcher)); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); + return TRUE; +} + +/** + * nm_setting_team_remove_link_watcher: + * @setting: the #NMSettingTeam + * @idx: index number of the link watcher to remove + * + * Removes the link watcher at index #idx. + * + * Since: 1.10.2 + **/ +void +nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + g_return_if_fail (NM_IS_SETTING_TEAM (setting)); + g_return_if_fail (idx < priv->link_watchers->len); + + g_ptr_array_remove_index (priv->link_watchers, idx); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); +} + +/** + * nm_setting_team_remove_link_watcher_by_value: + * @setting: the #NMSettingTeam + * @link_watcher: the link watcher to remove + * + * Removes the link watcher entry matching link_watcher. + * + * Returns: %TRUE if the link watcher was found and removed, %FALSE otherwise. + * + * Since: 1.10.2 + **/ +gboolean +nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting, + NMTeamLinkWatcher *link_watcher) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + guint i; + + g_return_val_if_fail (NM_IS_SETTING_TEAM (setting), FALSE); + + for (i = 0; i < priv->link_watchers->len; i++) { + if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) { + g_ptr_array_remove_index (priv->link_watchers, i); + g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_team_clear_link_watchers: + * @setting: the #NMSettingTeam + * + * Removes all configured link watchers. + * + * Since: 1.10.2 + **/ +void +nm_setting_team_clear_link_watchers (NMSettingTeam *setting) { + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (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); +} + +static GVariant * +team_link_watchers_to_dbus (const GValue *prop_value) +{ + return _nm_utils_team_link_watchers_to_variant (g_value_get_boxed (prop_value)); +} + +static void +team_link_watchers_from_dbus (GVariant *dbus_value, + GValue *prop_value) +{ + g_value_take_boxed (prop_value, _nm_utils_team_link_watchers_from_variant (dbus_value)); } static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) { NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + guint i; if (!_nm_connection_verify_required_interface_name (connection, error)) return FALSE; @@ -108,6 +1168,61 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if ( priv->runner + && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_BROADCAST) + && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_ROUNDROBIN) + && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP) + && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) + && g_ascii_strcasecmp (priv->runner, NM_SETTING_TEAM_RUNNER_LACP)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, + _("invalid runner \"%s\""), priv->runner); + + g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_TEAM_RUNNER); + return FALSE; + } + + /* Validate link watchers */ + for (i = 0; i < priv->link_watchers->len; i++) { + NMTeamLinkWatcher *link_watcher = priv->link_watchers->pdata[i]; + const char *name = nm_team_link_watcher_get_name (link_watcher); + + if (!name) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing link watcher name")); + g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), + NM_SETTING_TEAM_LINK_WATCHERS); + return FALSE; + } + if (!NM_IN_STRSET (name, + NM_TEAM_LINK_WATCHER_ETHTOOL, + NM_TEAM_LINK_WATCHER_ARP_PING, + NM_TEAM_LINK_WATCHER_NSNA_PING)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING, + _("unknown link watcher \"%s\""), name); + g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), + NM_SETTING_TEAM_LINK_WATCHERS); + return FALSE; + } + + if (NM_IN_STRSET (name, + NM_TEAM_LINK_WATCHER_ARP_PING, + NM_TEAM_LINK_WATCHER_NSNA_PING) + && !nm_team_link_watcher_get_target_host (link_watcher)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing target host")); + g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), + NM_SETTING_TEAM_LINK_WATCHERS); + return FALSE; + } + if (nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING) + && !nm_team_link_watcher_get_source_host (link_watcher)) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING, + _("missing source address")); + g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), + NM_SETTING_TEAM_LINK_WATCHERS); + return FALSE; + } + } /* NOTE: normalizable/normalizable-errors must appear at the end with decreasing severity. * Take care to properly order statements with priv->config above. */ @@ -120,7 +1235,9 @@ compare_property (NMSetting *setting, const GParamSpec *prop_spec, NMSettingCompareFlags flags) { + NMSettingTeamPrivate *a_priv, *b_priv; NMSettingClass *parent_class; + guint i, j; /* If we are trying to match a connection in order to assume it (and thus * @flags contains INFERRABLE), use the "relaxed" matching for team @@ -133,6 +1250,24 @@ compare_property (NMSetting *setting, NM_SETTING_TEAM_GET_PRIVATE (other)->config, FALSE); } + if (nm_streq0 (prop_spec->name, NM_SETTING_TEAM_LINK_WATCHERS)) { + a_priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + b_priv = NM_SETTING_TEAM_GET_PRIVATE (other); + + if (a_priv->link_watchers->len != b_priv->link_watchers->len) + return FALSE; + for (i = 0; i < a_priv->link_watchers->len; i++) { + for (j = 0; j < b_priv->link_watchers->len; j++) { + if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i], + b_priv->link_watchers->pdata[j])) { + break; + } + if (j == b_priv->link_watchers->len) + return FALSE; + } + } + return TRUE; + } /* Otherwise chain up to parent to handle generic compare */ parent_class = NM_SETTING_CLASS (nm_setting_team_parent_class); @@ -142,6 +1277,13 @@ compare_property (NMSetting *setting, static void nm_setting_team_init (NMSettingTeam *setting) { + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + + priv->runner = g_strdup (NM_SETTING_TEAM_RUNNER_ROUNDROBIN); + priv->runner_tx_balancer_interval = -1; + priv->runner_sys_prio = -1; + priv->runner_min_ports = -1; + priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); } static void @@ -150,25 +1292,201 @@ finalize (GObject *object) NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object); g_free (priv->config); + g_free (priv->runner); + g_free (priv->runner_hwaddr_policy); + g_free (priv->runner_tx_balancer); + g_free (priv->runner_agg_select_policy); + if (priv->runner_tx_hash) + g_ptr_array_unref (priv->runner_tx_hash); + g_ptr_array_unref (priv->link_watchers); G_OBJECT_CLASS (nm_setting_team_parent_class)->finalize (object); } + +#define JSON_TO_VAL(typ, id) _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], FALSE) + +static void +_align_team_properties (NMSettingTeam *setting) +{ + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); + char **strv; + int i; + + priv->notify_peers_count = JSON_TO_VAL (int, PROP_NOTIFY_PEERS_COUNT); + priv->notify_peers_interval = JSON_TO_VAL (int, PROP_NOTIFY_PEERS_INTERVAL); + priv->mcast_rejoin_count = JSON_TO_VAL (int, PROP_MCAST_REJOIN_COUNT); + priv->mcast_rejoin_interval = JSON_TO_VAL (int, PROP_MCAST_REJOIN_INTERVAL); + priv->runner_tx_balancer_interval = JSON_TO_VAL (int, PROP_RUNNER_TX_BALANCER_INTERVAL); + priv->runner_sys_prio = JSON_TO_VAL (int, PROP_RUNNER_SYS_PRIO); + priv->runner_min_ports = JSON_TO_VAL (int, PROP_RUNNER_MIN_PORTS); + + priv->runner_active = JSON_TO_VAL (boolean, PROP_RUNNER_ACTIVE); + priv->runner_fast_rate = JSON_TO_VAL (boolean, PROP_RUNNER_FAST_RATE); + + g_free (priv->runner); + g_free (priv->runner_hwaddr_policy); + g_free (priv->runner_tx_balancer); + g_free (priv->runner_agg_select_policy); + priv->runner = JSON_TO_VAL (string, PROP_RUNNER); + priv->runner_hwaddr_policy = JSON_TO_VAL (string, PROP_RUNNER_HWADDR_POLICY); + priv->runner_tx_balancer = JSON_TO_VAL (string, PROP_RUNNER_TX_BALANCER); + priv->runner_agg_select_policy = JSON_TO_VAL (string, PROP_RUNNER_AGG_SELECT_POLICY); + + if (priv->runner_tx_hash) { + g_ptr_array_unref (priv->runner_tx_hash); + priv->runner_tx_hash = NULL; + } + strv = JSON_TO_VAL (strv, PROP_RUNNER_TX_HASH); + if (strv) { + for (i = 0; strv[i]; i++) + nm_setting_team_add_runner_tx_hash (setting, strv[i]); + g_strfreev (strv); + } + + g_ptr_array_unref (priv->link_watchers); + priv->link_watchers = JSON_TO_VAL (ptr_array, PROP_LINK_WATCHERS); +} + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { + NMSettingTeam *setting = NM_SETTING_TEAM (object); NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object); + const GValue *align_value = NULL; + gboolean align_config = FALSE; + char **strv; switch (prop_id) { case PROP_CONFIG: g_free (priv->config); priv->config = g_value_dup_string (value); + _align_team_properties (setting); + break; + case PROP_NOTIFY_PEERS_COUNT: + if (priv->notify_peers_count == g_value_get_int (value)) + break; + priv->notify_peers_count = g_value_get_int (value); + align_value = value; + align_config = TRUE; + break; + case PROP_NOTIFY_PEERS_INTERVAL: + if (priv->notify_peers_interval == g_value_get_int (value)) + break; + priv->notify_peers_interval = g_value_get_int (value); + align_value = value; + align_config = TRUE; + break; + case PROP_MCAST_REJOIN_COUNT: + if (priv->mcast_rejoin_count == g_value_get_int (value)) + break; + priv->mcast_rejoin_count = g_value_get_int (value); + align_value = value; + align_config = TRUE; + break; + case PROP_MCAST_REJOIN_INTERVAL: + if (priv->mcast_rejoin_interval == g_value_get_int (value)) + break; + priv->mcast_rejoin_interval = g_value_get_int (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER: + if ( !g_value_get_string (value) + || nm_streq (priv->runner, g_value_get_string (value))) + break; + g_free (priv->runner); + priv->runner = g_value_dup_string (value); + _nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], value); + _align_team_properties (setting); + break; + case PROP_RUNNER_HWADDR_POLICY: + if (nm_streq0 (priv->runner_hwaddr_policy, g_value_get_string (value))) + break; + g_free (priv->runner_hwaddr_policy); + priv->runner_hwaddr_policy = g_value_dup_string (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER_TX_HASH: + if (priv->runner_tx_hash) + g_ptr_array_unref (priv->runner_tx_hash); + strv = g_value_get_boxed (value); + if (strv && strv[0]) { + priv->runner_tx_hash = _nm_utils_strv_to_ptrarray (strv); + align_value = value; + } else + priv->runner_tx_hash = NULL; + align_config = TRUE; + break; + case PROP_RUNNER_TX_BALANCER: + if (nm_streq0 (priv->runner_tx_balancer, g_value_get_string (value))) + break; + g_free (priv->runner_tx_balancer); + priv->runner_tx_balancer = g_value_dup_string (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER_TX_BALANCER_INTERVAL: + if (priv->runner_tx_balancer_interval == g_value_get_int (value)) + break; + priv->runner_tx_balancer_interval = g_value_get_int (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER_ACTIVE: + if (priv->runner_active == g_value_get_boolean (value)) + break; + priv->runner_active = g_value_get_boolean (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER_FAST_RATE: + if (priv->runner_fast_rate == g_value_get_boolean (value)) + break; + priv->runner_fast_rate = g_value_get_boolean (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER_SYS_PRIO: + if (priv->runner_sys_prio == g_value_get_int (value)) + break; + priv->runner_sys_prio = g_value_get_int (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER_MIN_PORTS: + if (priv->runner_min_ports == g_value_get_int (value)) + break; + priv->runner_min_ports = g_value_get_int (value); + align_value = value; + align_config = TRUE; + break; + case PROP_RUNNER_AGG_SELECT_POLICY: + if (nm_streq0 (priv->runner_agg_select_policy, g_value_get_string (value))) + break; + g_free (priv->runner_agg_select_policy); + priv->runner_agg_select_policy = g_value_dup_string (value); + align_value = value; + align_config = TRUE; + break; + case PROP_LINK_WATCHERS: + g_ptr_array_unref (priv->link_watchers); + priv->link_watchers = _nm_utils_copy_array (g_value_get_boxed (value), + (NMUtilsCopyFunc) nm_team_link_watcher_dup, + (GDestroyNotify) nm_team_link_watcher_unref); + if (priv->link_watchers->len) + align_value = value; + align_config = TRUE; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + + if (align_config) + _nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], align_value); } static void @@ -176,11 +1494,60 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMSettingTeam *setting = NM_SETTING_TEAM (object); + NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting); switch (prop_id) { case PROP_CONFIG: g_value_set_string (value, nm_setting_team_get_config (setting)); break; + case PROP_NOTIFY_PEERS_COUNT: + g_value_set_int (value, priv->notify_peers_count); + break; + case PROP_NOTIFY_PEERS_INTERVAL: + g_value_set_int (value, priv->notify_peers_interval); + break; + case PROP_MCAST_REJOIN_COUNT: + g_value_set_int (value, priv->mcast_rejoin_count); + break; + case PROP_MCAST_REJOIN_INTERVAL: + g_value_set_int (value, priv->mcast_rejoin_interval); + break; + case PROP_RUNNER: + g_value_set_string (value, nm_setting_team_get_runner (setting)); + break; + case PROP_RUNNER_HWADDR_POLICY: + g_value_set_string (value, nm_setting_team_get_runner_hwaddr_policy (setting)); + break; + case PROP_RUNNER_TX_HASH: + g_value_take_boxed (value, priv->runner_tx_hash ? + _nm_utils_ptrarray_to_strv (priv->runner_tx_hash): NULL); + break; + case PROP_RUNNER_TX_BALANCER: + g_value_set_string (value, nm_setting_team_get_runner_tx_balancer (setting)); + break; + case PROP_RUNNER_TX_BALANCER_INTERVAL: + g_value_set_int (value, priv->runner_tx_balancer_interval); + break; + case PROP_RUNNER_ACTIVE: + g_value_set_boolean (value, nm_setting_team_get_runner_active (setting)); + break; + case PROP_RUNNER_FAST_RATE: + g_value_set_boolean (value, nm_setting_team_get_runner_fast_rate (setting)); + break; + case PROP_RUNNER_SYS_PRIO: + g_value_set_int (value, priv->runner_sys_prio); + break; + case PROP_RUNNER_MIN_PORTS: + g_value_set_int (value, priv->runner_min_ports); + break; + case PROP_RUNNER_AGG_SELECT_POLICY: + g_value_set_string (value, nm_setting_team_get_runner_agg_select_policy (setting)); + break; + case PROP_LINK_WATCHERS: + g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers, + (NMUtilsCopyFunc) nm_team_link_watcher_dup, + (GDestroyNotify) nm_team_link_watcher_unref)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -225,6 +1592,232 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class) NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMSettingTeam:notify-peers-count: + * + * Corresponds to the teamd notify_peers.count. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_NOTIFY_PEERS_COUNT, + g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:notify-peers-interval: + * + * Corresponds to the teamd notify_peers.interval. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_NOTIFY_PEERS_INTERVAL, + g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:mcast-rejoin-count: + * + * Corresponds to the teamd mcast_rejoin.count. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_MCAST_REJOIN_COUNT, + g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_COUNT, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:mcast-rejoin-interval: + * + * Corresponds to the teamd mcast_rejoin.interval. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_MCAST_REJOIN_INTERVAL, + g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner: + * + * Corresponds to the teamd runner.name. + * Permitted values are: "roundrobin", "broadcast", "activebackup", + * "loadbalance", "lacp". + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER, + g_param_spec_string (NM_SETTING_TEAM_RUNNER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-hwaddr-policy: + * + * Corresponds to the teamd runner.hwaddr_policy. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_HWADDR_POLICY, + g_param_spec_string (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-tx-hash: + * + * Corresponds to the teamd runner.tx_hash. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_TX_HASH, + g_param_spec_boxed (NM_SETTING_TEAM_RUNNER_TX_HASH, "", "", + G_TYPE_STRV, + G_PARAM_READWRITE | + NM_SETTING_PARAM_INFERRABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-tx-balancer: + * + * Corresponds to the teamd runner.tx_balancer.name. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_TX_BALANCER, + g_param_spec_string (NM_SETTING_TEAM_RUNNER_TX_BALANCER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-tx-balancer-interval: + * + * Corresponds to the teamd runner.tx_balancer.interval. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_TX_BALANCER_INTERVAL, + g_param_spec_int (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-active: + * + * Corresponds to the teamd runner.active. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_ACTIVE, + g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_ACTIVE, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-fast-rate: + * + * Corresponds to the teamd runner.fast_rate. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_FAST_RATE, + g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_FAST_RATE, "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-sys-prio: + * + * Corresponds to the teamd runner.sys_prio. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_SYS_PRIO, + g_param_spec_int (NM_SETTING_TEAM_RUNNER_SYS_PRIO, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-min-ports: + * + * Corresponds to the teamd runner.min_ports. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_MIN_PORTS, + g_param_spec_int (NM_SETTING_TEAM_RUNNER_MIN_PORTS, "", "", + G_MININT32, G_MAXINT32, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:runner-agg-select-policy: + * + * Corresponds to the teamd runner.agg_select_policy. + * + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_RUNNER_AGG_SELECT_POLICY, + g_param_spec_string (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingTeam:link-watchers: + * + * 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. + * + * Element-Type: NMTeamLinkWatcher + * Since: 1.10.2 + **/ + g_object_class_install_property + (object_class, PROP_LINK_WATCHERS, + g_param_spec_boxed (NM_SETTING_TEAM_LINK_WATCHERS, "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + _nm_setting_class_transform_property (parent_class, + NM_SETTING_TEAM_LINK_WATCHERS, + G_VARIANT_TYPE ("aa{sv}"), + team_link_watchers_to_dbus, + team_link_watchers_from_dbus); + /* ---dbus--- * property: interface-name * format: string diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h index 532963aa..45207836 100644 --- a/libnm-core/nm-setting-team.h +++ b/libnm-core/nm-setting-team.h @@ -29,6 +29,80 @@ G_BEGIN_DECLS +/** + * NMTeamLinkWatcherArpPingFlags: + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE: no one among the arp_ping link watcher + * boolean options ('validate_active', 'validate_inactive', 'send_always') is + * enabled (set to true). + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE: the arp_ping link watcher + * option 'validate_active' is enabled (set to true). + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE: the arp_ping link watcher + * option 'validate_inactive' is enabled (set to true). + * @NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS: the arp_ping link watcher option + * 'send_always' is enabled (set to true). + */ +typedef enum { /*< flags >*/ + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE = 0, /*< skip >*/ + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE = (1 << 1), + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE = (1 << 2), + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS = (1 << 3) +} NMTeamLinkWatcherArpPingFlags; + +#define NM_TEAM_LINK_WATCHER_ETHTOOL "ethtool" +#define NM_TEAM_LINK_WATCHER_ARP_PING "arp_ping" +#define NM_TEAM_LINK_WATCHER_NSNA_PING "nsna_ping" + + +typedef struct NMTeamLinkWatcher NMTeamLinkWatcher; + +GType nm_team_link_watcher_get_type (void); + +NM_AVAILABLE_IN_1_10_2 +NMTeamLinkWatcher *nm_team_link_watcher_new_ethtool (gint delay_up, + gint delay_down, + GError **error); +NM_AVAILABLE_IN_1_10_2 +NMTeamLinkWatcher *nm_team_link_watcher_new_nsna_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + GError **error); +NM_AVAILABLE_IN_1_10_2 +NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping (gint init_wait, + gint interval, + gint missed_max, + const char *target_host, + const char *source_host, + NMTeamLinkWatcherArpPingFlags flags, + GError **error); +NM_AVAILABLE_IN_1_10_2 +void nm_team_link_watcher_ref (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +void nm_team_link_watcher_unref (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other); +NM_AVAILABLE_IN_1_10_2 +NMTeamLinkWatcher *nm_team_link_watcher_dup (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +const char *nm_team_link_watcher_get_name (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +int nm_team_link_watcher_get_delay_up (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +int nm_team_link_watcher_get_delay_down (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +int nm_team_link_watcher_get_init_wait (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +int nm_team_link_watcher_get_interval (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +int nm_team_link_watcher_get_missed_max (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +const char *nm_team_link_watcher_get_target_host (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +const char *nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher); +NM_AVAILABLE_IN_1_10_2 +NMTeamLinkWatcherArpPingFlags nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher); + + #define NM_TYPE_SETTING_TEAM (nm_setting_team_get_type ()) #define NM_SETTING_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TEAM, NMSettingTeam)) #define NM_SETTING_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_TEAM, NMSettingTeamClass)) @@ -38,7 +112,47 @@ G_BEGIN_DECLS #define NM_SETTING_TEAM_SETTING_NAME "team" -#define NM_SETTING_TEAM_CONFIG "config" +#define NM_SETTING_TEAM_CONFIG "config" +#define NM_SETTING_TEAM_NOTIFY_PEERS_COUNT "notify-peers-count" +#define NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL "notify-peers-interval" +#define NM_SETTING_TEAM_MCAST_REJOIN_COUNT "mcast-rejoin-count" +#define NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL "mcast-rejoin-interval" +#define NM_SETTING_TEAM_RUNNER "runner" +#define NM_SETTING_TEAM_RUNNER_HWADDR_POLICY "runner-hwaddr-policy" +#define NM_SETTING_TEAM_RUNNER_TX_HASH "runner-tx-hash" +#define NM_SETTING_TEAM_RUNNER_TX_BALANCER "runner-tx-balancer" +#define NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL "runner-tx-balancer-interval" +#define NM_SETTING_TEAM_RUNNER_ACTIVE "runner-active" +#define NM_SETTING_TEAM_RUNNER_FAST_RATE "runner-fast-rate" +#define NM_SETTING_TEAM_RUNNER_SYS_PRIO "runner-sys-prio" +#define NM_SETTING_TEAM_RUNNER_MIN_PORTS "runner-min-ports" +#define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY "runner-agg-select-policy" +#define NM_SETTING_TEAM_LINK_WATCHERS "link-watchers" + +#define NM_SETTING_TEAM_RUNNER_BROADCAST "broadcast" +#define NM_SETTING_TEAM_RUNNER_ROUNDROBIN "roundrobin" +#define NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP "activebackup" +#define NM_SETTING_TEAM_RUNNER_LOADBALANCE "loadbalance" +#define NM_SETTING_TEAM_RUNNER_LACP "lacp" + +#define NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL "same_all" +#define NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_BY_ACTIVE "by_active" +#define NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_ONLY_ACTIVE "only_active" + +#define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO "lacp_prio" +#define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO_STABLE "lacp_prio_stable" +#define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_BANDWIDTH "bandwidth" +#define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_COUNT "count" +#define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_PORT_CONFIG "port_config" + +#define NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT 1 +#define NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT 1 +#define NM_SETTING_TEAM_RUNNER_DEFAULT NM_SETTING_TEAM_RUNNER_ROUNDROBIN +#define NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL +#define NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT 50 +#define NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT 255 +#define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO + /** * NMSettingTeam: @@ -61,7 +175,56 @@ GType nm_setting_team_get_type (void); NMSetting * nm_setting_team_new (void); const char * nm_setting_team_get_config (NMSettingTeam *setting); - +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_get_notify_peers_count (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +const char * nm_setting_team_get_runner (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +const char * nm_setting_team_get_runner_hwaddr_policy (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +const char * nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_team_get_runner_active (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gint nm_setting_team_get_runner_min_ports (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +const char * nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting, const char *txhash); +NM_AVAILABLE_IN_1_10_2 +guint nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +const char *nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash); +NM_AVAILABLE_IN_1_10_2 +guint nm_setting_team_get_num_link_watchers (NMSettingTeam *setting); +NM_AVAILABLE_IN_1_10_2 +NMTeamLinkWatcher * nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_team_add_link_watcher (NMSettingTeam *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx); +NM_AVAILABLE_IN_1_10_2 +gboolean nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting, + NMTeamLinkWatcher *link_watcher); +NM_AVAILABLE_IN_1_10_2 +void nm_setting_team_clear_link_watchers (NMSettingTeam *setting); G_END_DECLS #endif /* __NM_SETTING_TEAM_H__ */ diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c index 91bcbe7a..de77a493 100644 --- a/libnm-core/nm-setting-wireless-security.c +++ b/libnm-core/nm-setting-wireless-security.c @@ -1814,10 +1814,11 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting **/ /* ---ifcfg-rh--- * property: wep-key-type - * variable: KEY or KEY_PASSPHRASE(+) + * variable: KEY or KEY_PASSPHRASE(+); KEY_TYPE(+) * description: KEY is used for "key" type (10 or 26 hexadecimal characters, * or 5 or 13 character string prefixed with "s:"). KEY_PASSPHRASE is used - * for WEP passphrases. + * for WEP passphrases. KEY_TYPE specifies the key type and can be either + * 'key' or 'passphrase'. KEY_TYPE is redundant and can be omitted. * example: KEY1=s:ahoj, KEY1=0a1c45bc02, KEY_PASSPHRASE1=mysupersecretkey * ---end--- */ diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 03c87907..c76b3200 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -15,7 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2005 - 2014 Red Hat, Inc. + * Copyright 2005 - 2017 Red Hat, Inc. */ #ifndef __NM_UTILS_PRIVATE_H__ @@ -33,6 +33,8 @@ struct _NMVariantAttributeSpec { const GVariantType *type; bool v4:1; bool v6:1; + bool no_value:1; + bool consumes_rest:1; char str_type; }; @@ -78,4 +80,120 @@ void _nm_utils_bytes_from_dbus (GVariant *dbus_value, char * _nm_utils_hwaddr_canonical_or_invalid (const char *mac, gssize length); +GPtrArray * _nm_utils_team_link_watchers_from_variant (GVariant *value); +GVariant * _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers); + +/* JSON to GValue conversion macros */ + +typedef struct { + const char *key1; + const char *key2; + const char *key3; + union { + int default_int; + gboolean default_bool; + const char *default_str; + }; +} _NMUtilsTeamPropertyKeys; + +static inline int +_nm_utils_json_extract_int (char *conf, + _NMUtilsTeamPropertyKeys key, + gboolean is_port) +{ + gs_free GValue *t_value = NULL; + int ret; + + t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); + if (!t_value) + return key.default_int; + + ret = g_value_get_int (t_value); + g_value_unset (t_value); + return ret; +} + +static inline gboolean +_nm_utils_json_extract_boolean (char *conf, + _NMUtilsTeamPropertyKeys key, + gboolean is_port) +{ + gs_free GValue *t_value = NULL; + gboolean ret; + + t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); + if (!t_value) + return key.default_bool; + + ret = g_value_get_boolean (t_value); + g_value_unset (t_value); + return ret; +} + +static inline char * +_nm_utils_json_extract_string (char *conf, + _NMUtilsTeamPropertyKeys key, + gboolean is_port) +{ + gs_free GValue *t_value = NULL; + char *ret; + + t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); + if (!t_value) + return g_strdup (key.default_str); + + ret = g_value_dup_string (t_value); + g_value_unset (t_value); + return ret; +} + +static inline char ** +_nm_utils_json_extract_strv (char *conf, + _NMUtilsTeamPropertyKeys key, + gboolean is_port) +{ + gs_free GValue *t_value = NULL; + char **ret; + + t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); + if (!t_value) + return NULL; + + ret = g_strdupv (g_value_get_boxed (t_value)); + g_value_unset (t_value); + return ret; +} + +static inline GPtrArray * +_nm_utils_json_extract_ptr_array (char *conf, + _NMUtilsTeamPropertyKeys key, + gboolean is_port) +{ + gs_free GValue *t_value = NULL; + GPtrArray *data, *ret; + guint i; + + ret = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + t_value = _nm_utils_team_config_get (conf, key.key1, key.key2, key.key3, is_port); + if (!t_value) + return ret; + + data = g_value_get_boxed (t_value); + if (!data) + return ret; + + for (i = 0; i < data->len; i++) + g_ptr_array_add (ret, nm_team_link_watcher_dup (data->pdata[i])); + g_value_unset (t_value); + return ret; +} + +static inline void +_nm_utils_json_append_gvalue (char **conf, + _NMUtilsTeamPropertyKeys key, + const GValue *val) +{ + _nm_utils_team_config_set (conf, key.key1, key.key2, key.key3, val); +} + #endif diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index bc955834..6cc92ec8 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -16,7 +16,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2005 - 2014 Red Hat, Inc. + * Copyright 2005 - 2017 Red Hat, Inc. */ #include "nm-default.h" @@ -33,6 +33,7 @@ #include #include #include +#include #if WITH_JANSSON #include @@ -494,17 +495,45 @@ _nm_utils_strdict_to_dbus (const GValue *prop_value) { GHashTable *hash; GHashTableIter iter; - gpointer key, value; + const char *key, *value; GVariantBuilder builder; + guint i, len; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); + hash = g_value_get_boxed (prop_value); - if (hash) { - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, &key, &value)) - g_variant_builder_add (&builder, "{ss}", key, value); + if (!hash) + goto out; + len = g_hash_table_size (hash); + if (!len) + goto out; + + g_hash_table_iter_init (&iter, hash); + if (!g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) + nm_assert_not_reached (); + + if (len == 1) + g_variant_builder_add (&builder, "{ss}", key, value); + else { + gs_free NMUtilsNamedValue *idx = NULL; + + idx = g_new (NMUtilsNamedValue, len); + i = 0; + do { + idx[i].name = key; + idx[i].value_str = value; + i++; + } while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)); + nm_assert (i == len); + + g_qsort_with_data (idx, len, sizeof (idx[0]), + nm_utils_named_entry_cmp_with_data, NULL); + + for (i = 0; i < len; i++) + g_variant_builder_add (&builder, "{ss}", idx[i].name, idx[i].value_str); } +out: return g_variant_builder_end (&builder); } @@ -822,41 +851,6 @@ _nm_utils_strv_equal (char **strv1, char **strv2) return !*strv1 && !*strv2; } -/** - * _nm_utils_strsplit_set: - * @str: string to split - * @delimiters: string of delimiter characters - * @max_tokens: the maximum number of tokens to split string into. When it is - * less than 1, the @str is split completely. - * - * Utility function for splitting string into a string array. It is a wrapper - * for g_strsplit_set(), but it also removes empty strings from the vector as - * they are not useful in most cases. - * - * Returns: (transfer full): a newly allocated NULL-terminated array of strings. - * The caller must free the returned array with g_strfreev(). - **/ -char ** -_nm_utils_strsplit_set (const char *str, const char *delimiters, int max_tokens) -{ - char **result; - uint i; - uint j; - - result = g_strsplit_set (str, delimiters, max_tokens); - - /* remove empty strings */ - for (i = 0; result && result[i]; i++) { - if (*result[i] == '\0') { - g_free (result[i]); - for (j = i; result[j]; j++) - result[j] = result[j + 1]; - i--; - } - } - return result; -} - static gboolean device_supports_ap_ciphers (guint32 dev_caps, guint32 ap_flags, @@ -2070,192 +2064,736 @@ next: /*****************************************************************************/ +static void +_string_append_tc_handle (GString *string, guint32 handle) +{ + g_string_append_printf (string, "%x:", TC_H_MAJ (handle) >> 16); + if (TC_H_MIN (handle) != TC_H_UNSPEC) + g_string_append_printf (string, "%x", TC_H_MIN (handle)); +} + /** - * nm_utils_uuid_generate_buf_: - * @buf: input buffer, must contain at least 37 bytes + * _nm_utils_string_append_tc_parent: + * @string: the string to write the parent handle to + * @prefix: optional prefix for the numeric handle + * @parent: the parent handle * - * Returns: generates a new random UUID, writes it to @buf and returns @buf. - **/ -char * -nm_utils_uuid_generate_buf_ (char *buf) + * This is used to either write out the parent handle to the tc qdisc string + * or to pretty-format (use symbolic name for root) the key in keyfile. + * The presence of prefix determnines which one is the case. + * + * Private API due to general uglyness and overall uselessness for anything + * sensible. + */ +void +_nm_utils_string_append_tc_parent (GString *string, const char *prefix, guint32 parent) { - uuid_t uuid; + if (parent == TC_H_ROOT) { + g_string_append (string, "root"); + } else { + if (prefix) { + if (parent == TC_H_INGRESS) + return; + g_string_append_printf (string, "%s ", prefix); + } + _string_append_tc_handle (string, parent); + } - uuid_generate_random (uuid); - uuid_unparse_lower (uuid, buf); - return buf; + if (prefix) + g_string_append_c (string, ' '); } /** - * nm_utils_uuid_generate: + * _nm_utils_parse_tc_handle: + * @str: the string representation of a qdisc handle + * @error: location of the error * - * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection - * object's #NMSettingConnection:id: property. Should be freed with g_free() - **/ -char * -nm_utils_uuid_generate (void) + * Parses tc style handle number into a numeric representation. + * Don't use this, use nm_utils_tc_qdisc_from_str() instead. + */ +guint32 +_nm_utils_parse_tc_handle (const char *str, GError **error) { - return nm_utils_uuid_generate_buf_ (g_malloc (37)); + gint64 maj, min; + char *sep; + + maj = g_ascii_strtoll (str, &sep, 0x10); + if (*sep == ':') + min = g_ascii_strtoll (&sep[1], &sep, 0x10); + else + min = 0; + + if (*sep != '\0' || maj <= 0 || maj > 0xffff || min < 0 || min > 0xffff) { + g_set_error (error, 1, 0, _("'%s' is not a valid handle."), str); + return TC_H_UNSPEC; + } + + return TC_H_MAKE (maj << 16, min); +} + +#define TC_ATTR_SPEC_PTR(name, type, no_value, consumes_rest, str_type) \ + &(NMVariantAttributeSpec) { name, type, FALSE, FALSE, no_value, consumes_rest, str_type } + +static const NMVariantAttributeSpec * const tc_object_attribute_spec[] = { + TC_ATTR_SPEC_PTR ("root", G_VARIANT_TYPE_BOOLEAN, TRUE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("parent", G_VARIANT_TYPE_STRING, FALSE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("handle", G_VARIANT_TYPE_STRING, FALSE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("kind", G_VARIANT_TYPE_STRING, TRUE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("", G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a' ), + NULL, +}; + +/*****************************************************************************/ + +/** + * _nm_utils_string_append_tc_qdisc_rest: + * @string: the string to write the formatted qdisc to + * @qdisc: the %NMTCQdisc + * + * This formats the rest of the qdisc string but the parent. Useful to format + * the keyfile value and nowhere else. + * Use nm_utils_tc_qdisc_to_str() that also includes the parent instead. + */ +void +_nm_utils_string_append_tc_qdisc_rest (GString *string, NMTCQdisc *qdisc) +{ + guint32 handle = nm_tc_qdisc_get_handle (qdisc); + const char *kind = nm_tc_qdisc_get_kind (qdisc); + + if (handle != TC_H_UNSPEC && strcmp (kind, "ingress") != 0) { + g_string_append (string, "handle "); + _string_append_tc_handle (string, handle); + g_string_append_c (string, ' '); + } + + g_string_append (string, kind); } /** - * nm_utils_uuid_generate_from_string: - * @s: a string to use as the seed for the UUID - * @slen: if negative, treat @s as zero terminated C string. - * Otherwise, assume the length as given (and allow @s to be - * non-null terminated or contain '\0'). - * @uuid_type: a type identifier which UUID format to generate. - * @type_args: additional arguments, depending on the uuid_type + * nm_utils_tc_qdisc_to_str: + * @qdisc: the %NMTCQdisc + * @error: location of the error * - * For a given @s, this function will always return the same UUID. + * Turns the %NMTCQdisc into a tc style string representation of the queueing + * discipline. * - * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection - * object's #NMSettingConnection:id: property - **/ + * Returns: formatted string or %NULL + * + * Since: 1.10.2 + */ char * -nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, gpointer type_args) +nm_utils_tc_qdisc_to_str (NMTCQdisc *qdisc, GError **error) { - uuid_t uuid; - char *buf; + GString *string; - g_return_val_if_fail (slen == 0 || s, FALSE); + string = g_string_sized_new (60); - g_return_val_if_fail (uuid_type == NM_UTILS_UUID_TYPE_LEGACY || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); - g_return_val_if_fail (!type_args || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); + _nm_utils_string_append_tc_parent (string, "parent", + nm_tc_qdisc_get_parent (qdisc)); + _nm_utils_string_append_tc_qdisc_rest (string, qdisc); - switch (uuid_type) { - case NM_UTILS_UUID_TYPE_LEGACY: - crypto_md5_hash (NULL, 0, s, slen, (char *) uuid, sizeof (uuid)); - break; - case NM_UTILS_UUID_TYPE_VARIANT3: { - uuid_t ns_uuid = { 0 }; + return g_string_free (string, FALSE); +} - if (type_args) { - /* type_args can be a name space UUID. Interpret it as (char *) */ - if (uuid_parse ((char *) type_args, ns_uuid) != 0) - g_return_val_if_reached (NULL); + +static gboolean +_tc_read_common_opts (const char *str, + guint32 *handle, + guint32 *parent, + char **kind, + char **rest, + GError **error) +{ + gs_unref_hashtable GHashTable *ht = NULL; + GVariant *variant; + + ht = nm_utils_parse_variant_attributes (str, + ' ', ' ', FALSE, + tc_object_attribute_spec, + error); + if (!ht) + return FALSE; + + if (g_hash_table_contains (ht, "root")) + *parent = TC_H_ROOT; + + variant = g_hash_table_lookup (ht, "parent"); + if (variant) { + if (*parent != TC_H_UNSPEC) { + g_set_error (error, 1, 0, + _("'%s' unexpected: parent already specified."), + g_variant_get_string (variant, NULL)); + return FALSE; } + *parent = _nm_utils_parse_tc_handle (g_variant_get_string (variant, NULL), error); + if (*parent == TC_H_UNSPEC) + return FALSE; + } - crypto_md5_hash (s, slen, (char *) ns_uuid, sizeof (ns_uuid), (char *) uuid, sizeof (uuid)); + variant = g_hash_table_lookup (ht, "handle"); + if (variant) { + *handle = _nm_utils_parse_tc_handle (g_variant_get_string (variant, NULL), error); + if (*handle == TC_H_UNSPEC) + return FALSE; + if (TC_H_MIN (*handle)) { + g_set_error (error, 1, 0, + _("invalid handle: '%s'"), + g_variant_get_string (variant, NULL)); + return FALSE; + } + } - uuid[6] = (uuid[6] & 0x0F) | 0x30; - uuid[8] = (uuid[8] & 0x3F) | 0x80; - break; + variant = g_hash_table_lookup (ht, "kind"); + if (variant) { + *kind = g_variant_dup_string (variant, NULL); + if (strcmp (*kind, "ingress") == 0) { + if (*parent == TC_H_UNSPEC) + *parent = TC_H_INGRESS; + if (*handle == TC_H_UNSPEC) + *handle = TC_H_MAKE (TC_H_INGRESS, 0); + } } - default: - g_return_val_if_reached (NULL); + + if (*parent == TC_H_UNSPEC) { + if (*kind) { + g_free (*kind); + *kind = NULL; + } + g_set_error_literal (error, 1, 0, _("parent not specified.")); + return FALSE; } - buf = g_malloc (37); - uuid_unparse_lower (uuid, &buf[0]); + variant = g_hash_table_lookup (ht, ""); + if (variant) + *rest = g_variant_dup_string (variant, NULL); - return buf; + return TRUE; } /** - * _nm_utils_uuid_generate_from_strings: - * @string1: a variadic list of strings. Must be NULL terminated. + * nm_utils_tc_qdisc_from_str: + * @str: the string representation of a qdisc + * @error: location of the error * - * Returns a variant3 UUID based on the concatenated C strings. - * It does not simply concatenate them, but also includes the - * terminating '\0' character. For example "a", "b", gives - * "a\0b\0". + * Parces the tc style string qdisc representation of the queueing + * discipline to a %NMTCQdisc instance. Supports a subset of the tc language. * - * This has the advantage, that the following invocations - * all give different UUIDs: (NULL), (""), ("",""), ("","a"), ("a",""), - * ("aa"), ("aa", ""), ("", "aa"), ... + * Returns: the %NMTCQdisc or %NULL + * + * Since: 1.10.2 */ -char * -_nm_utils_uuid_generate_from_strings (const char *string1, ...) -{ - GString *str; - va_list args; - const char *s; - char *uuid; +NMTCQdisc * +nm_utils_tc_qdisc_from_str (const char *str, GError **error) +{ + guint32 handle = TC_H_UNSPEC; + guint32 parent = TC_H_UNSPEC; + gs_free char *kind = NULL; + gs_free char *rest = NULL; + NMTCQdisc *qdisc = NULL; + gs_unref_hashtable GHashTable *ht = NULL; - if (!string1) - return nm_utils_uuid_generate_from_string (NULL, 0, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); + nm_assert (str); + nm_assert (!error || !*error); - str = g_string_sized_new (120); /* effectively allocates power of 2 (128)*/ + ht = nm_utils_parse_variant_attributes (str, + ' ', ' ', FALSE, + tc_object_attribute_spec, + error); + if (!ht) + return NULL; - g_string_append_len (str, string1, strlen (string1) + 1); + if (!_tc_read_common_opts (str, &handle, &parent, &kind, &rest, error)) + return NULL; - va_start (args, string1); - s = va_arg (args, const char *); - while (s) { - g_string_append_len (str, s, strlen (s) + 1); - s = va_arg (args, const char *); + if (rest) { + g_set_error (error, 1, 0, _("unsupported qdisc option: '%s'."), rest); + return NULL; } - va_end (args); - uuid = nm_utils_uuid_generate_from_string (str->str, str->len, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); + qdisc = nm_tc_qdisc_new (kind, parent, error); + if (!qdisc) + return NULL; - g_string_free (str, TRUE); - return uuid; -} + nm_tc_qdisc_set_handle (qdisc, handle); + return qdisc; +} /*****************************************************************************/ +static const NMVariantAttributeSpec * const tc_action_simple_attribute_spec[] = { + TC_ATTR_SPEC_PTR ("sdata", G_VARIANT_TYPE_BYTESTRING, FALSE, FALSE, 0 ), + NULL, +}; + +static const NMVariantAttributeSpec * const tc_action_attribute_spec[] = { + TC_ATTR_SPEC_PTR ("kind", G_VARIANT_TYPE_STRING, TRUE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("", G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a' ), + NULL, +}; + +static gboolean +_string_append_tc_action (GString *string, NMTCAction *action, GError **error) +{ + gs_unref_hashtable GHashTable *ht = NULL; + const char *kind = nm_tc_action_get_kind (action); + gs_strfreev char **attr_names = NULL; + gs_free char *str = NULL; + int i; + + ht = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); + + g_string_append (string, kind); + + attr_names = nm_tc_action_get_attribute_names (action); + for (i = 0; attr_names[i]; i++) { + g_hash_table_insert (ht, attr_names[i], + nm_tc_action_get_attribute (action, attr_names[i])); + } + + if (i) { + str = nm_utils_format_variant_attributes (ht, ' ', ' '); + g_string_append_c (string, ' '); + g_string_append (string, str); + } + + return TRUE; +} + /** - * nm_utils_rsa_key_encrypt: - * @data: (array length=len): RSA private key data to be encrypted - * @len: length of @data - * @in_password: (allow-none): existing password to use, if any - * @out_password: (out) (allow-none): if @in_password was %NULL, a random - * password will be generated and returned in this argument - * @error: detailed error information on return, if an error occurred + * nm_utils_tc_action_to_str: + * @action: the %NMTCAction + * @error: location of the error * - * Encrypts the given RSA private key data with the given password (or generates - * a password if no password was given) and converts the data to PEM format - * suitable for writing to a file. It uses Triple DES cipher for the encryption. + * Turns the %NMTCAction into a tc style string representation of the queueing + * discipline. * - * Returns: (transfer full): on success, PEM-formatted data suitable for writing - * to a PEM-formatted certificate/private key file. - **/ -GByteArray * -nm_utils_rsa_key_encrypt (const guint8 *data, - gsize len, - const char *in_password, - char **out_password, - GError **error) + * Returns: formatted string or %NULL + * + * Since: 1.10.2 + */ +char * +nm_utils_tc_action_to_str (NMTCAction *action, GError **error) { - char salt[16]; - int salt_len; - char *key = NULL, *enc = NULL, *pw_buf[32]; - gsize key_len = 0, enc_len = 0; - GString *pem = NULL; - char *tmp, *tmp_password = NULL; - int left; - const char *p; - GByteArray *ret = NULL; - - g_return_val_if_fail (data != NULL, NULL); - g_return_val_if_fail (len > 0, NULL); - if (out_password) - g_return_val_if_fail (*out_password == NULL, NULL); + GString *string; - /* Make the password if needed */ - if (!in_password) { - if (!crypto_randomize (pw_buf, sizeof (pw_buf), error)) - return NULL; - in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1); + string = g_string_sized_new (60); + if (!_string_append_tc_action (string, action, error)) { + g_string_free (string, TRUE); + return NULL; } - salt_len = 8; - if (!crypto_randomize (salt, salt_len, error)) - goto out; + return g_string_free (string, FALSE); +} - key = crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL); - if (!key) - g_return_val_if_reached (NULL); +/** + * nm_utils_tc_action_from_str: + * @str: the string representation of a action + * @error: location of the error + * + * Parces the tc style string action representation of the queueing + * discipline to a %NMTCAction instance. Supports a subset of the tc language. + * + * Returns: the %NMTCAction or %NULL + * + * Since: 1.10.2 + */ +NMTCAction * +nm_utils_tc_action_from_str (const char *str, GError **error) +{ + const char *kind = NULL; + const char *rest = NULL; + NMTCAction *action = NULL; + gs_unref_hashtable GHashTable *ht = NULL; + gs_unref_hashtable GHashTable *options = NULL; + GVariant *variant; + const NMVariantAttributeSpec * const *attrs; - enc = crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error); - if (!enc) - goto out; + nm_assert (str); + nm_assert (!error || !*error); - pem = g_string_sized_new (enc_len * 2 + 100); - g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n"); - g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n"); + ht = nm_utils_parse_variant_attributes (str, + ' ', ' ', FALSE, + tc_action_attribute_spec, + error); + if (!ht) + return FALSE; + + variant = g_hash_table_lookup (ht, "kind"); + if (variant) { + kind = g_variant_get_string (variant, NULL); + } else { + g_set_error_literal (error, 1, 0, _("action name missing.")); + return NULL; + } + + kind = g_variant_get_string (variant, NULL); + if (strcmp (kind, "simple") == 0) + attrs = tc_action_simple_attribute_spec; + else + attrs = NULL; + + variant = g_hash_table_lookup (ht, ""); + if (variant) + rest = g_variant_get_string (variant, NULL); + + action = nm_tc_action_new (kind, error); + if (!action) + return NULL; + + if (rest) { + GHashTableIter iter; + gpointer key, value; + + if (!attrs) { + nm_tc_action_unref (action); + g_set_error (error, 1, 0, _("unsupported action option: '%s'."), rest); + return NULL; + } + + options = nm_utils_parse_variant_attributes (rest, + ' ', ' ', FALSE, + attrs, + error); + if (!options) { + nm_tc_action_unref (action); + return NULL; + } + + g_hash_table_iter_init (&iter, options); + while (g_hash_table_iter_next (&iter, &key, &value)) + nm_tc_action_set_attribute (action, key, g_variant_ref_sink (value)); + } + + return action; +} + +/*****************************************************************************/ + +/** + * _nm_utils_string_append_tc_tfilter_rest: + * @string: the string to write the formatted tfilter to + * @tfilter: the %NMTCTfilter + * + * This formats the rest of the tfilter string but the parent. Useful to format + * the keyfile value and nowhere else. + * Use nm_utils_tc_tfilter_to_str() that also includes the parent instead. + */ +gboolean +_nm_utils_string_append_tc_tfilter_rest (GString *string, NMTCTfilter *tfilter, GError **error) +{ + guint32 handle = nm_tc_tfilter_get_handle (tfilter); + const char *kind = nm_tc_tfilter_get_kind (tfilter); + NMTCAction *action; + + if (handle != TC_H_UNSPEC) { + g_string_append (string, "handle "); + _string_append_tc_handle (string, handle); + g_string_append_c (string, ' '); + } + + g_string_append (string, kind); + + action = nm_tc_tfilter_get_action (tfilter); + if (action) { + g_string_append (string, " action "); + if (!_string_append_tc_action (string, action, error)) + return FALSE; + } + + return TRUE; +} + +/** + * nm_utils_tc_tfilter_to_str: + * @tfilter: the %NMTCTfilter + * @error: location of the error + * + * Turns the %NMTCTfilter into a tc style string representation of the queueing + * discipline. + * + * Returns: formatted string or %NULL + * + * Since: 1.10.2 + */ +char * +nm_utils_tc_tfilter_to_str (NMTCTfilter *tfilter, GError **error) +{ + GString *string; + + string = g_string_sized_new (60); + + _nm_utils_string_append_tc_parent (string, "parent", + nm_tc_tfilter_get_parent (tfilter)); + if (!_nm_utils_string_append_tc_tfilter_rest (string, tfilter, error)) { + g_string_free (string, TRUE); + return NULL; + } + + return g_string_free (string, FALSE); +} + +static const NMVariantAttributeSpec * const tc_tfilter_attribute_spec[] = { + TC_ATTR_SPEC_PTR ("action", G_VARIANT_TYPE_BOOLEAN, TRUE, FALSE, 0 ), + TC_ATTR_SPEC_PTR ("", G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a' ), + NULL, +}; + +/** + * nm_utils_tc_tfilter_from_str: + * @str: the string representation of a tfilter + * @error: location of the error + * + * Parces the tc style string tfilter representation of the queueing + * discipline to a %NMTCTfilter instance. Supports a subset of the tc language. + * + * Returns: the %NMTCTfilter or %NULL + * + * Since: 1.10.2 + */ +NMTCTfilter * +nm_utils_tc_tfilter_from_str (const char *str, GError **error) +{ + guint32 handle = TC_H_UNSPEC; + guint32 parent = TC_H_UNSPEC; + gs_free char *kind = NULL; + gs_free char *rest = NULL; + NMTCAction *action = NULL; + const char *extra_opts = NULL; + NMTCTfilter *tfilter = NULL; + gs_unref_hashtable GHashTable *ht = NULL; + GVariant *variant; + + nm_assert (str); + nm_assert (!error || !*error); + + if (!_tc_read_common_opts (str, &handle, &parent, &kind, &rest, error)) + return NULL; + + if (rest) { + ht = nm_utils_parse_variant_attributes (rest, + ' ', ' ', FALSE, + tc_tfilter_attribute_spec, + error); + if (!ht) + return NULL; + + variant = g_hash_table_lookup (ht, ""); + if (variant) + extra_opts = g_variant_get_string (variant, NULL); + + if (g_hash_table_contains (ht, "action")) { + action = nm_utils_tc_action_from_str (extra_opts, error); + if (!action) { + g_prefix_error (error, _("invalid action: ")); + return NULL; + } + } else { + g_set_error (error, 1, 0, _("unsupported tfilter option: '%s'."), rest); + return NULL; + } + } + + tfilter = nm_tc_tfilter_new (kind, parent, error); + if (!tfilter) + return NULL; + + nm_tc_tfilter_set_handle (tfilter, handle); + if (action) { + nm_tc_tfilter_set_action (tfilter, action); + nm_tc_action_unref (action); + } + + return tfilter; +} + +/*****************************************************************************/ + +/** + * nm_utils_uuid_generate_buf_: + * @buf: input buffer, must contain at least 37 bytes + * + * Returns: generates a new random UUID, writes it to @buf and returns @buf. + **/ +char * +nm_utils_uuid_generate_buf_ (char *buf) +{ + uuid_t uuid; + + uuid_generate_random (uuid); + uuid_unparse_lower (uuid, buf); + return buf; +} + +/** + * nm_utils_uuid_generate: + * + * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection + * object's #NMSettingConnection:id: property. Should be freed with g_free() + **/ +char * +nm_utils_uuid_generate (void) +{ + return nm_utils_uuid_generate_buf_ (g_malloc (37)); +} + +/** + * nm_utils_uuid_generate_from_string: + * @s: a string to use as the seed for the UUID + * @slen: if negative, treat @s as zero terminated C string. + * Otherwise, assume the length as given (and allow @s to be + * non-null terminated or contain '\0'). + * @uuid_type: a type identifier which UUID format to generate. + * @type_args: additional arguments, depending on the uuid_type + * + * For a given @s, this function will always return the same UUID. + * + * Returns: a newly allocated UUID suitable for use as the #NMSettingConnection + * object's #NMSettingConnection:id: property + **/ +char * +nm_utils_uuid_generate_from_string (const char *s, gssize slen, int uuid_type, gpointer type_args) +{ + uuid_t uuid; + char *buf; + + g_return_val_if_fail (slen == 0 || s, FALSE); + + g_return_val_if_fail (uuid_type == NM_UTILS_UUID_TYPE_LEGACY || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); + g_return_val_if_fail (!type_args || uuid_type == NM_UTILS_UUID_TYPE_VARIANT3, NULL); + + switch (uuid_type) { + case NM_UTILS_UUID_TYPE_LEGACY: + crypto_md5_hash (NULL, 0, s, slen, (char *) uuid, sizeof (uuid)); + break; + case NM_UTILS_UUID_TYPE_VARIANT3: { + uuid_t ns_uuid = { 0 }; + + if (type_args) { + /* type_args can be a name space UUID. Interpret it as (char *) */ + if (uuid_parse ((char *) type_args, ns_uuid) != 0) + g_return_val_if_reached (NULL); + } + + crypto_md5_hash (s, slen, (char *) ns_uuid, sizeof (ns_uuid), (char *) uuid, sizeof (uuid)); + + uuid[6] = (uuid[6] & 0x0F) | 0x30; + uuid[8] = (uuid[8] & 0x3F) | 0x80; + break; + } + default: + g_return_val_if_reached (NULL); + } + + buf = g_malloc (37); + uuid_unparse_lower (uuid, &buf[0]); + + return buf; +} + +/** + * _nm_utils_uuid_generate_from_strings: + * @string1: a variadic list of strings. Must be NULL terminated. + * + * Returns a variant3 UUID based on the concatenated C strings. + * It does not simply concatenate them, but also includes the + * terminating '\0' character. For example "a", "b", gives + * "a\0b\0". + * + * This has the advantage, that the following invocations + * all give different UUIDs: (NULL), (""), ("",""), ("","a"), ("a",""), + * ("aa"), ("aa", ""), ("", "aa"), ... + */ +char * +_nm_utils_uuid_generate_from_strings (const char *string1, ...) +{ + GString *str; + va_list args; + const char *s; + char *uuid; + + if (!string1) + return nm_utils_uuid_generate_from_string (NULL, 0, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); + + str = g_string_sized_new (120); /* effectively allocates power of 2 (128)*/ + + g_string_append_len (str, string1, strlen (string1) + 1); + + va_start (args, string1); + s = va_arg (args, const char *); + while (s) { + g_string_append_len (str, s, strlen (s) + 1); + s = va_arg (args, const char *); + } + va_end (args); + + uuid = nm_utils_uuid_generate_from_string (str->str, str->len, NM_UTILS_UUID_TYPE_VARIANT3, NM_UTILS_UUID_NS); + + g_string_free (str, TRUE); + return uuid; +} + +/*****************************************************************************/ + +/** + * nm_utils_rsa_key_encrypt: + * @data: (array length=len): RSA private key data to be encrypted + * @len: length of @data + * @in_password: (allow-none): existing password to use, if any + * @out_password: (out) (allow-none): if @in_password was %NULL, a random + * password will be generated and returned in this argument + * @error: detailed error information on return, if an error occurred + * + * Encrypts the given RSA private key data with the given password (or generates + * a password if no password was given) and converts the data to PEM format + * suitable for writing to a file. It uses Triple DES cipher for the encryption. + * + * Returns: (transfer full): on success, PEM-formatted data suitable for writing + * to a PEM-formatted certificate/private key file. + **/ +GByteArray * +nm_utils_rsa_key_encrypt (const guint8 *data, + gsize len, + const char *in_password, + char **out_password, + GError **error) +{ + char salt[16]; + int salt_len; + char *key = NULL, *enc = NULL, *pw_buf[32]; + gsize key_len = 0, enc_len = 0; + GString *pem = NULL; + char *tmp, *tmp_password = NULL; + int left; + const char *p; + GByteArray *ret = NULL; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (len > 0, NULL); + if (out_password) + g_return_val_if_fail (*out_password == NULL, NULL); + + /* Make the password if needed */ + if (!in_password) { + if (!crypto_randomize (pw_buf, sizeof (pw_buf), error)) + return NULL; + in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1); + } + + salt_len = 8; + if (!crypto_randomize (salt, salt_len, error)) + goto out; + + key = crypto_make_des_aes_key (CIPHER_DES_EDE3_CBC, &salt[0], salt_len, in_password, &key_len, NULL); + if (!key) + g_return_val_if_reached (NULL); + + enc = crypto_encrypt (CIPHER_DES_EDE3_CBC, data, len, salt, salt_len, key, key_len, &enc_len, error); + if (!enc) + goto out; + + pem = g_string_sized_new (enc_len * 2 + 100); + g_string_append (pem, "-----BEGIN RSA PRIVATE KEY-----\n"); + g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n"); /* Convert the salt to a hex string */ tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2); @@ -3762,6 +4300,22 @@ nm_utils_is_uuid (const char *str) static char _nm_utils_inet_ntop_buffer[NM_UTILS_INET_ADDRSTRLEN]; +const char * +nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst) +{ + const char *s; + + nm_assert_addr_family (addr_family); + nm_assert (addr); + + s = inet_ntop (addr_family, + addr, + dst ? dst : _nm_utils_inet_ntop_buffer, + addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN); + nm_assert (s); + return s; +} + /** * nm_utils_inet4_ntop: (skip) * @inaddr: the address that should be converted to string. @@ -4207,91 +4761,531 @@ gssize _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option) if (!_nm_utils_dns_option_validate (option, &option_name, NULL, FALSE, NULL)) return -1; - for (i = 0; i < array->len; i++) { - if (_nm_utils_dns_option_validate (array->pdata[i], &tmp_name, NULL, FALSE, NULL)) { - ret = strcmp (tmp_name, option_name); - g_free (tmp_name); - if (!ret) { - g_free (option_name); - return i; - } - } + for (i = 0; i < array->len; i++) { + if (_nm_utils_dns_option_validate (array->pdata[i], &tmp_name, NULL, FALSE, NULL)) { + ret = strcmp (tmp_name, option_name); + g_free (tmp_name); + if (!ret) { + g_free (option_name); + return i; + } + } + + } + + g_free (option_name); + return -1; +} + +/*****************************************************************************/ + +/** + * nm_utils_enum_to_str: + * @type: the %GType of the enum + * @value: the value to be translated + * + * Converts an enum value to its string representation. If the enum is a + * %G_TYPE_FLAGS the function returns a comma-separated list of matching values. + * If the value has no corresponding string representation, it is converted + * to a number. For enums it is converted to a decimal number, for flags + * to an (unsigned) hex number. + * + * Returns: a newly allocated string or %NULL + * + * Since: 1.2 + */ +char * +nm_utils_enum_to_str (GType type, int value) +{ + return _nm_utils_enum_to_str_full (type, value, ", "); +} + +/** + * nm_utils_enum_from_str: + * @type: the %GType of the enum + * @str: the input string + * @out_value: (out) (allow-none): the output value + * @err_token: (out) (allow-none) (transfer full): location to store the first unrecognized token + * + * Converts a string to the matching enum value. + * + * If the enum is a %G_TYPE_FLAGS the function returns the logical OR of values + * matching the comma-separated tokens in the string; if an unknown token is found + * the function returns %FALSE and stores a pointer to a newly allocated string + * containing the unrecognized token in @err_token. + * + * Returns: %TRUE if the conversion was successful, %FALSE otherwise + * + * Since: 1.2 + */ +gboolean +nm_utils_enum_from_str (GType type, const char *str, + int *out_value, char **err_token) +{ + return _nm_utils_enum_from_str_full (type, str, out_value, err_token, NULL); +} + +/** + * nm_utils_enum_get_values: + * @type: the %GType of the enum + * @from: the first element to be returned + * @to: the last element to be returned + * + * Returns the list of possible values for a given enum. + * + * Returns: (transfer container): a NULL-terminated dynamically-allocated array of static strings + * or %NULL on error + * + * Since: 1.2 + */ +const char **nm_utils_enum_get_values (GType type, gint from, gint to) +{ + return _nm_utils_enum_get_values (type, from, to); +} + +/*****************************************************************************/ + +#if WITH_JANSSON + +/* Added in Jansson v2.3 (released Jan 27 2012) */ +#ifndef json_object_foreach +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_iter_at (object, key) )); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_iter_at (object, key)))) +#endif + +/* Added in Jansson v2.4 (released Sep 23 2012), but travis.ci has v2.2. */ +#ifndef json_boolean +#define json_boolean(val) ((val) ? json_true() : json_false()) +#endif + +/* Added in Jansson v2.5 (released Sep 19 2013), but travis.ci has v2.2. */ +#ifndef json_array_foreach +#define json_array_foreach(array, index, value) \ + for (index = 0; \ + index < json_array_size(array) && (value = json_array_get(array, index)); \ + index++) +#endif + +/* Added in Jansson v2.7 */ +#ifndef json_boolean_value +#define json_boolean_value json_is_true +#endif + +static void +_json_add_object (json_t *json, + const char *key1, + const char *key2, + const char *key3, + json_t *value) +{ + json_t *json_element, *json_link; + + json_element = json_object_get (json, key1); + if (!json_element) { + json_element = value; + if (key2) { + if (key3) { + json_element = json_object (); + json_object_set_new (json_element, key3, value); + } + json_link = json_object (); + json_object_set_new (json_link, key2, json_element); + json_element = json_link; + } + json_object_set_new (json, key1, json_element); + return; + } + + if (!key2) + goto key_already_there; + + json_link = json_element; + json_element = json_object_get (json_element, key2); + if (!json_element) { + json_element = value; + if (key3) { + json_element = json_object (); + json_object_set_new (json_element, key3, value); + } + json_object_set_new (json_link, key2, json_element); + return; + } + + if (!key3) + goto key_already_there; + + json_link = json_element; + json_element = json_object_get (json_element, key3); + if (!json_element) { + json_object_set_new (json_link, key3, value); + return; + } + +key_already_there: + json_decref (value); +} + +/* + * Removes the specified key1[.key2.key3] from json. + * Returns TRUE if json has been modified, FALSE otherwise. */ +static gboolean +_json_del_object (json_t *json, + const char *key1, + const char *key2, + const char *key3) +{ + json_t *json_element = json; + json_t *json_link = NULL; + const char *iter_key = key1; + + if (key2) { + json_link = json; + json_element = json_object_get (json, key1); + if (!json_element) + return FALSE; + iter_key = key2; + } + if (key3) { + json_link = json_element; + json_element = json_object_get (json_element, key2); + if (!json_element) + return FALSE; + iter_key = key3; + } + + if (json_object_del (json_element, iter_key) != 0) + return FALSE; + + /* 1st level key only */ + if (!json_link) + return TRUE; + + if (json_object_size (json_element) == 0) + json_object_del (json_link, (key3 ? key2 : key1)); + + if (key3 && json_object_size (json_link) == 0) + json_object_del (json, key1); + + return TRUE; +} + +/* Adds in place to json the defaults for missing properties; + * the "add_implicit" allows to add to the json also the default + * values used but not shown with teamdctl */ +static void +_json_team_add_defaults (json_t *json, + gboolean port_config, + gboolean add_implicit) +{ + json_t *json_element; + const char *runner = NULL; + + if (port_config) { + _json_add_object (json, "link_watch", "name", NULL, + json_string (NM_TEAM_LINK_WATCHER_ETHTOOL)); + return; + } + + /* Retrieve runner or add default one */ + json_element = json_object_get (json, "runner"); + if (json_element) { + runner = json_string_value (json_object_get (json_element, "name")); + } else { + json_element = json_object (); + json_object_set_new (json, "runner", json_element); + } + if (!runner) { + runner = NM_SETTING_TEAM_RUNNER_DEFAULT; + json_object_set_new (json_element, "name", json_string (runner)); + } + + + if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) { + _json_add_object (json, "notify_peers", "count", NULL, + json_integer (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT)); + _json_add_object (json, "mcast_rejoin", "count", NULL, + json_integer (NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT)); + } else if ( nm_streq (runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) + || nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { + json_element = json_array (); + json_array_append_new (json_element, json_string ("eth")); + json_array_append_new (json_element, json_string ("ipv4")); + json_array_append_new (json_element, json_string ("ipv6")); + _json_add_object (json, "runner", "tx_hash", NULL, json_element); + } + + if (!add_implicit) + return; + + if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) + _json_add_object (json, "runner", "hwaddr_policy", NULL, json_string ("same_all")); + else if (NM_IN_STRSET (runner, + NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NM_SETTING_TEAM_RUNNER_LACP)) { + _json_add_object (json, "runner", "tx_balancer", "balancing_interval", + json_integer (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT)); + if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { + _json_add_object (json, "runner", "active", NULL, json_boolean (TRUE)); + _json_add_object (json, "runner", "sys_prio", NULL, + json_integer (NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT)); + _json_add_object (json, "runner", "min_ports", NULL, json_integer (0)); + _json_add_object (json, "runner", "agg_select_policy", NULL, + json_string (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT)); + } + } +} + +static json_t * +_json_find_object (json_t *json, + const char *key1, + const char *key2, + const char *key3) +{ + json_t *json_element; + + if (!key1) + return NULL; + json_element = json_object_get (json, key1); + if (!key2 || !json_element) + return json_element; + + json_element = json_object_get (json_element, key2); + if (!key3 || !json_element) + return json_element; + + json_element = json_object_get (json_element, key3); + return json_element; +} + +static inline void +_json_delete_object_on_int_match (json_t *json, + const char *key1, + const char *key2, + const char *key3, + int val) +{ + json_t *json_element; + + json_element = _json_find_object (json, key1, key2, key3); + if (!json_element || !json_is_integer (json_element)) + return; + if (json_integer_value (json_element) == val) + _json_del_object (json, key1, key2, key3); +} + +static inline void +_json_delete_object_on_bool_match (json_t *json, + const char *key1, + const char *key2, + const char *key3, + gboolean val) +{ + json_t *json_element; + + json_element = _json_find_object (json, key1, key2, key3); + if (!json_element || !json_is_boolean (json_element)) + return; + if (json_boolean_value (json_element) == val) + _json_del_object (json, key1, key2, key3); +} + +static inline void +_json_delete_object_on_string_match (json_t *json, + const char *key1, + const char *key2, + const char *key3, + const char *val) +{ + json_t *json_element; + + json_element = _json_find_object (json, key1, key2, key3); + if (!json_element || !json_is_string (json_element)) + return; + if (nm_streq0 (json_string_value (json_element), val)) + _json_del_object (json, key1, key2, key3); +} + +static void +_json_team_normalize_defaults (json_t *json, gboolean reset) +{ + json_t *json_element; + const char *runner = NM_SETTING_TEAM_RUNNER_DEFAULT; + int notify_peers_count = 0, notify_peers_interval = 0; + int mcast_rejoin_count = 0, mcast_rejoin_interval = 0; + int runner_tx_balancer_interval = -1; + gboolean runner_active = FALSE, runner_fast_rate = FALSE; + int runner_sys_prio = -1, runner_min_ports = -1; + + json_element = _json_find_object (json, "runner", "name", NULL); + if (json_element) { + runner = json_string_value (json_element); + _json_delete_object_on_string_match (json, "runner", "name", NULL, + NM_SETTING_TEAM_RUNNER_DEFAULT); + } + /* the runner changed: clear all the properties. Then team.config will be saved + * and reloaded triggering the reset of the values through _nm_utils_team_config_get + */ + if (reset) { + _json_del_object (json, "notify_peers", "count", NULL); + _json_del_object (json, "notify_peers", "interval", NULL); + _json_del_object (json, "mcast_rejoin", "count", NULL); + _json_del_object (json, "mcast_rejoin", "interval", NULL); + _json_del_object (json, "runner", "hwaddr_policy", NULL); + _json_del_object (json, "runner", "tx_hash", NULL); + _json_del_object (json, "runner", "tx_balancer", "name"); + _json_del_object (json, "runner", "tx_balancer", "balancing_interval"); + _json_del_object (json, "runner", "active", NULL); + _json_del_object (json, "runner", "fast_rate", NULL); + _json_del_object (json, "runner", "sys_prio", NULL); + _json_del_object (json, "runner", "min_ports", NULL); + _json_del_object (json, "runner", "agg_select_policy", NULL); + return; } - g_free (option_name); - return -1; + if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP)) { + notify_peers_count = 1; + mcast_rejoin_count = 1; + _json_delete_object_on_string_match (json, "runner", "hwaddr_policy", NULL, + NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT); + } else if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { + runner_tx_balancer_interval = 50; + runner_active = TRUE; + runner_sys_prio = 255; + runner_min_ports = 0; + _json_delete_object_on_string_match (json, "runner", "agg_select_policy", NULL, + NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT); + } else if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE)) + runner_tx_balancer_interval = 50; + + _json_delete_object_on_int_match (json, "notify_peers", "count", NULL, notify_peers_count); + _json_delete_object_on_int_match (json, "notify_peers", "interval", NULL, notify_peers_interval); + _json_delete_object_on_int_match (json, "mcast_rejoin", "count", NULL, mcast_rejoin_count); + _json_delete_object_on_int_match (json, "macst_rejoin", "interval", NULL, mcast_rejoin_interval); + _json_delete_object_on_int_match (json, "runner", "tx_balancer", "balancing_interval", + runner_tx_balancer_interval); + _json_delete_object_on_int_match (json, "runner", "sys_prio", NULL, runner_sys_prio); + _json_delete_object_on_int_match (json, "runner", "min_ports", NULL, runner_min_ports); + _json_delete_object_on_bool_match (json, "runner", "active", NULL, runner_active); + _json_delete_object_on_bool_match (json, "runner", "active", NULL, runner_active); + _json_delete_object_on_bool_match (json, "runner", "fast_rate", NULL, runner_fast_rate); } -/*****************************************************************************/ +static NMTeamLinkWatcher * +_nm_utils_team_link_watcher_from_json (json_t *json_element) +{ + const char *j_key; + json_t *j_val; + gs_free char *name = NULL, *target_host = NULL, *source_host = NULL; + int val1 = 0, val2 = 0, val3 = 3; + NMTeamLinkWatcherArpPingFlags flags = 0; + + g_return_val_if_fail (json_element, NULL); + + json_object_foreach (json_element, j_key, j_val) { + if (nm_streq (j_key, "name")) + name = strdup (json_string_value (j_val)); + else if (nm_streq (j_key, "target_host")) + target_host = strdup (json_string_value (j_val)); + else if (nm_streq (j_key, "source_host")) + source_host = strdup (json_string_value (j_val)); + else if (NM_IN_STRSET (j_key, "delay_up", "init_wait")) + val1 = json_integer_value (j_val); + else if (NM_IN_STRSET (j_key, "delay_down", "interval")) + val2 = json_integer_value (j_val); + else if (nm_streq (j_key, "missed_max")) + val3 = json_integer_value (j_val); + else if (nm_streq (j_key, "validate_active")) { + if (json_is_true (j_val)) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE; + } else if (nm_streq (j_key, "validate_inactive")) { + if (json_is_true (j_val)) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE; + } else if (nm_streq (j_key, "send_always")) { + if (json_is_true (j_val)) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS; + } + } -/** - * nm_utils_enum_to_str: - * @type: the %GType of the enum - * @value: the value to be translated - * - * Converts an enum value to its string representation. If the enum is a - * %G_TYPE_FLAGS the function returns a comma-separated list of matching values. - * If the value has no corresponding string representation, it is converted - * to a number. For enums it is converted to a decimal number, for flags - * to an (unsigned) hex number. - * - * Returns: a newly allocated string or %NULL - * - * Since: 1.2 - */ -char * -nm_utils_enum_to_str (GType type, int value) -{ - return _nm_utils_enum_to_str_full (type, value, ", "); + if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) + return nm_team_link_watcher_new_ethtool (val1, val2, NULL); + else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) + return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, NULL); + else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING)) { + return nm_team_link_watcher_new_arp_ping (val1, val2, val3, target_host, + source_host, flags, NULL); + } else + return NULL; } -/** - * nm_utils_enum_from_str: - * @type: the %GType of the enum - * @str: the input string - * @out_value: (out) (allow-none): the output value - * @err_token: (out) (allow-none) (transfer full): location to store the first unrecognized token - * - * Converts a string to the matching enum value. - * - * If the enum is a %G_TYPE_FLAGS the function returns the logical OR of values - * matching the comma-separated tokens in the string; if an unknown token is found - * the function returns %FALSE and stores a pointer to a newly allocated string - * containing the unrecognized token in @err_token. - * - * Returns: %TRUE if the conversion was successful, %FALSE otherwise - * - * Since: 1.2 - */ -gboolean -nm_utils_enum_from_str (GType type, const char *str, - int *out_value, char **err_token) +static json_t * +_nm_utils_team_link_watcher_to_json (NMTeamLinkWatcher *watcher) { - return _nm_utils_enum_from_str_full (type, str, out_value, err_token, NULL); -} + const char *name; + int int_val; + const char *str_val; + NMTeamLinkWatcherArpPingFlags flags = 0; + json_t *json_element; -/** - * nm_utils_enum_get_values: - * @type: the %GType of the enum - * @from: the first element to be returned - * @to: the last element to be returned - * - * Returns the list of possible values for a given enum. - * - * Returns: (transfer container): a NULL-terminated dynamically-allocated array of static strings - * or %NULL on error - * - * Since: 1.2 - */ -const char **nm_utils_enum_get_values (GType type, gint from, gint to) -{ - return _nm_utils_enum_get_values (type, from, to); + g_return_val_if_fail (watcher, NULL); + + json_element = json_object (); + name = nm_team_link_watcher_get_name (watcher); + if (!name) + goto fail; + + json_object_set_new (json_element, "name", json_string (name)); + + if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { + int_val = nm_team_link_watcher_get_delay_up (watcher); + if (int_val) + json_object_set_new (json_element, "delay_up", json_integer (int_val)); + int_val = nm_team_link_watcher_get_delay_down (watcher); + if (int_val) + json_object_set_new (json_element, "delay_down", json_integer (int_val)); + return json_element; + } + + int_val = nm_team_link_watcher_get_init_wait (watcher); + if (int_val) + json_object_set_new (json_element, "init_wait", json_integer (int_val)); + int_val = nm_team_link_watcher_get_interval (watcher); + if (int_val) + json_object_set_new (json_element, "interval", json_integer (int_val)); + int_val = nm_team_link_watcher_get_missed_max (watcher); + if (int_val != 3) + json_object_set_new (json_element, "missed_max", json_integer (int_val)); + str_val = nm_team_link_watcher_get_target_host (watcher); + if (!str_val) + goto fail; + json_object_set_new (json_element, "target_host", json_string (str_val)); + + if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) + return json_element; + + str_val = nm_team_link_watcher_get_source_host (watcher); + if (!str_val) + goto fail; + json_object_set_new (json_element, "source_host", json_string (str_val)); + + flags = nm_team_link_watcher_get_flags (watcher); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE) + json_object_set_new (json_element, "validate_active", json_string ("true")); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE) + json_object_set_new (json_element, "validate_inactive", json_string ("true")); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS) + json_object_set_new (json_element, "send_always", json_string ("true")); + + return json_element; + +fail: + json_decref (json_element); + return NULL; } -/*****************************************************************************/ -#if WITH_JANSSON /** * nm_utils_is_json_object: * @str: the JSON string to test @@ -4360,9 +5354,8 @@ _nm_utils_team_config_equal (const char *conf1, gboolean port_config) { json_t *json1 = NULL, *json2 = NULL, *json; - json_t *array, *name; gs_free char *dump1 = NULL, *dump2 = NULL; - json_t *value, *property; + json_t *value; json_error_t jerror; const char *key; gboolean ret; @@ -4386,33 +5379,8 @@ _nm_utils_team_config_equal (const char *conf1, * configuration. Add them with the default value if necessary, depending * on the configuration type. */ - for (i = 0, json = json1; i < 2; i++, json = json2) { - if (port_config) { - property = json_object_get (json, "link_watch"); - if (!property) { - property = json_object (); - json_object_set_new (property, "name", json_string ("ethtool")); - json_object_set_new (json, "link_watch", property); - } - } else { - property = json_object_get (json, "runner"); - if (!property) { - property = json_object (); - json_object_set_new (property, "name", json_string ("roundrobin")); - json_object_set_new (json, "runner", property); - } else if ( (name = json_object_get (property, "name")) - && NM_IN_STRSET (json_string_value (name), "lacp", "loadbalance")) { - /* Add default tx_hash when missing */ - if (!json_object_get (property, "tx_hash")) { - array = json_array (); - json_array_append_new (array, json_string ("eth")); - json_array_append_new (array, json_string ("ipv4")); - json_array_append_new (array, json_string ("ipv6")); - json_object_set_new (property, "tx_hash", array); - } - } - } - } + for (i = 0, json = json1; i < 2; i++, json = json2) + _json_team_add_defaults (json, port_config, FALSE); /* Only consider a given subset of nodes, others can change depending on * current state */ @@ -4437,6 +5405,239 @@ out: return ret; } + +GValue * +_nm_utils_team_config_get (const char *conf, + const char *key, + const char *key2, + const char *key3, + gboolean port_config) +{ + json_t *json; + json_t *json_element; + GValue *value = NULL; + json_error_t jerror; + + if (!key) + return NULL; + + json = json_loads (conf ?: "{}", JSON_REJECT_DUPLICATES, &jerror); + + /* Invalid json in conf */ + if (!json) + return NULL; + + /* Some properties are added by teamd when missing from the initial + * configuration. Add them with the default value if necessary, depending + * on the configuration type. + * Skip this for port config, as some properties change on the basis of the + * runner specified in the master connection... but we don't want to check + * against properties in another connection. Moreover, for team-port we have + * the link-watchers property only here: and for this compound property it is + * fine to show the default value only if explicitly set. + */ + if (!port_config) + _json_team_add_defaults (json, port_config, TRUE); + + /* Now search the property to retrieve */ + json_element = json_object_get (json, key); + if (json_element && key2) + json_element = json_object_get (json_element, key2); + if (json_element && key3) + json_element = json_object_get (json_element, key3); + + if (json_element) { + value = g_new0 (GValue, 1); + if (json_is_string (json_element)) { + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, json_string_value (json_element)); + } else if (json_is_integer (json_element)) { + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, json_integer_value (json_element)); + } else if (json_is_boolean (json_element)) { + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, json_boolean_value (json_element)); + } else if (nm_streq (key, "link_watch")) { + NMTeamLinkWatcher *watcher; + GPtrArray *data = g_ptr_array_new_with_free_func + ((GDestroyNotify) nm_team_link_watcher_unref); + + if (json_is_array (json_element)) { + json_t *j_watcher; + int index; + + json_array_foreach (json_element, index, j_watcher) { + watcher = _nm_utils_team_link_watcher_from_json (j_watcher); + if (watcher) + g_ptr_array_add (data, watcher); + } + } else { + watcher = _nm_utils_team_link_watcher_from_json (json_element); + if (watcher) + g_ptr_array_add (data, watcher); + } + if (data->len) { + g_value_init (value, G_TYPE_PTR_ARRAY); + g_value_take_boxed (value, data); + } else + g_ptr_array_free (data, TRUE); + + } else if (json_is_array (json_element)) { + GPtrArray *data = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + json_t *str_element; + int index; + + json_array_foreach (json_element, index, str_element) { + if (json_is_string (str_element)) + g_ptr_array_add (data, g_strdup (json_string_value (str_element))); + } + if (data->len) { + g_value_init (value, G_TYPE_STRV); + g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (data)); + } + g_ptr_array_free (data, TRUE); + } else { + g_assert_not_reached (); + g_free (value); + value = NULL; + } + } + + if (json) + json_decref (json); + + return value; +} + +/* if conf is updated in place returns TRUE */ +gboolean +_nm_utils_team_config_set (char **conf, + const char *key, + const char *key2, + const char *key3, + const GValue *value) +{ + json_t *json, *json_element, *json_link, *json_value = NULL; + json_error_t jerror; + gboolean updated = FALSE; + char **strv; + GPtrArray *array; + const char *iter_key = key; + int i; + NMTeamLinkWatcher *watcher; + + g_return_val_if_fail (key, FALSE); + + json = json_loads (*conf?: "{}", JSON_REJECT_DUPLICATES, &jerror); + if (!json) + return FALSE; + + /* no new value? delete element */ + if (!value) { + updated = _json_del_object (json, key, key2, key3); + goto done; + } + + /* insert new value */ + updated = TRUE; + if (G_VALUE_HOLDS_STRING (value)) + json_value = json_string (g_value_get_string (value)); + else if (G_VALUE_HOLDS_INT (value)) + json_value = json_integer (g_value_get_int (value)); + else if (G_VALUE_HOLDS_BOOLEAN (value)) + json_value = json_boolean (g_value_get_boolean (value)); + else if (G_VALUE_HOLDS_BOXED (value)) { + if (nm_streq (key, "link_watch")) { + array = g_value_get_boxed (value); + if (!array || !array->len) { + updated = FALSE; + goto done; + } + + /* + * json_value: will hold the final link_watcher json (array) object + * json_element: is the next link_watcher to append to json_value + * json_link: used to transit the json_value from a single link_watcher + * object to an array of link watcher objects + */ + json_value = NULL; + for (i = 0; i < array->len; i++) { + watcher = array->pdata[i]; + json_element = _nm_utils_team_link_watcher_to_json (watcher); + if (!json_element) + continue; + if (!json_value) { + json_value = json_element; + continue; + } + if (!json_is_array (json_value)) { + json_link = json_value; + json_value = json_array (); + json_array_append_new (json_value, json_link); + } + json_array_append_new (json_value, json_element); + } + } else if ( nm_streq (key, "runner") + && nm_streq0 (key2, "tx_hash")) { + strv = g_value_get_boxed (value); + if (!strv) { + updated = FALSE; + goto done; + } + json_value = json_array (); + for (i = 0; strv[i]; i++) + json_array_append_new (json_value, json_string (strv[i])); + } else { + updated = FALSE; + goto done; + } + } else { /* G_VALUE_HOLDS_? */ + g_assert_not_reached (); + updated = FALSE; + goto done; + } + + /* Simplest case: first level key only */ + json_element = json; + json_link = NULL; + + if (key2) { + json_link = json; + json_element = json_object_get (json, iter_key); + if (!json_element) { + json_element = json_object (); + json_object_set_new (json_link, iter_key, json_element); + } + iter_key = key2; + } + if (key3) { + json_link = json_element; + json_element = json_object_get (json_link, iter_key); + if (!json_element) { + json_element = json_object (); + json_object_set_new (json_link, iter_key, json_element); + } + iter_key = key3; + } + + json_object_set_new (json_element, iter_key, json_value); + +done: + if (updated) { + _json_team_normalize_defaults (json, ( nm_streq0 (key, "runner") + && nm_streq0 (key2, "name"))); + g_free (*conf); + *conf = json_dumps (json, JSON_PRESERVE_ORDER); + /* Don't save an empty config */ + if (nm_streq0 (*conf, "{}")) { + g_free (*conf); + *conf = NULL; + } + } + json_decref (json); + return updated; +} + #else /* WITH_JANSSON */ gboolean @@ -4491,8 +5692,224 @@ _nm_utils_team_config_equal (const char *conf1, { return nm_streq0 (conf1, conf2); } + +GValue * +_nm_utils_team_config_get (const char *conf, + const char *key, + const char *key2, + const char *key3, + gboolean port_config) +{ + return NULL; +} + +gboolean +_nm_utils_team_config_set (char **conf, + const char *key, + const char *key2, + const char *key3, + const GValue *value) +{ + return FALSE; +} #endif +/** + * _nm_utils_team_link_watchers_to_variant: + * @link_watchers: (element-type NMTeamLinkWatcher): array of #NMTeamLinkWatcher + * + * Utility function to convert a #GPtrArray of #NMTeamLinkWatcher objects + * representing link watcher configuration for team devices into a #GVariant + * of type 'aa{sv}' representing an array of link watchers. + * + * Returns: (transfer none): a new floating #GVariant representing link watchers. + **/ +GVariant * +_nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers) +{ + GVariantBuilder builder; + int i; + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); + + if (!link_watchers) + goto end; + + for (i = 0; i < link_watchers->len; i++) { + NMTeamLinkWatcher *watcher = link_watchers->pdata[i]; + GVariantBuilder watcher_builder; + const char *name; + int int_val; + NMTeamLinkWatcherArpPingFlags flags; + + g_variant_builder_init (&watcher_builder, G_VARIANT_TYPE ("a{sv}")); + + name = nm_team_link_watcher_get_name (watcher); + g_variant_builder_add (&watcher_builder, "{sv}", + "name", + g_variant_new_string (name)); + + if nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL) { + int_val = nm_team_link_watcher_get_delay_up (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "delay-up", + g_variant_new_int32 (int_val)); + } + int_val = nm_team_link_watcher_get_delay_down (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "delay-down", + g_variant_new_int32 (int_val)); + } + g_variant_builder_add (&builder, "a{sv}", &watcher_builder); + continue; + } + + /* Common properties for arp_ping and nsna_ping link watchers */ + int_val = nm_team_link_watcher_get_init_wait (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "init-wait", + g_variant_new_int32 (int_val)); + } + int_val = nm_team_link_watcher_get_interval (watcher); + if (int_val) { + g_variant_builder_add (&watcher_builder, "{sv}", + "interval", + g_variant_new_int32 (int_val)); + } + int_val = nm_team_link_watcher_get_missed_max (watcher); + if (int_val != 3) { + g_variant_builder_add (&watcher_builder, "{sv}", + "missed-max", + g_variant_new_int32 (int_val)); + } + g_variant_builder_add (&watcher_builder, "{sv}", + "target-host", + g_variant_new_string (nm_team_link_watcher_get_target_host (watcher))); + + if nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING) { + g_variant_builder_add (&builder, "a{sv}", &watcher_builder); + continue; + } + + /* arp_ping watcher only */ + g_variant_builder_add (&watcher_builder, "{sv}", + "source-host", + g_variant_new_string (nm_team_link_watcher_get_source_host (watcher))); + flags = nm_team_link_watcher_get_flags (watcher); + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE) { + g_variant_builder_add (&watcher_builder, "{sv}", + "validate-active", + g_variant_new_boolean (TRUE)); + } + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE) { + g_variant_builder_add (&watcher_builder, "{sv}", + "validate-inactive", + g_variant_new_boolean (TRUE)); + } + if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS) { + g_variant_builder_add (&watcher_builder, "{sv}", + "send-always", + g_variant_new_boolean (TRUE)); + } + g_variant_builder_add (&builder, "a{sv}", &watcher_builder); + } +end: + return g_variant_builder_end (&builder); +} + +/** + * _nm_utils_team_link_watchers_from_variant: + * @value: a #GVariant of type 'aa{sv}' + * + * Utility function to convert a #GVariant representing a list of team link + * watchers int a #GPtrArray of #NMTeamLinkWatcher objects. + * + * Returns: (transfer full) (element-type NMTeamLinkWatcher): a newly allocated + * #GPtrArray of #NMTeamLinkWatcher objects. + **/ +GPtrArray * +_nm_utils_team_link_watchers_from_variant (GVariant *value) +{ + GPtrArray *link_watchers; + GVariantIter iter; + GVariant *watcher_var; + + g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), NULL); + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_variant_iter_init (&iter, value); + + while (g_variant_iter_next (&iter, "@a{sv}", &watcher_var)) { + NMTeamLinkWatcher *watcher; + const char *name; + int val1, val2, val3 = 0; + const char *target_host = NULL, *source_host = NULL; + gboolean bval; + NMTeamLinkWatcherArpPingFlags flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE; + GError *error = NULL; + + if (!g_variant_lookup (watcher_var, "name", "&s", &name)) + goto next; + if (!NM_IN_STRSET (name, + NM_TEAM_LINK_WATCHER_ETHTOOL, + NM_TEAM_LINK_WATCHER_ARP_PING, + NM_TEAM_LINK_WATCHER_NSNA_PING)) { + goto next; + } + + if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) { + if (!g_variant_lookup (watcher_var, "delay-up", "i", &val1)) + val1 = 0; + if (!g_variant_lookup (watcher_var, "delay-down", "i", &val2)) + val2 = 0; + watcher = nm_team_link_watcher_new_ethtool (val1, val2, &error); + } else { + if (!g_variant_lookup (watcher_var, "target-host", "&s", &target_host)) + goto next; + if (!g_variant_lookup (watcher_var, "init_wait", "i", &val1)) + val1 = 0; + if (!g_variant_lookup (watcher_var, "interval", "i", &val2)) + val2 = 0; + if (!g_variant_lookup (watcher_var, "missed-max", "i", &val3)) + val3 = 3; + if nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING) { + if (!g_variant_lookup (watcher_var, "source-host", "&s", &source_host)) + goto next; + if (!g_variant_lookup (watcher_var, "validate-active", "b", &bval)) + bval = FALSE; + if (bval) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE; + if (!g_variant_lookup (watcher_var, "validate-inactive", "b", &bval)) + bval = FALSE; + if (bval) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE; + if (!g_variant_lookup (watcher_var, "send-always", "b", &bval)) + bval = FALSE; + if (bval) + flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS; + watcher = nm_team_link_watcher_new_arp_ping (val1, val2, val3, + target_host, source_host, + flags, &error); + } else + watcher = nm_team_link_watcher_new_nsna_ping (val1, val2, val3, + target_host, &error); + } + if (!watcher) { + g_clear_error (&error); + goto next; + } + + g_ptr_array_add (link_watchers, watcher); +next: + g_variant_unref (watcher_var); + } + + return link_watchers; +} + static char * attribute_escape (const char *src, char c1, char c2) { @@ -4574,8 +5991,10 @@ nm_utils_parse_variant_attributes (const char *string, if (*ptr == '\\') { ptr++; if (!*ptr) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - _("unterminated escape sequence")); + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("unterminated escape sequence")); return NULL; } goto next; @@ -4592,8 +6011,10 @@ nm_utils_parse_variant_attributes (const char *string, if (*sep == '\\') { sep++; if (!*sep) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - _("unterminated escape sequence")); + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("unterminated escape sequence")); return NULL; } } @@ -4601,18 +6022,16 @@ nm_utils_parse_variant_attributes (const char *string, break; } - if (*sep != key_value_separator) { - g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - _("missing key-value separator '%c'"), key_value_separator); - return NULL; - } - name = attribute_unescape (start, sep); - value = attribute_unescape (sep + 1, ptr); for (s = spec; *s; s++) { + if (g_hash_table_contains (ht, (*s)->name)) + continue; if (nm_streq (name, (*s)->name)) break; + if ( (*s)->no_value + && g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_STRING)) + break; } if (!*s) { @@ -4625,12 +6044,33 @@ nm_utils_parse_variant_attributes (const char *string, } } + if ((*s)->no_value) { + if ((*s)->consumes_rest) { + value = g_strdup (start); + ptr = strchr (start, '\0'); + } else { + value = g_steal_pointer (&name); + } + } else { + if (*sep != key_value_separator) { + g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, + _("missing key-value separator '%c' after '%s'"), key_value_separator, name); + return NULL; + } + + /* The attribute and key/value separators are the same. Look for the next one. */ + if (ptr == sep) + goto next; + + value = attribute_unescape (sep + 1, ptr); + } + if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_UINT32)) { gint64 num = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1); if (num == -1) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - _("invalid uint32 value '%s' for attribute '%s'"), value, name); + _("invalid uint32 value '%s' for attribute '%s'"), value, (*s)->name); return NULL; } variant = g_variant_new_uint32 (num); @@ -4639,33 +6079,32 @@ nm_utils_parse_variant_attributes (const char *string, if (num == -1) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - _("invalid uint8 value '%s' for attribute '%s'"), value, name); + _("invalid uint8 value '%s' for attribute '%s'"), value, (*s)->name); return NULL; } variant = g_variant_new_byte ((guchar) num); } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_BOOLEAN)) { - gboolean b; + int b; - if (nm_streq (value, "true")) - b = TRUE; - else if (nm_streq (value, "false")) - b = FALSE; - else { + b = (*s)->no_value ? TRUE :_nm_utils_ascii_str_to_bool (value, -1); + if (b == -1) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - _("invalid boolean value '%s' for attribute '%s'"), value, name); + _("invalid boolean value '%s' for attribute '%s'"), value, (*s)->name); return NULL; } variant = g_variant_new_boolean (b); } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_STRING)) { variant = g_variant_new_take_string (g_steal_pointer (&value)); + } else if (g_variant_type_equal ((*s)->type, G_VARIANT_TYPE_BYTESTRING)) { + variant = g_variant_new_bytestring (value); } else { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, - _("unsupported attribute '%s' of type '%s'"), name, + _("unsupported attribute '%s' of type '%s'"), (*s)->name, (char *) (*s)->type); return NULL; } - g_hash_table_insert (ht, g_steal_pointer (&name), variant); + g_hash_table_insert (ht, g_strdup ((*s)->name), variant); start = NULL; } next: @@ -4726,6 +6165,8 @@ nm_utils_format_variant_attributes (GHashTable *attributes, value = g_variant_get_boolean (variant) ? "true" : "false"; else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) value = g_variant_get_string (variant, NULL); + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTESTRING)) + value = g_variant_get_bytestring (variant); else continue; diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h index 77fe18a9..df9284b3 100644 --- a/libnm-core/nm-utils.h +++ b/libnm-core/nm-utils.h @@ -15,7 +15,7 @@ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * - * Copyright 2005 - 2014 Red Hat, Inc. + * Copyright 2005 - 2017 Red Hat, Inc. */ #ifndef __NM_UTILS_H__ @@ -35,6 +35,7 @@ #include "nm-core-enum-types.h" #include "nm-setting-wireless-security.h" +#include "nm-setting-tc-config.h" G_BEGIN_DECLS @@ -230,6 +231,23 @@ char * nm_utils_format_variant_attributes (GHashTable *attributes, char attr_separator, char key_value_separator); +/*****************************************************************************/ + +NM_AVAILABLE_IN_1_10_2 +NMTCQdisc *nm_utils_tc_qdisc_from_str (const char *str, GError **error); +NM_AVAILABLE_IN_1_10_2 +char *nm_utils_tc_qdisc_to_str (NMTCQdisc *qdisc, GError **error); + +NM_AVAILABLE_IN_1_10_2 +NMTCAction *nm_utils_tc_action_from_str (const char *str, GError **error); +NM_AVAILABLE_IN_1_10_2 +char *nm_utils_tc_action_to_str (NMTCAction *action, GError **error); + +NM_AVAILABLE_IN_1_10_2 +NMTCTfilter *nm_utils_tc_tfilter_from_str (const char *str, GError **error); +NM_AVAILABLE_IN_1_10_2 +char *nm_utils_tc_tfilter_to_str (NMTCTfilter *tfilter, GError **error); + G_END_DECLS #endif /* __NM_UTILS_H__ */ diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h index f6bfaf72..61bfa5b1 100644 --- a/libnm-core/nm-version.h +++ b/libnm-core/nm-version.h @@ -146,4 +146,10 @@ # define NM_AVAILABLE_IN_1_10 #endif +#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_10_2 +# define NM_AVAILABLE_IN_1_10_2 G_UNAVAILABLE(1,10.2) +#else +# define NM_AVAILABLE_IN_1_10_2 +#endif + #endif /* NM_VERSION_H */ diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 188cb28e..d743fd22 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -330,7 +330,7 @@ test_c_list_sort (void) if (headless) { lst = head.next; - c_list_unlink (&head); + c_list_unlink_stale (&head); lst = c_list_sort_headless (lst, _c_list_sort_cmp, NULL); g_assert (lst); g_assert (lst->next); diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c index 2dc929af..313932f0 100644 --- a/libnm-core/tests/test-keyfile.c +++ b/libnm-core/tests/test-keyfile.c @@ -99,6 +99,11 @@ test_encode_key (void) do_test_encode_key_bijection (kf, " ", "\\20 \\20"); do_test_encode_key_decode_surjection (kf, "f\\20c", "f c"); do_test_encode_key_decode_surjection (kf, "\\20\\20\\20", "\\20 \\20"); + + do_test_encode_key_bijection (kf, "\t", "\\09"); + do_test_encode_key_bijection (kf, "\t=x", "\\09\\3Dx"); + do_test_encode_key_bijection (kf, "(nm-openvpn-auth-dialog:10283): GdkPixbuf-DEBUG: \tCopy pixels == false", + "(nm-openvpn-auth-dialog:10283): GdkPixbuf-DEBUG: \\09Copy pixels \\3D\\3D false"); } /*****************************************************************************/ diff --git a/libnm-core/tests/test-setting-8021x.c b/libnm-core/tests/test-setting-8021x.c deleted file mode 100644 index 268a9f52..00000000 --- a/libnm-core/tests/test-setting-8021x.c +++ /dev/null @@ -1,404 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2008 - 2011 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include - -#include "nm-utils.h" -#include "nm-setting-connection.h" -#include "nm-setting-8021x.h" - -#include "nm-utils/nm-test-utils.h" - -static void -compare_blob_data (const char *test, - const char *key_path, - GBytes *key) -{ - char *contents = NULL; - gsize len = 0; - GError *error = NULL; - gboolean success; - - g_assert (key && g_bytes_get_size (key) > 0); - - success = g_file_get_contents (key_path, &contents, &len, &error); - nmtst_assert_success (success, error); - - g_assert_cmpmem (contents, len, g_bytes_get_data (key, NULL), g_bytes_get_size (key)); - - g_free (contents); -} - -static void -check_scheme_path (GBytes *value, const char *path) -{ - const guint8 *p; - - g_assert (value); - - p = g_bytes_get_data (value, NULL); - g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0); - p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); - g_assert (memcmp (p, path, strlen (path)) == 0); - p += strlen (path); - g_assert (*p == '\0'); -} - -static void -test_private_key_import (const char *path, - const char *password, - NMSetting8021xCKScheme scheme) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - NMSetting8021xCKFormat tmp_fmt; - GError *error = NULL; - GBytes *tmp_key = NULL, *client_cert = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_private_key (s_8021x, - path, - password, - scheme, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - tmp_fmt = nm_setting_802_1x_get_private_key_format (s_8021x); - g_assert (tmp_fmt == format); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (pw != NULL); - g_assert_cmpstr (pw, ==, password); - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - tmp_key = nm_setting_802_1x_get_private_key_blob (s_8021x); - compare_blob_data ("private-key-import", path, tmp_key); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { - g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); - check_scheme_path (tmp_key, path); - g_bytes_unref (tmp_key); - } else - g_assert_not_reached (); - - /* If it's PKCS#12 ensure the client cert is the same value */ - if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); - g_assert (tmp_key); - - g_object_get (s_8021x, NM_SETTING_802_1X_CLIENT_CERT, &client_cert, NULL); - g_assert (client_cert); - - /* make sure they are the same */ - g_assert (g_bytes_equal (tmp_key, client_cert)); - - g_bytes_unref (tmp_key); - g_bytes_unref (client_cert); - } - - g_object_unref (s_8021x); -} - -static void -test_phase2_private_key_import (const char *path, - const char *password, - NMSetting8021xCKScheme scheme) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - NMSetting8021xCKFormat tmp_fmt; - GError *error = NULL; - GBytes *tmp_key = NULL, *client_cert = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - path, - password, - scheme, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - tmp_fmt = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); - g_assert (tmp_fmt == format); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { - tmp_key = nm_setting_802_1x_get_phase2_private_key_blob (s_8021x); - compare_blob_data ("phase2-private-key-import", path, tmp_key); - } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { - g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); - check_scheme_path (tmp_key, path); - g_bytes_unref (tmp_key); - } else - g_assert_not_reached (); - - /* If it's PKCS#12 ensure the client cert is the same value */ - if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { - g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); - g_assert (tmp_key); - - g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &client_cert, NULL); - g_assert (client_cert); - - /* make sure they are the same */ - g_assert (g_bytes_equal (tmp_key, client_cert)); - - g_bytes_unref (tmp_key); - g_bytes_unref (client_cert); - } - - g_object_unref (s_8021x); -} - -static void -test_wrong_password_keeps_data (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Now try to set it to something that's not a certificate */ - format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - success = nm_setting_802_1x_set_private_key (s_8021x, - "Makefile.am", - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_no_success (success, error); - g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - g_clear_error (&error); - - /* Make sure the password hasn't changed */ - pw = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - g_object_unref (s_8021x); -} - -static void -test_clear_private_key (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - /* Now clear it */ - success = nm_setting_802_1x_set_private_key (s_8021x, - NULL, - NULL, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - NULL, - &error); - nmtst_assert_success (success, error); - - /* Ensure the password is also now clear */ - g_assert (!nm_setting_802_1x_get_private_key_password (s_8021x)); - - g_object_unref (s_8021x); -} - -static void -test_wrong_phase2_password_keeps_data (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Now try to set it to something that's not a certificate */ - format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - "Makefile.am", - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_no_success (success, error); - g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - g_clear_error (&error); - - /* Make sure the password hasn't changed */ - pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - g_object_unref (s_8021x); -} - -static void -test_clear_phase2_private_key (const char *path, const char *password) -{ - NMSetting8021x *s_8021x; - gboolean success; - NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; - GError *error = NULL; - const char *pw; - - s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); - g_assert (s_8021x); - - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - path, - password, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - &format, - &error); - nmtst_assert_success (success, error); - g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); - - /* Make sure the password is what we expect */ - pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); - g_assert (pw); - g_assert_cmpstr (pw, ==, password); - - /* Now clear it */ - success = nm_setting_802_1x_set_phase2_private_key (s_8021x, - NULL, - NULL, - NM_SETTING_802_1X_CK_SCHEME_BLOB, - NULL, - &error); - nmtst_assert_success (success, error); - - /* Ensure the password is also now clear */ - g_assert (!nm_setting_802_1x_get_phase2_private_key_password (s_8021x)); - - g_object_unref (s_8021x); -} - -static void -do_8021x_test (gconstpointer test_data) -{ - char **parts, *path, *password; - - parts = g_strsplit ((const char *) test_data, ", ", -1); - g_assert_cmpint (g_strv_length (parts), ==, 2); - - path = g_build_filename (TEST_CERT_DIR, parts[0], NULL); - password = parts[1]; - - /* Test phase1 and phase2 path scheme */ - test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); - test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); - - /* Test phase1 and phase2 blob scheme */ - test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); - test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); - - /* Test that using a wrong password does not change existing data */ - test_wrong_password_keeps_data (path, password); - test_wrong_phase2_password_keeps_data (path, password); - - /* Test clearing the private key */ - test_clear_private_key (path, password); - test_clear_phase2_private_key (path, password); - - g_free (path); - g_strfreev (parts); -} - -NMTST_DEFINE (); - -int -main (int argc, char **argv) -{ - nmtst_init (&argc, &argv, TRUE); - - g_test_add_data_func ("/libnm/setting-8021x/key-and-cert", - "test_key_and_cert.pem, test", - do_8021x_test); - g_test_add_data_func ("/libnm/setting-8021x/key-only", - "test-key-only.pem, test", - do_8021x_test); - g_test_add_data_func ("/libnm/setting-8021x/pkcs8-enc-key", - "pkcs8-enc-key.pem, 1234567890", - do_8021x_test); - g_test_add_data_func ("/libnm/setting-8021x/pkcs12", - "test-cert.p12, test", - do_8021x_test); - - return g_test_run (); -} - diff --git a/libnm-core/tests/test-setting-bond.c b/libnm-core/tests/test-setting-bond.c deleted file mode 100644 index e6a65bba..00000000 --- a/libnm-core/tests/test-setting-bond.c +++ /dev/null @@ -1,247 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2016 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-utils.h" -#include "nm-setting-bond.h" -#include "nm-connection.h" -#include "nm-simple-connection.h" -#include "nm-setting-connection.h" -#include "nm-errors.h" - -#include "nm-utils/nm-test-utils.h" - -static void -create_bond_connection (NMConnection **con, NMSettingBond **s_bond) -{ - NMSettingConnection *s_con; - - g_assert (con); - g_assert (s_bond); - - *con = nmtst_create_minimal_connection ("bond", - NULL, - NM_SETTING_BOND_SETTING_NAME, - &s_con); - g_assert (*con); - g_assert (s_con); - - g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "bond0", NULL); - - *s_bond = (NMSettingBond *) nm_setting_bond_new (); - g_assert (*s_bond); - - nm_connection_add_setting (*con, NM_SETTING (*s_bond)); -} - -#define test_verify_options(exp, ...) \ - G_STMT_START { \ - const char *__opts[] = { __VA_ARGS__ , NULL }; \ - \ - _test_verify_options (__opts, exp); \ - } G_STMT_END - -static void -_test_verify_options (const char **options, gboolean expected_result) -{ - gs_unref_object NMConnection *con = NULL; - NMSettingBond *s_bond; - GError *error = NULL; - gboolean success; - const char **option; - - create_bond_connection (&con, &s_bond); - - for (option = options; option[0] && option[1]; option += 2) - g_assert (nm_setting_bond_add_option (s_bond, option[0], option[1])); - - if (expected_result) { - nmtst_assert_connection_verifies_and_normalizable (con); - nmtst_connection_normalize (con); - success = nm_setting_verify ((NMSetting *) s_bond, con, &error); - nmtst_assert_success (success, error); - } else { - nmtst_assert_connection_unnormalizable (con, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY); - } -} - -static void -test_verify (void) -{ - test_verify_options (TRUE, - "mode", "3", - "arp_interval", "0"); - test_verify_options (FALSE, - /* arp_interval not supported in balance-alb mode */ - "mode", "balance-alb", - "arp_interval", "1", - "arp_ip_target", "1.2.3.4"); - test_verify_options (FALSE, - /* arp_ip_target requires arp_interval */ - "mode", "balance-rr", - "arp_ip_target", "1.2.3.4"); - test_verify_options (TRUE, - "mode", "balance-rr", - "arp_interval", "1", - "arp_ip_target", "1.2.3.4"); - test_verify_options (FALSE, - /* num_grat_arp, num_unsol_na cannot be different */ - "mode", "balance-rr", - "num_grat_arp", "3", - "num_unsol_na", "4"); - test_verify_options (TRUE, - "mode", "balance-rr", - "num_grat_arp", "5", - "num_unsol_na", "5"); - test_verify_options (TRUE, - "mode", "active-backup", - "primary", "eth0"); - test_verify_options (FALSE, - /* primary requires mode=active-backup */ - "mode", "802.3ad", - "primary", "eth0"); - test_verify_options (TRUE, - "mode", "802.3ad", - "lacp_rate", "fast"); - test_verify_options (FALSE, - /* lacp_rate=fast requires mode=802.3ad */ - "mode", "balance-rr", - "lacp_rate", "fast"); - test_verify_options (TRUE, - "mode", "802.3ad", - "ad_actor_system", "ae:00:11:33:44:55"); -} - -static void -test_compare_options (gboolean exp_res, const char **opts1, const char **opts2) -{ - gs_unref_object NMSettingBond *s_bond1 = NULL, *s_bond2 = NULL; - const char **p; - - s_bond1 = (NMSettingBond *) nm_setting_bond_new (); - g_assert (s_bond1); - s_bond2 = (NMSettingBond *) nm_setting_bond_new (); - g_assert (s_bond2); - - for (p = opts1; p[0] && p[1]; p += 2) - g_assert (nm_setting_bond_add_option (s_bond1, p[0], p[1])); - - for (p = opts2; p[0] && p[1]; p += 2) - g_assert (nm_setting_bond_add_option (s_bond2, p[0], p[1])); - - g_assert_cmpint (nm_setting_compare ((NMSetting *) s_bond1, - (NMSetting *) s_bond2, - NM_SETTING_COMPARE_FLAG_EXACT), - ==, - exp_res); -} - -static void -test_compare (void) -{ - test_compare_options (TRUE, - ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), - ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL })); - test_compare_options (FALSE, - ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), - ((const char *[]){ "mode", "balance-rr", "miimon", "2", NULL })); - - /* ignore default values */ - test_compare_options (TRUE, - ((const char *[]){ "miimon", "1", NULL }), - ((const char *[]){ "miimon", "1", "updelay", "0", NULL })); - - /* special handling of num_grat_arp, num_unsol_na */ - test_compare_options (FALSE, - ((const char *[]){ "num_grat_arp", "2", NULL }), - ((const char *[]){ "num_grat_arp", "1", NULL })); - test_compare_options (TRUE, - ((const char *[]){ "num_grat_arp", "3", NULL }), - ((const char *[]){ "num_unsol_na", "3", NULL })); - test_compare_options (TRUE, - ((const char *[]){ "num_grat_arp", "4", NULL }), - ((const char *[]){ "num_unsol_na", "4", "num_grat_arp", "4", NULL })); -} - -static void -test_normalize_options (const char **opts1, const char **opts2) -{ - gs_unref_object NMConnection *con = NULL; - NMSettingBond *s_bond; - GError *error = NULL; - gboolean success; - const char **p; - int num = 0; - - create_bond_connection (&con, &s_bond); - - for (p = opts1; p[0] && p[1]; p += 2) - g_assert (nm_setting_bond_add_option (s_bond, p[0], p[1])); - - nmtst_assert_connection_verifies_and_normalizable (con); - nmtst_connection_normalize (con); - success = nm_setting_verify ((NMSetting *) s_bond, con, &error); - nmtst_assert_success (success, error); - - for (p = opts2; p[0] && p[1]; p += 2) { - g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, p[0]), ==, p[1]); - num++; - } - - g_assert_cmpint (num, ==, nm_setting_bond_get_num_options (s_bond)); -} - -static void -test_normalize (void) -{ - test_normalize_options ( - ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL }), - ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "1", "miimon", "1", NULL }), - ((const char *[]){ "mode", "active-backup", "miimon", "1", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "balance-alb", "tlb_dynamic_lb", "1", NULL }), - ((const char *[]){ "mode", "balance-alb", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL }), - ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL })); - test_normalize_options ( - ((const char *[]){ "mode", "balance-rr", "ad_actor_sys_prio", "4", "packets_per_slave", "3", NULL }), - ((const char *[]){ "mode", "balance-rr", "packets_per_slave", "3", NULL })); -} - -#define TPATH "/libnm/settings/bond/" - -NMTST_DEFINE (); - -int -main (int argc, char **argv) -{ - nmtst_init (&argc, &argv, TRUE); - - g_test_add_func (TPATH "verify", test_verify); - g_test_add_func (TPATH "compare", test_compare); - g_test_add_func (TPATH "normalize", test_normalize); - - return g_test_run (); -} diff --git a/libnm-core/tests/test-setting-dcb.c b/libnm-core/tests/test-setting-dcb.c deleted file mode 100644 index 55c1c516..00000000 --- a/libnm-core/tests/test-setting-dcb.c +++ /dev/null @@ -1,321 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright 2013 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include - -#include "nm-utils.h" -#include "nm-setting-dcb.h" -#include "nm-connection.h" -#include "nm-errors.h" - -#include "nm-utils/nm-test-utils.h" - -#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \ - NM_SETTING_DCB_FLAG_ADVERTISE | \ - NM_SETTING_DCB_FLAG_WILLING) - -static void -test_dcb_flags_valid (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - guint i; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, 0); - g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, 0); - - g_object_set (G_OBJECT (s_dcb), - NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_APP_ISCSI_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_APP_FIP_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL, - NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL, - NULL); - /* Priority Group Bandwidth must total 100% */ - for (i = 0; i < 7; i++) - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 12); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); - - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); - g_assert_no_error (error); - g_assert (success); - - g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, DCB_FLAGS_ALL); - g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, DCB_FLAGS_ALL); -} - -#define TEST_FLAG(p, f, v) \ -{ \ - /* GObject property min/max should ensure the property does not get set to \ - * the invalid value, so we ensure the value we just tried to set is 0 and \ - * that verify is successful since the property never got set. \ - */ \ - g_object_set (G_OBJECT (s_dcb), p, v, NULL); \ - g_assert_cmpint (f (s_dcb), ==, 0); \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_no_error (error); \ - g_assert (success); \ -} - -static void -test_dcb_flags_invalid (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32); - g_test_assert_expected_messages (); - - g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); - TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags, - (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1); - g_test_assert_expected_messages (); -} - -#define TEST_APP_PRIORITY(lcprop, ucprop, v) \ -{ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ - \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, v, NULL); \ - g_assert_cmpint (nm_setting_dcb_get_app_##lcprop##_priority (s_dcb), ==, v); \ - \ - /* Assert that the setting is invalid while the app is disabled unless v is default */ \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - if (v >= 0) { \ - g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ - g_assert (success == FALSE); \ - } else { \ - g_assert_no_error (error); \ - g_assert (success); \ - } \ - g_clear_error (&error); \ - \ - /* Set the enable flag and re-verify, this time it should be valid */ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_no_error (error); \ - g_assert (success); \ - \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, 0, NULL); \ -} - -static void -test_dcb_app_priorities (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - /* Defaults */ - g_assert_cmpint (nm_setting_dcb_get_app_fcoe_priority (s_dcb), ==, -1); - g_assert_cmpint (nm_setting_dcb_get_app_iscsi_priority (s_dcb), ==, -1); - g_assert_cmpint (nm_setting_dcb_get_app_fip_priority (s_dcb), ==, -1); - - TEST_APP_PRIORITY (fcoe, FCOE, 6); - TEST_APP_PRIORITY (iscsi, ISCSI, 5); - TEST_APP_PRIORITY (fip, FIP, 4); - - TEST_APP_PRIORITY (fcoe, FCOE, -1); - TEST_APP_PRIORITY (iscsi, ISCSI, -1); - TEST_APP_PRIORITY (fip, FIP, -1); -} - -#define TEST_PRIORITY_VALID(fn, id, val, flagsprop, verify) \ -{ \ - /* Assert that setting the value gets the same value back out */ \ - nm_setting_dcb_set_priority_##fn (s_dcb, id, val); \ - g_assert_cmpint (nm_setting_dcb_get_priority_##fn (s_dcb, id), ==, val); \ - \ - if (verify) { \ - if (val != 0) { \ - /* Assert that verify fails because the flags do not include 'enabled' \ - * and a value has been set. \ - */ \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ - g_assert (success == FALSE); \ - g_clear_error (&error); \ - } \ - \ - /* Assert that adding the 'enabled' flag verifies the setting */ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ - g_assert_no_error (error); \ - g_assert (success); \ - } \ - \ - /* Reset everything */ \ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ - nm_setting_dcb_set_priority_##fn (s_dcb, id, 0); \ -} - -/* If Priority Groups are enabled, PG bandwidth must equal 100% */ -#define SET_VALID_PRIORITY_GROUP_BANDWIDTH \ -{ \ - guint x; \ - for (x = 0; x < 7; x++) \ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, x, 12); \ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); \ -} - -static void -test_dcb_priorities_valid (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - guint i; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - for (i = 0; i < 8; i++) - TEST_PRIORITY_VALID (flow_control, i, TRUE, FLOW_CONTROL, TRUE); - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) { - TEST_PRIORITY_VALID (group_id, i, i, GROUP, TRUE); - TEST_PRIORITY_VALID (group_id, i, 7 - i, GROUP, TRUE); - } - - /* Clear PG bandwidth from earlier tests */ - for (i = 0; i < 8; i++) - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 0); - - /* Priority Group Bandwidth must add up to 100% if enabled, which requires - * some dancing for verifying individual values here. - */ - for (i = 0; i < 8; i++) { - guint other = 7 - (i % 8); - - /* Set another priority group to the remaining bandwidth */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - i); - TEST_PRIORITY_VALID (group_bandwidth, i, i, GROUP, TRUE); - - /* Set another priority group to the remaining bandwidth */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - (7 - i)); - TEST_PRIORITY_VALID (group_bandwidth, i, 7 - i, GROUP, TRUE); - - /* Clear remaining bandwidth */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 0); - } - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) { - TEST_PRIORITY_VALID (bandwidth, i, i, GROUP, TRUE); - TEST_PRIORITY_VALID (bandwidth, i, 7 - i, GROUP, TRUE); - } - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) - TEST_PRIORITY_VALID (strict_bandwidth, i, TRUE, GROUP, TRUE); - - SET_VALID_PRIORITY_GROUP_BANDWIDTH - for (i = 0; i < 8; i++) { - TEST_PRIORITY_VALID (traffic_class, i, i, GROUP, TRUE); - TEST_PRIORITY_VALID (traffic_class, i, 7 - i, GROUP, TRUE); - } -} - -static void -test_dcb_bandwidth_sums (void) -{ - gs_unref_object NMSettingDcb *s_dcb = NULL; - GError *error = NULL; - gboolean success; - - s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); - g_assert (s_dcb); - - /* Assert that setting the value gets the same value back out */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 0, 9); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 1, 10); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 2, 11); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 3, 12); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 13); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 5, 14); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 6, 15); - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); - - /* Assert verify success when sums total 100% */ - g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); - g_assert_no_error (error); - g_assert (success); - - /* Assert verify fails when sums do not total 100% */ - nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 20); - success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); - g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); - g_assert (success == FALSE); - g_clear_error (&error); -} - -#define TPATH "/libnm/settings/dcb/" - -NMTST_DEFINE (); - -int -main (int argc, char **argv) -{ - nmtst_init (&argc, &argv, TRUE); - - g_test_add_func (TPATH "flags-valid", test_dcb_flags_valid); - g_test_add_func (TPATH "flags-invalid", test_dcb_flags_invalid); - g_test_add_func (TPATH "app-priorities", test_dcb_app_priorities); - g_test_add_func (TPATH "priorities", test_dcb_priorities_valid); - g_test_add_func (TPATH "bandwidth-sums", test_dcb_bandwidth_sums); - - return g_test_run (); -} - diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c new file mode 100644 index 00000000..efdcee25 --- /dev/null +++ b/libnm-core/tests/test-setting.c @@ -0,0 +1,1632 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2008 - 2017 Red Hat, Inc. + * + */ + +#include "nm-default.h" + +#include +#include + +#include "nm-utils.h" +#include "nm-setting-8021x.h" +#include "nm-setting-bond.h" +#include "nm-setting-dcb.h" +#include "nm-setting-team.h" +#include "nm-setting-team-port.h" +#include "nm-setting-tc-config.h" +#include "nm-setting-dummy.h" +#include "nm-connection.h" +#include "nm-simple-connection.h" +#include "nm-setting-connection.h" +#include "nm-errors.h" + +#include "nm-utils/nm-test-utils.h" + +/*****************************************************************************/ + +static void +compare_blob_data (const char *test, + const char *key_path, + GBytes *key) +{ + char *contents = NULL; + gsize len = 0; + GError *error = NULL; + gboolean success; + + g_assert (key && g_bytes_get_size (key) > 0); + + success = g_file_get_contents (key_path, &contents, &len, &error); + nmtst_assert_success (success, error); + + g_assert_cmpmem (contents, len, g_bytes_get_data (key, NULL), g_bytes_get_size (key)); + + g_free (contents); +} + +static void +check_scheme_path (GBytes *value, const char *path) +{ + const guint8 *p; + + g_assert (value); + + p = g_bytes_get_data (value, NULL); + g_assert (memcmp (p, NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH, strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH)) == 0); + p += strlen (NM_SETTING_802_1X_CERT_SCHEME_PREFIX_PATH); + g_assert (memcmp (p, path, strlen (path)) == 0); + p += strlen (path); + g_assert (*p == '\0'); +} + +static void +test_private_key_import (const char *path, + const char *password, + NMSetting8021xCKScheme scheme) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + NMSetting8021xCKFormat tmp_fmt; + GError *error = NULL; + GBytes *tmp_key = NULL, *client_cert = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + scheme, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + tmp_fmt = nm_setting_802_1x_get_private_key_format (s_8021x); + g_assert (tmp_fmt == format); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (pw != NULL); + g_assert_cmpstr (pw, ==, password); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + tmp_key = nm_setting_802_1x_get_private_key_blob (s_8021x); + compare_blob_data ("private-key-import", path, tmp_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); + check_scheme_path (tmp_key, path); + g_bytes_unref (tmp_key); + } else + g_assert_not_reached (); + + /* If it's PKCS#12 ensure the client cert is the same value */ + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + g_object_get (s_8021x, NM_SETTING_802_1X_PRIVATE_KEY, &tmp_key, NULL); + g_assert (tmp_key); + + g_object_get (s_8021x, NM_SETTING_802_1X_CLIENT_CERT, &client_cert, NULL); + g_assert (client_cert); + + /* make sure they are the same */ + g_assert (g_bytes_equal (tmp_key, client_cert)); + + g_bytes_unref (tmp_key); + g_bytes_unref (client_cert); + } + + g_object_unref (s_8021x); +} + +static void +test_phase2_private_key_import (const char *path, + const char *password, + NMSetting8021xCKScheme scheme) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + NMSetting8021xCKFormat tmp_fmt; + GError *error = NULL; + GBytes *tmp_key = NULL, *client_cert = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + scheme, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + tmp_fmt = nm_setting_802_1x_get_phase2_private_key_format (s_8021x); + g_assert (tmp_fmt == format); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) { + tmp_key = nm_setting_802_1x_get_phase2_private_key_blob (s_8021x); + compare_blob_data ("phase2-private-key-import", path, tmp_key); + } else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) { + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); + check_scheme_path (tmp_key, path); + g_bytes_unref (tmp_key); + } else + g_assert_not_reached (); + + /* If it's PKCS#12 ensure the client cert is the same value */ + if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12) { + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, &tmp_key, NULL); + g_assert (tmp_key); + + g_object_get (s_8021x, NM_SETTING_802_1X_PHASE2_CLIENT_CERT, &client_cert, NULL); + g_assert (client_cert); + + /* make sure they are the same */ + g_assert (g_bytes_equal (tmp_key, client_cert)); + + g_bytes_unref (tmp_key); + g_bytes_unref (client_cert); + } + + g_object_unref (s_8021x); +} + +static void +test_wrong_password_keeps_data (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Now try to set it to something that's not a certificate */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_private_key (s_8021x, + "Makefile.am", + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_no_success (success, error); + g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + g_clear_error (&error); + + /* Make sure the password hasn't changed */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + g_object_unref (s_8021x); +} + +static void +test_clear_private_key (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + /* Now clear it */ + success = nm_setting_802_1x_set_private_key (s_8021x, + NULL, + NULL, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NULL, + &error); + nmtst_assert_success (success, error); + + /* Ensure the password is also now clear */ + g_assert (!nm_setting_802_1x_get_private_key_password (s_8021x)); + + g_object_unref (s_8021x); +} + +static void +test_wrong_phase2_password_keeps_data (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Now try to set it to something that's not a certificate */ + format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + "Makefile.am", + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_no_success (success, error); + g_assert (format == NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + g_clear_error (&error); + + /* Make sure the password hasn't changed */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + g_object_unref (s_8021x); +} + +static void +test_clear_phase2_private_key (const char *path, const char *password) +{ + NMSetting8021x *s_8021x; + gboolean success; + NMSetting8021xCKFormat format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN; + GError *error = NULL; + const char *pw; + + s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); + g_assert (s_8021x); + + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + path, + password, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + &format, + &error); + nmtst_assert_success (success, error); + g_assert (format != NM_SETTING_802_1X_CK_FORMAT_UNKNOWN); + + /* Make sure the password is what we expect */ + pw = nm_setting_802_1x_get_phase2_private_key_password (s_8021x); + g_assert (pw); + g_assert_cmpstr (pw, ==, password); + + /* Now clear it */ + success = nm_setting_802_1x_set_phase2_private_key (s_8021x, + NULL, + NULL, + NM_SETTING_802_1X_CK_SCHEME_BLOB, + NULL, + &error); + nmtst_assert_success (success, error); + + /* Ensure the password is also now clear */ + g_assert (!nm_setting_802_1x_get_phase2_private_key_password (s_8021x)); + + g_object_unref (s_8021x); +} + +static void +test_8021x (gconstpointer test_data) +{ + char **parts, *path, *password; + + parts = g_strsplit ((const char *) test_data, ", ", -1); + g_assert_cmpint (g_strv_length (parts), ==, 2); + + path = g_build_filename (TEST_CERT_DIR, parts[0], NULL); + password = parts[1]; + + /* Test phase1 and phase2 path scheme */ + test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); + test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_PATH); + + /* Test phase1 and phase2 blob scheme */ + test_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); + test_phase2_private_key_import (path, password, NM_SETTING_802_1X_CK_SCHEME_BLOB); + + /* Test that using a wrong password does not change existing data */ + test_wrong_password_keeps_data (path, password); + test_wrong_phase2_password_keeps_data (path, password); + + /* Test clearing the private key */ + test_clear_private_key (path, password); + test_clear_phase2_private_key (path, password); + + g_free (path); + g_strfreev (parts); +} + +/*****************************************************************************/ + +static void +create_bond_connection (NMConnection **con, NMSettingBond **s_bond) +{ + NMSettingConnection *s_con; + + g_assert (con); + g_assert (s_bond); + + *con = nmtst_create_minimal_connection ("bond", + NULL, + NM_SETTING_BOND_SETTING_NAME, + &s_con); + g_assert (*con); + g_assert (s_con); + + g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "bond0", NULL); + + *s_bond = (NMSettingBond *) nm_setting_bond_new (); + g_assert (*s_bond); + + nm_connection_add_setting (*con, NM_SETTING (*s_bond)); +} + +#define test_verify_options(exp, ...) \ + G_STMT_START { \ + const char *__opts[] = { __VA_ARGS__ , NULL }; \ + \ + _test_verify_options (__opts, exp); \ + } G_STMT_END + +static void +_test_verify_options (const char **options, gboolean expected_result) +{ + gs_unref_object NMConnection *con = NULL; + NMSettingBond *s_bond; + GError *error = NULL; + gboolean success; + const char **option; + + create_bond_connection (&con, &s_bond); + + for (option = options; option[0] && option[1]; option += 2) + g_assert (nm_setting_bond_add_option (s_bond, option[0], option[1])); + + if (expected_result) { + nmtst_assert_connection_verifies_and_normalizable (con); + nmtst_connection_normalize (con); + success = nm_setting_verify ((NMSetting *) s_bond, con, &error); + nmtst_assert_success (success, error); + } else { + nmtst_assert_connection_unnormalizable (con, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY); + } +} + +static void +test_bond_verify (void) +{ + test_verify_options (TRUE, + "mode", "3", + "arp_interval", "0"); + test_verify_options (FALSE, + /* arp_interval not supported in balance-alb mode */ + "mode", "balance-alb", + "arp_interval", "1", + "arp_ip_target", "1.2.3.4"); + test_verify_options (FALSE, + /* arp_ip_target requires arp_interval */ + "mode", "balance-rr", + "arp_ip_target", "1.2.3.4"); + test_verify_options (TRUE, + "mode", "balance-rr", + "arp_interval", "1", + "arp_ip_target", "1.2.3.4"); + test_verify_options (FALSE, + /* num_grat_arp, num_unsol_na cannot be different */ + "mode", "balance-rr", + "num_grat_arp", "3", + "num_unsol_na", "4"); + test_verify_options (TRUE, + "mode", "balance-rr", + "num_grat_arp", "5", + "num_unsol_na", "5"); + test_verify_options (TRUE, + "mode", "active-backup", + "primary", "eth0"); + test_verify_options (FALSE, + /* primary requires mode=active-backup */ + "mode", "802.3ad", + "primary", "eth0"); + test_verify_options (TRUE, + "mode", "802.3ad", + "lacp_rate", "fast"); + test_verify_options (FALSE, + /* lacp_rate=fast requires mode=802.3ad */ + "mode", "balance-rr", + "lacp_rate", "fast"); + test_verify_options (TRUE, + "mode", "802.3ad", + "ad_actor_system", "ae:00:11:33:44:55"); +} + +static void +test_bond_compare_options (gboolean exp_res, const char **opts1, const char **opts2) +{ + gs_unref_object NMSettingBond *s_bond1 = NULL, *s_bond2 = NULL; + const char **p; + + s_bond1 = (NMSettingBond *) nm_setting_bond_new (); + g_assert (s_bond1); + s_bond2 = (NMSettingBond *) nm_setting_bond_new (); + g_assert (s_bond2); + + for (p = opts1; p[0] && p[1]; p += 2) + g_assert (nm_setting_bond_add_option (s_bond1, p[0], p[1])); + + for (p = opts2; p[0] && p[1]; p += 2) + g_assert (nm_setting_bond_add_option (s_bond2, p[0], p[1])); + + g_assert_cmpint (nm_setting_compare ((NMSetting *) s_bond1, + (NMSetting *) s_bond2, + NM_SETTING_COMPARE_FLAG_EXACT), + ==, + exp_res); +} + +static void +test_bond_compare (void) +{ + test_bond_compare_options (TRUE, + ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), + ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL })); + test_bond_compare_options (FALSE, + ((const char *[]){ "mode", "balance-rr", "miimon", "1", NULL }), + ((const char *[]){ "mode", "balance-rr", "miimon", "2", NULL })); + + /* ignore default values */ + test_bond_compare_options (TRUE, + ((const char *[]){ "miimon", "1", NULL }), + ((const char *[]){ "miimon", "1", "updelay", "0", NULL })); + + /* special handling of num_grat_arp, num_unsol_na */ + test_bond_compare_options (FALSE, + ((const char *[]){ "num_grat_arp", "2", NULL }), + ((const char *[]){ "num_grat_arp", "1", NULL })); + test_bond_compare_options (TRUE, + ((const char *[]){ "num_grat_arp", "3", NULL }), + ((const char *[]){ "num_unsol_na", "3", NULL })); + test_bond_compare_options (TRUE, + ((const char *[]){ "num_grat_arp", "4", NULL }), + ((const char *[]){ "num_unsol_na", "4", "num_grat_arp", "4", NULL })); +} + +static void +test_bond_normalize_options (const char **opts1, const char **opts2) +{ + gs_unref_object NMConnection *con = NULL; + NMSettingBond *s_bond; + GError *error = NULL; + gboolean success; + const char **p; + int num = 0; + + create_bond_connection (&con, &s_bond); + + for (p = opts1; p[0] && p[1]; p += 2) + g_assert (nm_setting_bond_add_option (s_bond, p[0], p[1])); + + nmtst_assert_connection_verifies_and_normalizable (con); + nmtst_connection_normalize (con); + success = nm_setting_verify ((NMSetting *) s_bond, con, &error); + nmtst_assert_success (success, error); + + for (p = opts2; p[0] && p[1]; p += 2) { + g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, p[0]), ==, p[1]); + num++; + } + + g_assert_cmpint (num, ==, nm_setting_bond_get_num_options (s_bond)); +} + +static void +test_bond_normalize (void) +{ + test_bond_normalize_options ( + ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL }), + ((const char *[]){ "mode", "802.3ad", "ad_actor_system", "00:02:03:04:05:06", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "1", "miimon", "1", NULL }), + ((const char *[]){ "mode", "active-backup", "miimon", "1", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "balance-alb", "tlb_dynamic_lb", "1", NULL }), + ((const char *[]){ "mode", "balance-alb", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL }), + ((const char *[]){ "mode", "balance-tlb", "tlb_dynamic_lb", "1", NULL })); + test_bond_normalize_options ( + ((const char *[]){ "mode", "balance-rr", "ad_actor_sys_prio", "4", "packets_per_slave", "3", NULL }), + ((const char *[]){ "mode", "balance-rr", "packets_per_slave", "3", NULL })); +} + +/*****************************************************************************/ + +#define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \ + NM_SETTING_DCB_FLAG_ADVERTISE | \ + NM_SETTING_DCB_FLAG_WILLING) + + +static void +test_dcb_flags_valid (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + guint i; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, 0); + g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, 0); + + g_object_set (G_OBJECT (s_dcb), + NM_SETTING_DCB_APP_FCOE_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_APP_ISCSI_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_APP_FIP_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, DCB_FLAGS_ALL, + NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, DCB_FLAGS_ALL, + NULL); + /* Priority Group Bandwidth must total 100% */ + for (i = 0; i < 7; i++) + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 12); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); + + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_no_error (error); + g_assert (success); + + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_app_fip_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_priority_flow_control_flags (s_dcb), ==, DCB_FLAGS_ALL); + g_assert_cmpint (nm_setting_dcb_get_priority_group_flags (s_dcb), ==, DCB_FLAGS_ALL); +} + +#define TEST_FLAG(p, f, v) \ +{ \ + /* GObject property min/max should ensure the property does not get set to \ + * the invalid value, so we ensure the value we just tried to set is 0 and \ + * that verify is successful since the property never got set. \ + */ \ + g_object_set (G_OBJECT (s_dcb), p, v, NULL); \ + g_assert_cmpint (f (s_dcb), ==, 0); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ +} + +static void +test_dcb_flags_invalid (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32); + g_test_assert_expected_messages (); + + g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*"); + TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags, + (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1); + g_test_assert_expected_messages (); +} + +#define TEST_APP_PRIORITY(lcprop, ucprop, v) \ +{ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ + \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, v, NULL); \ + g_assert_cmpint (nm_setting_dcb_get_app_##lcprop##_priority (s_dcb), ==, v); \ + \ + /* Assert that the setting is invalid while the app is disabled unless v is default */ \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + if (v >= 0) { \ + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ + g_assert (success == FALSE); \ + } else { \ + g_assert_no_error (error); \ + g_assert (success); \ + } \ + g_clear_error (&error); \ + \ + /* Set the enable flag and re-verify, this time it should be valid */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ + \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_APP_##ucprop##_PRIORITY, 0, NULL); \ +} + +static void +test_dcb_app_priorities (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + /* Defaults */ + g_assert_cmpint (nm_setting_dcb_get_app_fcoe_priority (s_dcb), ==, -1); + g_assert_cmpint (nm_setting_dcb_get_app_iscsi_priority (s_dcb), ==, -1); + g_assert_cmpint (nm_setting_dcb_get_app_fip_priority (s_dcb), ==, -1); + + TEST_APP_PRIORITY (fcoe, FCOE, 6); + TEST_APP_PRIORITY (iscsi, ISCSI, 5); + TEST_APP_PRIORITY (fip, FIP, 4); + + TEST_APP_PRIORITY (fcoe, FCOE, -1); + TEST_APP_PRIORITY (iscsi, ISCSI, -1); + TEST_APP_PRIORITY (fip, FIP, -1); +} + +#define TEST_PRIORITY_VALID(fn, id, val, flagsprop, verify) \ +{ \ + /* Assert that setting the value gets the same value back out */ \ + nm_setting_dcb_set_priority_##fn (s_dcb, id, val); \ + g_assert_cmpint (nm_setting_dcb_get_priority_##fn (s_dcb, id), ==, val); \ + \ + if (verify) { \ + if (val != 0) { \ + /* Assert that verify fails because the flags do not include 'enabled' \ + * and a value has been set. \ + */ \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); \ + g_assert (success == FALSE); \ + g_clear_error (&error); \ + } \ + \ + /* Assert that adding the 'enabled' flag verifies the setting */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); \ + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); \ + g_assert_no_error (error); \ + g_assert (success); \ + } \ + \ + /* Reset everything */ \ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_##flagsprop##_FLAGS, NM_SETTING_DCB_FLAG_NONE, NULL); \ + nm_setting_dcb_set_priority_##fn (s_dcb, id, 0); \ +} + +/* If Priority Groups are enabled, PG bandwidth must equal 100% */ +#define SET_VALID_PRIORITY_GROUP_BANDWIDTH \ +{ \ + guint x; \ + for (x = 0; x < 7; x++) \ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, x, 12); \ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); \ +} + +static void +test_dcb_priorities_valid (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + guint i; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + for (i = 0; i < 8; i++) + TEST_PRIORITY_VALID (flow_control, i, TRUE, FLOW_CONTROL, TRUE); + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (group_id, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (group_id, i, 7 - i, GROUP, TRUE); + } + + /* Clear PG bandwidth from earlier tests */ + for (i = 0; i < 8; i++) + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, i, 0); + + /* Priority Group Bandwidth must add up to 100% if enabled, which requires + * some dancing for verifying individual values here. + */ + for (i = 0; i < 8; i++) { + guint other = 7 - (i % 8); + + /* Set another priority group to the remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - i); + TEST_PRIORITY_VALID (group_bandwidth, i, i, GROUP, TRUE); + + /* Set another priority group to the remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 100 - (7 - i)); + TEST_PRIORITY_VALID (group_bandwidth, i, 7 - i, GROUP, TRUE); + + /* Clear remaining bandwidth */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, other, 0); + } + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (bandwidth, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (bandwidth, i, 7 - i, GROUP, TRUE); + } + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) + TEST_PRIORITY_VALID (strict_bandwidth, i, TRUE, GROUP, TRUE); + + SET_VALID_PRIORITY_GROUP_BANDWIDTH + for (i = 0; i < 8; i++) { + TEST_PRIORITY_VALID (traffic_class, i, i, GROUP, TRUE); + TEST_PRIORITY_VALID (traffic_class, i, 7 - i, GROUP, TRUE); + } +} + +static void +test_dcb_bandwidth_sums (void) +{ + gs_unref_object NMSettingDcb *s_dcb = NULL; + GError *error = NULL; + gboolean success; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + g_assert (s_dcb); + + /* Assert that setting the value gets the same value back out */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 0, 9); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 1, 10); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 2, 11); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 3, 12); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 13); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 5, 14); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 6, 15); + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 7, 16); + + /* Assert verify success when sums total 100% */ + g_object_set (G_OBJECT (s_dcb), NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, NM_SETTING_DCB_FLAG_ENABLE, NULL); + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_no_error (error); + g_assert (success); + + /* Assert verify fails when sums do not total 100% */ + nm_setting_dcb_set_priority_group_bandwidth (s_dcb, 4, 20); + success = nm_setting_verify (NM_SETTING (s_dcb), NULL, &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY); + g_assert (success == FALSE); + g_clear_error (&error); +} + +/*****************************************************************************/ + +static void +_test_team_config_sync (const char *team_config, + int notify_peer_count, + int notify_peers_interval, + int mcast_rejoin_count, + int mcast_rejoin_interval, + char *runner, + char *runner_hwaddr_policy, /* activebackup */ + GPtrArray *runner_tx_hash, /* lacp, loadbalance */ + char *runner_tx_balancer, /* lacp, loadbalance */ + int runner_tx_balancer_interval, /* lacp, loadbalance */ + gboolean runner_active, /* lacp */ + gboolean runner_fast_rate, /* lacp */ + int runner_sys_prio, /* lacp */ + int runner_min_ports, /* lacp */ + char *runner_agg_select_policy, /* lacp */ + GPtrArray *link_watchers) +{ + gs_unref_object NMSettingTeam *s_team = NULL; + guint i, j; + gboolean found; + + s_team = (NMSettingTeam *) nm_setting_team_new (); + g_assert (s_team); + + g_object_set (s_team, NM_SETTING_TEAM_CONFIG, team_config, NULL); + g_assert (nm_setting_team_get_notify_peers_count (s_team) == notify_peer_count); + g_assert (nm_setting_team_get_notify_peers_interval (s_team) == notify_peers_interval); + g_assert (nm_setting_team_get_mcast_rejoin_count (s_team) == mcast_rejoin_count); + g_assert (nm_setting_team_get_mcast_rejoin_interval (s_team) == mcast_rejoin_interval); + g_assert (nm_setting_team_get_runner_tx_balancer_interval (s_team) == runner_tx_balancer_interval); + g_assert (nm_setting_team_get_runner_active (s_team) == runner_active); + g_assert (nm_setting_team_get_runner_fast_rate (s_team) == runner_fast_rate); + g_assert (nm_setting_team_get_runner_sys_prio (s_team) == runner_sys_prio); + g_assert (nm_setting_team_get_runner_min_ports (s_team) == runner_min_ports); + g_assert (nm_streq0 (nm_setting_team_get_runner (s_team), runner)); + g_assert (nm_streq0 (nm_setting_team_get_runner_hwaddr_policy (s_team), runner_hwaddr_policy)); + g_assert (nm_streq0 (nm_setting_team_get_runner_tx_balancer (s_team), runner_tx_balancer)); + g_assert (nm_streq0 (nm_setting_team_get_runner_agg_select_policy (s_team), runner_agg_select_policy)); + + if (runner_tx_hash) { + g_assert (runner_tx_hash->len == nm_setting_team_get_num_runner_tx_hash (s_team)); + for (i = 0; i < runner_tx_hash->len; i++) { + found = FALSE; + for (j = 0; j < nm_setting_team_get_num_runner_tx_hash (s_team); j++) { + if (nm_streq0 (nm_setting_team_get_runner_tx_hash (s_team, j), + runner_tx_hash->pdata[i])) { + found = TRUE; + break; + } + } + g_assert (found); + } + } + + if (link_watchers) { + g_assert (link_watchers->len == nm_setting_team_get_num_link_watchers (s_team)); + for (i = 0; i < link_watchers->len; i++) { + found = FALSE; + for (j = 0; j < nm_setting_team_get_num_link_watchers (s_team); j++) { + if (nm_team_link_watcher_equal (link_watchers->pdata[i], + nm_setting_team_get_link_watcher (s_team, j))) { + found = TRUE; + break; + } + } + g_assert (found); + } + } + + g_assert (nm_setting_verify ((NMSetting *) s_team, NULL, NULL)); +} + + +static void +test_runner_roundrobin_sync_from_config (void) +{ + _test_team_config_sync ("", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_ROUNDROBIN, + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_broadcast_sync_from_config (void) +{ + _test_team_config_sync ("{\"runner\": {\"name\": \"broadcast\"}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_BROADCAST, + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_activebackup_sync_from_config (void) +{ + _test_team_config_sync ("{\"runner\": {\"name\": \"activebackup\"}}", + NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT, 0, + NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT, 0, + NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP, + NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_loadbalance_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *tx_hash = NULL; + + tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + g_ptr_array_add (tx_hash, g_strdup ("eth")); + g_ptr_array_add (tx_hash, g_strdup ("ipv4")); + g_ptr_array_add (tx_hash, g_strdup ("ipv6")); + + _test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\"}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + FALSE, FALSE, -1, -1, NULL, + NULL); + + _test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\", " + "\"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"]}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + FALSE, FALSE, -1, -1, NULL, + NULL); + + _test_team_config_sync ("{\"runner\": {\"name\": \"loadbalance\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"], " + "\"tx_balancer\": {\"name\": \"basic\", \"balancing_interval\": 30}}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LOADBALANCE, + NULL, + tx_hash, "basic", 30, + FALSE, FALSE, -1, -1, NULL, + NULL); +} + +static void +test_runner_lacp_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *tx_hash = NULL; + + tx_hash = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); + g_ptr_array_add (tx_hash, g_strdup ("eth")); + g_ptr_array_add (tx_hash, g_strdup ("ipv4")); + g_ptr_array_add (tx_hash, g_strdup ("ipv6")); + + _test_team_config_sync ("{\"runner\": {\"name\": \"lacp\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"]}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LACP, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + TRUE, FALSE, NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT, 0, + NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT, + NULL); + + _test_team_config_sync ("{\"runner\": {\"name\": \"lacp\", \"tx_hash\": [\"eth\", \"ipv4\", \"ipv6\"], " + "\"active\": false, \"fast_rate\": true, \"sys_prio\": 10, \"min_ports\": 5, " + "\"agg_select_policy\": \"port_config\"}}", + 0, 0, 0, 0, + NM_SETTING_TEAM_RUNNER_LACP, + NULL, + tx_hash, NULL, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT, + FALSE, TRUE, 10, 5, "port_config", + NULL); +} + +static void +test_watcher_ethtool_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_ethtool (0, 0, NULL)); + _test_team_config_sync ("{\"link_watch\": {\"name\": \"ethtool\"}}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +static void +test_watcher_nsna_ping_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_nsna_ping (0, 0, 3, "target.host", NULL)); + _test_team_config_sync ("{\"link_watch\": {\"name\": \"nsna_ping\", \"target_host\": \"target.host\"}}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +static void +test_watcher_arp_ping_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (0, 0, 3, "target.host", "source.host", 0, NULL)); + _test_team_config_sync ("{\"link_watch\": {\"name\": \"arp_ping\", \"target_host\": \"target.host\", " + "\"source_host\": \"source.host\"}}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +static void +test_multiple_watchers_sync_from_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_ethtool (2, 4, NULL)); + g_ptr_array_add (link_watchers, nm_team_link_watcher_new_nsna_ping (3, 6, 9, "target.host", NULL)); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (5, 10, 15, "target.host", "source.host", + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE + | NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE + | NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS, + NULL)); + _test_team_config_sync ("{\"link_watch\": [" + "{\"name\": \"ethtool\", \"delay_up\": 2, \"delay_down\": 4}, " + "{\"name\": \"arp_ping\", \"init_wait\": 5, \"interval\": 10, \"missed_max\": 15, " + "\"target_host\": \"target.host\", \"source_host\": \"source.host\", " + "\"validate_active\": true, \"validate_inactive\": true, \"send_always\": true}, " + "{\"name\": \"nsna_ping\", \"init_wait\": 3, \"interval\": 6, \"missed_max\": 9, " + "\"target_host\": \"target.host\"}]}", + 0, 0, 0, 0, + "roundrobin", + NULL, + NULL, NULL, -1, + FALSE, FALSE, -1, -1, NULL, + link_watchers); +} + +/*****************************************************************************/ + +static void +_test_team_port_config_sync (const char *team_port_config, + int queue_id, + int prio, + gboolean sticky, + int lacp_prio, + int lacp_key, + GPtrArray *link_watchers) +{ + gs_unref_object NMSettingTeamPort *s_team_port = NULL; + guint i, j; + gboolean found; + + s_team_port = (NMSettingTeamPort *) nm_setting_team_port_new (); + g_assert (s_team_port); + + g_object_set (s_team_port, NM_SETTING_TEAM_CONFIG, team_port_config, NULL); + g_assert (nm_setting_team_port_get_queue_id (s_team_port) == queue_id); + g_assert (nm_setting_team_port_get_prio (s_team_port) == prio); + g_assert (nm_setting_team_port_get_sticky (s_team_port) == sticky); + g_assert (nm_setting_team_port_get_lacp_prio (s_team_port) == lacp_prio); + g_assert (nm_setting_team_port_get_lacp_key (s_team_port) == lacp_key); + + if (link_watchers) { + g_assert (link_watchers->len == nm_setting_team_port_get_num_link_watchers (s_team_port)); + for (i = 0; i < link_watchers->len; i++) { + found = FALSE; + for (j = 0; j < nm_setting_team_port_get_num_link_watchers (s_team_port); j++) { + if (nm_team_link_watcher_equal (link_watchers->pdata[i], + nm_setting_team_port_get_link_watcher (s_team_port, + j))) { + found = TRUE; + break; + } + } + g_assert (found); + } + } + + g_assert (nm_setting_verify ((NMSetting *) s_team_port, NULL, NULL)); +} + + +static void +test_team_port_default (void) +{ + _test_team_port_config_sync ("", -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_queue_id (void) +{ + _test_team_port_config_sync ("{\"queue_id\": 3}", + 3, 0, FALSE, 255, 0, NULL); + _test_team_port_config_sync ("{\"queue_id\": 0}", + 0, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_prio (void) +{ + _test_team_port_config_sync ("{\"prio\": 6}", + -1, 6, FALSE, 255, 0, NULL); + _test_team_port_config_sync ("{\"prio\": 0}", + -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_sticky (void) +{ + _test_team_port_config_sync ("{\"sticky\": true}", + -1, 0, TRUE, 255, 0, NULL); + _test_team_port_config_sync ("{\"sticky\": false}", + -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_lacp_prio (void) +{ + _test_team_port_config_sync ("{\"lacp_prio\": 9}", + -1, 0, FALSE, 9, 0, NULL); + _test_team_port_config_sync ("{\"lacp_prio\": 0}", + -1, 0, FALSE, 0, 0, NULL); +} + +static void +test_team_port_lacp_key (void) +{ + _test_team_port_config_sync ("{\"lacp_key\": 12}", + -1, 0, FALSE, 255, 12, NULL); + _test_team_port_config_sync ("{\"lacp_key\": 0}", + -1, 0, FALSE, 255, 0, NULL); +} + +static void +test_team_port_full_config (void) +{ + gs_unref_ptrarray GPtrArray *link_watchers = NULL; + + link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (0, 3, 3, "1.2.3.2", "1.2.3.1", + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE, + NULL)); + g_ptr_array_add (link_watchers, + nm_team_link_watcher_new_arp_ping (1, 1, 0, "1.2.3.4", "1.2.3.1", + NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS, + NULL)); + + _test_team_port_config_sync ("{\"queue_id\": 10, \"prio\": 20, \"sticky\": true, \"lacp_prio\": 30, " + "\"lacp_key\": 40, \"link_watch\": [" + "{\"name\": \"arp_ping\", \"interval\": 3, \"target_host\": \"1.2.3.2\", " + "\"source_host\": \"1.2.3.1\", \"validate_inactive\": true}, " + "{\"name\": \"arp_ping\", \"init_wait\": 1, \"interval\": 1, " + "\"target_host\": \"1.2.3.4\", \"source_host\": \"1.2.3.1\", " + "\"send_always\": true}]}", + 10, 20, true, 30, 40, NULL); +} + +/*****************************************************************************/ + +static void +test_tc_config_qdisc (void) +{ + NMTCQdisc *qdisc1, *qdisc2; + char *str; + GError *error = NULL; + + qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error); + nmtst_assert_success (qdisc1, error); + + qdisc2 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error); + nmtst_assert_success (qdisc2, error); + + g_assert (nm_tc_qdisc_equal (qdisc1, qdisc2)); + + nm_tc_qdisc_unref (qdisc2); + qdisc2 = nm_tc_qdisc_dup (qdisc1); + + g_assert (nm_tc_qdisc_equal (qdisc1, qdisc2)); + + g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc1), ==, "fq_codel"); + g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_UNSPEC); + g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_ROOT); + + str = nm_utils_tc_qdisc_to_str (qdisc1, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "root fq_codel"); + g_free (str); + + nm_tc_qdisc_unref (qdisc1); + qdisc1 = nm_tc_qdisc_new ("ingress", TC_H_INGRESS, &error); + nmtst_assert_success (qdisc1, error); + + g_assert (!nm_tc_qdisc_equal (qdisc1, qdisc2)); + + str = nm_utils_tc_qdisc_to_str (qdisc1, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "ingress"); + g_free (str); + + nm_tc_qdisc_unref (qdisc1); + qdisc1 = nm_utils_tc_qdisc_from_str ("narodil sa kristus pan", &error); + nmtst_assert_no_success (qdisc1, error); + g_clear_error (&error); + + qdisc1 = nm_utils_tc_qdisc_from_str ("handle 1234 parent fff1:1 pfifo_fast", &error); + nmtst_assert_success (qdisc1, error); + + g_assert_cmpstr (nm_tc_qdisc_get_kind (qdisc1), ==, "pfifo_fast"); + g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_MAKE (0x1234 << 16, 0x0000)); + g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_MAKE (0xfff1 << 16, 0x0001)); + + str = nm_utils_tc_qdisc_to_str (qdisc1, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "parent fff1:1 handle 1234: pfifo_fast"); + g_free (str); + + nm_tc_qdisc_unref (qdisc2); + str = nm_utils_tc_qdisc_to_str (qdisc1, &error); + nmtst_assert_success (str, error); + qdisc2 = nm_utils_tc_qdisc_from_str (str, &error); + nmtst_assert_success (qdisc2, error); + g_free (str); + + g_assert (nm_tc_qdisc_equal (qdisc1, qdisc2)); + + nm_tc_qdisc_unref (qdisc1); + nm_tc_qdisc_unref (qdisc2); +} + +static void +test_tc_config_action (void) +{ + NMTCAction *action1, *action2; + char *str; + GError *error = NULL; + + action1 = nm_tc_action_new ("drop", &error); + nmtst_assert_success (action1, error); + action2 = nm_tc_action_new ("drop", &error); + nmtst_assert_success (action2, error); + + g_assert (nm_tc_action_equal (action1, action2)); + g_assert_cmpstr (nm_tc_action_get_kind (action1), ==, "drop"); + + nm_tc_action_unref (action1); + action1 = nm_tc_action_new ("simple", &error); + nmtst_assert_success (action1, error); + nm_tc_action_set_attribute (action1, "sdata", g_variant_new_bytestring ("Hello")); + + g_assert (!nm_tc_action_equal (action1, action2)); + + str = nm_utils_tc_action_to_str (action1, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "simple sdata Hello"); + g_free (str); + + str = nm_utils_tc_action_to_str (action2, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "drop"); + g_free (str); + + nm_tc_action_unref (action2); + action2 = nm_tc_action_dup (action1); + + g_assert (nm_tc_action_equal (action1, action2)); + + nm_tc_action_unref (action1); + action1 = nm_utils_tc_action_from_str ("narodil sa kristus pan", &error); + nmtst_assert_no_success (action1, error); + g_clear_error (&error); + + action1 = nm_utils_tc_action_from_str ("simple sdata Hello", &error); + nmtst_assert_success (action1, error); + + g_assert_cmpstr (nm_tc_action_get_kind (action1), ==, "simple"); + g_assert_cmpstr (g_variant_get_bytestring (nm_tc_action_get_attribute (action1, "sdata")), ==, "Hello"); + + nm_tc_action_unref (action1); + nm_tc_action_unref (action2); +} + +static void +test_tc_config_tfilter (void) +{ + NMTCAction *action1; + NMTCTfilter *tfilter1, *tfilter2; + char *str; + GError *error = NULL; + + tfilter1 = nm_tc_tfilter_new ("matchall", + TC_H_MAKE (0x1234 << 16, 0x0000), + &error); + nmtst_assert_success (tfilter1, error); + + tfilter2 = nm_tc_tfilter_new ("matchall", + TC_H_MAKE (0x1234 << 16, 0x0000), + &error); + nmtst_assert_success (tfilter2, error); + + g_assert (nm_tc_tfilter_equal (tfilter1, tfilter2)); + + action1 = nm_tc_action_new ("simple", &error); + nmtst_assert_success (action1, error); + nm_tc_action_set_attribute (action1, "sdata", g_variant_new_bytestring ("Hello")); + nm_tc_tfilter_set_action (tfilter1, action1); + nm_tc_action_unref (action1); + + g_assert (!nm_tc_tfilter_equal (tfilter1, tfilter2)); + + str = nm_utils_tc_tfilter_to_str (tfilter1, &error); + nmtst_assert_success (str, error); + g_assert_cmpstr (str, ==, "parent 1234: matchall action simple sdata Hello"); + g_free (str); + + nm_tc_tfilter_unref (tfilter2); + tfilter2 = nm_tc_tfilter_dup (tfilter1); + + g_assert (nm_tc_tfilter_equal (tfilter1, tfilter2)); + + nm_tc_tfilter_unref (tfilter1); + tfilter1 = nm_utils_tc_tfilter_from_str ("narodil sa kristus pan", &error); + nmtst_assert_no_success (tfilter1, error); + g_clear_error (&error); + + str = nm_utils_tc_tfilter_to_str (tfilter2, &error); + nmtst_assert_success (str, error); + tfilter1 = nm_utils_tc_tfilter_from_str (str, &error); + nmtst_assert_success (tfilter1, error); + g_free (str); + + g_assert (nm_tc_tfilter_equal (tfilter1, tfilter2)); + + nm_tc_tfilter_unref (tfilter1); + nm_tc_tfilter_unref (tfilter2); +} + +static void +test_tc_config_setting (void) +{ + gs_unref_object NMSettingTCConfig *s_tc = NULL; + NMTCQdisc *qdisc1, *qdisc2; + GError *error = NULL; + + s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new (); + + qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error); + nmtst_assert_success (qdisc1, error); + + qdisc2 = nm_tc_qdisc_new ("pfifo_fast", + TC_H_MAKE (0xfff1 << 16, 0x0001), + &error); + nmtst_assert_success (qdisc2, error); + nm_tc_qdisc_set_handle (qdisc2, TC_H_MAKE (0x1234 << 16, 0x0000)); + + g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 0); + g_assert (nm_setting_tc_config_add_qdisc (s_tc, qdisc1) == TRUE); + g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 1); + g_assert (nm_setting_tc_config_get_qdisc (s_tc, 0) != NULL); + g_assert (nm_setting_tc_config_remove_qdisc_by_value (s_tc, qdisc2) == FALSE); + g_assert (nm_setting_tc_config_add_qdisc (s_tc, qdisc2) == TRUE); + g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 2); + g_assert (nm_setting_tc_config_remove_qdisc_by_value (s_tc, qdisc1) == TRUE); + g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 1); + nm_setting_tc_config_clear_qdiscs (s_tc); + g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 0); + + nm_tc_qdisc_unref (qdisc1); + nm_tc_qdisc_unref (qdisc2); +} + +static void +test_tc_config_dbus (void) +{ + NMConnection *connection1, *connection2; + NMSetting *s_tc; + NMTCQdisc *qdisc1, *qdisc2; + NMTCTfilter *tfilter1, *tfilter2; + NMTCAction *action; + GVariant *dbus, *tc_dbus, *var1, *var2; + GError *error = NULL; + gboolean success; + + connection1 = nmtst_create_minimal_connection ("dummy", + NULL, + NM_SETTING_DUMMY_SETTING_NAME, + NULL); + + s_tc = nm_setting_tc_config_new (); + + qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error); + nmtst_assert_success (qdisc1, error); + nm_tc_qdisc_set_handle (qdisc1, TC_H_MAKE (0x1234 << 16, 0x0000)); + nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc1); + + qdisc2 = nm_tc_qdisc_new ("ingress", TC_H_INGRESS, &error); + nmtst_assert_success (qdisc2, error); + nm_tc_qdisc_set_handle (qdisc2, TC_H_MAKE (TC_H_INGRESS, 0)); + nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc2); + + tfilter1 = nm_tc_tfilter_new ("matchall", + TC_H_MAKE (0x1234 << 16, 0x0000), + &error); + nmtst_assert_success (tfilter1, error); + action = nm_tc_action_new ("drop", &error); + nmtst_assert_success (action, error); + nm_tc_tfilter_set_action (tfilter1, action); + nm_tc_action_unref (action); + nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter1); + nm_tc_tfilter_unref (tfilter1); + + tfilter2 = nm_tc_tfilter_new ("matchall", + TC_H_MAKE (TC_H_INGRESS, 0), + &error); + nmtst_assert_success (tfilter2, error); + action = nm_tc_action_new ("simple", &error); + nmtst_assert_success (action, error); + nm_tc_action_set_attribute (action, "sdata", g_variant_new_bytestring ("Hello")); + nm_tc_tfilter_set_action (tfilter2, action); + nm_tc_action_unref (action); + nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter2); + nm_tc_tfilter_unref (tfilter2); + + nm_connection_add_setting (connection1, s_tc); + + dbus = nm_connection_to_dbus (connection1, NM_CONNECTION_SERIALIZE_ALL); + + tc_dbus = g_variant_lookup_value (dbus, "tc", G_VARIANT_TYPE_VARDICT); + g_assert (tc_dbus); + + var1 = g_variant_lookup_value (tc_dbus, "qdiscs", G_VARIANT_TYPE ("aa{sv}")); + var2 = g_variant_new_parsed ("[{'kind': <'fq_codel'>," + " 'handle': ," + " 'parent': }," + " {'kind': <'ingress'>," + " 'handle': ," + " 'parent': }]"); + g_assert (g_variant_equal (var1, var2)); + g_variant_unref (var1); + g_variant_unref (var2); + + var1 = g_variant_lookup_value (tc_dbus, "tfilters", G_VARIANT_TYPE ("aa{sv}")); + var2 = g_variant_new_parsed ("[{'kind': <'matchall'>," + " 'handle': ," + " 'parent': ," + " 'action': <{'kind': <'drop'>}>}," + " {'kind': <'matchall'>," + " 'handle': ," + " 'parent': ," + " 'action': <{'kind': <'simple'>," + " 'sdata': }>}]"); + g_variant_unref (var1); + g_variant_unref (var2); + + g_variant_unref (tc_dbus); + + connection2 = nm_simple_connection_new (); + success = nm_connection_replace_settings (connection2, dbus, &error); + nmtst_assert_success (success, error); + + g_assert (nm_connection_diff (connection1, connection2, NM_SETTING_COMPARE_FLAG_EXACT, NULL)); + + g_variant_unref (dbus); + + nm_tc_qdisc_unref (qdisc1); + nm_tc_qdisc_unref (qdisc2); + + g_object_unref (connection1); + g_object_unref (connection2); +} + +/*****************************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + nmtst_init (&argc, &argv, TRUE); + + g_test_add_data_func ("/libnm/setting-8021x/key-and-cert", + "test_key_and_cert.pem, test", + test_8021x); + g_test_add_data_func ("/libnm/setting-8021x/key-only", + "test-key-only.pem, test", + test_8021x); + g_test_add_data_func ("/libnm/setting-8021x/pkcs8-enc-key", + "pkcs8-enc-key.pem, 1234567890", + test_8021x); + g_test_add_data_func ("/libnm/setting-8021x/pkcs12", + "test-cert.p12, test", + test_8021x); + + g_test_add_func ("/libnm/settings/bond/verify", test_bond_verify); + g_test_add_func ("/libnm/settings/bond/compare", test_bond_compare); + g_test_add_func ("/libnm/settings/bond/normalize", test_bond_normalize); + + g_test_add_func ("/libnm/settings/dcb/flags-valid", test_dcb_flags_valid); + g_test_add_func ("/libnm/settings/dcb/flags-invalid", test_dcb_flags_invalid); + g_test_add_func ("/libnm/settings/dcb/app-priorities", test_dcb_app_priorities); + 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/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); + g_test_add_func ("/libnm/settings/tc_config/setting", test_tc_config_setting); + g_test_add_func ("/libnm/settings/tc_config/dbus", test_tc_config_dbus); + +#if WITH_JANSSON + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin", + test_runner_roundrobin_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_broadcast", + test_runner_broadcast_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_activebackup", + test_runner_activebackup_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_loadbalance", + test_runner_loadbalance_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_runner_from_config_lacp", + test_runner_lacp_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_ethtool", + test_watcher_ethtool_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_nsna_ping", + test_watcher_nsna_ping_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_arp_ping", + test_watcher_arp_ping_sync_from_config); + g_test_add_func ("/libnm/settings/team/sync_watcher_from_config_all", + test_multiple_watchers_sync_from_config); + + g_test_add_func ("/libnm/settings/team-port/sync_from_config_defaults", test_team_port_default); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_queue_id", test_team_port_queue_id); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_prio", test_team_port_prio); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_sticky", test_team_port_sticky); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_prio", test_team_port_lacp_prio); + g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_key", test_team_port_lacp_key); + g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config); + +#endif + + return g_test_run (); +} -- cgit 1.3.0-6-gf8a5 From 50f6b47074e01dffb8dc536c0a20961dcf28ae9b Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Wed, 7 Feb 2018 02:39:23 +0100 Subject: New upstream version 1.10.4 --- libnm-core/nm-core-internal.h | 8 +- libnm-core/nm-dbus-interface.h | 4 + libnm-core/nm-dbus-types.xml | 10 +++ libnm-core/nm-json.c | 118 +++++++++++++++++++++++++ libnm-core/nm-json.h | 50 +++++++++++ libnm-core/nm-setting-ip4-config.c | 2 +- libnm-core/nm-setting-tc-config.h | 3 - libnm-core/nm-setting-team.c | 10 ++- libnm-core/nm-setting-team.h | 2 +- libnm-core/nm-setting-vpn.c | 23 +++-- libnm-core/nm-utils.c | 177 +++++++++++++++++++++++++------------ libnm-core/tests/test-general.c | 99 ++++++++++++++++++++- 12 files changed, 433 insertions(+), 73 deletions(-) create mode 100644 libnm-core/nm-json.c create mode 100644 libnm-core/nm-json.h (limited to 'libnm-core') diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 5a27d438..15e5dc2a 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -241,7 +241,13 @@ GPtrArray *_nm_utils_copy_object_array (const GPtrArray *array); gssize _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer needle); -gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data); +gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data, + gssize *out_idx_first, + gssize *out_idx_last); gssize _nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data); GSList * _nm_utils_strv_to_slist (char **strv, gboolean deep_copy); diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 98c94154..33e4850c 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -551,6 +551,8 @@ typedef enum { * @NM_DEVICE_STATE_REASON_PARENT_CHANGED: the device's parent changed * @NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED: the device parent's management changed * @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with OpenVSwitch 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 * * Device state change reason codes */ @@ -619,6 +621,8 @@ typedef enum { NM_DEVICE_STATE_REASON_PARENT_CHANGED = 61, NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED = 62, NM_DEVICE_STATE_REASON_OVSDB_FAILED = 63, + NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE = 64, + NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED = 65, } NMDeviceStateReason; /** diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml index bc0c023b..b8c5d84f 100644 --- a/libnm-core/nm-dbus-types.xml +++ b/libnm-core/nm-dbus-types.xml @@ -1122,6 +1122,16 @@ = 63 problem communicating with OpenVSwitch database + + NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE + = 64 + a duplicate IP address was detected + + + NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED + = 65 + The selected IP method is not supported + diff --git a/libnm-core/nm-json.c b/libnm-core/nm-json.c new file mode 100644 index 00000000..f9042b1f --- /dev/null +++ b/libnm-core/nm-json.c @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017, 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#define NM_JANSSON_C +#include "nm-json.h" + +#include + +void *_nm_jansson_json_object_iter_value; +void *_nm_jansson_json_object_key_to_iter; +void *_nm_jansson_json_integer; +void *_nm_jansson_json_object_del; +void *_nm_jansson_json_array_get; +void *_nm_jansson_json_array_size; +void *_nm_jansson_json_array_append_new; +void *_nm_jansson_json_string; +void *_nm_jansson_json_object_iter_next; +void *_nm_jansson_json_loads; +void *_nm_jansson_json_dumps; +void *_nm_jansson_json_object_iter_key; +void *_nm_jansson_json_object; +void *_nm_jansson_json_object_get; +void *_nm_jansson_json_array; +void *_nm_jansson_json_false; +void *_nm_jansson_json_delete; +void *_nm_jansson_json_true; +void *_nm_jansson_json_object_size; +void *_nm_jansson_json_object_set_new; +void *_nm_jansson_json_object_iter; +void *_nm_jansson_json_object_iter_at; +void *_nm_jansson_json_integer_value; +void *_nm_jansson_json_string_value; + +#define TRY_BIND_SYMBOL(symbol) \ + G_STMT_START { \ + void *sym = dlsym (handle, #symbol); \ + if (_nm_jansson_ ## symbol && sym != _nm_jansson_ ## symbol) \ + return FALSE; \ + _nm_jansson_ ## symbol = sym; \ + } G_STMT_END + +static gboolean +bind_symbols (void *handle) +{ + TRY_BIND_SYMBOL (json_object_iter_value); + TRY_BIND_SYMBOL (json_object_key_to_iter); + TRY_BIND_SYMBOL (json_integer); + TRY_BIND_SYMBOL (json_object_del); + TRY_BIND_SYMBOL (json_array_get); + TRY_BIND_SYMBOL (json_array_size); + TRY_BIND_SYMBOL (json_array_append_new); + TRY_BIND_SYMBOL (json_string); + TRY_BIND_SYMBOL (json_object_iter_next); + TRY_BIND_SYMBOL (json_loads); + TRY_BIND_SYMBOL (json_dumps); + TRY_BIND_SYMBOL (json_object_iter_key); + TRY_BIND_SYMBOL (json_object); + TRY_BIND_SYMBOL (json_object_get); + TRY_BIND_SYMBOL (json_array); + TRY_BIND_SYMBOL (json_false); + TRY_BIND_SYMBOL (json_delete); + TRY_BIND_SYMBOL (json_true); + TRY_BIND_SYMBOL (json_object_size); + TRY_BIND_SYMBOL (json_object_set_new); + TRY_BIND_SYMBOL (json_object_iter); + TRY_BIND_SYMBOL (json_object_iter_at); + TRY_BIND_SYMBOL (json_integer_value); + TRY_BIND_SYMBOL (json_string_value); + + return TRUE; +} + +gboolean +nm_jansson_load (void) +{ + static enum { + UNKNOWN, + AVAILABLE, + MISSING, + } state = UNKNOWN; + void *handle; + + if (G_LIKELY (state != UNKNOWN)) + goto out; + + /* First just resolve the symbols to see if there's a conflict already. */ + if (!bind_symbols (RTLD_DEFAULT)) + goto out; + + handle = dlopen (JANSSON_SONAME, RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND); + if (!handle) + goto out; + + /* Now do the actual binding. */ + if (!bind_symbols (handle)) + goto out; + + state = AVAILABLE; +out: + return state == AVAILABLE; +} diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h new file mode 100644 index 00000000..513b8d36 --- /dev/null +++ b/libnm-core/nm-json.h @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2017, 2018 Red Hat, Inc. + */ +#ifndef __NM_JSON_H__ +#define __NM_JSON_H__ + +gboolean nm_jansson_load (void); + +#ifndef NM_JANSSON_C +#define json_object_iter_value (*_nm_jansson_json_object_iter_value) +#define json_object_key_to_iter (*_nm_jansson_json_object_key_to_iter) +#define json_integer (*_nm_jansson_json_integer) +#define json_object_del (*_nm_jansson_json_object_del) +#define json_array_get (*_nm_jansson_json_array_get) +#define json_array_size (*_nm_jansson_json_array_size) +#define json_array_append_new (*_nm_jansson_json_array_append_new) +#define json_string (*_nm_jansson_json_string) +#define json_object_iter_next (*_nm_jansson_json_object_iter_next) +#define json_loads (*_nm_jansson_json_loads) +#define json_dumps (*_nm_jansson_json_dumps) +#define json_object_iter_key (*_nm_jansson_json_object_iter_key) +#define json_object (*_nm_jansson_json_object) +#define json_object_get (*_nm_jansson_json_object_get) +#define json_array (*_nm_jansson_json_array) +#define json_false (*_nm_jansson_json_false) +#define json_delete (*_nm_jansson_json_delete) +#define json_true (*_nm_jansson_json_true) +#define json_object_size (*_nm_jansson_json_object_size) +#define json_object_set_new (*_nm_jansson_json_object_set_new) +#define json_object_iter (*_nm_jansson_json_object_iter) +#define json_object_iter_at (*_nm_jansson_json_object_iter_at) +#define json_integer_value (*_nm_jansson_json_integer_value) +#define json_string_value (*_nm_jansson_json_string_value) +#endif + +#endif /* __NM_JSON_H__ */ diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c index 26e7f505..d9c0cbb1 100644 --- a/libnm-core/nm-setting-ip4-config.c +++ b/libnm-core/nm-setting-ip4-config.c @@ -191,7 +191,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->dhcp_client_id && !strlen (priv->dhcp_client_id)) { + if (priv->dhcp_client_id && !priv->dhcp_client_id[0]) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, diff --git a/libnm-core/nm-setting-tc-config.h b/libnm-core/nm-setting-tc-config.h index 736d5ad1..8117d5ea 100644 --- a/libnm-core/nm-setting-tc-config.h +++ b/libnm-core/nm-setting-tc-config.h @@ -28,7 +28,6 @@ G_BEGIN_DECLS -NM_AVAILABLE_IN_1_10_2 typedef struct NMTCQdisc NMTCQdisc; NM_AVAILABLE_IN_1_10_2 @@ -61,7 +60,6 @@ void nm_tc_qdisc_set_handle (NMTCQdisc *qdisc, NM_AVAILABLE_IN_1_10_2 guint32 nm_tc_qdisc_get_parent (NMTCQdisc *qdisc); -NM_AVAILABLE_IN_1_10_2 typedef struct NMTCAction NMTCAction; NM_AVAILABLE_IN_1_10_2 @@ -96,7 +94,6 @@ void nm_tc_action_set_attribute (NMTCAction *action, const char *name, GVariant *value); -NM_AVAILABLE_IN_1_10_2 typedef struct NMTCTfilter NMTCTfilter; NM_AVAILABLE_IN_1_10_2 diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c index d3688278..60bdf5f6 100644 --- a/libnm-core/nm-setting-team.c +++ b/libnm-core/nm-setting-team.c @@ -249,7 +249,7 @@ nm_team_link_watcher_new_arp_ping (gint init_wait, return NULL; } - if (strpbrk (target_host, " \\/\t=\"\'")) { + if (strpbrk (source_host, " \\/\t=\"\'")) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED, _("source-host '%s' contains invalid characters"), source_host); return NULL; @@ -1485,8 +1485,10 @@ set_property (GObject *object, guint prop_id, break; } - if (align_config) + if (align_config) { _nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], align_value); + _align_team_properties (setting); + } } static void @@ -1654,6 +1656,10 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class) * Corresponds to the teamd runner.name. * Permitted values are: "roundrobin", "broadcast", "activebackup", * "loadbalance", "lacp". + * When setting the runner, all the properties specific to the runner + * will be reset to the default value; all the properties specific to + * other runners will be set to an empty value (or if not possible to + * a default value). * * Since: 1.10.2 **/ diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h index 45207836..d423901f 100644 --- a/libnm-core/nm-setting-team.h +++ b/libnm-core/nm-setting-team.h @@ -150,7 +150,7 @@ NMTeamLinkWatcherArpPingFlags nm_team_link_watcher_get_flags (NMTeamLinkWatcher #define NM_SETTING_TEAM_RUNNER_DEFAULT NM_SETTING_TEAM_RUNNER_ROUNDROBIN #define NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL #define NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT 50 -#define NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT 255 +#define NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT 65535 #define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c index 6b42e0c7..34607252 100644 --- a/libnm-core/nm-setting-vpn.c +++ b/libnm-core/nm-setting-vpn.c @@ -614,17 +614,15 @@ need_secrets (NMSetting *setting) } static gboolean -compare_one_secret (NMSettingVpn *a, - NMSettingVpn *b, - NMSettingCompareFlags flags) +_compare_secrets (NMSettingVpn *a, + NMSettingVpn *b, + NMSettingCompareFlags flags) { - GHashTable *a_secrets, *b_secrets; + GHashTable *a_secrets; GHashTableIter iter; const char *key, *val; a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets; - b_secrets = NM_SETTING_VPN_GET_PRIVATE (b)->secrets; - g_hash_table_iter_init (&iter, a_secrets); while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) { NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE; @@ -653,6 +651,19 @@ compare_one_secret (NMSettingVpn *a, return TRUE; } +static gboolean +compare_one_secret (NMSettingVpn *a, + NMSettingVpn *b, + NMSettingCompareFlags flags) +{ + if (!_compare_secrets (a, b, flags)) + return FALSE; + if (!_compare_secrets (b, a, flags)) + return FALSE; + + return TRUE; +} + static gboolean compare_property (NMSetting *setting, NMSetting *other, diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 6cc92ec8..ebbbfd3a 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -36,6 +36,7 @@ #include #if WITH_JANSSON +#include "nm-json.h" #include #endif @@ -650,36 +651,82 @@ _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer ne } gssize -_nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data) -{ - gssize imin, imax, imid; +_nm_utils_ptrarray_find_binary_search (gconstpointer *list, + gsize len, + gconstpointer needle, + GCompareDataFunc cmpfcn, + gpointer user_data, + gssize *out_idx_first, + gssize *out_idx_last) +{ + gssize imin, imax, imid, i2min, i2max, i2mid; int cmp; g_return_val_if_fail (list || !len, ~((gssize) 0)); g_return_val_if_fail (cmpfcn, ~((gssize) 0)); imin = 0; - if (len == 0) - return ~imin; - - imax = len - 1; - - while (imin <= imax) { - imid = imin + (imax - imin) / 2; - - cmp = cmpfcn (list[imid], needle, user_data); - if (cmp == 0) - return imid; + if (len > 0) { + imax = len - 1; + + while (imin <= imax) { + imid = imin + (imax - imin) / 2; + + cmp = cmpfcn (list[imid], needle, user_data); + if (cmp == 0) { + /* we found a matching entry at index imid. + * + * Does the caller request the first/last index as well (in case that + * there are multiple entries which compare equal). */ + + if (out_idx_first) { + i2min = imin; + i2max = imid + 1; + while (i2min <= i2max) { + i2mid = i2min + (i2max - i2min) / 2; + + cmp = cmpfcn (list[i2mid], needle, user_data); + if (cmp == 0) + i2max = i2mid -1; + else { + nm_assert (cmp < 0); + i2min = i2mid + 1; + } + } + *out_idx_first = i2min; + } + if (out_idx_last) { + i2min = imid + 1; + i2max = imax; + while (i2min <= i2max) { + i2mid = i2min + (i2max - i2min) / 2; + + cmp = cmpfcn (list[i2mid], needle, user_data); + if (cmp == 0) + i2min = i2mid + 1; + else { + nm_assert (cmp > 0); + i2max = i2mid - 1; + } + } + *out_idx_last = i2min - 1; + } + return imid; + } - if (cmp < 0) - imin = imid + 1; - else - imax = imid - 1; + if (cmp < 0) + imin = imid + 1; + else + imax = imid - 1; + } } /* return the inverse of @imin. This is a negative number, but * also is ~imin the position where the value should be inserted. */ - return ~imin; + imin = ~imin; + NM_SET_OUT (out_idx_first, imin); + NM_SET_OUT (out_idx_last, imin); + return imin; } gssize @@ -4845,6 +4892,41 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to) /*****************************************************************************/ +static gboolean +_nm_utils_is_json_object_no_validation (const char *str, GError **error) +{ + if (str) { + /* libjansson also requires only utf-8 encoding. */ + if (!g_utf8_validate (str, -1, NULL)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("not valid utf-8")); + return FALSE; + } + while (g_ascii_isspace (str[0])) + str++; + } + + /* do some very basic validation to see if this might be a JSON object. */ + if (str[0] == '{') { + gsize l; + + l = strlen (str) - 1; + while (l > 0 && g_ascii_isspace (str[l])) + l--; + + if (str[l] == '}') + return TRUE; + } + + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("is not a JSON object")); + return FALSE; +} + #if WITH_JANSSON /* Added in Jansson v2.3 (released Jan 27 2012) */ @@ -5151,9 +5233,9 @@ _json_team_normalize_defaults (json_t *json, gboolean reset) _json_delete_object_on_string_match (json, "runner", "hwaddr_policy", NULL, NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT); } else if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) { - runner_tx_balancer_interval = 50; + runner_tx_balancer_interval = NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT; runner_active = TRUE; - runner_sys_prio = 255; + runner_sys_prio = NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT; runner_min_ports = 0; _json_delete_object_on_string_match (json, "runner", "agg_select_policy", NULL, NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT); @@ -5185,13 +5267,16 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element) g_return_val_if_fail (json_element, NULL); json_object_foreach (json_element, j_key, j_val) { - if (nm_streq (j_key, "name")) + if (nm_streq (j_key, "name")) { + g_free (name); name = strdup (json_string_value (j_val)); - else if (nm_streq (j_key, "target_host")) + } else if (nm_streq (j_key, "target_host")) { + g_free (target_host); target_host = strdup (json_string_value (j_val)); - else if (nm_streq (j_key, "source_host")) + } else if (nm_streq (j_key, "source_host")) { + g_free (source_host); source_host = strdup (json_string_value (j_val)); - else if (NM_IN_STRSET (j_key, "delay_up", "init_wait")) + } else if (NM_IN_STRSET (j_key, "delay_up", "init_wait")) val1 = json_integer_value (j_val); else if (NM_IN_STRSET (j_key, "delay_down", "interval")) val2 = json_integer_value (j_val); @@ -5314,6 +5399,9 @@ nm_utils_is_json_object (const char *str, GError **error) return FALSE; } + if (!nm_jansson_load ()) + return _nm_utils_is_json_object_no_validation (str, error); + json = json_loads (str, JSON_REJECT_DUPLICATES, &jerror); if (!json) { g_set_error (error, @@ -5364,6 +5452,8 @@ _nm_utils_team_config_equal (const char *conf1, if (nm_streq0 (conf1, conf2)) return TRUE; + else if (!nm_jansson_load ()) + return FALSE; /* A NULL configuration is equivalent to default value '{}' */ json1 = json_loads (conf1 ?: "{}", JSON_REJECT_DUPLICATES, &jerror); @@ -5421,6 +5511,9 @@ _nm_utils_team_config_get (const char *conf, if (!key) return NULL; + if (!nm_jansson_load ()) + return NULL; + json = json_loads (conf ?: "{}", JSON_REJECT_DUPLICATES, &jerror); /* Invalid json in conf */ @@ -5528,6 +5621,9 @@ _nm_utils_team_config_set (char **conf, g_return_val_if_fail (key, FALSE); + if (!nm_jansson_load ()) + return FALSE; + json = json_loads (*conf?: "{}", JSON_REJECT_DUPLICATES, &jerror); if (!json) return FALSE; @@ -5645,19 +5741,6 @@ nm_utils_is_json_object (const char *str, GError **error) { g_return_val_if_fail (!error || !*error, FALSE); - if (str) { - /* libjansson also requires only utf-8 encoding. */ - if (!g_utf8_validate (str, -1, NULL)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("not valid utf-8")); - return FALSE; - } - while (g_ascii_isspace (str[0])) - str++; - } - if (!str || !str[0]) { g_set_error_literal (error, NM_CONNECTION_ERROR, @@ -5666,23 +5749,7 @@ nm_utils_is_json_object (const char *str, GError **error) return FALSE; } - /* do some very basic validation to see if this might be a JSON object. */ - if (str[0] == '{') { - gsize l; - - l = strlen (str) - 1; - while (l > 0 && g_ascii_isspace (str[l])) - l--; - - if (str[l] == '}') - return TRUE; - } - - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_INVALID_PROPERTY, - _("is not a JSON object")); - return FALSE; + return _nm_utils_is_json_object_no_validation (str, error); } gboolean diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index d743fd22..520ed581 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -6139,7 +6139,7 @@ static void _test_find_binary_search_do (const int *array, gsize len) { gsize i; - gssize idx; + gssize idx, idx_first, idx_last; gs_free gconstpointer *parray = g_new (gconstpointer, len); const int NEEDLE = 0; gconstpointer pneedle = GINT_TO_POINTER (NEEDLE); @@ -6150,10 +6150,10 @@ _test_find_binary_search_do (const int *array, gsize len) expected_result = _nm_utils_ptrarray_find_first (parray, len, pneedle); - idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL); - if (expected_result >= 0) + idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL, &idx_first, &idx_last); + if (expected_result >= 0) { g_assert_cmpint (expected_result, ==, idx); - else { + } else { gssize idx2 = ~idx; g_assert_cmpint (idx, <, 0); @@ -6162,6 +6162,8 @@ _test_find_binary_search_do (const int *array, gsize len) g_assert (idx2 - 1 < 0 || _test_find_binary_search_cmp (parray[idx2 - 1], pneedle, NULL) < 0); g_assert (idx2 >= len || _test_find_binary_search_cmp (parray[idx2], pneedle, NULL) > 0); } + g_assert_cmpint (idx, ==, idx_first); + g_assert_cmpint (idx, ==, idx_last); for (i = 0; i < len; i++) { int cmp; @@ -6262,6 +6264,94 @@ test_nm_utils_ptrarray_find_binary_search (void) test_find_binary_search_do (-3, -2, -1, 1, 2, 3, 4); } +/*****************************************************************************/ + +#define BIN_SEARCH_W_DUPS_LEN 100 +#define BIN_SEARCH_W_DUPS_JITTER 10 + +static int +_test_bin_search2_cmp (gconstpointer pa, + gconstpointer pb, + gpointer user_data) +{ + int a = GPOINTER_TO_INT (pa); + int b = GPOINTER_TO_INT (pb); + + g_assert (a >= 0 && a <= BIN_SEARCH_W_DUPS_LEN + BIN_SEARCH_W_DUPS_JITTER); + g_assert (b >= 0 && b <= BIN_SEARCH_W_DUPS_LEN + BIN_SEARCH_W_DUPS_JITTER); + NM_CMP_DIRECT (a, b); + return 0; +} + +static int +_test_bin_search2_cmp_p (gconstpointer pa, + gconstpointer pb, + gpointer user_data) +{ + return _test_bin_search2_cmp (*((gpointer *) pa), *((gpointer *) pb), NULL); +} + +static void +test_nm_utils_ptrarray_find_binary_search_with_duplicates (void) +{ + gssize idx, idx2, idx_first2, idx_first, idx_last; + int i_test, i_len, i; + gssize j; + gconstpointer arr[BIN_SEARCH_W_DUPS_LEN]; + const int N_TEST = 10; + + for (i_test = 0; i_test < N_TEST; i_test++) { + for (i_len = 0; i_len < BIN_SEARCH_W_DUPS_LEN; i_len++) { + + /* fill with random numbers... surely there are some duplicates + * there... or maybe even there are none... */ + for (i = 0; i < i_len; i++) + arr[i] = GINT_TO_POINTER (nmtst_get_rand_int () % (i_len + BIN_SEARCH_W_DUPS_JITTER)); + g_qsort_with_data (arr, + i_len, + sizeof (gpointer), + _test_bin_search2_cmp_p, + NULL); + for (i = 0; i < i_len + BIN_SEARCH_W_DUPS_JITTER; i++) { + gconstpointer p = GINT_TO_POINTER (i); + + idx = _nm_utils_ptrarray_find_binary_search (arr, i_len, p, _test_bin_search2_cmp, NULL, &idx_first, &idx_last); + + idx_first2 = _nm_utils_ptrarray_find_first (arr, i_len, p); + + idx2 = _nm_utils_array_find_binary_search (arr, sizeof (gpointer), i_len, &p, _test_bin_search2_cmp_p, NULL); + g_assert_cmpint (idx, ==, idx2); + + if (idx_first2 < 0) { + g_assert_cmpint (idx, <, 0); + g_assert_cmpint (idx, ==, idx_first); + g_assert_cmpint (idx, ==, idx_last); + idx = ~idx; + g_assert_cmpint (idx, >=, 0); + g_assert_cmpint (idx, <=, i_len); + if (i_len == 0) + g_assert_cmpint (idx, ==, 0); + else { + g_assert (idx == i_len || GPOINTER_TO_INT (arr[idx]) > i); + g_assert (idx == 0 || GPOINTER_TO_INT (arr[idx - 1]) < i); + } + } else { + g_assert_cmpint (idx_first, ==, idx_first2); + g_assert_cmpint (idx_first, >=, 0); + g_assert_cmpint (idx_last, <, i_len); + g_assert_cmpint (idx_first, <=, idx_last); + g_assert_cmpint (idx, >=, idx_first); + g_assert_cmpint (idx, <=, idx_last); + for (j = idx_first; j < idx_last; j++) + g_assert (GPOINTER_TO_INT (arr[j]) == i); + g_assert (idx_first == 0 || GPOINTER_TO_INT (arr[idx_first - 1]) < i); + g_assert (idx_last == i_len - 1 || GPOINTER_TO_INT (arr[idx_last + 1]) > i); + } + } + } + } +} + /*****************************************************************************/ static void test_nm_utils_enum_from_str_do (GType type, const char *str, @@ -6952,6 +7042,7 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert); g_test_add_func ("/core/general/_glib_compat_g_hash_table_get_keys_as_array", test_g_hash_table_get_keys_as_array); g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search); + g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates); g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey); g_test_add_func ("/core/general/nm_ptrarray_len", test_nm_ptrarray_len); -- cgit 1.3.0-6-gf8a5