39587d59f981d24f9b28e7410f07db119c487364
[NetworkManager.git] / libnm-core / nm-setting-vlan.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 - 2014 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include "nm-setting-vlan.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "nm-utils.h"
30 #include "nm-core-types-internal.h"
31 #include "nm-setting-connection.h"
32 #include "nm-setting-private.h"
33 #include "nm-setting-wired.h"
34 #include "nm-connection-private.h"
35
36 /**
37  * SECTION:nm-setting-vlan
38  * @short_description: Describes connection properties for VLAN interfaces
39  *
40  * The #NMSettingVlan object is a #NMSetting subclass that describes properties
41  * necessary for connection to VLAN interfaces.
42  **/
43
44 G_DEFINE_TYPE_WITH_CODE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING,
45                          _nm_register_setting (VLAN, 1))
46 NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_VLAN)
47
48 #define NM_SETTING_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VLAN, NMSettingVlanPrivate))
49
50 typedef struct {
51         char *parent;
52         guint32 id;
53         guint32 flags;
54         GSList *ingress_priority_map;
55         GSList *egress_priority_map;
56 } NMSettingVlanPrivate;
57
58 enum {
59         PROP_0,
60         PROP_PARENT,
61         PROP_ID,
62         PROP_FLAGS,
63         PROP_INGRESS_PRIORITY_MAP,
64         PROP_EGRESS_PRIORITY_MAP,
65         LAST_PROP
66 };
67
68 #define MAX_SKB_PRIO   G_MAXUINT32
69 #define MAX_8021P_PRIO 7  /* Max 802.1p priority */
70
71 /**
72  * nm_setting_vlan_new:
73  *
74  * Creates a new #NMSettingVlan object with default values.
75  *
76  * Returns: (transfer full): the new empty #NMSettingVlan object
77  **/
78 NMSetting *
79 nm_setting_vlan_new (void)
80 {
81         return (NMSetting *) g_object_new (NM_TYPE_SETTING_VLAN, NULL);
82 }
83
84 /**
85  * nm_setting_vlan_get_parent:
86  * @setting: the #NMSettingVlan
87  *
88  * Returns: the #NMSettingVlan:parent property of the setting
89  **/
90 const char *
91 nm_setting_vlan_get_parent (NMSettingVlan *setting)
92 {
93         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), NULL);
94         return NM_SETTING_VLAN_GET_PRIVATE (setting)->parent;
95 }
96
97 /**
98  * nm_setting_vlan_get_id:
99  * @setting: the #NMSettingVlan
100  *
101  * Returns: the #NMSettingVlan:id property of the setting
102  **/
103 guint32
104 nm_setting_vlan_get_id (NMSettingVlan *setting)
105 {
106         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), 0);
107         return NM_SETTING_VLAN_GET_PRIVATE (setting)->id;
108 }
109
110 /**
111  * nm_setting_vlan_get_flags:
112  * @setting: the #NMSettingVlan
113  *
114  * Returns: the #NMSettingVlan:flags property of the setting
115  **/
116 guint32
117 nm_setting_vlan_get_flags (NMSettingVlan *setting)
118 {
119         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), 0);
120         return NM_SETTING_VLAN_GET_PRIVATE (setting)->flags;
121 }
122
123 static guint32
124 get_max_prio (NMVlanPriorityMap map, gboolean from)
125 {
126         if (map == NM_VLAN_INGRESS_MAP)
127                 return from ? MAX_8021P_PRIO : MAX_SKB_PRIO;
128         else if (map == NM_VLAN_EGRESS_MAP)
129                 return from ? MAX_SKB_PRIO : MAX_8021P_PRIO;
130         g_assert_not_reached ();
131 }
132
133 static NMVlanQosMapping *
134 priority_map_new_from_str (NMVlanPriorityMap map, const char *str)
135 {
136         NMVlanQosMapping *p = NULL;
137         gchar **t = NULL;
138         guint32 len;
139         guint64 from, to;
140
141         g_return_val_if_fail (str && str[0], NULL);
142
143         t = g_strsplit (str, ":", 0);
144         len = g_strv_length (t);
145         if (len == 2) {
146                 from = g_ascii_strtoull (t[0], NULL, 10);
147                 to = g_ascii_strtoull (t[1], NULL, 10);
148
149                 if ((from <= get_max_prio (map, TRUE)) && (to <= get_max_prio (map, FALSE))) {
150                         p = g_malloc0 (sizeof (NMVlanQosMapping));
151                         p->from = from;
152                         p->to = to;
153                 }
154         } else {
155                 /* Warn */
156                 g_warn_if_fail (len == 2);
157         }
158
159         g_strfreev (t);
160         return p;
161 }
162
163 static void
164 priority_map_free (NMVlanQosMapping *map)
165 {
166         g_return_if_fail (map != NULL);
167         g_free (map);
168 }
169
170 static GSList *
171 get_map (NMSettingVlan *self, NMVlanPriorityMap map)
172 {
173         if (map == NM_VLAN_INGRESS_MAP)
174                 return NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map;
175         else if (map == NM_VLAN_EGRESS_MAP)
176                 return NM_SETTING_VLAN_GET_PRIVATE (self)->egress_priority_map;
177         g_assert_not_reached ();
178         return NULL;
179 }
180
181 static gint
182 prio_map_compare (gconstpointer p_a, gconstpointer p_b)
183 {
184         const NMVlanQosMapping *a = p_a;
185         const NMVlanQosMapping *b = p_b;
186
187         return a->from < b->from
188                ? -1
189                : (a->from > b->from
190                   ? 1
191                   : (a->to < b->to ? -1 : (a->to > b->to ? 1 : 0)));
192 }
193
194 static void
195 set_map (NMSettingVlan *self, NMVlanPriorityMap map, GSList *list)
196 {
197         /* Assert that the list is sorted */
198 #if NM_MORE_ASSERTS >= 2
199         {
200                 GSList *iter, *last;
201
202                 last = list;
203                 iter = list ? list->next : NULL;
204                 while (iter) {
205                         const NMVlanQosMapping *l = last->data;
206                         const NMVlanQosMapping *m = iter->data;
207
208                         nm_assert (prio_map_compare (last->data, iter->data) < 0);
209
210                         /* Also reject duplicates (based on "from") */
211                         nm_assert (l->from < m->from);
212
213                         last = iter;
214                         iter = iter->next;
215                 }
216         }
217 #endif
218
219         if (map == NM_VLAN_INGRESS_MAP) {
220                 NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map = list;
221                 g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
222         } else if (map == NM_VLAN_EGRESS_MAP) {
223                 NM_SETTING_VLAN_GET_PRIVATE (self)->egress_priority_map = list;
224                 g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
225         } else
226                 g_assert_not_reached ();
227 }
228
229 static gboolean
230 check_replace_duplicate_priority (GSList *list, guint32 from, guint32 to)
231 {
232         GSList *iter;
233         NMVlanQosMapping *p;
234
235         for (iter = list; iter; iter = g_slist_next (iter)) {
236                 p = iter->data;
237                 if (p->from == from) {
238                         p->to = to;
239                         return TRUE;
240                 }
241         }
242         return FALSE;
243 }
244
245 /**
246  * nm_setting_vlan_add_priority_str:
247  * @setting: the #NMSettingVlan
248  * @map: the type of priority map
249  * @str: the string which contains a priority map, like "3:7"
250  *
251  * Adds a priority map entry into either the #NMSettingVlan:ingress_priority_map
252  * or the #NMSettingVlan:egress_priority_map properties.  The priority map maps
253  * the Linux SKB priorities to 802.1p priorities.
254  *
255  * Returns: %TRUE if the entry was successfully added to the list, or it
256  * overwrote the old value, %FALSE if error
257  */
258 gboolean
259 nm_setting_vlan_add_priority_str (NMSettingVlan *setting,
260                                   NMVlanPriorityMap map,
261                                   const char *str)
262 {
263         GSList *list = NULL;
264         NMVlanQosMapping *item = NULL;
265
266         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
267         g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
268         g_return_val_if_fail (str && str[0], FALSE);
269
270         list = get_map (setting, map);
271
272         item = priority_map_new_from_str (map, str);
273         if (!item)
274                 g_return_val_if_reached (FALSE);
275
276         /* Duplicates get replaced */
277         if (check_replace_duplicate_priority (list, item->from, item->to)) {
278                 g_free (item);
279                 if (map == NM_VLAN_INGRESS_MAP)
280                         g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
281                 else
282                         g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
283                 return TRUE;
284         }
285
286         set_map (setting, map, g_slist_insert_sorted (list, item, prio_map_compare));
287         return TRUE;
288 }
289
290 /**
291  * nm_setting_vlan_get_num_priorities:
292  * @setting: the #NMSettingVlan
293  * @map: the type of priority map
294  *
295  * Returns the number of entires in the
296  * #NMSettingVlan:ingress_priority_map or #NMSettingVlan:egress_priority_map
297  * properties of this setting.
298  *
299  * Returns: return the number of ingress/egress priority entries, -1 if error
300  **/
301 gint32
302 nm_setting_vlan_get_num_priorities (NMSettingVlan *setting, NMVlanPriorityMap map)
303 {
304         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), -1);
305         g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, -1);
306
307         return g_slist_length (get_map (setting, map));
308 }
309
310 /**
311  * nm_setting_vlan_get_priority:
312  * @setting: the #NMSettingVlan
313  * @map: the type of priority map
314  * @idx: the zero-based index of the ingress/egress priority map entry
315  * @out_from: (out): on return the value of the priority map's 'from' item
316  * @out_to: (out): on return the value of priority map's 'to' item
317  *
318  * Retrieve one of the entries of the #NMSettingVlan:ingress_priority_map
319  * or #NMSettingVlan:egress_priority_map properties of this setting.
320  *
321  * Returns: %TRUE if a priority map was returned, %FALSE if error
322  **/
323 gboolean
324 nm_setting_vlan_get_priority (NMSettingVlan *setting,
325                               NMVlanPriorityMap map,
326                               guint32 idx,
327                               guint32 *out_from,
328                               guint32 *out_to)
329 {
330         GSList *list = NULL;
331         NMVlanQosMapping *item = NULL;
332
333         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
334         g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
335         g_return_val_if_fail (out_from != NULL, FALSE);
336         g_return_val_if_fail (out_to != NULL, FALSE);
337
338         list = get_map (setting, map);
339         g_return_val_if_fail (idx < g_slist_length (list), FALSE);
340
341         item = g_slist_nth_data (list, idx);
342         g_assert (item);
343         *out_from = item->from;
344         *out_to = item->to;
345         return TRUE;
346 }
347
348 /**
349  * nm_setting_vlan_add_priority:
350  * @setting: the #NMSettingVlan
351  * @map: the type of priority map
352  * @from: the priority to map to @to
353  * @to: the priority to map @from to
354  *
355  * Adds a priority mapping to the #NMSettingVlan:ingress_priority_map or
356  * #NMSettingVlan:egress_priority_map properties of the setting. If @from is
357  * already in the given priority map, this function will overwrite the
358  * existing entry with the new @to.
359  *
360  * If @map is #NM_VLAN_INGRESS_MAP then @from is the incoming 802.1q VLAN
361  * Priority Code Point (PCP) value, and @to is the Linux SKB priority value.
362  *
363  * If @map is #NM_VLAN_EGRESS_MAP then @from is the Linux SKB priority value and
364  * @to is the outgoing 802.1q VLAN Priority Code Point (PCP) value.
365  *
366  * Returns: %TRUE if the new priority mapping was successfully added to the
367  * list, %FALSE if error
368  */
369 gboolean
370 nm_setting_vlan_add_priority (NMSettingVlan *setting,
371                               NMVlanPriorityMap map,
372                               guint32 from,
373                               guint32 to)
374 {
375         GSList *list = NULL;
376         NMVlanQosMapping *item;
377
378         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
379         g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
380
381         list = get_map (setting, map);
382         if (check_replace_duplicate_priority (list, from, to)) {
383                 if (map == NM_VLAN_INGRESS_MAP)
384                         g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
385                 else
386                         g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
387                 return TRUE;
388         }
389
390         item = g_malloc0 (sizeof (NMVlanQosMapping));
391         item->from = from;
392         item->to = to;
393         set_map (setting, map, g_slist_insert_sorted (list, item, prio_map_compare));
394
395         return TRUE;
396 }
397
398 gboolean
399 _nm_setting_vlan_set_priorities (NMSettingVlan *setting,
400                                  NMVlanPriorityMap map,
401                                  const NMVlanQosMapping *qos_map,
402                                  guint n_qos_map)
403 {
404         gboolean has_changes = FALSE;
405         GSList *map_prev, *map_new;
406         guint i;
407         gint64 from_last;
408
409         map_prev = get_map (setting, map);
410
411         if (n_qos_map != g_slist_length (map_prev))
412                 has_changes = TRUE;
413         else {
414                 const GSList *iter;
415
416                 iter = map_prev;
417                 for (i = 0; i < n_qos_map; i++, iter = iter->next) {
418                         const NMVlanQosMapping *m = iter->data;
419
420                         if (   m->from != qos_map[i].from
421                             || m->to != qos_map[i].to) {
422                                 has_changes = TRUE;
423                                 break;
424                         }
425                 }
426         }
427
428         if (!has_changes)
429                 return FALSE;
430
431         map_new = NULL;
432         from_last = G_MAXINT64;
433         for (i = n_qos_map; i > 0;) {
434                 const NMVlanQosMapping *m = &qos_map[--i];
435                 NMVlanQosMapping *item;
436
437                 /* We require the array to be presorted. */
438                 if (m->from >= from_last)
439                         g_return_val_if_reached (FALSE);
440                 from_last = m->from;
441
442                 item = g_malloc0 (sizeof (NMVlanQosMapping));
443                 item->from = m->from;
444                 item->to = m->to;
445                 map_new = g_slist_prepend (map_new, item);
446         }
447
448         g_slist_free_full (map_prev, g_free);
449         set_map (setting, map, map_new);
450
451         return TRUE;
452 }
453
454 void
455 _nm_setting_vlan_get_priorities (NMSettingVlan *setting,
456                                  NMVlanPriorityMap map,
457                                  NMVlanQosMapping **out_qos_map,
458                                  guint *out_n_qos_map)
459 {
460         GSList *list;
461         NMVlanQosMapping *qos_map = NULL;
462         guint n_qos_map, i;
463
464         list = get_map (setting, map);
465
466         n_qos_map = g_slist_length (list);
467
468         if (n_qos_map > 0) {
469                 qos_map = g_new (NMVlanQosMapping, n_qos_map);
470
471                 for (i = 0; list; i++, list = list->next) {
472                         nm_assert (i < n_qos_map);
473                         qos_map[i] = *((const NMVlanQosMapping *) list->data);
474                 }
475         }
476         *out_qos_map = qos_map;
477         *out_n_qos_map = n_qos_map;
478 }
479
480 /**
481  * nm_setting_vlan_remove_priority:
482  * @setting: the #NMSettingVlan
483  * @map: the type of priority map
484  * @idx: the zero-based index of the priority map to remove
485  *
486  * Removes the priority map at index @idx from the
487  * #NMSettingVlan:ingress_priority_map or #NMSettingVlan:egress_priority_map
488  * properties.
489  */
490 void
491 nm_setting_vlan_remove_priority (NMSettingVlan *setting,
492                                  NMVlanPriorityMap map,
493                                  guint32 idx)
494 {
495         GSList *list = NULL, *item = NULL;
496
497         g_return_if_fail (NM_IS_SETTING_VLAN (setting));
498         g_return_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP);
499
500         list = get_map (setting, map);
501         g_return_if_fail (idx < g_slist_length (list));
502
503         item = g_slist_nth (list, idx);
504         priority_map_free ((NMVlanQosMapping *) (item->data));
505         set_map (setting, map, g_slist_delete_link (list, item));
506 }
507
508 /**
509  * nm_setting_vlan_remove_priority_by_value:
510  * @setting: the #NMSettingVlan
511  * @map: the type of priority map
512  * @from: the priority to map to @to
513  * @to: the priority to map @from to
514  *
515  * Removes the priority map @form:@to from the #NMSettingVlan:ingress_priority_map
516  * or #NMSettingVlan:egress_priority_map (according to @map argument)
517  * properties.
518  *
519  * Returns: %TRUE if the priority mapping was found and removed; %FALSE if it was not.
520  */
521 gboolean
522 nm_setting_vlan_remove_priority_by_value (NMSettingVlan *setting,
523                                           NMVlanPriorityMap map,
524                                           guint32 from,
525                                           guint32 to)
526 {
527         GSList *list = NULL, *iter = NULL;
528         NMVlanQosMapping *item;
529
530         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
531         g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
532
533         list = get_map (setting, map);
534         for (iter = list; iter; iter = g_slist_next (iter)) {
535                 item = iter->data;
536                 if (item->from == from && item->to == to) {
537                         priority_map_free ((NMVlanQosMapping *) (iter->data));
538                         set_map (setting, map, g_slist_delete_link (list, iter));
539                         return TRUE;
540                 }
541         }
542         return FALSE;
543 }
544
545 /**
546  * nm_setting_vlan_remove_priority_str_by_value:
547  * @setting: the #NMSettingVlan
548  * @map: the type of priority map
549  * @str: the string which contains a priority map, like "3:7"
550  *
551  * Removes the priority map @str from the #NMSettingVlan:ingress_priority_map
552  * or #NMSettingVlan:egress_priority_map (according to @map argument)
553  * properties.
554  *
555  * Returns: %TRUE if the priority mapping was found and removed; %FALSE if it was not.
556  */
557 gboolean
558 nm_setting_vlan_remove_priority_str_by_value (NMSettingVlan *setting,
559                                               NMVlanPriorityMap map,
560                                               const char *str)
561 {
562         NMVlanQosMapping *item;
563         gboolean found;
564
565         g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE);
566         g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE);
567
568         item = priority_map_new_from_str (map, str);
569         if (!item)
570                 return FALSE;
571
572         found = nm_setting_vlan_remove_priority_by_value (setting, map, item->from, item->to);
573         g_free (item);
574         return found;
575 }
576
577 /**
578  * nm_setting_vlan_clear_priorities:
579  * @setting: the #NMSettingVlan
580  * @map: the type of priority map
581  *
582  * Clear all the entires from #NMSettingVlan:ingress_priority_map or
583  * #NMSettingVlan:egress_priority_map properties.
584  */
585 void
586 nm_setting_vlan_clear_priorities (NMSettingVlan *setting, NMVlanPriorityMap map)
587 {
588         GSList *list = NULL;
589
590         g_return_if_fail (NM_IS_SETTING_VLAN (setting));
591         g_return_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP);
592
593         list = get_map (setting, map);
594         g_slist_free_full (list, g_free);
595         set_map (setting, map, NULL);
596 }
597
598 /*********************************************************************/
599
600 static void
601 nm_setting_vlan_init (NMSettingVlan *setting)
602 {
603 }
604
605 static gboolean
606 verify (NMSetting *setting, NMConnection *connection, GError **error)
607 {
608         NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
609         NMSettingConnection *s_con;
610         NMSettingWired *s_wired;
611
612         if (connection) {
613                 s_con = nm_connection_get_setting_connection (connection);
614                 s_wired = nm_connection_get_setting_wired (connection);
615         } else {
616                 s_con = NULL;
617                 s_wired = NULL;
618         }
619
620         if (priv->parent) {
621                 if (nm_utils_is_uuid (priv->parent)) {
622                         /* If we have an NMSettingConnection:master with slave-type="vlan",
623                          * then it must be the same UUID.
624                          */
625                         if (s_con) {
626                                 const char *master = NULL, *slave_type = NULL;
627
628                                 slave_type = nm_setting_connection_get_slave_type (s_con);
629                                 if (!g_strcmp0 (slave_type, NM_SETTING_VLAN_SETTING_NAME))
630                                         master = nm_setting_connection_get_master (s_con);
631
632                                 if (master && g_strcmp0 (priv->parent, master) != 0) {
633                                         g_set_error (error,
634                                                      NM_CONNECTION_ERROR,
635                                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
636                                                      _("'%s' value doesn't match '%s=%s'"),
637                                                      priv->parent, NM_SETTING_CONNECTION_MASTER, master);
638                                         g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT);
639                                         return FALSE;
640                                 }
641                         }
642                 } else if (!nm_utils_iface_valid_name (priv->parent)) {
643                         /* parent must be either a UUID or an interface name */
644                         g_set_error (error,
645                                      NM_CONNECTION_ERROR,
646                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
647                                      _("'%s' is neither an UUID nor an interface name"),
648                                      priv->parent);
649                         g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT);
650                         return FALSE;
651                 }
652         } else {
653                 /* If parent is NULL, the parent must be specified via
654                  * NMSettingWired:mac-address.
655                  */
656                 if (   connection
657                     && (!s_wired || !nm_setting_wired_get_mac_address (s_wired))) {
658                         g_set_error (error,
659                                      NM_CONNECTION_ERROR,
660                                      NM_CONNECTION_ERROR_MISSING_PROPERTY,
661                                      _("property is not specified and neither is '%s:%s'"),
662                                      NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_MAC_ADDRESS);
663                         g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_PARENT);
664                         return FALSE;
665                 }
666         }
667
668         if (priv->id >= 4095) {
669                 g_set_error (error,
670                              NM_CONNECTION_ERROR,
671                              NM_CONNECTION_ERROR_INVALID_PROPERTY,
672                              _("the vlan id must be in range 0-4094 but is %u"),
673                              priv->id);
674                 g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_ID);
675         }
676
677         if (priv->flags & ~NM_VLAN_FLAGS_ALL) {
678                 g_set_error_literal (error,
679                                      NM_CONNECTION_ERROR,
680                                      NM_CONNECTION_ERROR_INVALID_PROPERTY,
681                                      _("flags are invalid"));
682                 g_prefix_error (error, "%s.%s: ", NM_SETTING_VLAN_SETTING_NAME, NM_SETTING_VLAN_FLAGS);
683                 return FALSE;
684         }
685
686         return TRUE;
687 }
688
689 static GVariant *
690 _override_flags_get (NMSetting *setting, const char *property)
691 {
692         return g_variant_new_uint32 (nm_setting_vlan_get_flags ((NMSettingVlan *) setting));
693 }
694
695 static void
696 _override_flags_not_set (NMSetting *setting,
697                           GVariant *connection_dict,
698                           const char *property)
699 {
700         /* we changed the default value for FLAGS. When an older client
701          * doesn't serialize the property, we assume it is the old default. */
702         g_object_set (G_OBJECT (setting),
703                       NM_SETTING_VLAN_FLAGS, (NMVlanFlags) 0,
704                       NULL);
705 }
706
707 static GSList *
708 priority_strv_to_maplist (NMVlanPriorityMap map, char **strv)
709 {
710         GSList *list = NULL;
711         int i;
712
713         for (i = 0; strv && strv[i]; i++) {
714                 NMVlanQosMapping *item;
715
716                 item = priority_map_new_from_str (map, strv[i]);
717                 if (item) {
718                         if (!check_replace_duplicate_priority (list, item->from, item->to))
719                                 list = g_slist_prepend (list, item);
720                 }
721         }
722         return g_slist_sort (list, prio_map_compare);
723 }
724
725 static void
726 set_property (GObject *object, guint prop_id,
727               const GValue *value, GParamSpec *pspec)
728 {
729         NMSettingVlan *setting = NM_SETTING_VLAN (object);
730         NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
731
732         switch (prop_id) {
733         case PROP_PARENT:
734                 g_free (priv->parent);
735                 priv->parent = g_value_dup_string (value);
736                 break;
737         case PROP_ID:
738                 priv->id = g_value_get_uint (value);
739                 break;
740         case PROP_FLAGS:
741                 priv->flags = g_value_get_flags (value);
742                 break;
743         case PROP_INGRESS_PRIORITY_MAP:
744                 g_slist_free_full (priv->ingress_priority_map, g_free);
745                 priv->ingress_priority_map =
746                         priority_strv_to_maplist (NM_VLAN_INGRESS_MAP, g_value_get_boxed (value));
747                 break;
748         case PROP_EGRESS_PRIORITY_MAP:
749                 g_slist_free_full (priv->egress_priority_map, g_free);
750                 priv->egress_priority_map =
751                         priority_strv_to_maplist (NM_VLAN_EGRESS_MAP, g_value_get_boxed (value));
752                 break;
753         default:
754                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
755                 break;
756         }
757 }
758
759 static char **
760 priority_maplist_to_strv (GSList *list)
761 {
762         GSList *iter;
763         GPtrArray *strv;
764
765         strv = g_ptr_array_new ();
766
767         for (iter = list; iter; iter = g_slist_next (iter)) {
768                 NMVlanQosMapping *item = iter->data;
769
770                 g_ptr_array_add (strv, g_strdup_printf ("%d:%d", item->from, item->to));
771         }
772         g_ptr_array_add (strv, NULL);
773
774         return (char **) g_ptr_array_free (strv, FALSE);
775 }
776
777 static void
778 get_property (GObject *object, guint prop_id,
779               GValue *value, GParamSpec *pspec)
780 {
781         NMSettingVlan *setting = NM_SETTING_VLAN (object);
782         NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
783
784         switch (prop_id) {
785         case PROP_PARENT:
786                 g_value_set_string (value, priv->parent);
787                 break;
788         case PROP_ID:
789                 g_value_set_uint (value, priv->id);
790                 break;
791         case PROP_FLAGS:
792                 g_value_set_flags (value, priv->flags);
793                 break;
794         case PROP_INGRESS_PRIORITY_MAP:
795                 g_value_take_boxed (value, priority_maplist_to_strv (priv->ingress_priority_map));
796                 break;
797         case PROP_EGRESS_PRIORITY_MAP:
798                 g_value_take_boxed (value, priority_maplist_to_strv (priv->egress_priority_map));
799                 break;
800         default:
801                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
802                 break;
803         }
804 }
805
806 static void
807 finalize (GObject *object)
808 {
809         NMSettingVlan *setting = NM_SETTING_VLAN (object);
810         NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
811
812         g_free (priv->parent);
813         g_slist_free_full (priv->ingress_priority_map, g_free);
814         g_slist_free_full (priv->egress_priority_map, g_free);
815
816         G_OBJECT_CLASS (nm_setting_vlan_parent_class)->finalize (object);
817 }
818
819 static void
820 nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
821 {
822         GObjectClass *object_class = G_OBJECT_CLASS (setting_class);
823         NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class);
824
825         g_type_class_add_private (setting_class, sizeof (NMSettingVlanPrivate));
826
827         /* virtual methods */
828         object_class->set_property = set_property;
829         object_class->get_property = get_property;
830         object_class->finalize     = finalize;
831         parent_class->verify       = verify;
832
833         /* Properties */
834
835         /**
836          * NMSettingVlan:parent:
837          *
838          * If given, specifies the parent interface name or parent connection UUID
839          * from which this VLAN interface should be created.  If this property is
840          * not specified, the connection must contain an #NMSettingWired setting
841          * with a #NMSettingWired:mac-address property.
842          **/
843         /* ---ifcfg-rh---
844          * property: parent
845          * variable: DEVICE or PHYSDEV
846          * description: Parent interface of the VLAN.
847          * ---end---
848          */
849         g_object_class_install_property
850                 (object_class, PROP_PARENT,
851                  g_param_spec_string (NM_SETTING_VLAN_PARENT, "", "",
852                                       NULL,
853                                       G_PARAM_READWRITE |
854                                       G_PARAM_CONSTRUCT |
855                                       NM_SETTING_PARAM_INFERRABLE |
856                                       G_PARAM_STATIC_STRINGS));
857
858         /**
859          * NMSettingVlan:id:
860          *
861          * The VLAN identifier that the interface created by this connection should
862          * be assigned. The valid range is from 0 to 4094, without the reserved id 4095.
863          **/
864         /* ---ifcfg-rh---
865          * property: id
866          * variable: VLAN_ID or DEVICE
867          * description: VLAN identifier.
868          * ---end---
869          */
870         g_object_class_install_property
871                 (object_class, PROP_ID,
872                  g_param_spec_uint (NM_SETTING_VLAN_ID, "", "",
873                                     0, 4095, 0,
874                                     G_PARAM_READWRITE |
875                                     G_PARAM_CONSTRUCT |
876                                     NM_SETTING_PARAM_INFERRABLE |
877                                     G_PARAM_STATIC_STRINGS));
878
879         /**
880          * NMSettingVlan:flags:
881          *
882          * One or more flags which control the behavior and features of the VLAN
883          * interface.  Flags include %NM_VLAN_FLAG_REORDER_HEADERS (reordering of
884          * output packet headers), %NM_VLAN_FLAG_GVRP (use of the GVRP protocol),
885          * and %NM_VLAN_FLAG_LOOSE_BINDING (loose binding of the interface to its
886          * master device's operating state). %NM_VLAN_FLAG_MVRP (use of the MVRP
887          * protocol).
888          *
889          * The default value of this property is NM_VLAN_FLAG_REORDER_HEADERS,
890          * but it used to be 0. To preserve backward compatibility, the default-value
891          * in the D-Bus API continues to be 0 and a missing property on D-Bus
892          * is still considered as 0.
893          **/
894         /* ---ifcfg-rh---
895          * property: flags
896          * variable: GVRP, MVRP, VLAN_FLAGS
897          * values: "yes or "no" for GVRP and MVRP; "LOOSE_BINDING" and "NO_REORDER_HDR" for VLAN_FLAGS
898          * description: VLAN flags.
899          * ---end---
900          */
901         g_object_class_install_property
902                 (object_class, PROP_FLAGS,
903                  g_param_spec_flags (NM_SETTING_VLAN_FLAGS, "", "",
904                                      NM_TYPE_VLAN_FLAGS,
905                                      NM_VLAN_FLAG_REORDER_HEADERS,
906                                      G_PARAM_READWRITE |
907                                      G_PARAM_CONSTRUCT |
908                                      NM_SETTING_PARAM_INFERRABLE |
909                                      G_PARAM_STATIC_STRINGS));
910         _nm_setting_class_override_property (parent_class, NM_SETTING_VLAN_FLAGS,
911                                              NULL,
912                                              _override_flags_get,
913                                              NULL,
914                                              _override_flags_not_set);
915
916         /**
917          * NMSettingVlan:ingress-priority-map:
918          *
919          * For incoming packets, a list of mappings from 802.1p priorities to Linux
920          * SKB priorities.  The mapping is given in the format "from:to" where both
921          * "from" and "to" are unsigned integers, ie "7:3".
922          **/
923         /* ---ifcfg-rh---
924          * property: ingress-priority-map
925          * variable: VLAN_INGRESS_PRIORITY_MAP
926          * description: Ingress priority mapping.
927          * example: VLAN_INGRESS_PRIORITY_MAP=4:2,3:5
928          * ---end---
929          */
930         g_object_class_install_property
931                 (object_class, PROP_INGRESS_PRIORITY_MAP,
932                  g_param_spec_boxed (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, "", "",
933                                      G_TYPE_STRV,
934                                      G_PARAM_READWRITE |
935                                      NM_SETTING_PARAM_INFERRABLE |
936                                      G_PARAM_STATIC_STRINGS));
937
938         /**
939          * NMSettingVlan:egress-priority-map:
940          *
941          * For outgoing packets, a list of mappings from Linux SKB priorities to
942          * 802.1p priorities.  The mapping is given in the format "from:to" where
943          * both "from" and "to" are unsigned integers, ie "7:3".
944          **/
945         /* ---ifcfg-rh---
946          * property: egress-priority-map
947          * variable: VLAN_EGRESS_PRIORITY_MAP
948          * description: Egress priority mapping.
949          * example: VLAN_EGRESS_PRIORITY_MAP=5:4,4:1,3:7
950          * ---end---
951          */
952         g_object_class_install_property
953                 (object_class, PROP_EGRESS_PRIORITY_MAP,
954                  g_param_spec_boxed (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, "", "",
955                                      G_TYPE_STRV,
956                                      G_PARAM_READWRITE |
957                                      NM_SETTING_PARAM_INFERRABLE |
958                                      G_PARAM_STATIC_STRINGS));
959
960         /* ---ifcfg-rh---
961          * property: interface-name
962          * variable: PHYSDEV and VLAN_ID, or DEVICE
963          * description: VLAN interface name.
964          *   If all variables are set, parent device from PHYSDEV takes precedence over DEVICE,
965          *   but VLAN id from DEVICE takes precedence over VLAN_ID.
966          * example: PHYSDEV=eth0, VLAN_ID=12; or DEVICE=eth0.12
967          * ---end---
968          * ---dbus---
969          * property: interface-name
970          * format: string
971          * description: Deprecated in favor of connection.interface-name, but can
972          *   be used for backward-compatibility with older daemons, to set the
973          *   vlan's interface name.
974          * ---end---
975          */
976         _nm_setting_class_add_dbus_only_property (parent_class, "interface-name",
977                                                   G_VARIANT_TYPE_STRING,
978                                                   _nm_setting_get_deprecated_virtual_interface_name,
979                                                   NULL);
980 }