4d40de00906af80a51c415612fed23be13c43337
[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         }
478
479         /* Verify bond mode */
480         mode_orig = value = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_MODE);
481         if (!value) {
482                 g_set_error (error,
483                              NM_CONNECTION_ERROR,
484                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
485                              _("mandatory option '%s' is missing"),
486                              NM_SETTING_BOND_OPTION_MODE);
487                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
488                 return FALSE;
489         }
490         mode = nm_utils_bond_mode_string_to_int (value);
491         if (mode == -1) {
492                 g_set_error (error,
493                              NM_CONNECTION_ERROR,
494                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
495                              _("'%s' is not a valid value for '%s'"),
496                              value, NM_SETTING_BOND_OPTION_MODE);
497                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
498                 return FALSE;
499         }
500         mode_new = value = nm_utils_bond_mode_int_to_string (mode);
501
502         /* Make sure mode is compatible with other settings */
503         if (   strcmp (value, "balance-alb") == 0
504             || strcmp (value, "balance-tlb") == 0) {
505                 if (arp_interval > 0) {
506                         g_set_error (error,
507                                      NM_CONNECTION_ERROR,
508                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
509                                      _("'%s=%s' is incompatible with '%s > 0'"),
510                                      NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
511                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
512                         return FALSE;
513                 }
514         }
515
516         primary = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_PRIMARY);
517         if (strcmp (value, "active-backup") == 0) {
518                 if (primary && !nm_utils_iface_valid_name (primary)) {
519                         g_set_error (error,
520                                      NM_CONNECTION_ERROR,
521                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
522                                      _("'%s' is not a valid interface name for '%s' option"),
523                                      primary, NM_SETTING_BOND_OPTION_PRIMARY);
524                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
525                         return FALSE;
526                 }
527         } else {
528                 if (primary) {
529                         g_set_error (error,
530                                      NM_CONNECTION_ERROR,
531                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
532                                      _("'%s' option is only valid for '%s=%s'"),
533                                      NM_SETTING_BOND_OPTION_PRIMARY,
534                                      NM_SETTING_BOND_OPTION_MODE, "active-backup");
535                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
536                         return FALSE;
537                 }
538         }
539
540         if (nm_connection_get_setting_infiniband (connection)) {
541                 if (strcmp (value, "active-backup") != 0) {
542                         g_set_error (error,
543                                      NM_CONNECTION_ERROR,
544                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
545                                      _("'%s=%s' is not a valid configuration for '%s'"),
546                                      NM_SETTING_BOND_OPTION_MODE, value, NM_SETTING_INFINIBAND_SETTING_NAME);
547                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
548                         return FALSE;
549                 }
550         }
551
552         if (miimon == 0) {
553                 /* updelay and downdelay can only be used with miimon */
554                 if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_UPDELAY)) {
555                         g_set_error (error,
556                                      NM_CONNECTION_ERROR,
557                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
558                                      _("'%s' option requires '%s' option to be set"),
559                                      NM_SETTING_BOND_OPTION_UPDELAY, NM_SETTING_BOND_OPTION_MIIMON);
560                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
561                         return FALSE;
562                 }
563                 if (g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_DOWNDELAY)) {
564                         g_set_error (error,
565                                      NM_CONNECTION_ERROR,
566                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
567                                      _("'%s' option requires '%s' option to be set"),
568                                      NM_SETTING_BOND_OPTION_DOWNDELAY, NM_SETTING_BOND_OPTION_MIIMON);
569                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
570                         return FALSE;
571                 }
572         }
573
574         /* arp_ip_target can only be used with arp_interval, and must
575          * contain a comma-separated list of IPv4 addresses.
576          */
577         arp_ip_target = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
578         if (arp_interval > 0) {
579                 char **addrs;
580                 guint32 addr;
581                 int i;
582
583                 if (!arp_ip_target) {
584                         g_set_error (error,
585                                      NM_CONNECTION_ERROR,
586                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
587                                      _("'%s' option requires '%s' option to be set"),
588                                      NM_SETTING_BOND_OPTION_ARP_INTERVAL, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
589                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
590                         return FALSE;
591                 }
592
593                 addrs = g_strsplit (arp_ip_target, ",", -1);
594                 if (!addrs[0]) {
595                         g_set_error (error,
596                                      NM_CONNECTION_ERROR,
597                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
598                                      _("'%s' option is empty"),
599                                      NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
600                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
601                         g_strfreev (addrs);
602                         return FALSE;
603                 }
604
605                 for (i = 0; addrs[i]; i++) {
606                         if (!inet_pton (AF_INET, addrs[i], &addr)) {
607                                 g_set_error (error,
608                                              NM_CONNECTION_ERROR,
609                                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
610                                              _("'%s' is not a valid IPv4 address for '%s' option"),
611                                              NM_SETTING_BOND_OPTION_ARP_IP_TARGET, addrs[i]);
612                                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
613                                 g_strfreev (addrs);
614                                 return FALSE;
615                         }
616                 }
617                 g_strfreev (addrs);
618         } else {
619                 if (arp_ip_target) {
620                         g_set_error (error,
621                                      NM_CONNECTION_ERROR,
622                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
623                                      _("'%s' option requires '%s' option to be set"),
624                                      NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
625                         g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
626                         return FALSE;
627                 }
628         }
629
630         lacp_rate = g_hash_table_lookup (priv->options, NM_SETTING_BOND_OPTION_LACP_RATE);
631         if (   lacp_rate
632             && (g_strcmp0 (value, "802.3ad") != 0 && g_strcmp0 (value, "4") != 0)
633             && (strcmp (lacp_rate, "slow") != 0 && strcmp (lacp_rate, "0") != 0)) {
634                 g_set_error (error,
635                              NM_CONNECTION_ERROR,
636                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
637                              _("'%s' option is only valid with mode '%s'"),
638                              NM_SETTING_BOND_OPTION_LACP_RATE, "802.3ad");
639                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
640                 return FALSE;
641         }
642
643         if (!_nm_connection_verify_required_interface_name (connection, error))
644                 return FALSE;
645
646         /* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */
647
648         if (g_strcmp0 (mode_orig, mode_new) != 0) {
649                 g_set_error (error,
650                              NM_CONNECTION_ERROR,
651                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
652                              _("'%s' option should be string"),
653                              NM_SETTING_BOND_OPTION_MODE);
654                 g_prefix_error (error, "%s.%s: ", NM_SETTING_BOND_SETTING_NAME, NM_SETTING_BOND_OPTIONS);
655                 return NM_SETTING_VERIFY_NORMALIZABLE;
656         }
657
658         return TRUE;
659 }
660
661 static void
662 nm_setting_bond_init (NMSettingBond *setting)
663 {
664         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting);
665
666         priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
667
668         /* Default values: */
669         nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr");
670 }
671
672 static void
673 finalize (GObject *object)
674 {
675         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
676
677         g_hash_table_destroy (priv->options);
678
679         G_OBJECT_CLASS (nm_setting_bond_parent_class)->finalize (object);
680 }
681
682 static void
683 set_property (GObject *object, guint prop_id,
684               const GValue *value, GParamSpec *pspec)
685 {
686         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
687
688         switch (prop_id) {
689         case PROP_OPTIONS:
690                 g_hash_table_unref (priv->options);
691                 priv->options = _nm_utils_copy_strdict (g_value_get_boxed (value));
692                 break;
693         default:
694                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
695                 break;
696         }
697 }
698
699 static void
700 get_property (GObject *object, guint prop_id,
701               GValue *value, GParamSpec *pspec)
702 {
703         NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (object);
704
705         switch (prop_id) {
706         case PROP_OPTIONS:
707                 g_value_take_boxed (value, _nm_utils_copy_strdict (priv->options));
708                 break;
709         default:
710                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
711                 break;
712         }
713 }
714
715 static void
716 nm_setting_bond_class_init (NMSettingBondClass *setting_class)
717 {
718         GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
719         NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
720
721         g_type_class_add_private (setting_class, sizeof (NMSettingBondPrivate));
722
723         /* virtual methods */
724         object_class->set_property = set_property;
725         object_class->get_property = get_property;
726         object_class->finalize     = finalize;
727         parent_class->verify       = verify;
728
729         /* Properties */
730         /**
731          * NMSettingBond:options:
732          *
733          * Dictionary of key/value pairs of bonding options.  Both keys and values
734          * must be strings. Option names must contain only alphanumeric characters
735          * (ie, [a-zA-Z0-9]).
736          *
737          * Type: GHashTable(utf8,utf8)
738          **/
739         /* ---ifcfg-rh---
740          * property: options
741          * variable: BONDING_OPTS
742          * description: Bonding options.
743          * example: BONDING_OPTS="miimon=100 mode=broadcast"
744          * ---end---
745          */
746          g_object_class_install_property
747                  (object_class, PROP_OPTIONS,
748                  g_param_spec_boxed (NM_SETTING_BOND_OPTIONS, "", "",
749                                      G_TYPE_HASH_TABLE,
750                                      G_PARAM_READWRITE |
751                                      NM_SETTING_PARAM_INFERRABLE |
752                                      G_PARAM_STATIC_STRINGS));
753          _nm_setting_class_transform_property (parent_class, NM_SETTING_BOND_OPTIONS,
754                                                G_VARIANT_TYPE ("a{ss}"),
755                                                _nm_utils_strdict_to_dbus,
756                                                _nm_utils_strdict_from_dbus);
757
758          /* ---dbus---
759           * property: interface-name
760           * format: string
761           * description: Deprecated in favor of connection.interface-name, but can
762           *   be used for backward-compatibility with older daemons, to set the
763           *   bond's interface name.
764           * ---end---
765           */
766          _nm_setting_class_add_dbus_only_property (parent_class, "interface-name",
767                                                    G_VARIANT_TYPE_STRING,
768                                                    _nm_setting_get_deprecated_virtual_interface_name,
769                                                    NULL);
770 }