]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_vlan.c
Make linux_ptrace() use linux_msg() instead of printf().
[FreeBSD/FreeBSD.git] / sys / netgraph / ng_vlan.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2003 IPNET Internet Communication Company
5  * Copyright (c) 2011 - 2012 Rozhuk Ivan <rozhuk.im@gmail.com>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * Author: Ruslan Ermilov <ru@FreeBSD.org>
30  *
31  * $FreeBSD$
32  */
33
34 #include <sys/param.h>
35 #include <sys/errno.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/queue.h>
40 #include <sys/socket.h>
41 #include <sys/systm.h>
42
43 #include <net/ethernet.h>
44 #include <net/if.h>
45 #include <net/if_vlan_var.h>
46
47 #include <netgraph/ng_message.h>
48 #include <netgraph/ng_parse.h>
49 #include <netgraph/ng_vlan.h>
50 #include <netgraph/netgraph.h>
51
52 struct ng_vlan_private {
53         hook_p          downstream_hook;
54         hook_p          nomatch_hook;
55         uint32_t        decap_enable;
56         uint32_t        encap_enable;
57         uint16_t        encap_proto;
58         hook_p          vlan_hook[(EVL_VLID_MASK + 1)];
59 };
60 typedef struct ng_vlan_private *priv_p;
61
62 #define ETHER_VLAN_HDR_LEN (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN)
63 #define VLAN_TAG_MASK   0xFFFF
64 #define HOOK_VLAN_TAG_SET_MASK ((uintptr_t)((~0) & ~(VLAN_TAG_MASK)))
65 #define IS_HOOK_VLAN_SET(hdata) \
66             ((((uintptr_t)hdata) & HOOK_VLAN_TAG_SET_MASK) == HOOK_VLAN_TAG_SET_MASK)
67
68 static ng_constructor_t ng_vlan_constructor;
69 static ng_rcvmsg_t      ng_vlan_rcvmsg;
70 static ng_shutdown_t    ng_vlan_shutdown;
71 static ng_newhook_t     ng_vlan_newhook;
72 static ng_rcvdata_t     ng_vlan_rcvdata;
73 static ng_disconnect_t  ng_vlan_disconnect;
74
75 /* Parse type for struct ng_vlan_filter. */
76 static const struct ng_parse_struct_field ng_vlan_filter_fields[] =
77         NG_VLAN_FILTER_FIELDS;
78 static const struct ng_parse_type ng_vlan_filter_type = {
79         &ng_parse_struct_type,
80         &ng_vlan_filter_fields
81 };
82
83 static int
84 ng_vlan_getTableLength(const struct ng_parse_type *type,
85     const u_char *start, const u_char *buf)
86 {
87         const struct ng_vlan_table *const table =
88             (const struct ng_vlan_table *)(buf - sizeof(u_int32_t));
89
90         return table->n;
91 }
92
93 /* Parse type for struct ng_vlan_table. */
94 static const struct ng_parse_array_info ng_vlan_table_array_info = {
95         &ng_vlan_filter_type,
96         ng_vlan_getTableLength
97 };
98 static const struct ng_parse_type ng_vlan_table_array_type = {
99         &ng_parse_array_type,
100         &ng_vlan_table_array_info
101 };
102 static const struct ng_parse_struct_field ng_vlan_table_fields[] =
103         NG_VLAN_TABLE_FIELDS;
104 static const struct ng_parse_type ng_vlan_table_type = {
105         &ng_parse_struct_type,
106         &ng_vlan_table_fields
107 };
108
109 /* List of commands and how to convert arguments to/from ASCII. */
110 static const struct ng_cmdlist ng_vlan_cmdlist[] = {
111         {
112           NGM_VLAN_COOKIE,
113           NGM_VLAN_ADD_FILTER,
114           "addfilter",
115           &ng_vlan_filter_type,
116           NULL
117         },
118         {
119           NGM_VLAN_COOKIE,
120           NGM_VLAN_DEL_FILTER,
121           "delfilter",
122           &ng_parse_hookbuf_type,
123           NULL
124         },
125         {
126           NGM_VLAN_COOKIE,
127           NGM_VLAN_GET_TABLE,
128           "gettable",
129           NULL,
130           &ng_vlan_table_type
131         },
132         {
133           NGM_VLAN_COOKIE,
134           NGM_VLAN_DEL_VID_FLT,
135           "delvidflt",
136           &ng_parse_uint16_type,
137           NULL
138         },
139         {
140           NGM_VLAN_COOKIE,
141           NGM_VLAN_GET_DECAP,
142           "getdecap",
143           NULL,
144           &ng_parse_hint32_type
145         },
146         {
147           NGM_VLAN_COOKIE,
148           NGM_VLAN_SET_DECAP,
149           "setdecap",
150           &ng_parse_hint32_type,
151           NULL
152         },
153         {
154           NGM_VLAN_COOKIE,
155           NGM_VLAN_GET_ENCAP,
156           "getencap",
157           NULL,
158           &ng_parse_hint32_type
159         },
160         {
161           NGM_VLAN_COOKIE,
162           NGM_VLAN_SET_ENCAP,
163           "setencap",
164           &ng_parse_hint32_type,
165           NULL
166         },
167         {
168           NGM_VLAN_COOKIE,
169           NGM_VLAN_GET_ENCAP_PROTO,
170           "getencapproto",
171           NULL,
172           &ng_parse_hint16_type
173         },
174         {
175           NGM_VLAN_COOKIE,
176           NGM_VLAN_SET_ENCAP_PROTO,
177           "setencapproto",
178           &ng_parse_hint16_type,
179           NULL
180         },
181         { 0 }
182 };
183
184 static struct ng_type ng_vlan_typestruct = {
185         .version =      NG_ABI_VERSION,
186         .name =         NG_VLAN_NODE_TYPE,
187         .constructor =  ng_vlan_constructor,
188         .rcvmsg =       ng_vlan_rcvmsg,
189         .shutdown =     ng_vlan_shutdown,
190         .newhook =      ng_vlan_newhook,
191         .rcvdata =      ng_vlan_rcvdata,
192         .disconnect =   ng_vlan_disconnect,
193         .cmdlist =      ng_vlan_cmdlist,
194 };
195 NETGRAPH_INIT(vlan, &ng_vlan_typestruct);
196
197
198 /*
199  * Helper functions.
200  */
201
202 static __inline int
203 m_chk(struct mbuf **mp, int len)
204 {
205
206         if ((*mp)->m_pkthdr.len < len) {
207                 m_freem((*mp));
208                 (*mp) = NULL;
209                 return (EINVAL);
210         }
211         if ((*mp)->m_len < len && ((*mp) = m_pullup((*mp), len)) == NULL)
212                 return (ENOBUFS);
213
214         return (0);
215 }
216
217
218 /*
219  * Netgraph node functions.
220  */
221
222 static int
223 ng_vlan_constructor(node_p node)
224 {
225         priv_p priv;
226
227         priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
228         priv->decap_enable = 0;
229         priv->encap_enable = VLAN_ENCAP_FROM_FILTER;
230         priv->encap_proto = htons(ETHERTYPE_VLAN);
231         NG_NODE_SET_PRIVATE(node, priv);
232         return (0);
233 }
234
235 static int
236 ng_vlan_newhook(node_p node, hook_p hook, const char *name)
237 {
238         const priv_p priv = NG_NODE_PRIVATE(node);
239
240         if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0)
241                 priv->downstream_hook = hook;
242         else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0)
243                 priv->nomatch_hook = hook;
244         else {
245                 /*
246                  * Any other hook name is valid and can
247                  * later be associated with a filter rule.
248                  */
249         }
250         NG_HOOK_SET_PRIVATE(hook, NULL);
251         return (0);
252 }
253
254 static int
255 ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook)
256 {
257         const priv_p priv = NG_NODE_PRIVATE(node);
258         struct ng_mesg *msg, *resp = NULL;
259         struct ng_vlan_filter *vf;
260         hook_p hook;
261         struct ng_vlan_table *t;
262         uintptr_t hook_data;
263         int i, vlan_count;
264         uint16_t vid;
265         int error = 0;
266
267         NGI_GET_MSG(item, msg);
268         /* Deal with message according to cookie and command. */
269         switch (msg->header.typecookie) {
270         case NGM_VLAN_COOKIE:
271                 switch (msg->header.cmd) {
272                 case NGM_VLAN_ADD_FILTER:
273                         /* Check that message is long enough. */
274                         if (msg->header.arglen != sizeof(*vf)) {
275                                 error = EINVAL;
276                                 break;
277                         }
278                         vf = (struct ng_vlan_filter *)msg->data;
279                         /* Sanity check the VLAN ID value. */
280 #ifdef  NG_VLAN_USE_OLD_VLAN_NAME
281                         if (vf->vid == 0 && vf->vid != vf->vlan) {
282                                 vf->vid = vf->vlan;
283                         } else if (vf->vid != 0 && vf->vlan != 0 &&
284                             vf->vid != vf->vlan) {
285                                 error = EINVAL;
286                                 break;
287                         }
288 #endif
289                         if (vf->vid & ~EVL_VLID_MASK ||
290                             vf->pcp & ~7 ||
291                             vf->cfi & ~1) {
292                                 error = EINVAL;
293                                 break;
294                         }
295                         /* Check that a referenced hook exists. */
296                         hook = ng_findhook(node, vf->hook_name);
297                         if (hook == NULL) {
298                                 error = ENOENT;
299                                 break;
300                         }
301                         /* And is not one of the special hooks. */
302                         if (hook == priv->downstream_hook ||
303                             hook == priv->nomatch_hook) {
304                                 error = EINVAL;
305                                 break;
306                         }
307                         /* And is not already in service. */
308                         if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) {
309                                 error = EEXIST;
310                                 break;
311                         }
312                         /* Check we don't already trap this VLAN. */
313                         if (priv->vlan_hook[vf->vid] != NULL) {
314                                 error = EEXIST;
315                                 break;
316                         }
317                         /* Link vlan and hook together. */
318                         NG_HOOK_SET_PRIVATE(hook,
319                             (void *)(HOOK_VLAN_TAG_SET_MASK |
320                             EVL_MAKETAG(vf->vid, vf->pcp, vf->cfi)));
321                         priv->vlan_hook[vf->vid] = hook;
322                         break;
323                 case NGM_VLAN_DEL_FILTER:
324                         /* Check that message is long enough. */
325                         if (msg->header.arglen != NG_HOOKSIZ) {
326                                 error = EINVAL;
327                                 break;
328                         }
329                         /* Check that hook exists and is active. */
330                         hook = ng_findhook(node, (char *)msg->data);
331                         if (hook == NULL) {
332                                 error = ENOENT;
333                                 break;
334                         }
335                         hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
336                         if (IS_HOOK_VLAN_SET(hook_data) == 0) {
337                                 error = ENOENT;
338                                 break;
339                         }
340
341                         KASSERT(priv->vlan_hook[EVL_VLANOFTAG(hook_data)] == hook,
342                             ("%s: NGM_VLAN_DEL_FILTER: Invalid VID for Hook = %s\n",
343                             __func__, (char *)msg->data));
344
345                         /* Purge a rule that refers to this hook. */
346                         priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
347                         NG_HOOK_SET_PRIVATE(hook, NULL);
348                         break;
349                 case NGM_VLAN_DEL_VID_FLT:
350                         /* Check that message is long enough. */
351                         if (msg->header.arglen != sizeof(uint16_t)) {
352                                 error = EINVAL;
353                                 break;
354                         }
355                         vid = (*((uint16_t *)msg->data));
356                         /* Sanity check the VLAN ID value. */
357                         if (vid & ~EVL_VLID_MASK) {
358                                 error = EINVAL;
359                                 break;
360                         }
361                         /* Check that hook exists and is active. */
362                         hook = priv->vlan_hook[vid];
363                         if (hook == NULL) {
364                                 error = ENOENT;
365                                 break;
366                         }
367                         hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
368                         if (IS_HOOK_VLAN_SET(hook_data) == 0) {
369                                 error = ENOENT;
370                                 break;
371                         }
372
373                         KASSERT(EVL_VLANOFTAG(hook_data) == vid,
374                             ("%s: NGM_VLAN_DEL_VID_FLT:"
375                             " Invalid VID Hook = %us, must be: %us\n",
376                             __func__, (uint16_t )EVL_VLANOFTAG(hook_data),
377                             vid));
378
379                         /* Purge a rule that refers to this hook. */
380                         priv->vlan_hook[vid] = NULL;
381                         NG_HOOK_SET_PRIVATE(hook, NULL);
382                         break;
383                 case NGM_VLAN_GET_TABLE:
384                         /* Calculate vlans. */
385                         vlan_count = 0;
386                         for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
387                                 if (priv->vlan_hook[i] != NULL &&
388                                     NG_HOOK_IS_VALID(priv->vlan_hook[i]))
389                                         vlan_count ++;
390                         }
391
392                         /* Allocate memory for response. */
393                         NG_MKRESPONSE(resp, msg, sizeof(*t) +
394                             vlan_count * sizeof(*t->filter), M_NOWAIT);
395                         if (resp == NULL) {
396                                 error = ENOMEM;
397                                 break;
398                         }
399
400                         /* Pack data to response. */
401                         t = (struct ng_vlan_table *)resp->data;
402                         t->n = 0;
403                         vf = &t->filter[0];
404                         for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
405                                 hook = priv->vlan_hook[i];
406                                 if (hook == NULL || NG_HOOK_NOT_VALID(hook))
407                                         continue;
408                                 hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
409                                 if (IS_HOOK_VLAN_SET(hook_data) == 0)
410                                         continue;
411
412                                 KASSERT(EVL_VLANOFTAG(hook_data) == i,
413                                     ("%s: NGM_VLAN_GET_TABLE:"
414                                     " hook %s VID = %us, must be: %i\n",
415                                     __func__, NG_HOOK_NAME(hook),
416                                     (uint16_t)EVL_VLANOFTAG(hook_data), i));
417
418 #ifdef  NG_VLAN_USE_OLD_VLAN_NAME
419                                 vf->vlan = i;
420 #endif
421                                 vf->vid = i;
422                                 vf->pcp = EVL_PRIOFTAG(hook_data);
423                                 vf->cfi = EVL_CFIOFTAG(hook_data);
424                                 strncpy(vf->hook_name,
425                                     NG_HOOK_NAME(hook), NG_HOOKSIZ);
426                                 vf ++;
427                                 t->n ++;
428                         }
429                         break;
430                 case NGM_VLAN_GET_DECAP:
431                         NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
432                         if (resp == NULL) {
433                                 error = ENOMEM;
434                                 break;
435                         }
436                         (*((uint32_t *)resp->data)) = priv->decap_enable;
437                         break;
438                 case NGM_VLAN_SET_DECAP:
439                         if (msg->header.arglen != sizeof(uint32_t)) {
440                                 error = EINVAL;
441                                 break;
442                         }
443                         priv->decap_enable = (*((uint32_t *)msg->data));
444                         break;
445                 case NGM_VLAN_GET_ENCAP:
446                         NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
447                         if (resp == NULL) {
448                                 error = ENOMEM;
449                                 break;
450                         }
451                         (*((uint32_t *)resp->data)) = priv->encap_enable;
452                         break;
453                 case NGM_VLAN_SET_ENCAP:
454                         if (msg->header.arglen != sizeof(uint32_t)) {
455                                 error = EINVAL;
456                                 break;
457                         }
458                         priv->encap_enable = (*((uint32_t *)msg->data));
459                         break;
460                 case NGM_VLAN_GET_ENCAP_PROTO:
461                         NG_MKRESPONSE(resp, msg, sizeof(uint16_t), M_NOWAIT);
462                         if (resp == NULL) {
463                                 error = ENOMEM;
464                                 break;
465                         }
466                         (*((uint16_t *)resp->data)) = ntohs(priv->encap_proto);
467                         break;
468                 case NGM_VLAN_SET_ENCAP_PROTO:
469                         if (msg->header.arglen != sizeof(uint16_t)) {
470                                 error = EINVAL;
471                                 break;
472                         }
473                         priv->encap_proto = htons((*((uint16_t *)msg->data)));
474                         break;
475                 default: /* Unknown command. */
476                         error = EINVAL;
477                         break;
478                 }
479                 break;
480         case NGM_FLOW_COOKIE:
481             {
482                 struct ng_mesg *copy;
483
484                 /*
485                  * Flow control messages should come only
486                  * from downstream.
487                  */
488
489                 if (lasthook == NULL)
490                         break;
491                 if (lasthook != priv->downstream_hook)
492                         break;
493                 /* Broadcast the event to all uplinks. */
494                 for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
495                         if (priv->vlan_hook[i] == NULL)
496                                 continue;
497
498                         NG_COPYMESSAGE(copy, msg, M_NOWAIT);
499                         if (copy == NULL)
500                                 continue;
501                         NG_SEND_MSG_HOOK(error, node, copy,
502                             priv->vlan_hook[i], 0);
503                 }
504                 break;
505             }
506         default: /* Unknown type cookie. */
507                 error = EINVAL;
508                 break;
509         }
510         NG_RESPOND_MSG(error, node, item, resp);
511         NG_FREE_MSG(msg);
512         return (error);
513 }
514
515 static int
516 ng_vlan_rcvdata(hook_p hook, item_p item)
517 {
518         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
519         struct ether_header *eh;
520         struct ether_vlan_header *evl;
521         int error;
522         uintptr_t hook_data;
523         uint16_t vid, eth_vtag;
524         struct mbuf *m;
525         hook_p dst_hook;
526
527
528         NGI_GET_M(item, m);
529
530         /* Make sure we have an entire header. */
531         error = m_chk(&m, ETHER_HDR_LEN);
532         if (error != 0)
533                 goto mchk_err;
534
535         eh = mtod(m, struct ether_header *);
536         if (hook == priv->downstream_hook) {
537                 /*
538                  * If from downstream, select between a match hook
539                  * or the nomatch hook.
540                  */
541
542                 dst_hook = priv->nomatch_hook;
543
544                 /* Skip packets without tag. */
545                 if ((m->m_flags & M_VLANTAG) == 0 &&
546                     eh->ether_type != priv->encap_proto) {
547                         if (dst_hook == NULL)
548                                 goto net_down;
549                         goto send_packet;
550                 }
551
552                 /* Process packets with tag. */
553                 if (m->m_flags & M_VLANTAG) {
554                         /*
555                          * Packet is tagged, m contains a normal
556                          * Ethernet frame; tag is stored out-of-band.
557                          */
558                         evl = NULL;
559                         vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
560                 } else { /* eh->ether_type == priv->encap_proto */
561                         error = m_chk(&m, ETHER_VLAN_HDR_LEN);
562                         if (error != 0)
563                                 goto mchk_err;
564                         evl = mtod(m, struct ether_vlan_header *);
565                         vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
566                 }
567
568                 if (priv->vlan_hook[vid] != NULL) {
569                         /*
570                          * VLAN filter: always remove vlan tags and
571                          * decapsulate packet.
572                          */
573                         dst_hook = priv->vlan_hook[vid];
574                         if (evl == NULL) { /* m->m_flags & M_VLANTAG */
575                                 m->m_pkthdr.ether_vtag = 0;
576                                 m->m_flags &= ~M_VLANTAG;
577                                 goto send_packet;
578                         }
579                 } else { /* nomatch_hook */
580                         if (dst_hook == NULL)
581                                 goto net_down;
582                         if (evl == NULL || priv->decap_enable == 0)
583                                 goto send_packet;
584                         /* Save tag out-of-band. */
585                         m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
586                         m->m_flags |= M_VLANTAG;
587                 }
588
589                 /*
590                  * Decapsulate:
591                  * TPID = ether type encap
592                  * Move DstMAC and SrcMAC to ETHER_TYPE.
593                  * Before:
594                  *  [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload]
595                  *  |-----------| >>>>>>>>>>>>>>>>>>>> |--------------------|
596                  * After:
597                  *  [free space ] [dmac] [smac] [ether_type] [payload]
598                  *                |-----------| |--------------------|
599                  */
600                 bcopy((char *)evl, ((char *)evl + ETHER_VLAN_ENCAP_LEN),
601                     (ETHER_ADDR_LEN * 2));
602                 m_adj(m, ETHER_VLAN_ENCAP_LEN);
603         } else {
604                 /*
605                  * It is heading towards the downstream.
606                  * If from nomatch, pass it unmodified.
607                  * Otherwise, do the VLAN encapsulation.
608                  */
609                 dst_hook = priv->downstream_hook;
610                 if (dst_hook == NULL)
611                         goto net_down;
612                 if (hook != priv->nomatch_hook) {/* Filter hook. */
613                         hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
614                         if (IS_HOOK_VLAN_SET(hook_data) == 0) {
615                                 /*
616                                  * Packet from hook not in filter
617                                  * call addfilter for this hook to fix.
618                                  */
619                                 error = EOPNOTSUPP;
620                                 goto drop;
621                         }
622                         eth_vtag = (hook_data & VLAN_TAG_MASK);
623                         if ((priv->encap_enable & VLAN_ENCAP_FROM_FILTER) == 0) {
624                                 /* Just set packet header tag and send. */
625                                 m->m_flags |= M_VLANTAG;
626                                 m->m_pkthdr.ether_vtag = eth_vtag;
627                                 goto send_packet;
628                         }
629                 } else { /* nomatch_hook */
630                         if ((priv->encap_enable & VLAN_ENCAP_FROM_NOMATCH) == 0 ||
631                             (m->m_flags & M_VLANTAG) == 0)
632                                 goto send_packet;
633                         /* Encapsulate tagged packet. */
634                         eth_vtag = m->m_pkthdr.ether_vtag;
635                         m->m_pkthdr.ether_vtag = 0;
636                         m->m_flags &= ~M_VLANTAG;
637                 }
638
639                 /*
640                  * Transform the Ethernet header into an Ethernet header
641                  * with 802.1Q encapsulation.
642                  * Mod of: ether_vlanencap.
643                  *
644                  * TPID = ether type encap
645                  * Move DstMAC and SrcMAC from ETHER_TYPE.
646                  * Before:
647                  *  [free space ] [dmac] [smac] [ether_type] [payload]
648                  *  <<<<<<<<<<<<< |-----------| |--------------------|
649                  * After:
650                  *  [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload]
651                  *  |-----------| |-- inserted tag --| |--------------------|
652                  */
653                 M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
654                 if (m == NULL)
655                         error = ENOMEM;
656                 else
657                         error = m_chk(&m, ETHER_VLAN_HDR_LEN);
658                 if (error != 0)
659                         goto mchk_err;
660
661                 evl = mtod(m, struct ether_vlan_header *);
662                 bcopy(((char *)evl + ETHER_VLAN_ENCAP_LEN),
663                     (char *)evl, (ETHER_ADDR_LEN * 2));
664                 evl->evl_encap_proto = priv->encap_proto;
665                 evl->evl_tag = htons(eth_vtag);
666         }
667
668 send_packet:
669         NG_FWD_NEW_DATA(error, item, dst_hook, m);
670         return (error);
671 net_down:
672         error = ENETDOWN;
673 drop:
674         m_freem(m);
675 mchk_err:
676         NG_FREE_ITEM(item);
677         return (error);
678 }
679
680 static int
681 ng_vlan_shutdown(node_p node)
682 {
683         const priv_p priv = NG_NODE_PRIVATE(node);
684
685         NG_NODE_SET_PRIVATE(node, NULL);
686         NG_NODE_UNREF(node);
687         free(priv, M_NETGRAPH);
688         return (0);
689 }
690
691 static int
692 ng_vlan_disconnect(hook_p hook)
693 {
694         const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
695         uintptr_t hook_data;
696
697         if (hook == priv->downstream_hook)
698                 priv->downstream_hook = NULL;
699         else if (hook == priv->nomatch_hook)
700                 priv->nomatch_hook = NULL;
701         else {
702                 /* Purge a rule that refers to this hook. */
703                 hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
704                 if (IS_HOOK_VLAN_SET(hook_data))
705                         priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
706         }
707         NG_HOOK_SET_PRIVATE(hook, NULL);
708         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
709             (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
710                 ng_rmnode_self(NG_HOOK_NODE(hook));
711         return (0);
712 }