1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program 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
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Copyright 2011 - 2012 Red Hat, Inc.
19 */
20
21 #include "config.h"
22
23 #include <glib.h>
24 #include <glib/gi18n.h>
25
26 #include <sys/socket.h>
27 #include <linux/if.h>
28 #include <netinet/ether.h>
29
30 #include "nm-device-vlan.h"
31 #include "nm-logging.h"
32 #include "nm-utils.h"
33 #include "NetworkManagerUtils.h"
34 #include "nm-device-private.h"
35 #include "nm-enum-types.h"
36 #include "nm-dbus-manager.h"
37 #include "nm-platform.h"
38
39 #include "nm-device-vlan-glue.h"
40
41
42 G_DEFINE_TYPE (NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE)
43
44 #define NM_DEVICE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VLAN, NMDeviceVlanPrivate))
45
46 #define NM_VLAN_ERROR (nm_vlan_error_quark ())
47
48 typedef struct {
49 guint8 initial_hw_addr[ETH_ALEN];
50
51 gboolean disposed;
52 gboolean invalid;
53
54 NMDevice *parent;
55 guint parent_state_id;
56
57 guint vlan_id;
58 } NMDeviceVlanPrivate;
59
60 enum {
61 PROP_0,
62 PROP_PARENT,
63 PROP_VLAN_ID,
64
65 LAST_PROP
66 };
67
68 /******************************************************************/
69
70 static GQuark
71 nm_vlan_error_quark (void)
72 {
73 static GQuark quark = 0;
74 if (!quark)
75 quark = g_quark_from_static_string ("nm-vlan-error");
76 return quark;
77 }
78
79 /******************************************************************/
80
81 static void
82 update_initial_hw_address (NMDevice *dev)
83 {
84 NMDeviceVlan *self = NM_DEVICE_VLAN (dev);
85 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
86 char *mac_str;
87
88 memcpy (priv->initial_hw_addr, nm_device_get_hw_address (dev, NULL), ETH_ALEN);
89
90 mac_str = nm_utils_hwaddr_ntoa (priv->initial_hw_addr, ARPHRD_ETHER);
91 nm_log_dbg (LOGD_DEVICE | LOGD_VLAN, "(%s): read initial MAC address %s",
92 nm_device_get_iface (dev), mac_str);
93 g_free (mac_str);
94 }
95
96 static guint32
97 get_generic_capabilities (NMDevice *dev)
98 {
99 /* We assume VLAN interfaces always support carrier detect */
100 return NM_DEVICE_CAP_CARRIER_DETECT;
101 }
102
103 static gboolean
104 bring_up (NMDevice *dev, gboolean *no_firmware)
105 {
106 gboolean success = FALSE;
107 guint i = 20;
108
109 while (i-- > 0 && !success) {
110 success = NM_DEVICE_CLASS (nm_device_vlan_parent_class)->bring_up (dev, no_firmware);
111 g_usleep (50);
112 }
113
114 return success;
115 }
116
117 /******************************************************************/
118
119 static gboolean
120 match_parent (NMDeviceVlan *self, const char *parent, GError **error)
121 {
122 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
123
124 g_return_val_if_fail (parent != NULL, FALSE);
125
126 if (nm_utils_is_uuid (parent)) {
127 NMActRequest *parent_req;
128 NMConnection *parent_connection;
129
130 /* If the parent is a UUID, the connection matches if our parent
131 * device has that connection activated.
132 */
133
134 parent_req = nm_device_get_act_request (priv->parent);
135 if (!parent_req) {
136 g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
137 "Parent interface not active; could not match UUID");
138 return FALSE;
139 }
140
141 parent_connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (parent_req));
142 if (!parent_connection) {
143 g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
144 "Parent interface had no connection; could not match UUID");
145 return FALSE;
146 }
147 if (g_strcmp0 (parent, nm_connection_get_uuid (parent_connection)) != 0) {
148 g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
149 "Parent interface UUID did not match connection UUID");
150 return FALSE;
151 }
152 } else {
153 /* interface name */
154 if (g_strcmp0 (parent, nm_device_get_ip_iface (priv->parent)) != 0) {
155 g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
156 "Parent interface name did not match connection");
157 return FALSE;
158 }
159 }
160
161 return TRUE;
162 }
163
164 static gboolean
165 match_hwaddr (NMDevice *device, NMConnection *connection, gboolean fail_if_no_hwaddr)
166 {
167 NMSettingWired *s_wired;
168 const GByteArray *mac;
169 const guint8 *device_mac;
170 guint device_mac_len;
171
172 s_wired = nm_connection_get_setting_wired (connection);
173 if (!s_wired)
174 return !fail_if_no_hwaddr;
175
176 mac = nm_setting_wired_get_mac_address (s_wired);
177 if (!mac)
178 return !fail_if_no_hwaddr;
179
180 device_mac = nm_device_get_hw_address (device, &device_mac_len);
181
182 return ( mac->len == device_mac_len
183 && memcmp (mac->data, device_mac, device_mac_len) == 0);
184 }
185
186 static gboolean
187 check_connection_compatible (NMDevice *device,
188 NMConnection *connection,
189 GError **error)
190 {
191 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
192 NMSettingVlan *s_vlan;
193 const char *parent, *iface = NULL;
194
195 if (!NM_DEVICE_CLASS (nm_device_vlan_parent_class)->check_connection_compatible (device, connection, error))
196 return FALSE;
197
198 s_vlan = nm_connection_get_setting_vlan (connection);
199 if (!s_vlan) {
200 g_set_error (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
201 "The connection was not a VLAN connection.");
202 return FALSE;
203 }
204
205 if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id) {
206 g_set_error (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
207 "The connection's VLAN ID did not match the device's VLAN ID.");
208 return FALSE;
209 }
210
211 /* Check parent interface; could be an interface name or a UUID */
212 parent = nm_setting_vlan_get_parent (s_vlan);
213 if (parent) {
214 if (!match_parent (NM_DEVICE_VLAN (device), parent, error))
215 return FALSE;
216 } else {
217 /* Parent could be a MAC address in an NMSettingWired */
218 if (!match_hwaddr (device, connection, TRUE)) {
219 g_set_error (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
220 "Failed to match the VLAN parent interface via hardware address.");
221 return FALSE;
222 }
223 }
224
225 /* Ensure the interface name matches. If not specified we assume a match
226 * since both the parent interface and the VLAN ID matched by the time we
227 * get here.
228 */
229 iface = nm_connection_get_virtual_iface_name (connection);
230 if (iface) {
231 if (g_strcmp0 (nm_device_get_ip_iface (device), iface) != 0) {
232 g_set_error (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
233 "The VLAN connection virtual interface name did not match.");
234 return FALSE;
235 }
236 }
237
238 return TRUE;
239 }
240
241 static gboolean
242 complete_connection (NMDevice *device,
243 NMConnection *connection,
244 const char *specific_object,
245 const GSList *existing_connections,
246 GError **error)
247 {
248 NMSettingVlan *s_vlan;
249
250 nm_utils_complete_generic (connection,
251 NM_SETTING_VLAN_SETTING_NAME,
252 existing_connections,
253 _("VLAN connection %d"),
254 NULL,
255 TRUE);
256
257 s_vlan = nm_connection_get_setting_vlan (connection);
258 if (!s_vlan) {
259 g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
260 "A 'vlan' setting is required.");
261 return FALSE;
262 }
263
264 /* If there's no VLAN interface, no parent, and no hardware address in the
265 * settings, then there's not enough information to complete the setting.
266 */
267 if ( !nm_setting_vlan_get_parent (s_vlan)
268 && !match_hwaddr (device, connection, TRUE)) {
269 g_set_error_literal (error, NM_VLAN_ERROR, NM_VLAN_ERROR_CONNECTION_INVALID,
270 "The 'vlan' setting had no interface name, parent, or hardware address.");
271 return FALSE;
272 }
273
274 return TRUE;
275 }
276
277 static gboolean
278 match_l2_config (NMDevice *device, NMConnection *connection)
279 {
280 NMSettingVlan *s_vlan;
281 gboolean fail_if_no_hwaddr = FALSE;
282
283 s_vlan = nm_connection_get_setting_vlan (connection);
284 g_assert (s_vlan);
285
286 if ( !nm_setting_vlan_get_parent (s_vlan)
287 && !nm_setting_vlan_get_interface_name (s_vlan)) {
288 /* If there's no parent and no interface name given, then the only way
289 * we have to identify the VLAN interface the connection matches is
290 * a hardware-specific setting's hardware address property, so we want
291 * to fail the match below if we there is none.
292 */
293 fail_if_no_hwaddr = TRUE;
294 }
295
296 /* MAC address check; we ask the parent to check our own MAC address,
297 * because only the parent knows what kind of NMSetting the MAC
298 * address will be in. The VLAN device shouldn't have to know what kind
299 * of interface the parent is.
300 */
301 if (!match_hwaddr (device, connection, fail_if_no_hwaddr))
302 return FALSE;
303
304 /* FIXME: any more L2 checks? */
305 return TRUE;
306 }
307
308 static NMActStageReturn
309 act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
310 {
311 NMActRequest *req;
312 NMConnection *connection;
313 NMSettingVlan *s_vlan;
314 NMSettingWired *s_wired;
315 const GByteArray *cloned_mac;
316 NMActStageReturn ret;
317
318 g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
319
320 ret = NM_DEVICE_CLASS (nm_device_vlan_parent_class)->act_stage1_prepare (dev, reason);
321 if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
322 return ret;
323
324 req = nm_device_get_act_request (dev);
325 g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
326
327 connection = nm_act_request_get_connection (req);
328 g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
329
330 s_wired = nm_connection_get_setting_wired (connection);
331 if (s_wired) {
332 /* Set device MAC address if the connection wants to change it */
333 cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
334 if (cloned_mac && (cloned_mac->len == ETH_ALEN))
335 nm_device_set_hw_addr (dev, (const guint8 *) cloned_mac->data, "set", LOGD_VLAN);
336 }
337
338 s_vlan = nm_connection_get_setting_vlan (connection);
339 if (s_vlan) {
340 int ifindex = nm_device_get_ifindex (dev);
341 int num, i;
342 guint32 from, to;
343
344 num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_INGRESS_MAP);
345 for (i = 0; i < num; i++) {
346 if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_INGRESS_MAP, i, &from, &to))
347 nm_platform_vlan_set_ingress_map (ifindex, from, to);
348 }
349 num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_EGRESS_MAP);
350 for (i = 0; i < num; i++) {
351 if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_EGRESS_MAP, i, &from, &to))
352 nm_platform_vlan_set_egress_map (ifindex, from, to);
353 }
354 }
355
356 return ret;
357 }
358
359 static void
360 ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
361 {
362 NMConnection *connection;
363 NMSettingWired *s_wired;
364 guint32 mtu;
365
366 connection = nm_device_get_connection (device);
367 g_assert (connection);
368
369 s_wired = nm_connection_get_setting_wired (connection);
370 if (s_wired) {
371 mtu = nm_setting_wired_get_mtu (s_wired);
372 if (mtu)
373 nm_ip4_config_set_mtu (config, mtu);
374 }
375 }
376
377 static void
378 deactivate (NMDevice *device)
379 {
380 NMDeviceVlan *self = NM_DEVICE_VLAN (device);
381 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
382
383 /* Reset MAC address back to initial address */
384 nm_device_set_hw_addr (device, priv->initial_hw_addr, "reset", LOGD_VLAN);
385 }
386
387 /******************************************************************/
388
389 static void
390 parent_state_changed (NMDevice *parent,
391 NMDeviceState new_state,
392 NMDeviceState old_state,
393 NMDeviceStateReason reason,
394 gpointer user_data)
395 {
396 NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
397
398 /* We'll react to our own carrier state notifications. Ignore the parent's. */
399 if (reason == NM_DEVICE_STATE_REASON_CARRIER)
400 return;
401
402 if (new_state < NM_DEVICE_STATE_DISCONNECTED) {
403 /* If the parent becomes unavailable or unmanaged so does the VLAN */
404 nm_device_state_changed (NM_DEVICE (self), new_state, reason);
405 } else if ( new_state == NM_DEVICE_STATE_DISCONNECTED
406 && old_state < NM_DEVICE_STATE_DISCONNECTED) {
407 /* Mark VLAN interface as available/disconnected when the parent
408 * becomes available as a result of becoming initialized.
409 */
410 nm_device_state_changed (NM_DEVICE (self), new_state, reason);
411 }
412 }
413
414 /******************************************************************/
415
416 NMDevice *
417 nm_device_vlan_new (NMPlatformLink *platform_device, NMDevice *parent)
418 {
419 NMDevice *device;
420
421 g_return_val_if_fail (platform_device != NULL, NULL);
422 g_return_val_if_fail (NM_IS_DEVICE (parent), NULL);
423
424 device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
425 NM_DEVICE_PLATFORM_DEVICE, platform_device,
426 NM_DEVICE_VLAN_PARENT, parent,
427 NM_DEVICE_DRIVER, "8021q",
428 NM_DEVICE_TYPE_DESC, "VLAN",
429 NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
430 NULL);
431 if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
432 g_object_unref (device);
433 device = NULL;
434 }
435
436 return device;
437 }
438
439 NMDevice *
440 nm_device_vlan_new_for_connection (NMConnection *connection, NMDevice *parent)
441 {
442 NMDevice *device;
443 NMSettingVlan *s_vlan;
444 char *iface;
445
446 g_return_val_if_fail (connection != NULL, NULL);
447 g_return_val_if_fail (NM_IS_DEVICE (parent), NULL);
448
449 s_vlan = nm_connection_get_setting_vlan (connection);
450 g_return_val_if_fail (s_vlan != NULL, NULL);
451
452 iface = g_strdup (nm_connection_get_virtual_iface_name (connection));
453 if (!iface) {
454 iface = nm_utils_new_vlan_name (nm_device_get_ip_iface (parent),
455 nm_setting_vlan_get_id (s_vlan));
456 }
457
458 if ( !nm_platform_vlan_add (iface,
459 nm_device_get_ifindex (parent),
460 nm_setting_vlan_get_id (s_vlan),
461 nm_setting_vlan_get_flags (s_vlan))
462 && nm_platform_get_error () != NM_PLATFORM_ERROR_EXISTS) {
463 nm_log_warn (LOGD_DEVICE | LOGD_VLAN, "(%s): failed to add VLAN interface for '%s'",
464 iface, nm_connection_get_id (connection));
465 g_free (iface);
466 return NULL;
467 }
468
469 device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_VLAN,
470 NM_DEVICE_IFACE, iface,
471 NM_DEVICE_VLAN_PARENT, parent,
472 NM_DEVICE_DRIVER, "8021q",
473 NM_DEVICE_TYPE_DESC, "VLAN",
474 NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_VLAN,
475 NULL);
476 g_free (iface);
477 if (NM_DEVICE_VLAN_GET_PRIVATE (device)->invalid) {
478 g_object_unref (device);
479 device = NULL;
480 }
481
482 return device;
483 }
484
485 static void
486 nm_device_vlan_init (NMDeviceVlan * self)
487 {
488 }
489
490 static void
491 constructed (GObject *object)
492 {
493 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
494 NMDevice *device = NM_DEVICE (object);
495 const char *iface = nm_device_get_iface (device);
496 int ifindex = nm_device_get_ifindex (device);
497 int parent_ifindex = -1, itype;
498 int vlan_id;
499
500 if (G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed)
501 G_OBJECT_CLASS (nm_device_vlan_parent_class)->constructed (object);
502
503 if (!priv->parent) {
504 nm_log_err (LOGD_VLAN, "(%s): no parent specified.", iface);
505 priv->invalid = TRUE;
506 return;
507 }
508
509 itype = nm_platform_link_get_type (ifindex);
510 if (itype != NM_LINK_TYPE_VLAN) {
511 nm_log_err (LOGD_VLAN, "(%s): failed to get VLAN interface type.", iface);
512 priv->invalid = TRUE;
513 return;
514 }
515
516 if (!nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id)) {
517 nm_log_warn (LOGD_VLAN, "(%s): failed to get VLAN interface info.", iface);
518 priv->invalid = TRUE;
519 return;
520 }
521
522 if ( parent_ifindex < 0
523 || parent_ifindex != nm_device_get_ip_ifindex (priv->parent)
524 || vlan_id < 0) {
525 nm_log_warn (LOGD_VLAN, "(%s): VLAN parent ifindex (%d) or VLAN ID (%d) invalid.",
526 iface, parent_ifindex, priv->vlan_id);
527 priv->invalid = TRUE;
528 return;
529 }
530
531 priv->vlan_id = vlan_id;
532 nm_log_dbg (LOGD_HW | LOGD_VLAN, "(%s): kernel ifindex %d", iface, ifindex);
533 nm_log_info (LOGD_HW | LOGD_VLAN, "(%s): VLAN ID %d with parent %s",
534 iface, priv->vlan_id, nm_device_get_iface (priv->parent));
535 }
536
537 static void
538 get_property (GObject *object, guint prop_id,
539 GValue *value, GParamSpec *pspec)
540 {
541 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
542
543 switch (prop_id) {
544 case PROP_VLAN_ID:
545 g_value_set_uint (value, priv->vlan_id);
546 break;
547 default:
548 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
549 break;
550 }
551 }
552
553 static void
554 set_property (GObject *object, guint prop_id,
555 const GValue *value, GParamSpec *pspec)
556 {
557 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
558
559 switch (prop_id) {
560 case PROP_PARENT:
561 priv->parent = g_value_dup_object (value);
562 priv->parent_state_id = g_signal_connect (priv->parent,
563 "state-changed",
564 G_CALLBACK (parent_state_changed),
565 object);
566 break;
567 case PROP_VLAN_ID:
568 priv->vlan_id = g_value_get_uint (value);
569 break;
570 default:
571 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
572 break;
573 }
574 }
575
576 static void
577 dispose (GObject *object)
578 {
579 NMDeviceVlan *self = NM_DEVICE_VLAN (object);
580 NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self);
581
582 if (priv->disposed) {
583 G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object);
584 return;
585 }
586 priv->disposed = TRUE;
587
588 g_signal_handler_disconnect (priv->parent, priv->parent_state_id);
589 g_object_unref (priv->parent);
590
591 G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object);
592 }
593
594 static void
595 nm_device_vlan_class_init (NMDeviceVlanClass *klass)
596 {
597 GObjectClass *object_class = G_OBJECT_CLASS (klass);
598 NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
599
600 g_type_class_add_private (object_class, sizeof (NMDeviceVlanPrivate));
601
602 /* virtual methods */
603 object_class->constructed = constructed;
604 object_class->get_property = get_property;
605 object_class->set_property = set_property;
606 object_class->dispose = dispose;
607
608 parent_class->update_initial_hw_address = update_initial_hw_address;
609 parent_class->get_generic_capabilities = get_generic_capabilities;
610 parent_class->bring_up = bring_up;
611 parent_class->act_stage1_prepare = act_stage1_prepare;
612 parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
613 parent_class->deactivate = deactivate;
614
615 parent_class->check_connection_compatible = check_connection_compatible;
616 parent_class->complete_connection = complete_connection;
617 parent_class->match_l2_config = match_l2_config;
618
619 /* properties */
620 g_object_class_install_property
621 (object_class, PROP_PARENT,
622 g_param_spec_object (NM_DEVICE_VLAN_PARENT,
623 "Parent",
624 "Parent",
625 NM_TYPE_DEVICE,
626 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
627 g_object_class_install_property
628 (object_class, PROP_VLAN_ID,
629 g_param_spec_uint (NM_DEVICE_VLAN_ID,
630 "VLAN ID",
631 "VLAN ID",
632 0, 4095, 0,
633 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
634
635 nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
636 G_TYPE_FROM_CLASS (klass),
637 &dbus_glib_nm_device_vlan_object_info);
638
639 dbus_g_error_domain_register (NM_VLAN_ERROR, NULL, NM_TYPE_VLAN_ERROR);
640 }
641