From 5d125e83afd7a4e2246ff7484eea78a1ce063d67 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Tue, 2 Jul 2013 11:43:47 -0400 Subject: [PATCH 1/1] bond: add proper properties to NMSettingBond Make NMSettingBond have individual properties like other settings types. https://bugzilla.redhat.com/show_bug.cgi?id=1032808 --- cli/src/connections.c | 47 +- cli/src/settings.c | 45 +- cli/src/utils.h | 1 + libnm-util/libnm-util.ver | 23 +- libnm-util/nm-setting-bond.c | 1859 ++++++++++++++++---- libnm-util/nm-setting-bond.h | 103 +- libnm-util/tests/test-general.c | 8 +- src/devices/nm-device-bond.c | 160 +- src/settings/plugins/ifcfg-rh/reader.c | 171 +- .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 6 +- src/settings/plugins/ifcfg-rh/writer.c | 29 +- src/settings/plugins/keyfile/reader.c | 2 +- 12 files changed, 1836 insertions(+), 618 deletions(-) diff --git a/cli/src/connections.c b/cli/src/connections.c index 6a75a5c..6e5807b 100644 --- a/cli/src/connections.c +++ b/cli/src/connections.c @@ -3698,111 +3698,112 @@ cleanup_vlan: } else if (!strcmp (con_type, NM_SETTING_BOND_SETTING_NAME)) { /* Build up the settings required for 'bond' */ gboolean success = FALSE; char *bond_ifname = NULL; const char *ifname = NULL; const char *bond_mode_c = NULL; char *bond_mode = NULL; const char *bond_primary_c = NULL; char *bond_primary = NULL; const char *bond_miimon_c = NULL; char *bond_miimon = NULL; const char *bond_downdelay_c = NULL; char *bond_downdelay = NULL; const char *bond_updelay_c = NULL; char *bond_updelay = NULL; const char *bond_arpinterval_c = NULL; char *bond_arpinterval = NULL; const char *bond_arpiptarget_c = NULL; char *bond_arpiptarget = NULL; - nmc_arg_t exp_args[] = { {"mode", TRUE, &bond_mode_c, FALSE}, - {"primary", TRUE, &bond_primary_c, FALSE}, - {"miimon", TRUE, &bond_miimon_c, FALSE}, - {"downdelay", TRUE, &bond_downdelay_c, FALSE}, - {"updelay", TRUE, &bond_updelay_c, FALSE}, - {"arp-interval", TRUE, &bond_arpinterval_c, FALSE}, - {"arp-ip-target", TRUE, &bond_arpiptarget_c, FALSE}, + nmc_arg_t exp_args[] = { {NM_SETTING_BOND_MODE, TRUE, &bond_mode_c, FALSE, FALSE, &bond_mode}, + {NM_SETTING_BOND_PRIMARY, TRUE, &bond_primary_c, FALSE, FALSE, &bond_primary}, + {NM_SETTING_BOND_MIIMON, TRUE, &bond_miimon_c, FALSE, FALSE, &bond_miimon}, + {NM_SETTING_BOND_DOWNDELAY, TRUE, &bond_downdelay_c, FALSE, FALSE, &bond_downdelay}, + {NM_SETTING_BOND_UPDELAY, TRUE, &bond_updelay_c, FALSE, FALSE, &bond_updelay}, + {NM_SETTING_BOND_ARP_INTERVAL, TRUE, &bond_arpinterval_c, FALSE, FALSE, &bond_arpinterval}, + {NM_SETTING_BOND_ARP_IP_TARGET, TRUE, &bond_arpiptarget_c, FALSE, FALSE, &bond_arpiptarget}, {NULL} }; + nmc_arg_t *option; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; /* Also ask for all optional arguments if '--ask' is specified. */ bond_mode = bond_mode_c ? g_strdup (bond_mode_c) : NULL; bond_primary = bond_primary_c ? g_strdup (bond_primary_c) : NULL; bond_miimon = bond_miimon_c ? g_strdup (bond_miimon_c) : NULL; bond_downdelay = bond_downdelay_c ? g_strdup (bond_downdelay_c) : NULL; bond_updelay = bond_updelay_c ? g_strdup (bond_updelay_c) : NULL; bond_arpinterval = bond_arpinterval_c ? g_strdup (bond_arpinterval_c) : NULL; bond_arpiptarget = bond_arpiptarget_c ? g_strdup (bond_arpiptarget_c) : NULL; if (ask) do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon, &bond_downdelay, &bond_updelay, &bond_arpinterval, &bond_arpiptarget); /* Use connection's ifname as 'bond' ifname if exists, else generate one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) bond_ifname = unique_master_iface_ifname (all_connections, NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_INTERFACE_NAME, "nm-bond"); else bond_ifname = g_strdup (ifname); /* Add 'bond' setting */ s_bond = (NMSettingBond *) nm_setting_bond_new (); nm_connection_add_setting (connection, NM_SETTING (s_bond)); /* Set bond options */ g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, bond_ifname, NULL); + if (bond_mode) { + /* resolve the bond option. */ GError *err = NULL; const char *bm; if (!(bm = nmc_bond_validate_mode (bond_mode, &err))) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'mode': %s."), err->message); g_clear_error (&err); goto cleanup_bond; } - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, bm); + g_free (bond_mode); + bond_mode = g_strdup (bm); } - if (bond_primary) { - if (!nm_utils_iface_valid_name (bond_primary)) { + for(option = exp_args; option->name; option++) { + GError *err = NULL; + const char *value = *((const char **) option->value); + if (value && !nm_setting_bond_validate_string (option->name, value, &err)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: 'primary': '%s' is not a valid interface name."), - bond_primary); + _("Error: '%s': %s."), option->name, err->message); + g_clear_error (&err); goto cleanup_bond; } - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_PRIMARY, bond_primary); } - if (bond_miimon) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, bond_miimon); - if (bond_downdelay && strcmp (bond_downdelay, "0") != 0) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, bond_downdelay); - if (bond_updelay && strcmp (bond_updelay, "0") != 0) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, bond_updelay); - if (bond_arpinterval && strcmp (bond_arpinterval, "0") != 0) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL, bond_arpinterval); - if (bond_arpiptarget) - nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, bond_arpiptarget); + for(option = exp_args; option->name; option++) { + const char *value = *((const char **) option->value); + + if (value) + nm_setting_bond_set_string (s_bond, option->name, *((char **) option->value)); + } success = TRUE; cleanup_bond: g_free (bond_ifname); g_free (bond_mode); g_free (bond_primary); g_free (bond_miimon); g_free (bond_downdelay); g_free (bond_updelay); g_free (bond_arpinterval); g_free (bond_arpiptarget); if (!success) return FALSE; } else if (!strcmp (con_type, "bond-slave")) { /* Build up the settings required for 'bond-slave' */ const char *master = NULL; char *master_ask = NULL; const char *type = NULL; nmc_arg_t exp_args[] = { {"master", TRUE, &master, !ask}, diff --git a/cli/src/settings.c b/cli/src/settings.c index 712622b..3d2aa93 100644 --- a/cli/src/settings.c +++ b/cli/src/settings.c @@ -977,50 +977,50 @@ nmc_property_802_1X_get_phase2_private_key (NMSetting *setting) DEFINE_GETTER (nmc_property_adsl_get_username, NM_SETTING_ADSL_USERNAME) DEFINE_GETTER (nmc_property_adsl_get_password, NM_SETTING_ADSL_PASSWORD) DEFINE_SECRET_FLAGS_GETTER (nmc_property_adsl_get_password_flags, NM_SETTING_ADSL_PASSWORD_FLAGS) DEFINE_GETTER (nmc_property_adsl_get_protocol, NM_SETTING_ADSL_PROTOCOL) DEFINE_GETTER (nmc_property_adsl_get_encapsulation, NM_SETTING_ADSL_ENCAPSULATION) DEFINE_GETTER (nmc_property_adsl_get_vpi, NM_SETTING_ADSL_VPI) DEFINE_GETTER (nmc_property_adsl_get_vci, NM_SETTING_ADSL_VCI) /* --- NM_SETTING_BLUETOOTH_SETTING_NAME property get functions --- */ DEFINE_HWADDR_GETTER (nmc_property_bluetooth_get_bdaddr, NM_SETTING_BLUETOOTH_BDADDR) DEFINE_GETTER (nmc_property_bluetooth_get_type, NM_SETTING_BLUETOOTH_TYPE) /* --- NM_SETTING_BOND_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_bond_get_interface_name, NM_SETTING_BOND_INTERFACE_NAME) static char * nmc_property_bond_get_options (NMSetting *setting) { NMSettingBond *s_bond = NM_SETTING_BOND (setting); GString *bond_options_s; - int i; + const char *const* kernel_names; bond_options_s = g_string_new (NULL); - for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) { - const char *key, *value; - - nm_setting_bond_get_option (s_bond, i, &key, &value); - g_string_append_printf (bond_options_s, "%s=%s,", key, value); + kernel_names = nm_setting_bond_get_kernel_names (); + for (; *kernel_names; kernel_names++) { + g_string_append_printf (bond_options_s, "%s=%s,", *kernel_names, + nm_setting_bond_get_string (s_bond, *kernel_names)); } - g_string_truncate (bond_options_s, bond_options_s->len-1); /* chop off trailing ',' */ + if (bond_options_s->len > 0) + g_string_truncate (bond_options_s, bond_options_s->len-1); /* chop off trailing ',' */ return g_string_free (bond_options_s, FALSE); } /* --- NM_SETTING_BRIDGE_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_bridge_get_interface_name, NM_SETTING_BRIDGE_INTERFACE_NAME) DEFINE_GETTER (nmc_property_bridge_get_stp, NM_SETTING_BRIDGE_STP) DEFINE_GETTER (nmc_property_bridge_get_priority, NM_SETTING_BRIDGE_PRIORITY) DEFINE_GETTER (nmc_property_bridge_get_forward_delay, NM_SETTING_BRIDGE_FORWARD_DELAY) DEFINE_GETTER (nmc_property_bridge_get_hello_time, NM_SETTING_BRIDGE_HELLO_TIME) DEFINE_GETTER (nmc_property_bridge_get_max_age, NM_SETTING_BRIDGE_MAX_AGE) DEFINE_GETTER (nmc_property_bridge_get_ageing_time, NM_SETTING_BRIDGE_AGEING_TIME) /* --- NM_SETTING_BRIDGE_PORT_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_bridge_port_get_priority, NM_SETTING_BRIDGE_PORT_PRIORITY) DEFINE_GETTER (nmc_property_bridge_port_get_path_cost, NM_SETTING_BRIDGE_PORT_PATH_COST) DEFINE_GETTER (nmc_property_bridge_port_get_hairpin_mode, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE) /* --- NM_SETTING_TEAM_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_team_get_interface_name, NM_SETTING_TEAM_INTERFACE_NAME) @@ -2462,107 +2462,114 @@ DEFINE_ALLOWED_VAL_FUNC (nmc_property_adsl_allowed_encapsulation, adsl_valid_enc /* --- NM_SETTING_BLUETOOTH_SETTING_NAME property setter functions --- */ /* 'type' */ static gboolean nmc_property_bluetooth_set_type (NMSetting *setting, const char *prop, const char *val, GError **error) { const char *types[] = { NM_SETTING_BLUETOOTH_TYPE_DUN, NM_SETTING_BLUETOOTH_TYPE_PANU, NULL }; return check_and_set_string (setting, prop, val, types, error); } /* --- NM_SETTING_BOND_SETTING_NAME property setter functions --- */ /* 'options' */ /* example: miimon=100,mode=balance-rr, updelay=5 */ static gboolean _validate_and_remove_bond_option (NMSettingBond *setting, const char *option) { const char *opt; - const char **valid_options; + const char *const*valid_options; - valid_options = nm_setting_bond_get_valid_options (setting); - opt = nmc_string_is_valid (option, valid_options, NULL); + valid_options = nm_setting_bond_get_kernel_names (); + opt = nmc_string_is_valid (option, (const char **) valid_options, NULL); - if (opt) - return nm_setting_bond_remove_option (setting, opt); - else + if (!opt) return FALSE; + + nm_setting_bond_set_default (setting, opt); + return TRUE; } /* Validate bonding 'options' values */ static const char * _validate_bond_option_value (const char *option, const char *value, GError **error) { if (!g_strcmp0 (option, NM_SETTING_BOND_OPTION_MODE)) return nmc_bond_validate_mode (value, error); return value; } +static const char ** +_nm_setting_bond_get_kernel_names (NMSettingBond *bond) +{ + return (const char **) nm_setting_bond_get_kernel_names (); +} + DEFINE_SETTER_OPTIONS (nmc_property_bond_set_options, NM_SETTING_BOND, NMSettingBond, - nm_setting_bond_add_option, - nm_setting_bond_get_valid_options, + nm_setting_bond_set_string, + _nm_setting_bond_get_kernel_names, _validate_bond_option_value) DEFINE_REMOVER_OPTION (nmc_property_bond_remove_option_options, NM_SETTING_BOND, _validate_and_remove_bond_option) static const char * nmc_property_bond_describe_options (NMSetting *setting, const char *prop) { static char *desc = NULL; - const char **valid_options; + const char *const*valid_options; char *options_str; if (G_UNLIKELY (desc == NULL)) { - valid_options = nm_setting_bond_get_valid_options (NM_SETTING_BOND (setting)); + valid_options = nm_setting_bond_get_kernel_names (); options_str = g_strjoinv (", ", (char **) valid_options); desc = g_strdup_printf (_("Enter a list of bonding options formatted as:\n" " option = , option = ,... \n" "Valid options are: %s\n" "'mode' can be provided as a name or a number:\n" "balance-rr = 0\n" "active-backup = 1\n" "balance-xor = 2\n" "broadcast = 3\n" "802.3ad = 4\n" "balance-tlb = 5\n" "balance-alb = 6\n\n" "Example: mode=2,miimon=120\n"), options_str); g_free (options_str); } return desc; } static const char * nmc_property_bond_allowed_options (NMSetting *setting, const char *prop) { - const char **valid_options; + const char *const*valid_options; static char *allowed_vals = NULL; if (G_UNLIKELY (allowed_vals == NULL)) { - valid_options = nm_setting_bond_get_valid_options (NM_SETTING_BOND (setting)); + valid_options = nm_setting_bond_get_kernel_names (); allowed_vals = g_strjoinv (", ", (char **) valid_options); } return allowed_vals; } /* --- NM_SETTING_INFINIBAND_SETTING_NAME property setter functions --- */ /* 'mac-addresss' */ static gboolean nmc_property_ib_set_mac (NMSetting *setting, const char *prop, const char *val, GError **error) { GByteArray *array; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); array = nm_utils_hwaddr_atoba (val, ARPHRD_INFINIBAND); if (!array) { g_set_error (error, 1, 0, _("'%s' is not a valid InfiniBand MAC"), val); return FALSE; } diff --git a/cli/src/utils.h b/cli/src/utils.h index c1c8824..d6d6654 100644 --- a/cli/src/utils.h +++ b/cli/src/utils.h @@ -15,40 +15,41 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * (C) Copyright 2010 - 2013 Red Hat, Inc. */ #ifndef NMC_UTILS_H #define NMC_UTILS_H #include #include "nmcli.h" /* === Types === */ typedef struct { const char *name; gboolean has_value; const char **value; gboolean mandatory; gboolean found; + void *user_data; } nmc_arg_t; /* === Functions === */ int matches (const char *cmd, const char *pattern); int next_arg (int *argc, char ***argv); gboolean nmc_arg_is_help (const char *arg); gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***argv, GError **error); char *ssid_to_hex (const char *str, gsize len); gboolean nmc_string_to_int_base (const char *str, int base, gboolean range_check, long int min, long int max, long int *value); gboolean nmc_string_to_uint_base (const char *str, int base, gboolean range_check, unsigned long int min, unsigned long int max, unsigned long int *value); diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index bcaa426..00844df 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -182,50 +182,71 @@ global: nm_setting_adsl_error_get_type; nm_setting_adsl_error_quark; nm_setting_adsl_get_encapsulation; nm_setting_adsl_get_password; nm_setting_adsl_get_password_flags; nm_setting_adsl_get_protocol; nm_setting_adsl_get_type; nm_setting_adsl_get_username; nm_setting_adsl_get_vci; nm_setting_adsl_get_vpi; nm_setting_adsl_new; nm_setting_bluetooth_error_get_type; nm_setting_bluetooth_error_quark; nm_setting_bluetooth_get_bdaddr; nm_setting_bluetooth_get_connection_type; nm_setting_bluetooth_get_type; nm_setting_bluetooth_new; nm_setting_bond_add_option; nm_setting_bond_error_get_type; nm_setting_bond_error_quark; + nm_setting_bond_get_ad_select; + nm_setting_bond_get_arp_interval; + nm_setting_bond_get_arp_ip_target; + nm_setting_bond_get_arp_validate; + nm_setting_bond_get_downdelay; + nm_setting_bond_get_fail_over_mac; nm_setting_bond_get_interface_name; + nm_setting_bond_get_kernel_name; + nm_setting_bond_get_kernel_names; + nm_setting_bond_get_miimon; + nm_setting_bond_get_mode; nm_setting_bond_get_num_options; nm_setting_bond_get_option; nm_setting_bond_get_option_by_name; nm_setting_bond_get_option_default; + nm_setting_bond_get_primary; + nm_setting_bond_get_primary_reselect; + nm_setting_bond_get_property_name; + nm_setting_bond_get_resend_igmp; + nm_setting_bond_get_string; nm_setting_bond_get_type; + nm_setting_bond_get_updelay; + nm_setting_bond_get_use_carrier; nm_setting_bond_get_valid_options; + nm_setting_bond_get_xmit_hash_policy; + nm_setting_bond_is_default; nm_setting_bond_new; nm_setting_bond_remove_option; - nm_setting_bond_validate_option; + nm_setting_bond_set_default; + nm_setting_bond_set_string; + nm_setting_bond_validate_string; nm_setting_bridge_error_get_type; nm_setting_bridge_error_quark; nm_setting_bridge_get_ageing_time; nm_setting_bridge_get_forward_delay; nm_setting_bridge_get_hello_time; nm_setting_bridge_get_interface_name; nm_setting_bridge_get_max_age; nm_setting_bridge_get_priority; nm_setting_bridge_get_stp; nm_setting_bridge_get_type; nm_setting_bridge_new; nm_setting_bridge_port_error_get_type; nm_setting_bridge_port_error_quark; nm_setting_bridge_port_get_hairpin_mode; nm_setting_bridge_port_get_path_cost; nm_setting_bridge_port_get_priority; nm_setting_bridge_port_get_type; nm_setting_bridge_port_new; nm_setting_cdma_error_get_type; nm_setting_cdma_error_quark; diff --git a/libnm-util/nm-setting-bond.c b/libnm-util/nm-setting-bond.c index 3fe5f51..6eb4529 100644 --- a/libnm-util/nm-setting-bond.c +++ b/libnm-util/nm-setting-bond.c @@ -17,807 +17,1956 @@ * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * * (C) Copyright 2011 - 2013 Red Hat, Inc. */ #include #include #include #include #include #include #include #include "nm-setting-bond.h" #include "nm-param-spec-specialized.h" #include "nm-utils.h" #include "nm-utils-private.h" #include "nm-dbus-glib-types.h" +#include "nm-glib-compat.h" #include "nm-setting-private.h" /** * SECTION:nm-setting-bond * @short_description: Describes connection properties for bonds * @include: nm-setting-bond.h * * The #NMSettingBond object is a #NMSetting subclass that describes properties * necessary for bond connections. **/ /** * nm_setting_bond_error_quark: * * Registers an error quark for #NMSettingBond if necessary. * * Returns: the error quark used for #NMSettingBond errors. **/ GQuark nm_setting_bond_error_quark (void) { static GQuark quark; if (G_UNLIKELY (!quark)) quark = g_quark_from_static_string ("nm-setting-bond-error-quark"); return quark; } G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING, _nm_register_setting (NM_SETTING_BOND_SETTING_NAME, g_define_type_id, 1, NM_SETTING_BOND_ERROR)) 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 { char *interface_name; + + char *mode; + int miimon; + int downdelay; + int updelay; + int arp_interval; + char **arp_ip_target; + char *arp_validate; + char *primary; + char *primary_reselect; + char *fail_over_mac; + int use_carrier; + char *ad_select; + char *xmit_hash_policy; + int resend_igmp; + GHashTable *options; } NMSettingBondPrivate; enum { PROP_0, PROP_INTERFACE_NAME, + + PROP_MODE, + PROP_MIIMON, + PROP_DOWNDELAY, + PROP_UPDELAY, + PROP_ARP_INTERVAL, + PROP_ARP_IP_TARGET, + PROP_ARP_VALIDATE, + PROP_PRIMARY, + PROP_PRIMARY_RESELECT, + PROP_FAIL_OVER_MAC, + PROP_USE_CARRIER, + PROP_AD_SELECT, + PROP_XMIT_HASH_POLICY, + PROP_RESEND_IGMP, + PROP_OPTIONS, LAST_PROP }; +#define _FIRST_KERNEL_PROP PROP_MODE +#define _LAST_KERNEL_PROP PROP_RESEND_IGMP enum { + TYPE_NONE, /* must be 0, so that it is the default in props if not explicitly set. */ TYPE_INT, TYPE_STR, TYPE_BOTH, TYPE_IP, TYPE_IFNAME, }; typedef struct { - const char *opt; - const char *val; guint opt_type; - guint min; - guint max; - char *list[10]; -} BondDefault; - -static const BondDefault defaults[] = { - { NM_SETTING_BOND_OPTION_MODE, "balance-rr", TYPE_BOTH, 0, 6, - { "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb", NULL } }, - { NM_SETTING_BOND_OPTION_MIIMON, "100", TYPE_INT, 0, G_MAXINT }, - { NM_SETTING_BOND_OPTION_DOWNDELAY, "0", TYPE_INT, 0, G_MAXINT }, - { NM_SETTING_BOND_OPTION_UPDELAY, "0", TYPE_INT, 0, G_MAXINT }, - { NM_SETTING_BOND_OPTION_ARP_INTERVAL, "0", TYPE_INT, 0, G_MAXINT }, - { NM_SETTING_BOND_OPTION_ARP_IP_TARGET, "", TYPE_IP }, - { NM_SETTING_BOND_OPTION_ARP_VALIDATE, "0", TYPE_BOTH, 0, 3, - { "none", "active", "backup", "all", NULL } }, - { NM_SETTING_BOND_OPTION_PRIMARY, "", TYPE_IFNAME }, - { NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, "0", TYPE_BOTH, 0, 2, - { "always", "better", "failure", NULL } }, - { NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, "0", TYPE_BOTH, 0, 2, - { "none", "active", "follow", NULL } }, - { NM_SETTING_BOND_OPTION_USE_CARRIER, "1", TYPE_INT, 0, 1 }, - { NM_SETTING_BOND_OPTION_AD_SELECT, "0", TYPE_BOTH, 0, 2, - { "stable", "bandwidth", "count", NULL } }, - { NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, "0", TYPE_BOTH, 0, 2, - { "layer2", "layer3+4", "layer2+3", NULL } }, - { NM_SETTING_BOND_OPTION_RESEND_IGMP, "1", TYPE_INT, 0, 255 }, + const char *kernel_name; + const char *list[7]; + const char *list_sentinel[1]; /* dummy, to NULL terminate the previous 'list' array */ + GParamSpec *pspec; + char *defval; +} BondProperty; + +static BondProperty props[LAST_PROP] = { + /* specifying the kernel name is only necessary, when it differs from the property name. */ + + [PROP_MODE] = { TYPE_BOTH, NULL, + { "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb" } }, + [PROP_MIIMON] = { TYPE_INT }, + [PROP_DOWNDELAY] = { TYPE_INT }, + [PROP_UPDELAY] = { TYPE_INT }, + [PROP_ARP_INTERVAL] = { TYPE_INT, NM_SETTING_BOND_OPTION_ARP_INTERVAL }, + [PROP_ARP_IP_TARGET] = { TYPE_IP, NM_SETTING_BOND_OPTION_ARP_IP_TARGET }, + [PROP_ARP_VALIDATE] = { TYPE_BOTH, NM_SETTING_BOND_OPTION_ARP_VALIDATE, + { "none", "active", "backup", "all" } }, + [PROP_PRIMARY] = { TYPE_IFNAME }, + [PROP_PRIMARY_RESELECT] = { TYPE_BOTH, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, + { "always", "better", "failure" } }, + [PROP_FAIL_OVER_MAC] = { TYPE_BOTH, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, + { "none", "active", "follow" } }, + [PROP_USE_CARRIER] = { TYPE_INT, NM_SETTING_BOND_OPTION_USE_CARRIER }, + [PROP_AD_SELECT] = { TYPE_BOTH, NM_SETTING_BOND_OPTION_AD_SELECT, + { "stable", "bandwidth", "count" } }, + [PROP_XMIT_HASH_POLICY] = { TYPE_STR, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, + { "layer2", "layer2+3", "layer3+4", "encap2+3", "encap3+4" } }, + [PROP_RESEND_IGMP] = { TYPE_INT, NM_SETTING_BOND_OPTION_RESEND_IGMP }, }; /** * 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_interface_name: * @setting: the #NMSettingBond * * Returns: the #NMSettingBond:interface-name property of the setting **/ const char * -nm_setting_bond_get_interface_name (NMSettingBond *setting) +nm_setting_bond_get_interface_name (const NMSettingBond *setting) { g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); return NM_SETTING_BOND_GET_PRIVATE (setting)->interface_name; } /** - * nm_setting_bond_get_num_options: + * nm_setting_bond_get_mode: * @setting: the #NMSettingBond * - * Returns the number of options that should be set for this bond when it - * is activated. This can be used to retrieve each option individually - * using nm_setting_bond_get_option(). + * Returns: the #NMSettingBond:mode property of the setting * - * Returns: the number of bonding options + * Since: 0.9.10 **/ -guint32 -nm_setting_bond_get_num_options (NMSettingBond *setting) +const char * +nm_setting_bond_get_mode (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->mode; +} + +/** + * nm_setting_bond_get_miimon: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:miimon property of the setting + * + * Since: 0.9.10 + **/ +guint +nm_setting_bond_get_miimon (const NMSettingBond *setting) { g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); - return g_hash_table_size (NM_SETTING_BOND_GET_PRIVATE (setting)->options); + return NM_SETTING_BOND_GET_PRIVATE (setting)->miimon; } /** - * nm_setting_bond_get_option: + * nm_setting_bond_get_downdelay: * @setting: the #NMSettingBond - * @idx: index of the desired option, from 0 to - * nm_setting_bond_get_num_options() - 1 - * @out_name: (out): on return, the name of the bonding option; this - * value is owned by the setting and should not be modified - * @out_value: (out): on return, the value of the name of the bonding - * option; this value is owned by the setting and should not be modified * - * Given an index, return the value of the bonding option at that index. Indexes - * are *not* guaranteed to be static across modifications to options done by - * nm_setting_bond_add_option() and nm_setting_bond_remove_option(), - * and should not be used to refer to options except for short periods of time - * such as during option iteration. + * Returns: the #NMSettingBond:downdelay property of the setting * - * Returns: %TRUE on success if the index was valid and an option was found, - * %FALSE if the index was invalid (ie, greater than the number of options - * currently held by the setting) + * Since: 0.9.10 **/ -gboolean -nm_setting_bond_get_option (NMSettingBond *setting, - guint32 idx, - const char **out_name, - const char **out_value) +guint +nm_setting_bond_get_downdelay (const NMSettingBond *setting) { - NMSettingBondPrivate *priv; - GList *keys; - const char *_key = NULL, *_value = NULL; + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->downdelay; +} + +/** + * nm_setting_bond_get_updelay: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:updelay property of the setting + * + * Since: 0.9.10 + **/ +guint +nm_setting_bond_get_updelay (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->updelay; +} + +/** + * nm_setting_bond_get_arp_interval: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:arp-interval property of the setting + * + * Since: 0.9.10 + **/ +guint +nm_setting_bond_get_arp_interval (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->arp_interval; +} + +/** + * nm_setting_bond_get_arp_ip_target: + * @setting: the #NMSettingBond + * + * Returns: (transfer none): the #NMSettingBond:arp-ip-target property + * of the setting (which belongs to the setting and must not be freed). + * + * Since: 0.9.10 + **/ +const char *const* +nm_setting_bond_get_arp_ip_target (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + return (const char *const*) NM_SETTING_BOND_GET_PRIVATE (setting)->arp_ip_target; +} + +/** + * nm_setting_bond_get_arp_validate: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:arp-validate property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_bond_get_arp_validate (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->arp_validate; +} + +/** + * nm_setting_bond_get_primary: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:primary property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_bond_get_primary (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->primary; +} + +/** + * nm_setting_bond_get_primary_reselect: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:primary-reselect property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_bond_get_primary_reselect (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + return NM_SETTING_BOND_GET_PRIVATE (setting)->primary_reselect; +} + +/** + * nm_setting_bond_get_fail_over_mac: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:fail-over-mac property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_bond_get_fail_over_mac (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + return NM_SETTING_BOND_GET_PRIVATE (setting)->fail_over_mac; +} + +/** + * nm_setting_bond_get_use_carrier: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:use-carrier property of the setting + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_bond_get_use_carrier (const NMSettingBond *setting) +{ g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); - priv = NM_SETTING_BOND_GET_PRIVATE (setting); + return NM_SETTING_BOND_GET_PRIVATE (setting)->use_carrier; +} - if (idx >= nm_setting_bond_get_num_options (setting)) - return FALSE; +/** + * nm_setting_bond_get_ad_select: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:ad-select property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_bond_get_ad_select (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); - keys = g_hash_table_get_keys (priv->options); - _key = g_list_nth_data (keys, idx); - _value = g_hash_table_lookup (priv->options, _key); + return NM_SETTING_BOND_GET_PRIVATE (setting)->ad_select; +} - if (out_name) - *out_name = _key; - if (out_value) - *out_value = _value; +/** + * nm_setting_bond_get_xmit_hash_policy: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:xmit-hash-policy property of the setting + * + * Since: 0.9.10 + **/ +const char * +nm_setting_bond_get_xmit_hash_policy (const NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); - g_list_free (keys); - return TRUE; + return NM_SETTING_BOND_GET_PRIVATE (setting)->xmit_hash_policy; } -static gboolean -validate_int (const char *name, const char *value, const BondDefault *def) +/** + * nm_setting_bond_get_resend_igmp: + * @setting: the #NMSettingBond + * + * Returns: the #NMSettingBond:resend-igmp property of the setting + * + * Since: 0.9.10 + **/ +guint +nm_setting_bond_get_resend_igmp (const NMSettingBond *setting) { - glong num; - guint i; + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); - for (i = 0; i < strlen (value); i++) { - if (!g_ascii_isdigit (value[i]) && value[i] != '-') - return FALSE; + return NM_SETTING_BOND_GET_PRIVATE (setting)->resend_igmp; +} + +/*****************************************************************************/ + +static BondProperty * +find_property_by_pspec (const GParamSpec *pspec, guint *out_idx, gboolean kernel_only) +{ + guint i = kernel_only ? _FIRST_KERNEL_PROP : PROP_0 + 1; + guint end = kernel_only ? _LAST_KERNEL_PROP + 1 : LAST_PROP; + + g_return_val_if_fail (pspec != NULL, NULL); + + for (; i < end; i++) { + if (props[i].pspec == pspec) { + if (out_idx) + *out_idx = i; + return &props[i]; + } } + if (out_idx) + *out_idx = LAST_PROP; + return NULL; +} - errno = 0; - num = strtol (value, NULL, 10); - if (errno) - return FALSE; - if (num < def->min || num > def->max) +/* Depending on kernel_only, find only kernel properties. */ +static BondProperty * +find_property_by_name (const char *name, guint *out_idx, gboolean kernel_only) +{ + guint i = kernel_only ? _FIRST_KERNEL_PROP : PROP_0 + 1; + guint end = kernel_only ? _LAST_KERNEL_PROP + 1 : LAST_PROP; + + g_return_val_if_fail (name != NULL, NULL); + + for (; i < end; i++) { + const char *new_name = g_param_spec_get_name (props[i].pspec); + + if (strcmp (name, new_name) == 0 || g_strcmp0 (name, props[i].kernel_name) == 0) { + if (out_idx) + *out_idx = i; + return &props[i]; + } + } + if (out_idx) + *out_idx = LAST_PROP; + return NULL; +} + +/* For a property or kernel name, returns the property name */ +static const char * +get_property_name (const BondProperty *prop) +{ + return prop ? g_param_spec_get_name (prop->pspec) : NULL; +} + +/* For a property or kernel name, returns the kernel option name */ +static const char * +get_kernel_name (const BondProperty *prop) +{ + if (!prop) + return NULL; + return prop->kernel_name ? prop->kernel_name : g_param_spec_get_name (prop->pspec); +} + +static gboolean +int_from_string (const char *s, glong *out_num) +{ + long int n; + char *end; + + if (!s) return FALSE; + errno = 0; + n = strtol (s, &end, 10); + if (out_num) + *out_num = n; + return !errno && !*end; +} - return TRUE; +static gboolean +validate_int (const BondProperty *prop, const char *value, int *out_num) +{ + GParamSpecInt *ispec; + glong num = 0; + gboolean success = FALSE; + + g_assert (G_IS_PARAM_SPEC_INT (prop->pspec)); + if (!int_from_string (value, &num)) + goto out; + + ispec = G_PARAM_SPEC_INT (prop->pspec); + success = (num >= ispec->minimum && num <= ispec->maximum); +out: + if (out_num) + *out_num = success ? num : 0; + return success; } static gboolean -validate_list (const char *name, const char *value, const BondDefault *def) +validate_list (const BondProperty *prop, const char *value) { - guint i; + const char *const*ptr; - for (i = 0; i < G_N_ELEMENTS (def->list) && def->list[i]; i++) { - if (g_strcmp0 (def->list[i], value) == 0) - return TRUE; + if (value) { + for (ptr = prop->list; *ptr; ptr++) { + if (strcmp (*ptr, value) == 0) + return TRUE; + } } - - /* empty validation list means all values pass */ - return def->list[0] == NULL ? TRUE : FALSE; + return FALSE; } +/* by making it a macro, we don't have to worry about using glong as type of idx (otherwise, we would have to check for integer overflow too. */ +#define IS_VALID_LIST_INDEX(prop, idx) ( ((idx) >= 0) && ((idx) < g_strv_length ((char **) (prop)->list)) ) + static gboolean -validate_ip (const char *name, const char *value) +validate_both (const BondProperty *prop, const char *value) +{ + glong num = -1; + + if (!value) + return FALSE; + + if (validate_list (prop, value)) + return TRUE; + + if (!int_from_string (value, &num)) + return FALSE; + + /* Ensure number is within bounds of string list */ + return IS_VALID_LIST_INDEX (prop, num); +} + +static char ** +parse_ip (const char *value, gboolean warn_on_error) { char **ips, **iter; - gboolean success = TRUE; struct in_addr addr; - if (!value || !value[0]) + if (!value || !value[0]) { + /* missing value is valid, just return NULL instead of an empty array. */ + return NULL; + } + + /* lets be more forgiving when accepting the input string. */ + ips = g_strsplit_set (value, " ,", 0); + for (iter = ips; *iter; iter++) { + if (!*iter) { + /* don't be so strict, just skip over empty values. */ + continue; + } + if (!inet_aton (*iter, &addr)) { + g_strfreev (ips); + g_return_val_if_fail (!warn_on_error, NULL); + return NULL; + } + } + return ips; +} + +static gboolean +validate_ip (const char *value) +{ + char **ips; + + if (!value || !value[0]) { + /* there is only one TYPE_IP, and that property is not mandatory. + * Accept empty as valid. + **/ + return TRUE; + } + + /* make reuse of parse_ip, as it should validate the input anyway. */ + ips = parse_ip (value, FALSE); + if (!ips) return FALSE; - ips = g_strsplit_set (value, ",", 0); - for (iter = ips; iter && *iter && success; iter++) - success = !!inet_aton (*iter, &addr); g_strfreev (ips); + return TRUE; +} - return success; +static gboolean +validate_ifname (const char *value) +{ + if (!value || !value[0]) { + /* there is only one TYPE_IFNAME, and that property is not mandatory. + * Accept empty as valid. + **/ + return TRUE; + } + + return nm_utils_iface_valid_name (value); } +/* Checks whether @value is is a valid value for @prop. + * + * Returns: TRUE, if the @value is valid for the given name. + * If @value is NULL, false will be returned. + **/ static gboolean -validate_ifname (const char *name, const char *value) +validate_property (const BondProperty *prop, const char *value) +{ + switch (prop->opt_type) { + case TYPE_INT: + return validate_int (prop, value, NULL); + case TYPE_STR: + return validate_list (prop, value); + case TYPE_BOTH: + return validate_both (prop, value); + case TYPE_IP: + return validate_ip (value); + case TYPE_IFNAME: + return validate_ifname (value); + case TYPE_NONE: + default: + g_assert_not_reached(); + } + return FALSE; +} + +/*****************************************************************************/ + +const char * +nm_setting_bond_get_property_name (const char *name) +{ + return get_property_name (find_property_by_name (name, NULL, TRUE)); +} + +const char * +nm_setting_bond_get_kernel_name (const char *name) +{ + return get_kernel_name (find_property_by_name (name, NULL, TRUE)); +} + +const char *const* +nm_setting_bond_get_kernel_names () +{ + static const char *array[_LAST_KERNEL_PROP - _FIRST_KERNEL_PROP + 1 + 1] = { NULL }; + + /* initialize the array once */ + if (G_UNLIKELY (array[0] == NULL)) { + guint prop, i; + + for (prop = _FIRST_KERNEL_PROP, i = 0; prop <= _LAST_KERNEL_PROP; prop++, i++) + array[i] = get_kernel_name (&props[prop]); + } + + return array; +} + +/** + * nm_setting_bond_get_string: + * @setting: the #NMSettingBond + * @name: the option name for which to retrieve the value + * + * Returns the value for the given name, converted to string. + * + * Returns: the value as string, or %NULL if the name does not + * exist. + **/ +const char * +nm_setting_bond_get_string (const NMSettingBond *setting, + const char *name) { - if (!value || !value[0]) + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); + const char *value; + gboolean result; + BondProperty *prop; + + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); + + if (!name) + return NULL; + + result = g_hash_table_lookup_extended (priv->options, name, NULL, (void **) &value); + if (result) + return value; + + /* Try to lookup by property name instead of the kernel name that is used + * to index the options hash table... */ + prop = find_property_by_name (name, NULL, TRUE); + if (!prop) + return NULL; + + /* 'name' is a valid property name, but we did not find it in the options hash. + * Since every element *must* be in the options hash, this can only mean, that + * the user tried to lookup by property name for items that have a different + * kernel_name. Support lookup by this alias. */ + result = g_hash_table_lookup_extended (priv->options, get_kernel_name (prop), NULL, (void **) &value); + g_assert (result); + + return value; +} + +gboolean +nm_setting_bond_is_default (const NMSettingBond *setting, const char *name, GValue *default_value) +{ + GValue val = G_VALUE_INIT; + GValue def = G_VALUE_INIT; + BondProperty *prop; + gboolean is_default; + + if (!NM_IS_SETTING_BOND (setting)) + goto ERROR_OUT; + prop = find_property_by_name (name, NULL, TRUE); + if (!prop) + goto ERROR_OUT; + + g_value_init (&val, prop->pspec->value_type); + g_value_init (&def, prop->pspec->value_type); + + g_object_get_property (G_OBJECT (setting), get_property_name (prop), &val); + g_param_value_set_default (prop->pspec, &def); + + if (G_VALUE_HOLDS_INT (&val)) + is_default = g_value_get_int (&val) == g_value_get_int (&def); + else if (G_VALUE_HOLDS_STRING (&val)) + is_default = g_strcmp0 (g_value_get_string (&val), g_value_get_string (&def)) == 0; + else if (G_VALUE_HOLDS (&val, G_TYPE_STRV)) { + char **v, **d; + + v = (char **) g_value_get_boxed (&val); + d = (char **) g_value_get_boxed (&def); + + is_default = v == d; + if (!is_default && v && d) { + /* We know, that our only STRV type (ARP_IP_TARGET) has a default value of NULL, + * so don't implement any further comparison now. */ + g_assert_not_reached (); + } + } else + g_assert_not_reached(); + + if (default_value) + g_value_copy (&def, default_value); + + g_value_unset (&val); + g_value_unset (&def); + + return is_default; + +ERROR_OUT: + if (default_value) + g_value_unset (default_value); + g_return_val_if_fail (FALSE, FALSE); + return FALSE; +} + +/** + * nm_setting_bond_set_string: + * @setting: the #NMSettingBond + * @name: name for the option + * @value: value for the option + * + * Set a parameter to a value given as string. The value will be + * converted into the proper type. If the string cannot be converted + * the function does nothing and returns %FALSE. + * + * Returns: %TRUE if the option was valid and successfully set, %FALSE if it was not. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_bond_set_string (NMSettingBond *setting, + const char *name, + const char *value) +{ + NMSettingBondPrivate *priv; + GObject *object = G_OBJECT (setting); + const BondProperty *prop = NULL; + const char *prop_name; + glong num = 0; + + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); + + priv = NM_SETTING_BOND_GET_PRIVATE (setting); + + prop = find_property_by_name (name, NULL, TRUE); + prop_name = get_property_name (prop); + if (!prop_name) + return FALSE; + if (!validate_property (prop, value)) return FALSE; - return nm_utils_iface_valid_name (value); + switch (prop->opt_type) { + case TYPE_INT: + if (!int_from_string (value, &num)) + g_assert_not_reached (); + g_object_set (object, prop_name, (gint) num, NULL); + break; + case TYPE_BOTH: { + const char *str_value = value; + + /* Might be an integer-as-string; find the string */ + if (int_from_string (value, &num)) { + /* FIXME: do we really want to coerce the value? verify() currently accepts numeric values + * for the TYPE_BOTH items. NMDeviceBond has to cope with the ambiguity of the options + * names anyway. It might be better, to support the same names as the kernel does, + * including numeric values. Also, when reading the value from sysfs, we will also + * encounter numeric values (so, either we ~always~ coerce -> set_property), or not at all. + **/ + str_value = prop->list[num]; + } + g_object_set (object, prop_name, str_value, NULL); + break; + } + case TYPE_IFNAME: + case TYPE_STR: + g_object_set (object, prop_name, value, NULL); + break; + case TYPE_IP: { + char **ip = parse_ip (value, TRUE); + + g_object_set (object, prop_name, ip, NULL); + g_strfreev (ip); + break; + } + case TYPE_NONE: + default: + g_assert_not_reached (); + break; + } + + return TRUE; } /** - * nm_setting_bond_validate_option: - * @name: the name of the option to validate - * @value: the value of the option to validate + * nm_setting_bond_set_default: + * @setting: the #NMSettingBond + * @name: name of the option to remove + * + * Resets the bonding option to the default value. + * + * Since: 0.9.10 + **/ +void +nm_setting_bond_set_default (NMSettingBond *setting, + const char *name) +{ + GObject *object = G_OBJECT (setting); + NMSettingBondPrivate *priv; + const BondProperty *prop; + const char *prop_name; + GValue defval = G_VALUE_INIT; + + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); + priv = NM_SETTING_BOND_GET_PRIVATE (setting); + + prop = find_property_by_name (name, NULL, TRUE); + prop_name = get_property_name (prop); + if (!prop_name) + return; + + g_value_init (&defval, prop->pspec->value_type); + g_param_value_set_default (prop->pspec, &defval); + g_object_set_property (object, prop_name, &defval); + g_value_unset (&defval); +} + +/** + * nm_setting_bond_validate_default: + * @name: the name of the option + * @value: name value to be validated. + * @error: (out) (allow-none): The error description * - * Checks whether @name is a valid bond option and @value is a valid value for - * the @name. If @value is NULL, the function only validates the option name. + * Validates a given name and value, where the value is as string. + * + * Since: 0.9.10 + **/ +gboolean +nm_setting_bond_validate_string (const char *name, const char *value, GError **error) +{ + const BondProperty *prop = find_property_by_name (name, NULL, TRUE); + + if (!prop) { + g_set_error_literal (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("property is invalid")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, name); + return FALSE; + } + + if (!validate_property (prop, value)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for '%s'"), + value, name); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, name); + return FALSE; + } + + return TRUE; +} + + +/*****************************************************************************/ + +/** + * nm_setting_bond_get_num_options: + * @setting: the #NMSettingBond + * + * Returns the number of options that are set in the legacy + * #NMSettingBond:options property. This does not include other bond + * properties which are not included in #NMSettingBond:options. + * + * Returns: the number of legacy bonding options + * + * Deprecated: use the option-specific getters instead. + **/ +guint32 +nm_setting_bond_get_num_options (NMSettingBond *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0); + + return _LAST_KERNEL_PROP - _FIRST_KERNEL_PROP + 1; +} + +/** + * nm_setting_bond_get_option: + * @setting: the #NMSettingBond + * @idx: index of the desired option, from 0 to + * nm_setting_bond_get_num_options() - 1 + * @out_name: (out): on return, the name of the bonding option; this + * value is owned by the setting and should not be modified + * @out_value: (out): on return, the value of the name of the bonding + * option; this value is owned by the setting and should not be modified + * + * Given an index, return the value of the bonding option at that index. Indexes + * are *not* guaranteed to be static across modifications to options done by + * nm_setting_bond_add_option() and nm_setting_bond_remove_option(), + * and should not be used to refer to options except for short periods of time + * such as during option iteration. * - * Returns: TRUE, if the @value is valid for the given name. - * If the @name is not a valid option, FALSE will be returned. + * Returns: %TRUE on success if the index was valid and an option was found, + * %FALSE if the index was invalid (ie, greater than the number of options + * currently held by the setting) * - * Since: 0.9.10 + * Deprecated: use the option-specific getters instead. **/ gboolean -nm_setting_bond_validate_option (const char *name, - const char *value) +nm_setting_bond_get_option (NMSettingBond *setting, + guint32 idx, + const char **out_name, + const char **out_value) { - guint i; + NMSettingBondPrivate *priv; + const char *kernel_name = NULL, *value = NULL; + gboolean result = FALSE; - if (!name || !name[0]) - return FALSE; + g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); + priv = NM_SETTING_BOND_GET_PRIVATE (setting); - for (i = 0; i < G_N_ELEMENTS (defaults); i++) { - if (g_strcmp0 (defaults[i].opt, name) == 0) { - if (value == NULL) - return TRUE; - switch (defaults[i].opt_type) { - case TYPE_INT: - return validate_int (name, value, &defaults[i]); - case TYPE_STR: - return validate_list (name, value, &defaults[i]); - case TYPE_BOTH: - return validate_int (name, value, &defaults[i]) - || validate_list (name, value, &defaults[i]); - case TYPE_IP: - return validate_ip (name, value); - case TYPE_IFNAME: - return validate_ifname (name, value); - } - return FALSE; - } - } - return FALSE; + if (idx >= _LAST_KERNEL_PROP - _FIRST_KERNEL_PROP) + goto out; + idx += _FIRST_KERNEL_PROP; + + kernel_name = get_kernel_name (&props[idx]); + g_assert (kernel_name); + result = g_hash_table_lookup_extended (priv->options, kernel_name, NULL, (void **) &value); + g_assert (result); + +out: + if (out_name) + *out_name = kernel_name; + if (out_value) + *out_value = value; + return result; } /** * nm_setting_bond_get_option_by_name: * @setting: the #NMSettingBond * @name: the option name for which to retrieve the value * * Returns the value associated with the bonding option specified by * @name, if it exists. * * Returns: the value, or %NULL if the key/value pair was never added to the * setting; the value is owned by the setting and must not be modified + * + * Deprecated: use the option-specific getters instead. **/ const char * nm_setting_bond_get_option_by_name (NMSettingBond *setting, const char *name) { - g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); - - if (!nm_setting_bond_validate_option (name, NULL)) - return NULL; - - return g_hash_table_lookup (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name); + return nm_setting_bond_get_string (setting, name); } /** * nm_setting_bond_add_option: * @setting: the #NMSettingBond * @name: name for the option * @value: value for the option * * Add an option to the table. The option is compared to an internal list * of allowed options. Option names may contain only alphanumeric characters - * (ie [a-zA-Z0-9]). Adding a new name replaces any existing name/value pair + * (ie [a-zA-Z0-9_]). Adding a new name replaces any existing name/value pair * that may already exist. * - * The order of how to set several options is relevant because there are options - * that conflict with each other. - * * Returns: %TRUE if the option was valid and was added to the internal option * list, %FALSE if it was not. + * + * Deprecated: use the option-specific properties instead or nm_setting_bond_set_string() **/ gboolean nm_setting_bond_add_option (NMSettingBond *setting, const char *name, const char *value) { - NMSettingBondPrivate *priv; - - g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); - - if (!value || !nm_setting_bond_validate_option (name, value)) - return FALSE; - - priv = NM_SETTING_BOND_GET_PRIVATE (setting); - - g_hash_table_insert (priv->options, g_strdup (name), g_strdup (value)); - - if ( !strcmp (name, NM_SETTING_BOND_OPTION_MIIMON) - && strcmp (value, "0") != 0) { - g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL); - g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); - } else if ( !strcmp (name, NM_SETTING_BOND_OPTION_ARP_INTERVAL) - && strcmp (value, "0") != 0) { - g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_MIIMON); - g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY); - g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_UPDELAY); - } - - g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS); - - return TRUE; + return nm_setting_bond_set_string (setting, name, value); } /** * nm_setting_bond_remove_option: * @setting: the #NMSettingBond * @name: name of the option to remove * * Remove the bonding option referenced by @name from the internal option - * list. + * list. As the option list is deprected, you can no longer actually remove + * an item from the option hash. Removing it is now equivalent to resetting + * the default value. * * Returns: %TRUE if the option was found and removed from the internal option * list, %FALSE if it was not. + * + * Deprecated: use the option-specific properties instead. **/ gboolean nm_setting_bond_remove_option (NMSettingBond *setting, const char *name) { - gboolean found; - - g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE); - - if (!nm_setting_bond_validate_option (name, NULL)) - return FALSE; - - found = g_hash_table_remove (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name); - if (found) - g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS); - return found; + /* We don't really remove the property, instead we reset the default value. + * Thus, the number of options is always constant (all of them) and the option + * hash always contains every kernel option. */ + nm_setting_bond_set_default (setting, name); + return TRUE; } /** * nm_setting_bond_get_valid_options: * @setting: the #NMSettingBond * * Returns a list of valid bond options. * * Returns: (transfer none): a %NULL-terminated array of strings of valid bond options. + * + * Deprecated: the valid options are defined by the #NMSettingBond + * properties. **/ const char ** nm_setting_bond_get_valid_options (NMSettingBond *setting) { - static const char *array[G_N_ELEMENTS (defaults) + 1] = { NULL }; - int i; - - /* initialize the array once */ - if (G_UNLIKELY (array[0] == NULL)) { - for (i = 0; i < G_N_ELEMENTS (defaults); i++) - array[i] = defaults[i].opt; - array[i] = NULL; - } - return array; + return (const char **) nm_setting_bond_get_kernel_names (); } /** * nm_setting_bond_get_option_default: * @setting: the #NMSettingBond * @name: the name of the option * * Returns: the value of the bond option if not overridden by an entry in * the #NMSettingBond:options property. + * + * Deprecated: Use the default values of the option-specific properties. **/ const char * nm_setting_bond_get_option_default (NMSettingBond *setting, const char *name) { - guint i; + BondProperty *prop; + GValue defval = G_VALUE_INIT; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); - g_return_val_if_fail (nm_setting_bond_validate_option (name, NULL), NULL); - for (i = 0; i < G_N_ELEMENTS (defaults); i++) { - if (g_strcmp0 (defaults[i].opt, name) == 0) - return defaults[i].val; + prop = find_property_by_name (name, NULL, TRUE); + + if (!prop) + return NULL; + + if (G_UNLIKELY (prop->defval == NULL)) { + g_value_init (&defval, prop->pspec->value_type); + g_param_value_set_default (prop->pspec, &defval); + prop->defval = g_strdup_value_contents (&defval); + g_value_unset (&defval); + g_return_val_if_fail (prop->defval, NULL); + } + return prop->defval; +} + +/*****************************************************************/ + +static void +set_properties_from_hash (NMSettingBond *self, GHashTable *options) +{ + const char *value; + guint i; + + g_object_freeze_notify (G_OBJECT (self)); + + /* Set each property to the value given by @options, or if not present + * in @options, to the default value. + */ + for (i = _FIRST_KERNEL_PROP; i <= _LAST_KERNEL_PROP; i++) { + const char *property_name = get_property_name (&props[i]); + const char *kernel_name = get_kernel_name (&props[i]); + GValue defval = G_VALUE_INIT; + + if (!g_hash_table_lookup_extended (options, kernel_name, NULL, (void **)&value)) { + /* for setting options, we also support the new property names instead + * of the kernel names + **/ + if (!g_hash_table_lookup_extended (options, property_name, NULL, (void **)&value)) + value = NULL; + } + + if (value) + nm_setting_bond_set_string (self, kernel_name, value); + else { + g_value_init (&defval, props[i].pspec->value_type); + g_param_value_set_default (props[i].pspec, &defval); + g_object_set_property (G_OBJECT (self), property_name, &defval); + g_value_unset (&defval); + } } - /* Any option that passes nm_setting_bond_validate_option() should also be found in defaults */ - g_assert_not_reached (); + + g_object_thaw_notify (G_OBJECT (self)); } static gboolean verify (NMSetting *setting, GSList *all_settings, GError **error) { NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting); - GHashTableIter iter; - const char *key, *value; - const char *valid_modes[] = { "balance-rr", - "active-backup", - "balance-xor", - "broadcast", - "802.3ad", - "balance-tlb", - "balance-alb", - NULL }; - int miimon = 0, arp_interval = 0; - const char *arp_ip_target = NULL; - const char *primary; - - if (!priv->interface_name || !strlen(priv->interface_name)) { + + if (!priv->interface_name || !strlen (priv->interface_name)) { g_set_error_literal (error, NM_SETTING_BOND_ERROR, NM_SETTING_BOND_ERROR_MISSING_PROPERTY, _("property is missing")); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_INTERFACE_NAME); return FALSE; } - if (!nm_utils_iface_valid_name (priv->interface_name)) { g_set_error_literal (error, NM_SETTING_BOND_ERROR, NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("property is invalid")); g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_INTERFACE_NAME); return FALSE; } - g_hash_table_iter_init (&iter, priv->options); - while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) { - if (!value[0] || !nm_setting_bond_validate_option (key, value)) { - g_set_error (error, - NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("invalid option '%s' or its value '%s'"), - key, value); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); - return FALSE; - } - } - - value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MIIMON); - if (value) - miimon = atoi (value); - value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL); - if (value) - arp_interval = atoi (value); - /* Can only set one of miimon and arp_interval */ - if (miimon > 0 && arp_interval > 0) { + if (priv->miimon > 0 && priv->arp_interval > 0) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("only one of '%s' and '%s' can be set"), - NM_SETTING_BOND_OPTION_MIIMON, - NM_SETTING_BOND_OPTION_ARP_INTERVAL); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_MIIMON, + NM_SETTING_BOND_ARP_INTERVAL); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, + priv->arp_ip_target == NULL && priv->arp_ip_target[0] ? NM_SETTING_BOND_ARP_INTERVAL : NM_SETTING_BOND_MIIMON); } - value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MODE); - if (!value) { + if (!priv->mode) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_MISSING_OPTION, - _("mandatory option '%s' is missing"), - NM_SETTING_BOND_OPTION_MODE); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_ERROR_MISSING_PROPERTY, + _("mandatory property '%s' is missing"), + NM_SETTING_BOND_MODE); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_MODE); return FALSE; } - if (!_nm_utils_string_in_list (value, valid_modes)) { + if (!validate_property (&props[PROP_MODE], priv->mode)) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s' is not a valid value for '%s'"), - value, NM_SETTING_BOND_OPTION_MODE); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + priv->mode, NM_SETTING_BOND_MODE); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_MODE); return FALSE; } /* Make sure mode is compatible with other settings */ - if ( strcmp (value, "balance-alb") == 0 - || strcmp (value, "balance-tlb") == 0) { - if (arp_interval > 0) { + if (__NM_SETTING_BOND_MODE_IS_balance_alb (priv->mode) || + __NM_SETTING_BOND_MODE_IS_balance_tlb (priv->mode)) { + if (priv->arp_interval > 0) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s=%s' is incompatible with '%s > 0'"), - NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_BOND_OPTION_ARP_INTERVAL); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_MODE, priv->mode, + NM_SETTING_BOND_ARP_INTERVAL); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_ARP_INTERVAL); return FALSE; } } - primary = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_PRIMARY); - if (strcmp (value, "active-backup") == 0) { - if (primary && !nm_utils_iface_valid_name (primary)) { + if (__NM_SETTING_BOND_MODE_IS_active_backup (priv->mode)) { + if (priv->primary && !nm_utils_iface_valid_name (priv->primary)) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("'%s' is not a valid interface name for '%s' option"), - primary, NM_SETTING_BOND_OPTION_PRIMARY); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid interface name"), + priv->primary); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_PRIMARY); return FALSE; } } else { - if (primary) { + if (priv->primary) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("'%s' option is only valid for '%s=%s'"), - NM_SETTING_BOND_OPTION_PRIMARY, - NM_SETTING_BOND_OPTION_MODE, "active-backup"); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is only valid for '%s=%s'"), + NM_SETTING_BOND_PRIMARY, + NM_SETTING_BOND_MODE, "active-backup"); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_PRIMARY); return FALSE; } } if (nm_setting_find_in_list (all_settings, NM_SETTING_INFINIBAND_SETTING_NAME)) { - if (strcmp (value, "active-backup") != 0) { + if (__NM_SETTING_BOND_MODE_IS_active_backup (priv->mode)) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, _("'%s=%s' is not a valid configuration for '%s'"), - NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_INFINIBAND_SETTING_NAME); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_MODE, priv->mode, + NM_SETTING_INFINIBAND_SETTING_NAME); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_MODE); return FALSE; } } - if (miimon == 0) { + if (priv->miimon == 0) { /* updelay and downdelay can only be used with miimon */ - if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_UPDELAY)) { + if (priv->updelay > 0) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("'%s' option requires '%s' option to be set"), - NM_SETTING_BOND_OPTION_UPDELAY, NM_SETTING_BOND_OPTION_MIIMON); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' property requires '%s' property to be set"), + NM_SETTING_BOND_UPDELAY, NM_SETTING_BOND_MIIMON); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_UPDELAY); return FALSE; } - if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY)) { + if (priv->downdelay > 0) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("'%s' option requires '%s' option to be set"), - NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_MIIMON); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' property requires '%s' property to be set"), + NM_SETTING_BOND_DOWNDELAY, NM_SETTING_BOND_MIIMON); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_DOWNDELAY); return FALSE; } } /* arp_ip_target can only be used with arp_interval, and must - * contain a comma-separated list of IPv4 addresses. + * contain IPv4 addresses. */ - arp_ip_target = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); - if (arp_interval > 0) { - char **addrs; + if (priv->arp_interval > 0) { guint32 addr; int i; - if (!arp_ip_target) { + if (!priv->arp_ip_target) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_MISSING_OPTION, - _("'%s' option requires '%s' option to be set"), - NM_SETTING_BOND_OPTION_ARP_INTERVAL, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_ERROR_MISSING_PROPERTY, + _("'%s' property requires '%s' property to be set"), + NM_SETTING_BOND_ARP_INTERVAL, NM_SETTING_BOND_ARP_IP_TARGET); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_ARP_IP_TARGET); return FALSE; } - addrs = g_strsplit (arp_ip_target, ",", -1); - if (!addrs[0]) { + if (!priv->arp_ip_target[0]) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("'%s' option is empty"), - NM_SETTING_BOND_OPTION_ARP_IP_TARGET); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); - g_strfreev (addrs); + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' property is empty"), + NM_SETTING_BOND_ARP_IP_TARGET); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_ARP_IP_TARGET); return FALSE; } - for (i = 0; addrs[i]; i++) { - if (!inet_pton (AF_INET, addrs[i], &addr)) { + for (i = 0; priv->arp_ip_target[i]; i++) { + if (!inet_pton (AF_INET, priv->arp_ip_target[i], &addr)) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("'%s' is not a valid IPv4 address for '%s' option"), - NM_SETTING_BOND_OPTION_ARP_IP_TARGET, addrs[i]); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); - g_strfreev (addrs); + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid IPv4 address for '%s' property"), + priv->arp_ip_target[i], NM_SETTING_BOND_ARP_IP_TARGET); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_ARP_IP_TARGET); return FALSE; } } - g_strfreev (addrs); } else { - if (arp_ip_target) { + if (priv->arp_ip_target && priv->arp_ip_target[0]) { g_set_error (error, NM_SETTING_BOND_ERROR, - NM_SETTING_BOND_ERROR_INVALID_OPTION, - _("'%s' option requires '%s' option to be set"), - NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_ARP_INTERVAL); - g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS); + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' property requires '%s' property to be set"), + NM_SETTING_BOND_ARP_IP_TARGET, NM_SETTING_BOND_ARP_INTERVAL); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_ARP_IP_TARGET); return FALSE; } } + /* FIXME: maybe we should not be too excessively about validating the strings, + * because the kernel might add new values (which we would then not support). + * OTOH, the checking above already requires some deep knowledge about the exact + * meaning of the flags, so, why check there, but not here? + **/ + if (priv->arp_validate && !validate_property (&props[PROP_ARP_VALIDATE], priv->arp_validate)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for '%s'"), + priv->arp_validate, NM_SETTING_BOND_ARP_VALIDATE); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_ARP_VALIDATE); + return FALSE; + } + + if (priv->primary_reselect && !validate_property (&props[PROP_PRIMARY_RESELECT], priv->primary_reselect)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for '%s'"), + priv->primary_reselect, NM_SETTING_BOND_PRIMARY_RESELECT); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_PRIMARY_RESELECT); + return FALSE; + } + + if (priv->fail_over_mac && !validate_property (&props[PROP_FAIL_OVER_MAC], priv->fail_over_mac)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for '%s'"), + priv->fail_over_mac, NM_SETTING_BOND_FAIL_OVER_MAC); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_FAIL_OVER_MAC); + return FALSE; + } + + if (priv->ad_select && !validate_property (&props[PROP_AD_SELECT], priv->ad_select)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for '%s'"), + priv->ad_select, NM_SETTING_BOND_AD_SELECT); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_AD_SELECT); + return FALSE; + } + + if (priv->xmit_hash_policy && !validate_property (&props[PROP_XMIT_HASH_POLICY], priv->xmit_hash_policy)) { + g_set_error (error, + NM_SETTING_BOND_ERROR, + NM_SETTING_BOND_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid value for '%s'"), + priv->xmit_hash_policy, NM_SETTING_BOND_XMIT_HASH_POLICY); + g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_XMIT_HASH_POLICY); + return FALSE; + } + return TRUE; } static const char * get_virtual_iface_name (NMSetting *setting) { NMSettingBond *self = NM_SETTING_BOND (setting); return nm_setting_bond_get_interface_name (self); } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + BondProperty *prop = find_property_by_pspec (prop_spec, NULL, TRUE); + gboolean result = FALSE; + + if (!prop) + goto CHAIN; + + switch (prop->opt_type) { + case TYPE_BOTH: { + char *a0, *b0; + const char *a, *b; + + g_object_get (setting, prop_spec->name, &a0, NULL); + g_object_get (setting, prop_spec->name, &b0, NULL); + + a = a0; + b = b0; + if (!a || !b) + result = (a == b); + else if (strcmp (a, b) == 0) + result = TRUE; + else { + int idx; + + if (validate_int (prop, a, &idx)) { + if (!IS_VALID_LIST_INDEX (prop, idx)) + goto BOTH_FINISHED; + a = prop->list[idx]; + } + if (validate_int (prop, b, &idx)) { + if (!IS_VALID_LIST_INDEX (prop, idx)) + goto BOTH_FINISHED; + b = prop->list[idx]; + } + result = (strcmp (a, b) == 0); + } + +BOTH_FINISHED: + g_free (a0); + g_free (b0); + return result; + } + case TYPE_IP: { + char **a, **b; + struct in_addr *addr_a = NULL, *addr_b = NULL; + + g_object_get (setting, prop_spec->name, &a, NULL); + g_object_get (setting, prop_spec->name, &b, NULL); + + if (!a || !a[0] || !b || !b[0]) + result = ((a ? a[0] : NULL) == (b ? b[0] : NULL)); + else { + /* both arrays are not empty. We compare them by + * converting every item into a struct in_addr and looking + * whether we find it in the other array (and vice versa). + * If one of the addresses cannot be converted, the result + * is always false (because nothing compares to an invalid property). + **/ + + guint i, j; + guint alen = g_strv_length (a); + guint blen = g_strv_length (b); + + /* convert all strings to struct in_addr */ + addr_a = g_new (struct in_addr, alen); + for (i = 0; i < alen; i++) { + if (!inet_aton (a[i], &addr_a[i])) + goto IP_FINISHED; + } + + addr_b = g_new (struct in_addr, blen); + for (i = 0; i < blen; i++) { + if (!inet_aton (b[i], &addr_b[i])) + goto IP_FINISHED; + } + + /* ensure that we find every address in the other array too */ + for (i = 0; i < alen; i++) { + for (j = 0; j < blen; j++) { + if (addr_a[i].s_addr == addr_b[j].s_addr) + break; + } + if (j >= blen) + goto IP_FINISHED; + } + + for (i = 0; i < blen; i++) { + for (j = 0; j < alen; j++) { + if (addr_b[i].s_addr == addr_a[j].s_addr) + break; + } + if (j >= alen) + goto IP_FINISHED; + } + result = TRUE; + } +IP_FINISHED: + g_free (addr_a); + g_free (addr_b); + g_strfreev (a); + g_strfreev (b); + return result; + } + default: + /* other types shall be compared by the default implementation. */ + goto CHAIN; + } + +CHAIN: + return NM_SETTING_CLASS (nm_setting_bond_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); - g_object_set (setting, NM_SETTING_NAME, NM_SETTING_BOND_SETTING_NAME, - NULL); + priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); + + g_object_set (setting, NM_SETTING_NAME, NM_SETTING_BOND_SETTING_NAME, NULL); +} - priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); +static void +constructed (GObject *object) +{ + G_OBJECT_CLASS (nm_setting_bond_parent_class)->constructed (object); - /* Default values: */ - nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr"); + g_assert (g_hash_table_size (NM_SETTING_BOND_GET_PRIVATE (object)->options) == (_LAST_KERNEL_PROP - _FIRST_KERNEL_PROP + 1)); } static void finalize (GObject *object) { NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); + g_assert (g_hash_table_size (priv->options) == (_LAST_KERNEL_PROP - _FIRST_KERNEL_PROP + 1)); + g_free (priv->interface_name); + g_free (priv->mode); + g_free (priv->primary); + g_strfreev (priv->arp_ip_target); g_hash_table_destroy (priv->options); G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object); } static void -copy_hash (gpointer key, gpointer value, gpointer user_data) -{ - g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value)); -} - -static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { + NMSettingBond *setting = NM_SETTING_BOND (object); NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); - GHashTable *new_hash; + BondProperty *prop = find_property_by_pspec (pspec, NULL, TRUE); + char *kernel_value = NULL; switch (prop_id) { case PROP_INTERFACE_NAME: + g_free (priv->interface_name); priv->interface_name = g_value_dup_string (value); break; + case PROP_MODE: + g_free (priv->mode); + priv->mode = g_value_dup_string (value); + kernel_value = g_value_dup_string (value); + break; + case PROP_MIIMON: + priv->miimon = g_value_get_int (value); + kernel_value = g_strdup_printf ("%u", g_value_get_int (value)); + break; + case PROP_DOWNDELAY: + priv->downdelay = g_value_get_int (value); + kernel_value = g_strdup_printf ("%u", g_value_get_int (value)); + break; + case PROP_UPDELAY: + priv->updelay = g_value_get_int (value); + kernel_value = g_strdup_printf ("%u", g_value_get_int (value)); + break; + case PROP_ARP_INTERVAL: + priv->arp_interval = g_value_get_int (value); + kernel_value = g_strdup_printf ("%u", g_value_get_int (value)); + break; + case PROP_ARP_IP_TARGET: + g_strfreev (priv->arp_ip_target); + priv->arp_ip_target = g_value_dup_boxed (value); + kernel_value = priv->arp_ip_target ? g_strjoinv (",", priv->arp_ip_target) : g_strdup (""); + break; + case PROP_ARP_VALIDATE: + g_free (priv->arp_validate); + priv->arp_validate = g_value_dup_string (value); + kernel_value = g_value_dup_string (value); + break; + case PROP_PRIMARY: + g_free (priv->primary); + priv->primary = g_value_dup_string (value); + kernel_value = g_value_dup_string (value); + break; + case PROP_PRIMARY_RESELECT: + g_free (priv->primary_reselect); + priv->primary_reselect = g_value_dup_string (value); + kernel_value = g_value_dup_string (value); + break; + case PROP_FAIL_OVER_MAC: + g_free (priv->fail_over_mac); + priv->fail_over_mac = g_value_dup_string (value); + kernel_value = g_value_dup_string (value); + break; + case PROP_USE_CARRIER: + priv->use_carrier = !!g_value_get_int (value); + kernel_value = g_strdup_printf ("%u", priv->use_carrier); + break; + case PROP_AD_SELECT: + g_free (priv->ad_select); + priv->ad_select = g_value_dup_string (value); + kernel_value = g_value_dup_string (value); + break; + case PROP_XMIT_HASH_POLICY: + g_free (priv->xmit_hash_policy); + priv->xmit_hash_policy = g_value_dup_string (value); + kernel_value = g_value_dup_string (value); + break; + case PROP_RESEND_IGMP: + priv->resend_igmp = g_value_get_int (value); + kernel_value = g_strdup_printf ("%u", g_value_get_int (value)); + break; case PROP_OPTIONS: - /* Must make a deep copy of the hash table here... */ - g_hash_table_remove_all (priv->options); - new_hash = g_value_get_boxed (value); - if (new_hash) - g_hash_table_foreach (new_hash, copy_hash, priv->options); + set_properties_from_hash (setting, g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + + if (prop) { + const char *kernel_name = get_kernel_name (prop); + const char *old_value; + + if ( !g_hash_table_lookup_extended (priv->options, kernel_name, NULL, (void **) &old_value) + || g_strcmp0 (old_value, kernel_value)) { + g_hash_table_insert (priv->options, (void *) kernel_name, kernel_value); + g_object_notify (object, NM_SETTING_BOND_OPTIONS); + } + } } static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); NMSettingBond *setting = NM_SETTING_BOND (object); + NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object); switch (prop_id) { case PROP_INTERFACE_NAME: g_value_set_string (value, nm_setting_bond_get_interface_name (setting)); break; + case PROP_MODE: + g_value_set_string (value, nm_setting_bond_get_mode (setting)); + break; + case PROP_MIIMON: + g_value_set_int (value, nm_setting_bond_get_miimon (setting)); + break; + case PROP_DOWNDELAY: + g_value_set_int (value, nm_setting_bond_get_downdelay (setting)); + break; + case PROP_UPDELAY: + g_value_set_int (value, nm_setting_bond_get_updelay (setting)); + break; + case PROP_ARP_INTERVAL: + g_value_set_int (value, nm_setting_bond_get_arp_interval (setting)); + break; + case PROP_ARP_IP_TARGET: + g_value_set_boxed (value, nm_setting_bond_get_arp_ip_target (setting)); + break; + case PROP_ARP_VALIDATE: + g_value_set_string (value, nm_setting_bond_get_arp_validate (setting)); + break; + case PROP_PRIMARY: + g_value_set_string (value, nm_setting_bond_get_primary (setting)); + break; + case PROP_PRIMARY_RESELECT: + g_value_set_string (value, nm_setting_bond_get_primary_reselect (setting)); + break; + case PROP_FAIL_OVER_MAC: + g_value_set_string (value, nm_setting_bond_get_fail_over_mac (setting)); + break; + case PROP_USE_CARRIER: + g_value_set_int (value, nm_setting_bond_get_use_carrier (setting)); + break; + case PROP_AD_SELECT: + g_value_set_string (value, nm_setting_bond_get_ad_select (setting)); + break; + case PROP_XMIT_HASH_POLICY: + g_value_set_string (value, nm_setting_bond_get_xmit_hash_policy (setting)); + break; + case PROP_RESEND_IGMP: + g_value_set_int (value, nm_setting_bond_get_resend_igmp (setting)); + break; case PROP_OPTIONS: g_value_set_boxed (value, priv->options); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void nm_setting_bond_class_init (NMSettingBondClass *setting_class) { GObjectClass *object_class = G_OBJECT_CLASS (setting_class); NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + guint i; g_type_class_add_private (setting_class, sizeof (NMSettingBondPrivate)); /* virtual methods */ - object_class->set_property = set_property; - object_class->get_property = get_property; - object_class->finalize = finalize; - parent_class->verify = verify; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->constructed = constructed; + object_class->finalize = finalize; + parent_class->verify = verify; + parent_class->compare_property = compare_property; parent_class->get_virtual_iface_name = get_virtual_iface_name; /* Properties */ /** * NMSettingBond:interface-name: * * The name of the virtual in-kernel bonding network interface **/ - g_object_class_install_property - (object_class, PROP_INTERFACE_NAME, - g_param_spec_string (NM_SETTING_BOND_INTERFACE_NAME, - "InterfaceName", - "The name of the virtual in-kernel bonding network interface", - NULL, - G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + props[PROP_INTERFACE_NAME].pspec = + g_param_spec_string (NM_SETTING_BOND_INTERFACE_NAME, + "InterfaceName", + "The name of the virtual in-kernel bonding network interface", + NULL, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:mode: + * + * The bonding mode. One of 'balance-rr', 'active-backup', + * 'balance-xor', 'broadcast', '802.3ad', 'balance-tlb', or + * 'balance-alb'. + **/ + props[PROP_MODE].pspec = + g_param_spec_string (NM_SETTING_BOND_MODE, + "Mode", + "The bonding mode", + "balance-rr", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:miimon: + * + * The MII link monitoring frequency, in milliseconds. Either this + * or #NMSettingBond:arp-interval should be set, and they can't + * both be set. + * + * Since: 0.9.10 + **/ + props[PROP_MIIMON].pspec = + g_param_spec_int (NM_SETTING_BOND_MIIMON, + "miimon", + "The MII link monitoring frequence", + 0, G_MAXINT, 100, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:downdelay: + * + * The time, in milliseconds, to wait before disabling a slave + * after it goes down. Only valid if #NMSettingBond:miimon is + * non-0. + * + * Since: 0.9.10 + **/ + props[PROP_DOWNDELAY].pspec = + g_param_spec_int (NM_SETTING_BOND_DOWNDELAY, + "downdelay", + "downdelay", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:updelay: + * + * The time, in milliseconds, to wait before enabling a slave + * after it comes up. Only valid if #NMSettingBond:miimon is + * non-0. + * + * Since: 0.9.10 + **/ + props[PROP_UPDELAY].pspec = + g_param_spec_int (NM_SETTING_BOND_UPDELAY, + "updelay", + "updelay", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:arp-interval: + * + * The ARP-based link monitoring frequency, in milliseconds. + * Either this or #NMSettingBond:miimon should be set, and they + * can't both be set. + * + * Since: 0.9.10 + **/ + props[PROP_ARP_INTERVAL].pspec = + g_param_spec_int (NM_SETTING_BOND_ARP_INTERVAL, + "ARP interval", + "The ARP-based link monitoring frequence", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:arp-ip-target: + * + * An array of IPv4 addresses to ping when using ARP-based link monitoring. + * This only has an effect when #NMSettingBond:arp-interval is also set. + * + * Since: 0.9.10 + **/ + props[PROP_ARP_IP_TARGET].pspec = + g_param_spec_boxed (NM_SETTING_BOND_ARP_IP_TARGET, + "ARP IP target", + "ARP monitoring target IP addresses", + G_TYPE_STRV, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:arp-validate: + * + * Specifies whether or not ARP probes and replies should be + * validated in the active-backup mode. One of + * 'none', 'active', 'backup', 'all'. + * + * Since: 0.9.10 + **/ + props[PROP_ARP_VALIDATE].pspec = + g_param_spec_string (NM_SETTING_BOND_ARP_VALIDATE, + "arp-validate", + "Specifies whether or not ARP probes and replies should " + "be validate in the active-backup mode", + "none", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:primary: + * + * The primary interface to use in 'active-backup' mode. + **/ + props[PROP_PRIMARY].pspec = + g_param_spec_string (NM_SETTING_BOND_PRIMARY, + "Primary", + "The primary interface", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:primary-reselect: + * + * Specifies the reselection policy for the primary slave. + * One of 'always', 'better', 'failure'. + * + * Since: 0.9.10 + **/ + props[PROP_PRIMARY_RESELECT].pspec = + g_param_spec_string (NM_SETTING_BOND_PRIMARY_RESELECT, + "primary-reselect", + "Specifies the reselection policy for the primary slave", + "always", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:fail-over-mac: + * + * Specifies whether active-backup mode should set all slaves to + * the same MAC address at enslavement (the traditional + * behavior), or, when enabled, perform special handling of the + * bond's MAC address in accordance with the selected policy. + * One of 'none', 'active', 'follow'. + * + * Since: 0.9.10 + **/ + props[PROP_FAIL_OVER_MAC].pspec = + g_param_spec_string (NM_SETTING_BOND_FAIL_OVER_MAC, + "fail-over-mac", + "fail_over_mac", + "none", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:use-carrier: + * + * Specifies whether or not miimon should use MII or ETHTOOL + * ioctls vs. netif_carrier_ok() to determine the link + * status. + * + * Since: 0.9.10 + **/ + props[PROP_USE_CARRIER].pspec = + g_param_spec_int (NM_SETTING_BOND_USE_CARRIER, + "use-carrier", + "use_carrier", + 0, 1, 1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:ad-select: + * + * Specifies the 802.3ad aggregation selection logic to use. + * One of 'stable', 'bandwidth', or 'count'. + **/ + props[PROP_AD_SELECT].pspec = + g_param_spec_string (NM_SETTING_BOND_AD_SELECT, + "ad-select", + "ad_select", + "stable", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:xmit-hash-policy: + * + * Selects the transmit hash policy to use for slave selection in + * balance-xor and 802.3ad modes. One of 'layer2', 'layer2+3', + * 'layer3+4', 'encap2+3', or 'encap3+4'. + * + **/ + props[PROP_XMIT_HASH_POLICY].pspec = + g_param_spec_string (NM_SETTING_BOND_XMIT_HASH_POLICY, + "xmit-hash-policy", + "xmit_hash_policy", + "layer2", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); + + /** + * NMSettingBond:resend-igmp: + * + * Specifies the number of IGMP membership reports to be issued after + * a failover event. One membership report is issued immediately after + * the failover, subsequent packets are sent in each 200ms interval. + * + * The valid range is 0 - 255; the default value is 1. A value of 0 + * prevents the IGMP membership report from being issued in response + * to the failover event. + * + * Since: 0.9.10 + **/ + props[PROP_RESEND_IGMP].pspec = + g_param_spec_int (NM_SETTING_BOND_RESEND_IGMP, + "resend-igmp", + "resend_igmp", + 0, 255, 1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE); /** * NMSettingBond:options: * * Dictionary of key/value pairs of bonding options. Both keys * and values must be strings. Option names must contain only - * alphanumeric characters (ie, [a-zA-Z0-9]). + * alphanumeric characters (ie, [a-zA-Z0-9_). + * + * Deprecated: use the specific properties **/ - g_object_class_install_property - (object_class, PROP_OPTIONS, - _nm_param_spec_specialized (NM_SETTING_BOND_OPTIONS, - "Options", - "Dictionary of key/value pairs of bonding " - "options. Both keys and values must be " - "strings. Option names must contain only " - "alphanumeric characters (ie, [a-zA-Z0-9]).", - DBUS_TYPE_G_MAP_OF_STRING, - G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + props[PROP_OPTIONS].pspec = + _nm_param_spec_specialized (NM_SETTING_BOND_OPTIONS, + "Options", + "Dictionary of key/value pairs of bonding " + "options. Both keys and values must be " + "strings. Option names must contain only " + "alphanumeric characters (ie, [a-zA-Z0-9_]).", + DBUS_TYPE_G_MAP_OF_STRING, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE); + + /* Skip PROP_0 */ + for (i = 1; i < LAST_PROP; i++) + g_object_class_install_property (object_class, i, props[i].pspec); } diff --git a/libnm-util/nm-setting-bond.h b/libnm-util/nm-setting-bond.h index 383e0ea..65a7780 100644 --- a/libnm-util/nm-setting-bond.h +++ b/libnm-util/nm-setting-bond.h @@ -38,81 +38,132 @@ G_BEGIN_DECLS #define NM_SETTING_BOND_SETTING_NAME "bond" /** * NMSettingBondError: * @NM_SETTING_BOND_ERROR_UNKNOWN: unknown or unclassified error * @NM_SETTING_BOND_ERROR_INVALID_PROPERTY: the property was invalid * @NM_SETTING_BOND_ERROR_MISSING_PROPERTY: the property was missing and is * required */ typedef enum { NM_SETTING_BOND_ERROR_UNKNOWN = 0, /*< nick=UnknownError >*/ NM_SETTING_BOND_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ NM_SETTING_BOND_ERROR_MISSING_PROPERTY, /*< nick=MissingProperty >*/ NM_SETTING_BOND_ERROR_INVALID_OPTION, /*< nick=InvalidOption >*/ NM_SETTING_BOND_ERROR_MISSING_OPTION, /*< nick=MissingOption >*/ } NMSettingBondError; #define NM_SETTING_BOND_ERROR nm_setting_bond_error_quark () GQuark nm_setting_bond_error_quark (void); -#define NM_SETTING_BOND_INTERFACE_NAME "interface-name" -#define NM_SETTING_BOND_OPTIONS "options" - -/* Valid options for the 'options' property */ +#define NM_SETTING_BOND_INTERFACE_NAME "interface-name" +#define NM_SETTING_BOND_MODE "mode" +#define NM_SETTING_BOND_PRIMARY "primary" +#define NM_SETTING_BOND_MIIMON "miimon" +#define NM_SETTING_BOND_DOWNDELAY "downdelay" +#define NM_SETTING_BOND_UPDELAY "updelay" +#define NM_SETTING_BOND_ARP_INTERVAL "arp-interval" +#define NM_SETTING_BOND_ARP_IP_TARGET "arp-ip-target" +#define NM_SETTING_BOND_ARP_VALIDATE "arp-validate" +#define NM_SETTING_BOND_PRIMARY "primary" +#define NM_SETTING_BOND_PRIMARY_RESELECT "primary-reselect" +#define NM_SETTING_BOND_FAIL_OVER_MAC "fail-over-mac" +#define NM_SETTING_BOND_USE_CARRIER "use-carrier" +#define NM_SETTING_BOND_AD_SELECT "ad-select" +#define NM_SETTING_BOND_XMIT_HASH_POLICY "xmit-hash-policy" +#define NM_SETTING_BOND_RESEND_IGMP "resend-igmp" + +/* Deprecated */ +#define NM_SETTING_BOND_OPTIONS "options" + +/* Valid options as named by the kernal */ #define NM_SETTING_BOND_OPTION_MODE "mode" #define NM_SETTING_BOND_OPTION_MIIMON "miimon" #define NM_SETTING_BOND_OPTION_DOWNDELAY "downdelay" #define NM_SETTING_BOND_OPTION_UPDELAY "updelay" #define NM_SETTING_BOND_OPTION_ARP_INTERVAL "arp_interval" #define NM_SETTING_BOND_OPTION_ARP_IP_TARGET "arp_ip_target" #define NM_SETTING_BOND_OPTION_ARP_VALIDATE "arp_validate" #define NM_SETTING_BOND_OPTION_PRIMARY "primary" #define NM_SETTING_BOND_OPTION_PRIMARY_RESELECT "primary_reselect" #define NM_SETTING_BOND_OPTION_FAIL_OVER_MAC "fail_over_mac" #define NM_SETTING_BOND_OPTION_USE_CARRIER "use_carrier" #define NM_SETTING_BOND_OPTION_AD_SELECT "ad_select" #define NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY "xmit_hash_policy" #define NM_SETTING_BOND_OPTION_RESEND_IGMP "resend_igmp" +#define __NM_SETTING_BOND_MODE_IS_balance_rr(mode) ((mode) && (!strcmp ((mode), "0") || !strcmp ((mode), "balance-rr"))) +#define __NM_SETTING_BOND_MODE_IS_active_backup(mode) ((mode) && (!strcmp ((mode), "1") || !strcmp ((mode), "active-backup"))) +#define __NM_SETTING_BOND_MODE_IS_balance_xor(mode) ((mode) && (!strcmp ((mode), "2") || !strcmp ((mode), "balance-xor"))) +#define __NM_SETTING_BOND_MODE_IS_broadcast(mode) ((mode) && (!strcmp ((mode), "3") || !strcmp ((mode), "broadcast"))) +#define __NM_SETTING_BOND_MODE_IS_802_3ad(mode) ((mode) && (!strcmp ((mode), "4") || !strcmp ((mode), "802.3ad"))) +#define __NM_SETTING_BOND_MODE_IS_balance_tlb(mode) ((mode) && (!strcmp ((mode), "5") || !strcmp ((mode), "balance-tlb"))) +#define __NM_SETTING_BOND_MODE_IS_balance_alb(mode) ((mode) && (!strcmp ((mode), "6") || !strcmp ((mode), "balance-alb"))) + typedef struct { NMSetting parent; } NMSettingBond; typedef struct { NMSettingClass parent; /* Padding for future expansion */ void (*_reserved1) (void); void (*_reserved2) (void); void (*_reserved3) (void); void (*_reserved4) (void); } NMSettingBondClass; GType nm_setting_bond_get_type (void); -NMSetting * nm_setting_bond_new (void); -const char * nm_setting_bond_get_interface_name (NMSettingBond *setting); -guint32 nm_setting_bond_get_num_options (NMSettingBond *setting); -gboolean nm_setting_bond_get_option (NMSettingBond *setting, - guint32 idx, - const char **out_name, - const char **out_value); -const char * nm_setting_bond_get_option_by_name (NMSettingBond *setting, - const char *name); -gboolean nm_setting_bond_add_option (NMSettingBond *setting, - const char *name, - const char *value); -gboolean nm_setting_bond_remove_option (NMSettingBond *setting, - const char *name); - -gboolean nm_setting_bond_validate_option (const char *name, - const char *value); - -const char **nm_setting_bond_get_valid_options (NMSettingBond *setting); - -const char * nm_setting_bond_get_option_default (NMSettingBond *setting, - const char *name); +NMSetting * nm_setting_bond_new (void); + +const char * nm_setting_bond_get_interface_name (const NMSettingBond *setting); + +const char * nm_setting_bond_get_mode (const NMSettingBond *setting); +const char * nm_setting_bond_get_primary (const NMSettingBond *setting); +guint nm_setting_bond_get_miimon (const NMSettingBond *setting); +guint nm_setting_bond_get_downdelay (const NMSettingBond *setting); +guint nm_setting_bond_get_updelay (const NMSettingBond *setting); +guint nm_setting_bond_get_arp_interval (const NMSettingBond *setting); +const char *const* nm_setting_bond_get_arp_ip_target (const NMSettingBond *setting); +const char * nm_setting_bond_get_arp_validate (const NMSettingBond *setting); +const char * nm_setting_bond_get_primary_reselect (const NMSettingBond *setting); +const char * nm_setting_bond_get_fail_over_mac (const NMSettingBond *setting); +gboolean nm_setting_bond_get_use_carrier (const NMSettingBond *setting); +const char * nm_setting_bond_get_ad_select (const NMSettingBond *setting); +const char * nm_setting_bond_get_xmit_hash_policy (const NMSettingBond *setting); +guint nm_setting_bond_get_resend_igmp (const NMSettingBond *setting); + +const char * nm_setting_bond_get_property_name (const char *name); +const char * nm_setting_bond_get_kernel_name (const char *name); +const char *const* nm_setting_bond_get_kernel_names (void); + +const char * nm_setting_bond_get_string (const NMSettingBond *setting, const char *name); +gboolean nm_setting_bond_is_default (const NMSettingBond *setting, const char *name, GValue *default_value); +gboolean nm_setting_bond_validate_string (const char *name, const char *value, GError **error); +gboolean nm_setting_bond_set_string (NMSettingBond *setting, const char *name, const char *value); +void nm_setting_bond_set_default (NMSettingBond *setting, const char *name); + + +/* Deprecated */ +G_GNUC_DEPRECATED guint32 nm_setting_bond_get_num_options (NMSettingBond *setting); +G_GNUC_DEPRECATED gboolean nm_setting_bond_get_option (NMSettingBond *setting, + guint32 idx, + const char **out_name, + const char **out_value); +G_GNUC_DEPRECATED const char * nm_setting_bond_get_option_by_name (NMSettingBond *setting, + const char *name); +G_GNUC_DEPRECATED gboolean nm_setting_bond_add_option (NMSettingBond *setting, + const char *name, + const char *value); +G_GNUC_DEPRECATED gboolean nm_setting_bond_remove_option (NMSettingBond *setting, + const char *name); + +G_GNUC_DEPRECATED const char **nm_setting_bond_get_valid_options (NMSettingBond *setting); + +G_GNUC_DEPRECATED const char * nm_setting_bond_get_option_default (NMSettingBond *setting, + const char *name); G_END_DECLS #endif /* NM_SETTING_BOND_H */ diff --git a/libnm-util/tests/test-general.c b/libnm-util/tests/test-general.c index 56d6e67..f555901 100644 --- a/libnm-util/tests/test-general.c +++ b/libnm-util/tests/test-general.c @@ -1739,43 +1739,45 @@ test_setting_connection_changed_signal (void) g_object_unref (connection); } static void test_setting_bond_changed_signal (void) { NMConnection *connection; gboolean changed = FALSE; NMSettingBond *s_bond; connection = nm_connection_new (); g_signal_connect (connection, NM_CONNECTION_CHANGED, (GCallback) test_connection_changed_cb, &changed); s_bond = (NMSettingBond *) nm_setting_bond_new (); nm_connection_add_setting (connection, NM_SETTING (s_bond)); - ASSERT_CHANGED (nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "10")); - ASSERT_CHANGED (nm_setting_bond_remove_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY)); - ASSERT_UNCHANGED (nm_setting_bond_remove_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY)); + ASSERT_CHANGED (nm_setting_bond_set_string (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "10")); + ASSERT_CHANGED (nm_setting_bond_set_string (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "10")); + ASSERT_UNCHANGED (nm_setting_bond_set_string (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "10x")); + ASSERT_CHANGED (nm_setting_bond_set_default (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY)); + ASSERT_CHANGED (g_object_set (G_OBJECT (s_bond), NM_SETTING_BOND_DOWNDELAY, 20, NULL)); g_object_unref (connection); } static void test_setting_ip4_changed_signal (void) { NMConnection *connection; gboolean changed = FALSE; NMSettingIP4Config *s_ip4; NMIP4Address *addr; NMIP4Route *route; connection = nm_connection_new (); g_signal_connect (connection, NM_CONNECTION_CHANGED, (GCallback) test_connection_changed_cb, &changed); s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 8d9939b..f09cfbd 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -183,227 +183,199 @@ complete_connection (NMDevice *device, return TRUE; } /******************************************************************/ static gboolean set_bond_attr (NMDevice *device, const char *attr, const char *value) { gboolean ret; int ifindex = nm_device_get_ifindex (device); ret = nm_platform_master_set_option (ifindex, attr, value); if (!ret) { nm_log_warn (LOGD_HW, "(%s): failed to set bonding attribute " "'%s' to '%s'", nm_device_get_ip_iface (device), attr, value); } return ret; } +static gboolean +set_bond_attr_int (NMDevice *device, const char *attr, int value) +{ + char buf[20]; + + snprintf (buf, sizeof (buf), "%d", value); + buf[sizeof (buf) -1] = 0; + return set_bond_attr (device, attr, buf); +} + /* Ignore certain bond options if they are zero (off/disabled) */ static gboolean ignore_if_zero (const char *option, const char *value) { - if (strcmp (option, "arp_interval") && - strcmp (option, "miimon") && - strcmp (option, "downdelay") && - strcmp (option, "updelay")) + if (strcmp (option, NM_SETTING_BOND_OPTION_ARP_INTERVAL) && + strcmp (option, NM_SETTING_BOND_OPTION_MIIMON) && + strcmp (option, NM_SETTING_BOND_OPTION_DOWNDELAY) && + strcmp (option, NM_SETTING_BOND_OPTION_UPDELAY)) return FALSE; return g_strcmp0 (value, "0") == 0 ? TRUE : FALSE; } static void update_connection (NMDevice *device, NMConnection *connection) { NMSettingBond *s_bond = nm_connection_get_setting_bond (connection); const char *ifname = nm_device_get_iface (device); int ifindex = nm_device_get_ifindex (device); - const char **options; + const char *const*options; if (!s_bond) { s_bond = (NMSettingBond *) nm_setting_bond_new (); - nm_connection_add_setting (connection, (NMSetting *) s_bond); g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, ifname, NULL); + nm_connection_add_setting (connection, (NMSetting *) s_bond); } /* Read bond options from sysfs and update the Bond setting to match */ - options = nm_setting_bond_get_valid_options (s_bond); + options = nm_setting_bond_get_kernel_names (); while (options && *options) { gs_free char *value = nm_platform_master_get_option (ifindex, *options); - const char *defvalue = nm_setting_bond_get_option_default (s_bond, *options); - - if (value && !ignore_if_zero (*options, value) && (g_strcmp0 (value, defvalue) != 0)) { - /* Replace " " with "," for arp_ip_targets from the kernel */ - if (strcmp (*options, "arp_ip_target") == 0) { - char *p = value; - - while (p && *p) { - if (*p == ' ') - *p = ','; - p++; - } - } - nm_setting_bond_add_option (s_bond, *options, value); - } + /* FIXME: why this check for ignore_if_zero? */ + if (value && !ignore_if_zero (*options, value)) { + if (!nm_setting_bond_set_string (s_bond, *options, value)) + nm_log_dbg (LOGD_BOND, "Could not set bonding option %s from sysfs value '%s'", *options, value); + } else + nm_setting_bond_set_default (s_bond, *options); options++; } } static void set_arp_targets (NMDevice *device, - const char *value, - const char *delim, + const char *const *values, const char *prefix) { - char **items, **iter, *tmp; + char *tmp; - if (!value || !*value) + if (!values) return; - items = g_strsplit_set (value, delim, 0); - for (iter = items; iter && *iter; iter++) { - if (*iter[0]) { - tmp = g_strdup_printf ("%s%s", prefix, *iter); - set_bond_attr (device, "arp_ip_target", tmp); + for (; *values; values++) { + if (*values[0]) { + tmp = g_strconcat (prefix, *values, NULL); + set_bond_attr (device, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, tmp); g_free (tmp); } } - g_strfreev (items); -} - -static void -set_simple_option (NMDevice *device, - const char *attr, - NMSettingBond *s_bond, - const char *opt) -{ - const char *value; - - value = nm_setting_bond_get_option_by_name (s_bond, opt); - if (!value) - value = nm_setting_bond_get_option_default (s_bond, opt); - set_bond_attr (device, attr, value); } static NMActStageReturn apply_bonding_config (NMDevice *device) { NMConnection *connection; NMSettingBond *s_bond; int ifindex = nm_device_get_ifindex (device); const char *mode, *value; + int ivalue; char *contents; gboolean set_arp_interval = TRUE; /* Option restrictions: * * arp_interval conflicts miimon > 0 * arp_interval conflicts [ alb, tlb ] * arp_validate needs [ active-backup ] * downdelay needs miimon * updelay needs miimon * primary needs [ active-backup, tlb, alb ] * * clearing miimon requires that arp_interval be 0, but clearing * arp_interval doesn't require miimon to be 0 */ connection = nm_device_get_connection (device); g_assert (connection); s_bond = nm_connection_get_setting_bond (connection); g_assert (s_bond); - mode = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE); - if (mode == NULL) - mode = "balance-rr"; + mode = nm_setting_bond_get_mode (s_bond); + g_assert (mode && !mode[0]); - value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON); - if (value && atoi (value)) { + ivalue = nm_setting_bond_get_miimon (s_bond); + if (ivalue > 0) { /* clear arp interval */ - set_bond_attr (device, "arp_interval", "0"); + set_bond_attr (device, NM_SETTING_BOND_OPTION_ARP_INTERVAL, "0"); set_arp_interval = FALSE; - set_bond_attr (device, "miimon", value); - set_simple_option (device, "updelay", s_bond, NM_SETTING_BOND_OPTION_UPDELAY); - set_simple_option (device, "downdelay", s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY); - } else if (!value) { - /* If not given, and arp_interval is not given, default to 100 */ - long int val_int; - char *end; - - value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); - errno = 0; - val_int = strtol (value ? value : "0", &end, 10); - if (!value || (val_int == 0 && errno == 0 && *end == '\0')) - set_bond_attr (device, "miimon", "100"); + set_bond_attr_int (device, NM_SETTING_BOND_OPTION_MIIMON, ivalue); + set_bond_attr_int (device, NM_SETTING_BOND_OPTION_UPDELAY, nm_setting_bond_get_updelay (s_bond)); + set_bond_attr_int (device, NM_SETTING_BOND_OPTION_DOWNDELAY, nm_setting_bond_get_downdelay (s_bond)); } /* The stuff after 'mode' requires the given mode or doesn't care */ - set_bond_attr (device, "mode", mode); + set_bond_attr (device, NM_SETTING_BOND_OPTION_MODE, mode); /* arp_interval not compatible with ALB, TLB */ - if (g_strcmp0 (mode, "balance-alb") == 0 || g_strcmp0 (mode, "balance-tlb") == 0) + if (__NM_SETTING_BOND_MODE_IS_balance_alb (mode) || __NM_SETTING_BOND_MODE_IS_balance_tlb (mode)) set_arp_interval = FALSE; if (set_arp_interval) { - set_simple_option (device, "arp_interval", s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + set_bond_attr_int (device, NM_SETTING_BOND_OPTION_ARP_INTERVAL, nm_setting_bond_get_arp_interval (s_bond)); /* Just let miimon get cleared automatically; even setting miimon to * 0 (disabled) clears arp_interval. */ } - value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE); - /* arp_validate > 0 only valid in active-backup mode */ - if ( value - && g_strcmp0 (value, "0") != 0 - && g_strcmp0 (value, "none") != 0 - && g_strcmp0 (mode, "active-backup") == 0) - set_bond_attr (device, "arp_validate", value); - else - set_bond_attr (device, "arp_validate", "0"); - - if ( g_strcmp0 (mode, "active-backup") == 0 - || g_strcmp0 (mode, "balance-alb") == 0 - || g_strcmp0 (mode, "balance-tlb") == 0) { - value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY); - set_bond_attr (device, "primary", value ? value : ""); + value = nm_setting_bond_get_arp_validate (s_bond); + set_bond_attr (device, NM_SETTING_BOND_OPTION_ARP_VALIDATE, value); + + if ( __NM_SETTING_BOND_MODE_IS_active_backup (mode) + || __NM_SETTING_BOND_MODE_IS_balance_alb (mode) + || __NM_SETTING_BOND_MODE_IS_balance_tlb (mode)) { + value = nm_setting_bond_get_primary (s_bond); + set_bond_attr (device, NM_SETTING_BOND_OPTION_PRIMARY, value ? value : ""); } /* Clear ARP targets */ - contents = nm_platform_master_get_option (ifindex, "arp_ip_target"); - set_arp_targets (device, contents, " \n", "-"); + contents = nm_platform_master_get_option (ifindex, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + if (contents && contents[0]) { + char **items; + + items = g_strsplit_set (contents, " \n", 0); + set_arp_targets (device, (const char *const*) items, "-"); + g_strfreev (items); + } g_free (contents); /* Add new ARP targets */ - value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); - set_arp_targets (device, value, ",", "+"); - - set_simple_option (device, "primary_reselect", s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT); - set_simple_option (device, "fail_over_mac", s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC); - set_simple_option (device, "use_carrier", s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER); - set_simple_option (device, "ad_select", s_bond, NM_SETTING_BOND_OPTION_AD_SELECT); - set_simple_option (device, "xmit_hash_policy", s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY); - set_simple_option (device, "resend_igmp", s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP); + set_arp_targets (device, nm_setting_bond_get_arp_ip_target (s_bond), "+"); + + set_bond_attr (device, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, nm_setting_bond_get_primary_reselect (s_bond)); + set_bond_attr (device, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC, nm_setting_bond_get_fail_over_mac (s_bond)); + set_bond_attr_int (device, NM_SETTING_BOND_OPTION_USE_CARRIER, nm_setting_bond_get_use_carrier (s_bond)); + set_bond_attr (device, NM_SETTING_BOND_OPTION_AD_SELECT, nm_setting_bond_get_ad_select (s_bond)); + set_bond_attr (device, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, nm_setting_bond_get_xmit_hash_policy (s_bond)); + set_bond_attr_int (device, NM_SETTING_BOND_OPTION_RESEND_IGMP, nm_setting_bond_get_resend_igmp (s_bond)); return NM_ACT_STAGE_RETURN_SUCCESS; } static NMActStageReturn act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) { NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; gboolean no_firmware = FALSE; g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); ret = NM_DEVICE_CLASS (nm_device_bond_parent_class)->act_stage1_prepare (dev, reason); if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; /* Interface must be down to set bond options */ nm_device_take_down (dev, TRUE); ret = apply_bonding_config (dev); diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index e2cff0b..b46daf9 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -46,44 +46,44 @@ #include #include #include #include #include #include #include #include #include "wifi-utils.h" #include "nm-posix-signals.h" #include "NetworkManagerUtils.h" #include "common.h" #include "shvar.h" #include "utils.h" #include "reader.h" #define PLUGIN_PRINT(pname, fmt, args...) \ - { g_message (" " pname ": " fmt, ##args); } + G_STMT_START { g_message (" " pname ": " fmt, ##args); } G_STMT_END #define PLUGIN_WARN(pname, fmt, args...) \ - { g_warning (" " pname ": " fmt, ##args); } + G_STMT_START { g_warning (" " pname ": " fmt, ##args); } G_STMT_END static gboolean get_int (const char *str, int *value) { char *e; long int tmp; errno = 0; tmp = strtol (str, &e, 0); if (errno || *e != '\0' || tmp > G_MAXINT || tmp < G_MININT) return FALSE; *value = (int) tmp; return TRUE; } static gboolean get_uint (const char *str, guint32 *value) { char *e; long unsigned int tmp; @@ -4042,106 +4042,152 @@ infiniband_connection_from_ifcfg (const char *file, } check_if_bond_slave (ifcfg, NM_SETTING_CONNECTION (con_setting)); check_if_team_slave (ifcfg, NM_SETTING_CONNECTION (con_setting)); nm_connection_add_setting (connection, con_setting); infiniband_setting = make_infiniband_setting (ifcfg, file, error); if (!infiniband_setting) { g_object_unref (connection); return NULL; } nm_connection_add_setting (connection, infiniband_setting); if (!nm_connection_verify (connection, error)) { g_object_unref (connection); return NULL; } return connection; } +typedef void (*IfcfgRhOptFunc) (NMSetting *setting, + const char *key, + const char *value, + gpointer data); + +static void +handle_ifcfg_rh_opts (NMSetting *setting, + const char *value, + IfcfgRhOptFunc func, + gpointer data) +{ + char **items, **iter; + + g_return_if_fail (value); + if (!value || !*value) + return; + + items = g_strsplit_set (value, " ", -1); + for (iter = items; *iter; iter++) { + if (**iter) { + char **keys, *key, *val; + + keys = g_strsplit_set (*iter, "=", 2); + if (*keys) { + key = keys[0]; + val = keys[1]; + if (val && *key && *val) + func (setting, key, val, data); + } + + g_strfreev (keys); + } + } + g_strfreev (items); +} + static void -handle_bond_option (NMSettingBond *s_bond, +handle_bond_option (NMSetting *setting, const char *key, - const char *value) + const char *value, + gpointer data) { - char *sanitized = NULL, *j; - const char *p = value; + NMSettingBond *s_bond = NM_SETTING_BOND (setting); + const char *value0 = value; + char *value_cleanup = NULL; - /* Remove any quotes or +/- from arp_ip_target */ - if (!g_strcmp0 (key, NM_SETTING_BOND_OPTION_ARP_IP_TARGET) && value && value[0]) { - if (*p == '\'' || *p == '"') - p++; - j = sanitized = g_malloc0 (strlen (p) + 1); - while (*p) { - if (*p != '+' && *p != '-' && *p != '\'' && *p != '"') - *j++ = *p; - p++; + g_return_if_fail (key); + + if (!strcmp (key, NM_SETTING_BOND_OPTION_ARP_IP_TARGET)) { + char **arp_ip_target = NULL; + char *sanitized = NULL, *j; + const char *p = value; + + /* Remove any quotes or +/- from arp_ip_target */ + if (value[0]) { + if (*p == '\'' || *p == '"') + p++; + j = sanitized = g_malloc0 (strlen (p) + 1); + while (*p) { + if (*p != '+' && *p != '-' && *p != '\'' && *p != '"') + *j++ = *p; + p++; + } + value = sanitized; } + + if (value && value[0]) { + arp_ip_target = g_strsplit (value, ",", -1); + /* don't set the GObject property to arp_ip_target, because we want to reuse the + * validation from nm_setting_bond_set_string. */ + value_cleanup = g_strjoinv (",", arp_ip_target); + value = value_cleanup; + g_strfreev (arp_ip_target); + } else + value = NULL; + g_free (sanitized); } - if (!nm_setting_bond_add_option (s_bond, key, sanitized ? sanitized : value)) - PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid bonding option '%s'", key); - g_free (sanitized); + if (!nm_setting_bond_set_string (s_bond, key, value)) { + if (!nm_setting_bond_get_property_name (key)) + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: unrecognized bond property '%s'", key); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid bond property '%s'='%s'", key, value0); + return; + } + + g_free (value_cleanup); } static NMSetting * make_bond_setting (shvarFile *ifcfg, const char *file, GError **error) { NMSettingBond *s_bond; char *value; s_bond = NM_SETTING_BOND (nm_setting_bond_new ()); value = svGetValue (ifcfg, "DEVICE", FALSE); if (!value || !strlen (value)) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "mandatory DEVICE keyword missing"); goto error; } g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, value, NULL); g_free (value); value = svGetValue (ifcfg, "BONDING_OPTS", FALSE); if (value) { - char **items, **iter; - - items = g_strsplit_set (value, " ", -1); - for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - char **keys, *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && strlen(key) && strlen(val)) - handle_bond_option (s_bond, key, val); - } - - g_strfreev (keys); - } - } + handle_ifcfg_rh_opts (NM_SETTING (s_bond), value, handle_bond_option, NULL); g_free (value); - g_strfreev (items); } return (NMSetting *) s_bond; error: g_object_unref (s_bond); return NULL; } static NMConnection * bond_connection_from_ifcfg (const char *file, shvarFile *ifcfg, GError **error) { NMConnection *connection = NULL; NMSetting *con_setting = NULL; NMSetting *bond_setting = NULL; NMSetting *wired_setting = NULL; NMSetting8021x *s_8021x = NULL; @@ -4249,110 +4295,79 @@ team_connection_from_ifcfg (const char *file, nm_connection_add_setting (connection, team_setting); wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error); if (!wired_setting) { g_object_unref (connection); return NULL; } nm_connection_add_setting (connection, wired_setting); if (s_8021x) nm_connection_add_setting (connection, NM_SETTING (s_8021x)); if (!nm_connection_verify (connection, error)) { g_object_unref (connection); return NULL; } return connection; } -typedef void (*BridgeOptFunc) (NMSetting *setting, - gboolean stp, - const char *key, - const char *value); - static void handle_bridge_option (NMSetting *setting, - gboolean stp, const char *key, - const char *value) + const char *value, + gpointer data) { + gboolean stp = GPOINTER_TO_INT (data); guint32 u = 0; if (!strcmp (key, "priority")) { if (stp == FALSE) { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: 'priority' invalid when STP is disabled"); } else if (get_uint (value, &u)) g_object_set (setting, NM_SETTING_BRIDGE_PRIORITY, u, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid priority value '%s'", value); } else if (!strcmp (key, "hello_time")) { if (stp == FALSE) { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: 'hello_time' invalid when STP is disabled"); } else if (get_uint (value, &u)) g_object_set (setting, NM_SETTING_BRIDGE_HELLO_TIME, u, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid hello_time value '%s'", value); } else if (!strcmp (key, "max_age")) { if (stp == FALSE) { PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: 'max_age' invalid when STP is disabled"); } else if (get_uint (value, &u)) g_object_set (setting, NM_SETTING_BRIDGE_MAX_AGE, u, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid max_age value '%s'", value); } else if (!strcmp (key, "ageing_time")) { if (get_uint (value, &u)) g_object_set (setting, NM_SETTING_BRIDGE_AGEING_TIME, u, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid ageing_time value '%s'", value); } else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: unhandled bridge option '%s'", key); } -static void -handle_bridging_opts (NMSetting *setting, - gboolean stp, - const char *value, - BridgeOptFunc func) -{ - char **items, **iter; - - items = g_strsplit_set (value, " ", -1); - for (iter = items; iter && *iter; iter++) { - if (strlen (*iter)) { - char **keys, *key, *val; - - keys = g_strsplit_set (*iter, "=", 2); - if (keys && *keys) { - key = *keys; - val = *(keys + 1); - if (val && strlen(key) && strlen(val)) - func (setting, stp, key, val); - } - - g_strfreev (keys); - } - } - g_strfreev (items); -} - static NMSetting * make_bridge_setting (shvarFile *ifcfg, const char *file, GError **error) { NMSettingBridge *s_bridge; char *value; guint32 u; gboolean stp = FALSE; gboolean stp_set = FALSE; s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ()); value = svGetValue (ifcfg, "DEVICE", FALSE); if (!value || !strlen (value)) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "mandatory DEVICE keyword missing"); goto error; } g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, value, NULL); @@ -4374,165 +4389,167 @@ make_bridge_setting (shvarFile *ifcfg, if (!stp_set) { /* Missing or invalid STP property means "no" */ g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL); } value = svGetValue (ifcfg, "DELAY", FALSE); if (value) { if (stp) { if (get_uint (value, &u)) g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, u, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid forward delay value '%s'", value); } else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: DELAY invalid when STP is disabled"); g_free (value); } value = svGetValue (ifcfg, "BRIDGING_OPTS", FALSE); if (value) { - handle_bridging_opts (NM_SETTING (s_bridge), stp, value, handle_bridge_option); + handle_ifcfg_rh_opts (NM_SETTING (s_bridge), value, handle_bridge_option, + GINT_TO_POINTER (stp)); g_free (value); } return (NMSetting *) s_bridge; error: g_object_unref (s_bridge); return NULL; } static NMConnection * bridge_connection_from_ifcfg (const char *file, shvarFile *ifcfg, GError **error) { NMConnection *connection = NULL; NMSetting *con_setting = NULL; NMSetting *bridge_setting = NULL; g_return_val_if_fail (file != NULL, NULL); g_return_val_if_fail (ifcfg != NULL, NULL); connection = nm_connection_new (); con_setting = make_connection_setting (file, ifcfg, NM_SETTING_BRIDGE_SETTING_NAME, NULL, _("Bridge")); if (!con_setting) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Failed to create connection setting."); g_object_unref (connection); return NULL; } nm_connection_add_setting (connection, con_setting); bridge_setting = make_bridge_setting (ifcfg, file, error); if (!bridge_setting) { g_object_unref (connection); return NULL; } - nm_connection_add_setting (connection, bridge_setting); + nm_connection_add_setting (connection, bridge_setting); if (!nm_connection_verify (connection, error)) { g_object_unref (connection); return NULL; } return connection; } static void handle_bridge_port_option (NMSetting *setting, - gboolean stp, const char *key, - const char *value) + const char *value, + gpointer data) { guint32 u = 0; if (!strcmp (key, "priority")) { if (get_uint (value, &u)) g_object_set (setting, NM_SETTING_BRIDGE_PORT_PRIORITY, u, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid priority value '%s'", value); } else if (!strcmp (key, "path_cost")) { if (get_uint (value, &u)) g_object_set (setting, NM_SETTING_BRIDGE_PORT_PATH_COST, u, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid path_cost value '%s'", value); } else if (!strcmp (key, "hairpin_mode")) { if (!strcasecmp (value, "on") || !strcasecmp (value, "yes") || !strcmp (value, "1")) g_object_set (setting, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, TRUE, NULL); else if (!strcasecmp (value, "off") || !strcasecmp (value, "no")) g_object_set (setting, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, FALSE, NULL); else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid hairpin_mode value '%s'", value); } else PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: unhandled bridge port option '%s'", key); } static NMSetting * make_bridge_port_setting (shvarFile *ifcfg) { NMSetting *s_port = NULL; char *value; g_return_val_if_fail (ifcfg != NULL, FALSE); value = svGetValue (ifcfg, "BRIDGE", FALSE); if (value) { g_free (value); s_port = nm_setting_bridge_port_new (); value = svGetValue (ifcfg, "BRIDGING_OPTS", FALSE); if (value) - handle_bridging_opts (s_port, FALSE, value, handle_bridge_port_option); + handle_ifcfg_rh_opts (NM_SETTING (s_port), value, handle_bridge_port_option, NULL); + g_free (value); } return s_port; } static NMSetting * make_team_port_setting (shvarFile *ifcfg) { NMSetting *s_port = NULL; char *value; value = svGetValue (ifcfg, "TEAM_PORT_CONFIG", TRUE); if (value) { s_port = nm_setting_team_port_new (); g_object_set (s_port, NM_SETTING_TEAM_PORT_CONFIG, value, NULL); g_free (value); } return s_port; } static gboolean is_bond_device (const char *name, shvarFile *parsed) { g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (parsed != NULL, FALSE); if (svTrueValue (parsed, "BONDING_MASTER", FALSE)) return TRUE; - + /* XXX: Check for "bond[\d]+"? */ return FALSE; } static gboolean is_vlan_device (const char *name, shvarFile *parsed) { g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (parsed != NULL, FALSE); if (svTrueValue (parsed, "VLAN", FALSE)) return TRUE; return FALSE; } static void parse_prio_map_list (NMSettingVlan *s_vlan, shvarFile *ifcfg, diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 14c4fb9..bc8c974 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -12084,43 +12084,43 @@ test_read_bond_main (void) &error, &ignore_error); ASSERT (connection != NULL, "bond-main-read", "unexpected failure reading %s", TEST_IFCFG_BOND_MAIN); ASSERT (nm_connection_verify (connection, &error), "bond-main-read", "failed to verify %s: %s", TEST_IFCFG_BOND_MAIN, error->message); /* ===== Bonding SETTING ===== */ s_bond = nm_connection_get_setting_bond (connection); ASSERT (s_bond != NULL, "bond-main", "failed to verify %s: missing %s setting", TEST_IFCFG_BOND_MAIN, NM_SETTING_BOND_SETTING_NAME); ASSERT (g_strcmp0 (nm_setting_bond_get_interface_name (s_bond), "bond0") == 0, "bond-main", "failed to verify %s: DEVICE=%s does not match bond0", TEST_IFCFG_BOND_MAIN, nm_setting_bond_get_interface_name (s_bond)); - ASSERT (g_strcmp0 (nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON), "100") == 0, - "bond-main", "failed to verify %s: miimon=%s does not match 100", - TEST_IFCFG_BOND_MAIN, nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON)); + ASSERT (nm_setting_bond_get_miimon (s_bond) == 100, + "bond-main", "failed to verify %s: miimon=%u does not match 100", + TEST_IFCFG_BOND_MAIN, nm_setting_bond_get_miimon (s_bond)); g_free (unmanaged); g_free (keyfile); g_free (routefile); g_free (route6file); g_object_unref (connection); } static void test_write_bond_main (void) { NMConnection *connection; NMConnection *reread; NMSettingConnection *s_con; NMSettingBond *s_bond; NMSettingIP4Config *s_ip4; NMSettingIP6Config *s_ip6; NMSettingWired *s_wired; char *uuid; const guint32 ip1 = htonl (0x01010103); diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index 6abbe2a..adf0847 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1233,77 +1233,74 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired, svSetValue (ifcfg, "MACADDR", tmp, FALSE); g_free (tmp); } mtu = nm_setting_wired_get_mtu (s_wired); if (mtu) { tmp = g_strdup_printf ("%u", mtu); svSetValue (ifcfg, "MTU", tmp, FALSE); g_free (tmp); } } return TRUE; } static gboolean write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingBond *s_bond; const char *iface; - guint32 i, num_opts; + const char *const* kernel_names; + GString *str; s_bond = nm_connection_get_setting_bond (connection); if (!s_bond) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing '%s' setting", NM_SETTING_BOND_SETTING_NAME); return FALSE; } iface = nm_setting_bond_get_interface_name (s_bond); if (!iface) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing interface name"); return FALSE; } svSetValue (ifcfg, "DEVICE", iface, FALSE); svSetValue (ifcfg, "BONDING_OPTS", NULL, FALSE); - num_opts = nm_setting_bond_get_num_options (s_bond); - if (num_opts > 0) { - GString *str = g_string_sized_new (64); - - for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) { - const char *key, *value; + str = g_string_sized_new (64); - if (!nm_setting_bond_get_option (s_bond, i, &key, &value)) - continue; + kernel_names = nm_setting_bond_get_kernel_names(); + for (; *kernel_names; kernel_names++) { + if (nm_setting_bond_is_default (s_bond, *kernel_names, NULL)) { + const char *strval = nm_setting_bond_get_string (s_bond, *kernel_names); - if (str->len) - g_string_append_c (str, ' '); - - g_string_append_printf (str, "%s=%s", key, value); + /* FIXME: how to represent NULL values that are not default values? */ + g_string_append_printf (str, "%s=%s ", *kernel_names, strval ? strval : ""); } + } + if (str->len > 0) { + g_string_truncate (str, str->len-1); - if (str->len) - svSetValue (ifcfg, "BONDING_OPTS", str->str, FALSE); - + svSetValue (ifcfg, "BONDING_OPTS", str->str, FALSE); g_string_free (str, TRUE); } svSetValue (ifcfg, "TYPE", TYPE_BOND, FALSE); svSetValue (ifcfg, "BONDING_MASTER", "yes", FALSE); return TRUE; } static gboolean write_team_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingTeam *s_team; const char *iface; const char *config; s_team = nm_connection_get_setting_team (connection); if (!s_team) { g_set_error (error, IFCFG_PLUGIN_ERROR, 0, "Missing '%s' setting", NM_SETTING_TEAM_SETTING_NAME); diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c index e8c06ff..335fe9a 100644 --- a/src/settings/plugins/keyfile/reader.c +++ b/src/settings/plugins/keyfile/reader.c @@ -565,41 +565,41 @@ read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key) { char **keys, **iter; char *value; const char *setting_name = nm_setting_get_name (setting); keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL); if (!keys || !*keys) return; for (iter = keys; *iter; iter++) { value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL); if (!value) continue; if (NM_IS_SETTING_VPN (setting)) { if (strcmp (*iter, NM_SETTING_VPN_SERVICE_TYPE)) nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), *iter, value); } if (NM_IS_SETTING_BOND (setting)) { if (strcmp (*iter, NM_SETTING_BOND_INTERFACE_NAME)) - nm_setting_bond_add_option (NM_SETTING_BOND (setting), *iter, value); + nm_setting_bond_set_string (NM_SETTING_BOND (setting), *iter, value); } g_free (value); } g_strfreev (keys); } static void unescape_semicolons (char *str) { int i; gsize len = strlen (str); for (i = 0; i < len; i++) { if (str[i] == '\\' && str[i+1] == ';') { memmove(str + i, str + i + 1, len - (i + 1)); len--; } str[len] = '\0'; } } -- 1.8.4.1.559.gdb9bdfb