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