f4b5057449592850ca534ab80765b4406d9652ca
[NetworkManager.git] / libnm-core / nm-setting-bond.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
3 /*
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301 USA.
18  *
19  * Copyright 2011 - 2013 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include <string.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29
30 #include "nm-setting-bond.h"
31 #include "nm-utils.h"
32 #include "nm-utils-private.h"
33 #include "nm-connection-private.h"
34 #include "nm-setting-infiniband.h"
35 #include "nm-core-internal.h"
36
37 /**
38  * SECTION:nm-setting-bond
39  * @short_description: Describes connection properties for bonds
40  *
41  * The #NMSettingBond object is a #NMSetting subclass that describes properties
42  * necessary for bond connections.
43  **/
44
45 G_DEFINE_TYPE_WITH_CODE (NMSettingBond, nm_setting_bond, NM_TYPE_SETTING,
46                          _nm_register_setting (BOND, 1))
47 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_BOND)
48
49 #define NM_SETTING_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BOND, NMSettingBondPrivate))
50
51 typedef struct {
52         GHashTable *options;
53 } NMSettingBondPrivate;
54
55 enum {
56         PROP_0,
57         PROP_OPTIONS,
58         LAST_PROP
59 };
60
61 typedef struct {
62         const char *opt;
63         const char *val;
64         guint opt_type;
65         guint min;
66         guint max;
67         char *list[10];
68 } BondDefault;
69
70 static const BondDefault defaults[] = {
71         { NM_SETTING_BOND_OPTION_MODE,             "balance-rr", NM_BOND_OPTION_TYPE_BOTH, 0, 6,
72           { "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb", NULL } },
73         { NM_SETTING_BOND_OPTION_MIIMON,           "100",        NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT },
74         { NM_SETTING_BOND_OPTION_DOWNDELAY,        "0",          NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT },
75         { NM_SETTING_BOND_OPTION_UPDELAY,          "0",          NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT },
76         { NM_SETTING_BOND_OPTION_ARP_INTERVAL,     "0",          NM_BOND_OPTION_TYPE_INT, 0, G_MAXINT },
77         { NM_SETTING_BOND_OPTION_ARP_IP_TARGET,    "",           NM_BOND_OPTION_TYPE_IP },
78         { NM_SETTING_BOND_OPTION_ARP_VALIDATE,     "none",       NM_BOND_OPTION_TYPE_BOTH, 0, 3,
79           { "none", "active", "backup", "all", NULL } },
80         { NM_SETTING_BOND_OPTION_PRIMARY,          "",           NM_BOND_OPTION_TYPE_IFNAME },
81         { NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, "always",     NM_BOND_OPTION_TYPE_BOTH, 0, 2,
82           { "always", "better", "failure", NULL } },
83         { NM_SETTING_BOND_OPTION_FAIL_OVER_MAC,    "none",       NM_BOND_OPTION_TYPE_BOTH, 0, 2,
84           { "none", "active", "follow", NULL } },
85         { NM_SETTING_BOND_OPTION_USE_CARRIER,      "1",          NM_BOND_OPTION_TYPE_INT, 0, 1 },
86         { NM_SETTING_BOND_OPTION_AD_SELECT,        "stable",     NM_BOND_OPTION_TYPE_BOTH, 0, 2,
87           { "stable", "bandwidth", "count", NULL } },
88         { NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, "layer2",     NM_BOND_OPTION_TYPE_BOTH, 0, 2,
89           { "layer2", "layer3+4", "layer2+3", NULL } },
90         { NM_SETTING_BOND_OPTION_RESEND_IGMP,      "1",          NM_BOND_OPTION_TYPE_INT, 0, 255 },
91         { NM_SETTING_BOND_OPTION_LACP_RATE,        "slow",       NM_BOND_OPTION_TYPE_BOTH, 0, 1,
92           { "slow", "fast", NULL } },
93 };
94
95 /**
96  * nm_setting_bond_new:
97  *
98  * Creates a new #NMSettingBond object with default values.
99  *
100  * Returns: (transfer full): the new empty #NMSettingBond object
101  **/
102 NMSetting *
103 nm_setting_bond_new (void)
104 {
105         return (NMSetting *) g_object_new (NM_TYPE_SETTING_BOND, NULL);
106 }
107
108 /**
109  * nm_setting_bond_get_num_options:
110  * @setting: the #NMSettingBond
111  *
112  * Returns the number of options that should be set for this bond when it
113  * is activated. This can be used to retrieve each option individually
114  * using nm_setting_bond_get_option().
115  *
116  * Returns: the number of bonding options
117  **/
118 guint32
119 nm_setting_bond_get_num_options (NMSettingBond *setting)
120 {
121         g_return_val_if_fail (NM_IS_SETTING_BOND (setting), 0);
122
123         return g_hash_table_size (NM_SETTING_BOND_GET_PRIVATE (setting)->options);
124 }
125
126 /**
127  * nm_setting_bond_get_option:
128  * @setting: the #NMSettingBond
129  * @idx: index of the desired option, from 0 to
130  * nm_setting_bond_get_num_options() - 1
131  * @out_name: (out) (transfer none): on return, the name of the bonding option;
132  *   this value is owned by the setting and should not be modified
133  * @out_value: (out) (transfer none): on return, the value of the name of the
134  *   bonding option; this value is owned by the setting and should not be
135  *   modified
136  *
137  * Given an index, return the value of the bonding option at that index.  Indexes
138  * are *not* guaranteed to be static across modifications to options done by
139  * nm_setting_bond_add_option() and nm_setting_bond_remove_option(),
140  * and should not be used to refer to options except for short periods of time
141  * such as during option iteration.
142  *
143  * Returns: %TRUE on success if the index was valid and an option was found,
144  * %FALSE if the index was invalid (ie, greater than the number of options
145  * currently held by the setting)
146  **/
147 gboolean
148 nm_setting_bond_get_option (NMSettingBond *setting,
149                             guint32 idx,
150                             const char **out_name,
151                             const char **out_value)
152 {
153         NMSettingBondPrivate *priv;
154         GList *keys;
155         const char *_key = NULL, *_value = NULL;
156
157         g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE);
158
159         priv = NM_SETTING_BOND_GET_PRIVATE (setting);
160
161         if (idx >= nm_setting_bond_get_num_options (setting))
162                 return FALSE;
163
164         keys = g_hash_table_get_keys (priv->options);
165         _key = g_list_nth_data (keys, idx);
166         _value = g_hash_table_lookup (priv->options, _key);
167
168         if (out_name)
169                 *out_name = _key;
170         if (out_value)
171                 *out_value = _value;
172
173         g_list_free (keys);
174         return TRUE;
175 }
176
177 static gboolean
178 validate_int (const char *name, const char *value, const BondDefault *def)
179 {
180         glong num;
181         guint i;
182
183         for (i = 0; i < strlen (value); i++) {
184                 if (!g_ascii_isdigit (value[i]) && value[i] != '-')
185                         return FALSE;
186         }
187
188         errno = 0;
189         num = strtol (value, NULL, 10);
190         if (errno)
191                 return FALSE;
192         if (num < def->min || num > def->max)
193                 return FALSE;
194
195         return TRUE;
196 }
197
198 static gboolean
199 validate_list (const char *name, const char *value, const BondDefault *def)
200 {
201         guint i;
202
203         for (i = 0; i < G_N_ELEMENTS (def->list) && def->list[i]; i++) {
204                 if (g_strcmp0 (def->list[i], value) == 0)
205                         return TRUE;
206         }
207
208         /* empty validation list means all values pass */
209         return def->list[0] == NULL ? TRUE : FALSE;
210 }
211
212 static gboolean
213 validate_ip (const char *name, const char *value)
214 {
215         char **ips, **iter;
216         gboolean success = TRUE;
217         struct in_addr addr;
218
219         if (!value || !value[0])
220                 return FALSE;
221
222         ips = g_strsplit_set (value, ",", 0);
223         for (iter = ips; iter && *iter && success; iter++)
224                 success = !!inet_aton (*iter, &addr);
225         g_strfreev (ips);
226
227         return success;
228 }
229
230 static gboolean
231 validate_ifname (const char *name, const char *value)
232 {
233         if (!value || !value[0])
234                 return FALSE;
235
236         return nm_utils_iface_valid_name (value);
237 }
238
239 /**
240  * nm_setting_bond_validate_option:
241  * @name: the name of the option to validate
242  * @value: the value of the option to validate
243  *
244  * Checks whether @name is a valid bond option and @value is a valid value for
245  * the @name. If @value is %NULL, the function only validates the option name.
246  *
247  * Returns: %TRUE, if the @value is valid for the given name.
248  * If the @name is not a valid option, %FALSE will be returned.
249  **/
250 gboolean
251 nm_setting_bond_validate_option (const char *name,
252                                  const char *value)
253 {
254         guint i;
255
256         if (!name || !name[0])
257                 return FALSE;
258
259         for (i = 0; i < G_N_ELEMENTS (defaults); i++) {
260                 if (g_strcmp0 (defaults[i].opt, name) == 0) {
261                         if (value == NULL)
262                                 return TRUE;
263                         switch (defaults[i].opt_type) {
264                         case NM_BOND_OPTION_TYPE_INT:
265                                 return validate_int (name, value, &defaults[i]);
266                         case NM_BOND_OPTION_TYPE_STRING:
267                                 return validate_list (name, value, &defaults[i]);
268                         case NM_BOND_OPTION_TYPE_BOTH:
269                                 return (   validate_int (name, value, &defaults[i])
270                                         || validate_list (name, value, &defaults[i]));
271                         case NM_BOND_OPTION_TYPE_IP:
272                                 return validate_ip (name, value);
273                         case NM_BOND_OPTION_TYPE_IFNAME:
274                                 return validate_ifname (name, value);
275                         }
276                         return FALSE;
277                 }
278         }
279         return FALSE;
280 }
281
282 /**
283  * nm_setting_bond_get_option_by_name:
284  * @setting: the #NMSettingBond
285  * @name: the option name for which to retrieve the value
286  *
287  * Returns the value associated with the bonding option specified by
288  * @name, if it exists.
289  *
290  * Returns: the value, or %NULL if the key/value pair was never added to the
291  * setting; the value is owned by the setting and must not be modified
292  **/
293 const char *
294 nm_setting_bond_get_option_by_name (NMSettingBond *setting,
295                                     const char *name)
296 {
297         g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL);
298
299         if (!nm_setting_bond_validate_option (name, NULL))
300                 return NULL;
301
302         return g_hash_table_lookup (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name);
303 }
304
305 /**
306  * nm_setting_bond_add_option:
307  * @setting: the #NMSettingBond
308  * @name: name for the option
309  * @value: value for the option
310  *
311  * Add an option to the table.  The option is compared to an internal list
312  * of allowed options.  Option names may contain only alphanumeric characters
313  * (ie [a-zA-Z0-9]).  Adding a new name replaces any existing name/value pair
314  * that may already exist.
315  *
316  * The order of how to set several options is relevant because there are options
317  * that conflict with each other.
318  *
319  * Returns: %TRUE if the option was valid and was added to the internal option
320  * list, %FALSE if it was not.
321  **/
322 gboolean
323 nm_setting_bond_add_option (NMSettingBond *setting,
324                             const char *name,
325                             const char *value)
326 {
327         NMSettingBondPrivate *priv;
328
329         g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE);
330
331         if (!value || !nm_setting_bond_validate_option (name, value))
332                 return FALSE;
333
334         priv = NM_SETTING_BOND_GET_PRIVATE (setting);
335
336         g_hash_table_insert (priv->options, g_strdup (name), g_strdup (value));
337
338         if (   !strcmp (name, NM_SETTING_BOND_OPTION_MIIMON)
339             && strcmp (value, "0") != 0) {
340                 g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
341                 g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
342         } else if (   !strcmp (name, NM_SETTING_BOND_OPTION_ARP_INTERVAL)
343                    && strcmp (value, "0") != 0) {
344                 g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_MIIMON);
345                 g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY);
346                 g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_UPDELAY);
347         }
348
349         g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS);
350
351         return TRUE;
352 }
353
354 /**
355  * nm_setting_bond_remove_option:
356  * @setting: the #NMSettingBond
357  * @name: name of the option to remove
358  *
359  * Remove the bonding option referenced by @name from the internal option
360  * list.
361  *
362  * Returns: %TRUE if the option was found and removed from the internal option
363  * list, %FALSE if it was not.
364  **/
365 gboolean
366 nm_setting_bond_remove_option (NMSettingBond *setting,
367                                const char *name)
368 {
369         gboolean found;
370
371         g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE);
372
373         if (!nm_setting_bond_validate_option (name, NULL))
374                 return FALSE;
375
376         found = g_hash_table_remove (NM_SETTING_BOND_GET_PRIVATE (setting)->options, name);
377         if (found)
378                 g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS);
379         return found;
380 }
381
382 /**
383  * nm_setting_bond_get_valid_options:
384  * @setting: the #NMSettingBond
385  *
386  * Returns a list of valid bond options.
387  *
388  * Returns: (transfer none): a %NULL-terminated array of strings of valid bond options.
389  **/
390 const char **
391 nm_setting_bond_get_valid_options  (NMSettingBond *setting)
392 {
393         static const char *array[G_N_ELEMENTS (defaults) + 1] = { NULL };
394         int i;
395
396         /* initialize the array once */
397         if (G_UNLIKELY (array[0] == NULL)) {
398                 for (i = 0; i < G_N_ELEMENTS (defaults); i++)
399                         array[i] = defaults[i].opt;
400                 array[i] = NULL;
401         }
402         return array;
403 }
404
405 /**
406  * nm_setting_bond_get_option_default:
407  * @setting: the #NMSettingBond
408  * @name: the name of the option
409  *
410  * Returns: the value of the bond option if not overridden by an entry in
411  *   the #NMSettingBond:options property.
412  **/
413 const char *
414 nm_setting_bond_get_option_default (NMSettingBond *setting, const char *name)
415 {
416         guint i;
417
418         g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL);
419         g_return_val_if_fail (nm_setting_bond_validate_option (name, NULL), NULL);
420
421         for (i = 0; i < G_N_ELEMENTS (defaults); i++) {
422                 if (g_strcmp0 (defaults[i].opt, name) == 0)
423                         return defaults[i].val;
424         }
425         /* Any option that passes nm_setting_bond_validate_option() should also be found in defaults */
426         g_assert_not_reached ();
427 }
428
429 /**
430  * nm_setting_bond_get_option_type:
431  * @setting: the #NMSettingBond
432  * @name: the name of the option
433  *
434  * Returns: the type of the bond option.
435  **/
436 NMBondOptionType
437 _nm_setting_bond_get_option_type (NMSettingBond *setting, const char *name)
438 {
439         guint i;
440
441         g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NM_BOND_OPTION_TYPE_INT);
442         g_return_val_if_fail (nm_setting_bond_validate_option (name, NULL), NM_BOND_OPTION_TYPE_INT);
443
444         for (i = 0; i < G_N_ELEMENTS (defaults); i++) {
445                 if (nm_streq0 (defaults[i].opt, name))
446                         return defaults[i].opt_type;
447         }
448         /* Any option that passes nm_setting_bond_validate_option() should also be found in defaults */
449         g_assert_not_reached ();
450 }
451
452 static gboolean
453 verify (NMSetting *setting, NMConnection *connection, GError **error)
454 {
455         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting);
456         GHashTableIter iter;
457         const char *key, *value;
458         int mode, miimon = 0, arp_interval = 0;
459         const char *mode_orig, *mode_new;
460         const char *arp_ip_target = NULL;
461         const char *lacp_rate;
462         const char *primary;
463
464         g_hash_table_iter_init (&iter, priv->options);
465         while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) {
466                 if (!value[0] || !nm_setting_bond_validate_option (key, value)) {
467                         g_set_error (error,
468                                      NM_CONNECTION_ERROR,
469                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
470                                      _("invalid option '%s' or its value '%s'"),
471                                      key, value);
472                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
473                         return FALSE;
474                 }
475         }
476
477         value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MIIMON);
478         if (value)
479                 miimon = atoi (value);
480         value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
481         if (value)
482                 arp_interval = atoi (value);
483
484         /* Can only set one of miimon and arp_interval */
485         if (miimon > 0 && arp_interval > 0) {
486                 g_set_error (error,
487                              NM_CONNECTION_ERROR,
488                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
489                              _("only one of '%s' and '%s' can be set"),
490                              NM_SETTING_BOND_OPTION_MIIMON,
491                              NM_SETTING_BOND_OPTION_ARP_INTERVAL);
492                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
493                 return FALSE;
494         }
495
496         /* Verify bond mode */
497         mode_orig = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MODE);
498         if (!mode_orig) {
499                 g_set_error (error,
500                              NM_CONNECTION_ERROR,
501                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
502                              _("mandatory option '%s' is missing"),
503                              NM_SETTING_BOND_OPTION_MODE);
504                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
505                 return FALSE;
506         }
507         mode = nm_utils_bond_mode_string_to_int (mode_orig);
508         if (mode == -1) {
509                 g_set_error (error,
510                              NM_CONNECTION_ERROR,
511                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
512                              _("'%s' is not a valid value for '%s'"),
513                              value, NM_SETTING_BOND_OPTION_MODE);
514                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
515                 return FALSE;
516         }
517         mode_new = nm_utils_bond_mode_int_to_string (mode);
518
519         /* Make sure mode is compatible with other settings */
520         if (   strcmp (mode_new, "balance-alb") == 0
521             || strcmp (mode_new, "balance-tlb") == 0) {
522                 if (arp_interval > 0) {
523                         g_set_error (error,
524                                      NM_CONNECTION_ERROR,
525                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
526                                      _("'%s=%s' is incompatible with '%s > 0'"),
527                                      NM_SETTING_BOND_OPTION_MODE, mode_new, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
528                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
529                         return FALSE;
530                 }
531         }
532
533         primary = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_PRIMARY);
534         if (strcmp (mode_new, "active-backup") == 0) {
535                 if (primary && !nm_utils_iface_valid_name (primary)) {
536                         g_set_error (error,
537                                      NM_CONNECTION_ERROR,
538                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
539                                      _("'%s' is not a valid interface name for '%s' option"),
540                                      primary, NM_SETTING_BOND_OPTION_PRIMARY);
541                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
542                         return FALSE;
543                 }
544         } else {
545                 if (primary) {
546                         g_set_error (error,
547                                      NM_CONNECTION_ERROR,
548                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
549                                      _("'%s' option is only valid for '%s=%s'"),
550                                      NM_SETTING_BOND_OPTION_PRIMARY,
551                                      NM_SETTING_BOND_OPTION_MODE, "active-backup");
552                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
553                         return FALSE;
554                 }
555         }
556
557         if (nm_connection_get_setting_infiniband (connection)) {
558                 if (strcmp (mode_new, "active-backup") != 0) {
559                         g_set_error (error,
560                                      NM_CONNECTION_ERROR,
561                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
562                                      _("'%s=%s' is not a valid configuration for '%s'"),
563                                      NM_SETTING_BOND_OPTION_MODE, mode_new, NM_SETTING_INFINIBAND_SETTING_NAME);
564                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
565                         return FALSE;
566                 }
567         }
568
569         if (miimon == 0) {
570                 /* updelay and downdelay can only be used with miimon */
571                 if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_UPDELAY)) {
572                         g_set_error (error,
573                                      NM_CONNECTION_ERROR,
574                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
575                                      _("'%s' option requires '%s' option to be set"),
576                                      NM_SETTING_BOND_OPTION_UPDELAY, NM_SETTING_BOND_OPTION_MIIMON);
577                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
578                         return FALSE;
579                 }
580                 if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY)) {
581                         g_set_error (error,
582                                      NM_CONNECTION_ERROR,
583                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
584                                      _("'%s' option requires '%s' option to be set"),
585                                      NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_MIIMON);
586                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
587                         return FALSE;
588                 }
589         }
590
591         /* arp_ip_target can only be used with arp_interval, and must
592          * contain a comma-separated list of IPv4 addresses.
593          */
594         arp_ip_target = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
595         if (arp_interval > 0) {
596                 char **addrs;
597                 guint32 addr;
598                 int i;
599
600                 if (!arp_ip_target) {
601                         g_set_error (error,
602                                      NM_CONNECTION_ERROR,
603                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
604                                      _("'%s' option requires '%s' option to be set"),
605                                      NM_SETTING_BOND_OPTION_ARP_INTERVAL, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
606                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
607                         return FALSE;
608                 }
609
610                 addrs = g_strsplit (arp_ip_target, ",", -1);
611                 if (!addrs[0]) {
612                         g_set_error (error,
613                                      NM_CONNECTION_ERROR,
614                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
615                                      _("'%s' option is empty"),
616                                      NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
617                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
618                         g_strfreev (addrs);
619                         return FALSE;
620                 }
621
622                 for (i = 0; addrs[i]; i++) {
623                         if (!inet_pton (AF_INET, addrs[i], &addr)) {
624                                 g_set_error (error,
625                                              NM_CONNECTION_ERROR,
626                                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
627                                              _("'%s' is not a valid IPv4 address for '%s' option"),
628                                              NM_SETTING_BOND_OPTION_ARP_IP_TARGET, addrs[i]);
629                                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
630                                 g_strfreev (addrs);
631                                 return FALSE;
632                         }
633                 }
634                 g_strfreev (addrs);
635         } else {
636                 if (arp_ip_target) {
637                         g_set_error (error,
638                                      NM_CONNECTION_ERROR,
639                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
640                                      _("'%s' option requires '%s' option to be set"),
641                                      NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
642                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
643                         return FALSE;
644                 }
645         }
646
647         lacp_rate = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_LACP_RATE);
648         if (   lacp_rate
649             && g_strcmp0 (mode_new, "802.3ad")
650             && strcmp (lacp_rate, "slow") != 0
651             && strcmp (lacp_rate, "0") != 0) {
652                 g_set_error (error,
653                              NM_CONNECTION_ERROR,
654                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
655                              _("'%s' option is only valid with mode '%s'"),
656                              NM_SETTING_BOND_OPTION_LACP_RATE, "802.3ad");
657                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
658                 return FALSE;
659         }
660
661         if (!_nm_connection_verify_required_interface_name (connection, error))
662                 return FALSE;
663
664         /* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */
665
666         if (g_strcmp0 (mode_orig, mode_new) != 0) {
667                 g_set_error (error,
668                              NM_CONNECTION_ERROR,
669                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
670                              _("'%s' option should be string"),
671                              NM_SETTING_BOND_OPTION_MODE);
672                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
673                 return NM_SETTING_VERIFY_NORMALIZABLE;
674         }
675
676         return TRUE;
677 }
678
679 static void
680 nm_setting_bond_init (NMSettingBond *setting)
681 {
682         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting);
683
684         priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
685
686         /* Default values: */
687         nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr");
688 }
689
690 static void
691 finalize (GObject *object)
692 {
693         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
694
695         g_hash_table_destroy (priv->options);
696
697         G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object);
698 }
699
700 static void
701 set_property (GObject *object, guint prop_id,
702               const GValue *value, GParamSpec *pspec)
703 {
704         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
705
706         switch (prop_id) {
707         case PROP_OPTIONS:
708                 g_hash_table_unref (priv->options);
709                 priv->options = _nm_utils_copy_strdict (g_value_get_boxed (value));
710                 break;
711         default:
712                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
713                 break;
714         }
715 }
716
717 static void
718 get_property (GObject *object, guint prop_id,
719               GValue *value, GParamSpec *pspec)
720 {
721         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
722
723         switch (prop_id) {
724         case PROP_OPTIONS:
725                 g_value_take_boxed (value, _nm_utils_copy_strdict (priv->options));
726                 break;
727         default:
728                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
729                 break;
730         }
731 }
732
733 static void
734 nm_setting_bond_class_init (NMSettingBondClass *setting_class)
735 {
736         GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
737         NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
738
739         g_type_class_add_private (setting_class, sizeof (NMSettingBondPrivate));
740
741         /* virtual methods */
742         object_class->set_property = set_property;
743         object_class->get_property = get_property;
744         object_class->finalize     = finalize;
745         parent_class->verify       = verify;
746
747         /* Properties */
748         /**
749          * NMSettingBond:options:
750          *
751          * Dictionary of key/value pairs of bonding options.  Both keys and values
752          * must be strings. Option names must contain only alphanumeric characters
753          * (ie, [a-zA-Z0-9]).
754          *
755          * Type: GHashTable(utf8,utf8)
756          **/
757         /* ---ifcfg-rh---
758          * property: options
759          * variable: BONDING_OPTS
760          * description: Bonding options.
761          * example: BONDING_OPTS="miimon=100 mode=broadcast"
762          * ---end---
763          */
764          g_object_class_install_property
765                  (object_class, PROP_OPTIONS,
766                  g_param_spec_boxed (NM_SETTING_BOND_OPTIONS, "", "",
767                                      G_TYPE_HASH_TABLE,
768                                      G_PARAM_READWRITE |
769                                      NM_SETTING_PARAM_INFERRABLE |
770                                      G_PARAM_STATIC_STRINGS));
771          _nm_setting_class_transform_property (parent_class, NM_SETTING_BOND_OPTIONS,
772                                                G_VARIANT_TYPE ("a{ss}"),
773                                                _nm_utils_strdict_to_dbus,
774                                                _nm_utils_strdict_from_dbus);
775
776          /* ---dbus---
777           * property: interface-name
778           * format: string
779           * description: Deprecated in favor of connection.interface-name, but can
780           *   be used for backward-compatibility with older daemons, to set the
781           *   bond's interface name.
782           * ---end---
783           */
784          _nm_setting_class_add_dbus_only_property (parent_class, "interface-name",
785                                                    G_VARIANT_TYPE_STRING,
786                                                    _nm_setting_get_deprecated_virtual_interface_name,
787                                                    NULL);
788 }