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