1407ea290SRuslan Ermilov /*- 2407ea290SRuslan Ermilov * Copyright (c) 2003 IPNET Internet Communication Company 3407ea290SRuslan Ermilov * All rights reserved. 4407ea290SRuslan Ermilov * 5407ea290SRuslan Ermilov * Redistribution and use in source and binary forms, with or without 6407ea290SRuslan Ermilov * modification, are permitted provided that the following conditions 7407ea290SRuslan Ermilov * are met: 8407ea290SRuslan Ermilov * 1. Redistributions of source code must retain the above copyright 9407ea290SRuslan Ermilov * notice, this list of conditions and the following disclaimer. 10407ea290SRuslan Ermilov * 2. Redistributions in binary form must reproduce the above copyright 11407ea290SRuslan Ermilov * notice, this list of conditions and the following disclaimer in the 12407ea290SRuslan Ermilov * documentation and/or other materials provided with the distribution. 13407ea290SRuslan Ermilov * 14407ea290SRuslan Ermilov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15407ea290SRuslan Ermilov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16407ea290SRuslan Ermilov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17407ea290SRuslan Ermilov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18407ea290SRuslan Ermilov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19407ea290SRuslan Ermilov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20407ea290SRuslan Ermilov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21407ea290SRuslan Ermilov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22407ea290SRuslan Ermilov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23407ea290SRuslan Ermilov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24407ea290SRuslan Ermilov * SUCH DAMAGE. 25407ea290SRuslan Ermilov * 26407ea290SRuslan Ermilov * Author: Ruslan Ermilov <ru@FreeBSD.org> 27407ea290SRuslan Ermilov * 28407ea290SRuslan Ermilov * $FreeBSD$ 29407ea290SRuslan Ermilov */ 30407ea290SRuslan Ermilov 31407ea290SRuslan Ermilov #include <sys/param.h> 32407ea290SRuslan Ermilov #include <sys/errno.h> 33407ea290SRuslan Ermilov #include <sys/kernel.h> 34407ea290SRuslan Ermilov #include <sys/malloc.h> 35407ea290SRuslan Ermilov #include <sys/mbuf.h> 36407ea290SRuslan Ermilov #include <sys/queue.h> 37407ea290SRuslan Ermilov #include <sys/socket.h> 38407ea290SRuslan Ermilov #include <sys/systm.h> 39407ea290SRuslan Ermilov 40407ea290SRuslan Ermilov #include <net/ethernet.h> 41407ea290SRuslan Ermilov #include <net/if.h> 42407ea290SRuslan Ermilov #include <net/if_vlan_var.h> 43407ea290SRuslan Ermilov 44407ea290SRuslan Ermilov #include <netgraph/ng_message.h> 45407ea290SRuslan Ermilov #include <netgraph/ng_parse.h> 46407ea290SRuslan Ermilov #include <netgraph/ng_vlan.h> 47407ea290SRuslan Ermilov #include <netgraph/netgraph.h> 48407ea290SRuslan Ermilov 49407ea290SRuslan Ermilov static ng_constructor_t ng_vlan_constructor; 50407ea290SRuslan Ermilov static ng_rcvmsg_t ng_vlan_rcvmsg; 51407ea290SRuslan Ermilov static ng_shutdown_t ng_vlan_shutdown; 52407ea290SRuslan Ermilov static ng_newhook_t ng_vlan_newhook; 53407ea290SRuslan Ermilov static ng_rcvdata_t ng_vlan_rcvdata; 54407ea290SRuslan Ermilov static ng_disconnect_t ng_vlan_disconnect; 55407ea290SRuslan Ermilov 56407ea290SRuslan Ermilov /* Parse type for struct ng_vlan_filter. */ 57407ea290SRuslan Ermilov static const struct ng_parse_struct_field ng_vlan_filter_fields[] = 58407ea290SRuslan Ermilov NG_VLAN_FILTER_FIELDS; 59407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_filter_type = { 60407ea290SRuslan Ermilov &ng_parse_struct_type, 61407ea290SRuslan Ermilov &ng_vlan_filter_fields 62407ea290SRuslan Ermilov }; 63407ea290SRuslan Ermilov 64407ea290SRuslan Ermilov static int 65407ea290SRuslan Ermilov ng_vlan_getTableLength(const struct ng_parse_type *type, 66407ea290SRuslan Ermilov const u_char *start, const u_char *buf) 67407ea290SRuslan Ermilov { 68407ea290SRuslan Ermilov const struct ng_vlan_table *const table = 69407ea290SRuslan Ermilov (const struct ng_vlan_table *)(buf - sizeof(u_int32_t)); 70407ea290SRuslan Ermilov 71407ea290SRuslan Ermilov return table->n; 72407ea290SRuslan Ermilov } 73407ea290SRuslan Ermilov 74407ea290SRuslan Ermilov /* Parse type for struct ng_vlan_table. */ 75407ea290SRuslan Ermilov static const struct ng_parse_array_info ng_vlan_table_array_info = { 76407ea290SRuslan Ermilov &ng_vlan_filter_type, 77407ea290SRuslan Ermilov ng_vlan_getTableLength 78407ea290SRuslan Ermilov }; 79407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_table_array_type = { 80407ea290SRuslan Ermilov &ng_parse_array_type, 81407ea290SRuslan Ermilov &ng_vlan_table_array_info 82407ea290SRuslan Ermilov }; 83407ea290SRuslan Ermilov static const struct ng_parse_struct_field ng_vlan_table_fields[] = 84407ea290SRuslan Ermilov NG_VLAN_TABLE_FIELDS; 85407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_table_type = { 86407ea290SRuslan Ermilov &ng_parse_struct_type, 87407ea290SRuslan Ermilov &ng_vlan_table_fields 88407ea290SRuslan Ermilov }; 89407ea290SRuslan Ermilov 90407ea290SRuslan Ermilov /* List of commands and how to convert arguments to/from ASCII. */ 91407ea290SRuslan Ermilov static const struct ng_cmdlist ng_vlan_cmdlist[] = { 92407ea290SRuslan Ermilov { 93407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 94407ea290SRuslan Ermilov NGM_VLAN_ADD_FILTER, 95407ea290SRuslan Ermilov "addfilter", 96407ea290SRuslan Ermilov &ng_vlan_filter_type, 97407ea290SRuslan Ermilov NULL 98407ea290SRuslan Ermilov }, 99407ea290SRuslan Ermilov { 100407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 101407ea290SRuslan Ermilov NGM_VLAN_DEL_FILTER, 102407ea290SRuslan Ermilov "delfilter", 103407ea290SRuslan Ermilov &ng_parse_hookbuf_type, 104407ea290SRuslan Ermilov NULL 105407ea290SRuslan Ermilov }, 106407ea290SRuslan Ermilov { 107407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 108407ea290SRuslan Ermilov NGM_VLAN_GET_TABLE, 109407ea290SRuslan Ermilov "gettable", 110407ea290SRuslan Ermilov NULL, 111407ea290SRuslan Ermilov &ng_vlan_table_type 112407ea290SRuslan Ermilov }, 113407ea290SRuslan Ermilov { 0 } 114407ea290SRuslan Ermilov }; 115407ea290SRuslan Ermilov 116407ea290SRuslan Ermilov static struct ng_type ng_vlan_typestruct = { 117f8aae777SJulian Elischer .version = NG_ABI_VERSION, 118f8aae777SJulian Elischer .name = NG_VLAN_NODE_TYPE, 119f8aae777SJulian Elischer .constructor = ng_vlan_constructor, 120f8aae777SJulian Elischer .rcvmsg = ng_vlan_rcvmsg, 121f8aae777SJulian Elischer .shutdown = ng_vlan_shutdown, 122f8aae777SJulian Elischer .newhook = ng_vlan_newhook, 123f8aae777SJulian Elischer .rcvdata = ng_vlan_rcvdata, 124f8aae777SJulian Elischer .disconnect = ng_vlan_disconnect, 125f8aae777SJulian Elischer .cmdlist = ng_vlan_cmdlist, 126407ea290SRuslan Ermilov }; 127407ea290SRuslan Ermilov NETGRAPH_INIT(vlan, &ng_vlan_typestruct); 128407ea290SRuslan Ermilov 129407ea290SRuslan Ermilov struct filter { 130407ea290SRuslan Ermilov LIST_ENTRY(filter) next; 131407ea290SRuslan Ermilov u_int16_t vlan; 132407ea290SRuslan Ermilov hook_p hook; 133407ea290SRuslan Ermilov }; 134407ea290SRuslan Ermilov 135407ea290SRuslan Ermilov #define HASHSIZE 16 136407ea290SRuslan Ermilov #define HASH(id) ((((id) >> 8) ^ ((id) >> 4) ^ (id)) & 0x0f) 137407ea290SRuslan Ermilov LIST_HEAD(filterhead, filter); 138407ea290SRuslan Ermilov 139407ea290SRuslan Ermilov typedef struct { 140407ea290SRuslan Ermilov hook_p downstream_hook; 141407ea290SRuslan Ermilov hook_p nomatch_hook; 142407ea290SRuslan Ermilov struct filterhead hashtable[HASHSIZE]; 143407ea290SRuslan Ermilov u_int32_t nent; 144407ea290SRuslan Ermilov } *priv_p; 145407ea290SRuslan Ermilov 146407ea290SRuslan Ermilov static struct filter * 147407ea290SRuslan Ermilov ng_vlan_findentry(priv_p priv, u_int16_t vlan) 148407ea290SRuslan Ermilov { 149407ea290SRuslan Ermilov struct filterhead *chain = &priv->hashtable[HASH(vlan)]; 150407ea290SRuslan Ermilov struct filter *f; 151407ea290SRuslan Ermilov 152407ea290SRuslan Ermilov LIST_FOREACH(f, chain, next) 153407ea290SRuslan Ermilov if (f->vlan == vlan) 154407ea290SRuslan Ermilov return (f); 155407ea290SRuslan Ermilov return (NULL); 156407ea290SRuslan Ermilov } 157407ea290SRuslan Ermilov 158407ea290SRuslan Ermilov static int 159407ea290SRuslan Ermilov ng_vlan_constructor(node_p node) 160407ea290SRuslan Ermilov { 161407ea290SRuslan Ermilov priv_p priv; 162407ea290SRuslan Ermilov int i; 163407ea290SRuslan Ermilov 164407ea290SRuslan Ermilov MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 165407ea290SRuslan Ermilov if (priv == NULL) 166407ea290SRuslan Ermilov return (ENOMEM); 167407ea290SRuslan Ermilov for (i = 0; i < HASHSIZE; i++) 168407ea290SRuslan Ermilov LIST_INIT(&priv->hashtable[i]); 169407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, priv); 170407ea290SRuslan Ermilov return (0); 171407ea290SRuslan Ermilov } 172407ea290SRuslan Ermilov 173407ea290SRuslan Ermilov static int 174407ea290SRuslan Ermilov ng_vlan_newhook(node_p node, hook_p hook, const char *name) 175407ea290SRuslan Ermilov { 176407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 177407ea290SRuslan Ermilov 178407ea290SRuslan Ermilov if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0) 179407ea290SRuslan Ermilov priv->downstream_hook = hook; 180407ea290SRuslan Ermilov else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0) 181407ea290SRuslan Ermilov priv->nomatch_hook = hook; 182407ea290SRuslan Ermilov else { 183407ea290SRuslan Ermilov /* 184407ea290SRuslan Ermilov * Any other hook name is valid and can 185407ea290SRuslan Ermilov * later be associated with a filter rule. 186407ea290SRuslan Ermilov */ 187407ea290SRuslan Ermilov } 188407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 189407ea290SRuslan Ermilov return (0); 190407ea290SRuslan Ermilov } 191407ea290SRuslan Ermilov 192407ea290SRuslan Ermilov static int 193407ea290SRuslan Ermilov ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook) 194407ea290SRuslan Ermilov { 195407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 196407ea290SRuslan Ermilov int error = 0; 197407ea290SRuslan Ermilov struct ng_mesg *msg, *resp = NULL; 198407ea290SRuslan Ermilov struct ng_vlan_filter *vf; 199407ea290SRuslan Ermilov struct filter *f; 200407ea290SRuslan Ermilov hook_p hook; 201407ea290SRuslan Ermilov struct ng_vlan_table *t; 202407ea290SRuslan Ermilov int i; 203407ea290SRuslan Ermilov 204407ea290SRuslan Ermilov NGI_GET_MSG(item, msg); 205407ea290SRuslan Ermilov /* Deal with message according to cookie and command. */ 206407ea290SRuslan Ermilov switch (msg->header.typecookie) { 207407ea290SRuslan Ermilov case NGM_VLAN_COOKIE: 208407ea290SRuslan Ermilov switch (msg->header.cmd) { 209407ea290SRuslan Ermilov case NGM_VLAN_ADD_FILTER: 210407ea290SRuslan Ermilov /* Check that message is long enough. */ 211407ea290SRuslan Ermilov if (msg->header.arglen != sizeof(*vf)) { 212407ea290SRuslan Ermilov error = EINVAL; 213407ea290SRuslan Ermilov break; 214407ea290SRuslan Ermilov } 215407ea290SRuslan Ermilov vf = (struct ng_vlan_filter *)msg->data; 216407ea290SRuslan Ermilov /* Sanity check the VLAN ID value. */ 217407ea290SRuslan Ermilov if (vf->vlan & ~EVL_VLID_MASK) { 218407ea290SRuslan Ermilov error = EINVAL; 219407ea290SRuslan Ermilov break; 220407ea290SRuslan Ermilov } 221407ea290SRuslan Ermilov /* Check that a referenced hook exists. */ 222407ea290SRuslan Ermilov hook = ng_findhook(node, vf->hook); 223407ea290SRuslan Ermilov if (hook == NULL) { 224407ea290SRuslan Ermilov error = ENOENT; 225407ea290SRuslan Ermilov break; 226407ea290SRuslan Ermilov } 227407ea290SRuslan Ermilov /* And is not one of the special hooks. */ 228407ea290SRuslan Ermilov if (hook == priv->downstream_hook || 229407ea290SRuslan Ermilov hook == priv->nomatch_hook) { 230407ea290SRuslan Ermilov error = EINVAL; 231407ea290SRuslan Ermilov break; 232407ea290SRuslan Ermilov } 233407ea290SRuslan Ermilov /* And is not already in service. */ 234407ea290SRuslan Ermilov if (NG_HOOK_PRIVATE(hook) != NULL) { 235407ea290SRuslan Ermilov error = EEXIST; 236407ea290SRuslan Ermilov break; 237407ea290SRuslan Ermilov } 238407ea290SRuslan Ermilov /* Check we don't already trap this VLAN. */ 239407ea290SRuslan Ermilov if (ng_vlan_findentry(priv, vf->vlan)) { 240407ea290SRuslan Ermilov error = EEXIST; 241407ea290SRuslan Ermilov break; 242407ea290SRuslan Ermilov } 243407ea290SRuslan Ermilov /* Create filter. */ 244407ea290SRuslan Ermilov MALLOC(f, struct filter *, sizeof(*f), 245407ea290SRuslan Ermilov M_NETGRAPH, M_NOWAIT | M_ZERO); 246407ea290SRuslan Ermilov if (f == NULL) { 247407ea290SRuslan Ermilov error = ENOMEM; 248407ea290SRuslan Ermilov break; 249407ea290SRuslan Ermilov } 250407ea290SRuslan Ermilov /* Link filter and hook together. */ 251407ea290SRuslan Ermilov f->hook = hook; 252407ea290SRuslan Ermilov f->vlan = vf->vlan; 253407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, f); 254407ea290SRuslan Ermilov /* Register filter in a hash table. */ 255407ea290SRuslan Ermilov LIST_INSERT_HEAD( 256407ea290SRuslan Ermilov &priv->hashtable[HASH(f->vlan)], f, next); 257407ea290SRuslan Ermilov priv->nent++; 258407ea290SRuslan Ermilov break; 259407ea290SRuslan Ermilov case NGM_VLAN_DEL_FILTER: 260407ea290SRuslan Ermilov /* Check that message is long enough. */ 261407ea290SRuslan Ermilov if (msg->header.arglen != NG_HOOKSIZ) { 262407ea290SRuslan Ermilov error = EINVAL; 263407ea290SRuslan Ermilov break; 264407ea290SRuslan Ermilov } 265407ea290SRuslan Ermilov /* Check that hook exists and is active. */ 266407ea290SRuslan Ermilov hook = ng_findhook(node, (char *)msg->data); 267407ea290SRuslan Ermilov if (hook == NULL || 268407ea290SRuslan Ermilov (f = NG_HOOK_PRIVATE(hook)) == NULL) { 269407ea290SRuslan Ermilov error = ENOENT; 270407ea290SRuslan Ermilov break; 271407ea290SRuslan Ermilov } 272407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 273407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 274407ea290SRuslan Ermilov LIST_REMOVE(f, next); 275407ea290SRuslan Ermilov priv->nent--; 276407ea290SRuslan Ermilov FREE(f, M_NETGRAPH); 277407ea290SRuslan Ermilov break; 278407ea290SRuslan Ermilov case NGM_VLAN_GET_TABLE: 279407ea290SRuslan Ermilov NG_MKRESPONSE(resp, msg, sizeof(*t) + 280407ea290SRuslan Ermilov priv->nent * sizeof(*t->filter), M_NOWAIT); 281407ea290SRuslan Ermilov if (resp == NULL) { 282407ea290SRuslan Ermilov error = ENOMEM; 283407ea290SRuslan Ermilov break; 284407ea290SRuslan Ermilov } 285407ea290SRuslan Ermilov t = (struct ng_vlan_table *)resp->data; 286407ea290SRuslan Ermilov t->n = priv->nent; 287407ea290SRuslan Ermilov vf = &t->filter[0]; 288407ea290SRuslan Ermilov for (i = 0; i < HASHSIZE; i++) { 289407ea290SRuslan Ermilov LIST_FOREACH(f, &priv->hashtable[i], next) { 290407ea290SRuslan Ermilov vf->vlan = f->vlan; 291407ea290SRuslan Ermilov strncpy(vf->hook, NG_HOOK_NAME(f->hook), 292407ea290SRuslan Ermilov NG_HOOKSIZ); 293407ea290SRuslan Ermilov vf++; 294407ea290SRuslan Ermilov } 295407ea290SRuslan Ermilov } 296407ea290SRuslan Ermilov break; 297407ea290SRuslan Ermilov default: /* Unknown command. */ 298407ea290SRuslan Ermilov error = EINVAL; 299407ea290SRuslan Ermilov break; 300407ea290SRuslan Ermilov } 301407ea290SRuslan Ermilov break; 302407ea290SRuslan Ermilov default: /* Unknown type cookie. */ 303407ea290SRuslan Ermilov error = EINVAL; 304407ea290SRuslan Ermilov break; 305407ea290SRuslan Ermilov } 306407ea290SRuslan Ermilov NG_RESPOND_MSG(error, node, item, resp); 307407ea290SRuslan Ermilov NG_FREE_MSG(msg); 308407ea290SRuslan Ermilov return (error); 309407ea290SRuslan Ermilov } 310407ea290SRuslan Ermilov 311407ea290SRuslan Ermilov static int 312407ea290SRuslan Ermilov ng_vlan_rcvdata(hook_p hook, item_p item) 313407ea290SRuslan Ermilov { 314407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 315407ea290SRuslan Ermilov struct ether_header *eh; 316407ea290SRuslan Ermilov struct ether_vlan_header *evl; 317407ea290SRuslan Ermilov int error; 318407ea290SRuslan Ermilov u_int16_t vlan; 319407ea290SRuslan Ermilov struct mbuf *m; 320407ea290SRuslan Ermilov struct m_tag *mtag; 321407ea290SRuslan Ermilov struct filter *f; 322407ea290SRuslan Ermilov 323407ea290SRuslan Ermilov /* Make sure we have an entire header. */ 324407ea290SRuslan Ermilov NGI_GET_M(item, m); 325407ea290SRuslan Ermilov if (m->m_len < sizeof(*eh) && 326407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*eh))) == NULL) { 327407ea290SRuslan Ermilov NG_FREE_ITEM(item); 328407ea290SRuslan Ermilov return (EINVAL); 329407ea290SRuslan Ermilov } 330407ea290SRuslan Ermilov eh = mtod(m, struct ether_header *); 331407ea290SRuslan Ermilov if (hook == priv->downstream_hook) { 332407ea290SRuslan Ermilov /* 333407ea290SRuslan Ermilov * If from downstream, select between a match hook 334407ea290SRuslan Ermilov * or the nomatch hook. 335407ea290SRuslan Ermilov */ 336407ea290SRuslan Ermilov mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL); 337407ea290SRuslan Ermilov if (mtag != NULL || eh->ether_type == htons(ETHERTYPE_VLAN)) { 338407ea290SRuslan Ermilov if (mtag != NULL) { 339407ea290SRuslan Ermilov /* 340407ea290SRuslan Ermilov * Packet is tagged, m contains a normal 341407ea290SRuslan Ermilov * Ethernet frame; tag is stored out-of-band. 342407ea290SRuslan Ermilov */ 343407ea290SRuslan Ermilov vlan = EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)); 344407ea290SRuslan Ermilov (void)&evl; /* XXX silence GCC */ 345407ea290SRuslan Ermilov } else { 346407ea290SRuslan Ermilov if (m->m_len < sizeof(*evl) && 347407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*evl))) == NULL) { 348407ea290SRuslan Ermilov NG_FREE_ITEM(item); 349407ea290SRuslan Ermilov return (EINVAL); 350407ea290SRuslan Ermilov } 351407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 352407ea290SRuslan Ermilov vlan = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 353407ea290SRuslan Ermilov } 354407ea290SRuslan Ermilov if ((f = ng_vlan_findentry(priv, vlan)) != NULL) { 355407ea290SRuslan Ermilov if (mtag != NULL) 356407ea290SRuslan Ermilov m_tag_delete(m, mtag); 357407ea290SRuslan Ermilov else { 358407ea290SRuslan Ermilov evl->evl_encap_proto = evl->evl_proto; 359407ea290SRuslan Ermilov bcopy(mtod(m, caddr_t), 360407ea290SRuslan Ermilov mtod(m, caddr_t) + 361407ea290SRuslan Ermilov ETHER_VLAN_ENCAP_LEN, 362407ea290SRuslan Ermilov ETHER_HDR_LEN); 363407ea290SRuslan Ermilov m_adj(m, ETHER_VLAN_ENCAP_LEN); 364407ea290SRuslan Ermilov } 365407ea290SRuslan Ermilov } 366407ea290SRuslan Ermilov } else 367407ea290SRuslan Ermilov f = NULL; 368407ea290SRuslan Ermilov if (f != NULL) 369407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, f->hook, m); 370407ea290SRuslan Ermilov else 371407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, priv->nomatch_hook, m); 372407ea290SRuslan Ermilov } else { 373407ea290SRuslan Ermilov /* 374407ea290SRuslan Ermilov * It is heading towards the downstream. 375407ea290SRuslan Ermilov * If from nomatch, pass it unmodified. 376407ea290SRuslan Ermilov * Otherwise, do the VLAN encapsulation. 377407ea290SRuslan Ermilov */ 378407ea290SRuslan Ermilov if (hook != priv->nomatch_hook) { 379407ea290SRuslan Ermilov if ((f = NG_HOOK_PRIVATE(hook)) == NULL) { 380407ea290SRuslan Ermilov NG_FREE_ITEM(item); 381407ea290SRuslan Ermilov NG_FREE_M(m); 382407ea290SRuslan Ermilov return (EOPNOTSUPP); 383407ea290SRuslan Ermilov } 384407ea290SRuslan Ermilov M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT); 385407ea290SRuslan Ermilov /* M_PREPEND takes care of m_len and m_pkthdr.len. */ 386407ea290SRuslan Ermilov if (m == NULL || (m->m_len < sizeof(*evl) && 387407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*evl))) == NULL)) { 388407ea290SRuslan Ermilov NG_FREE_ITEM(item); 389407ea290SRuslan Ermilov return (ENOMEM); 390407ea290SRuslan Ermilov } 391407ea290SRuslan Ermilov /* 392407ea290SRuslan Ermilov * Transform the Ethernet header into an Ethernet header 393407ea290SRuslan Ermilov * with 802.1Q encapsulation. 394407ea290SRuslan Ermilov */ 395407ea290SRuslan Ermilov bcopy(mtod(m, char *) + ETHER_VLAN_ENCAP_LEN, 396407ea290SRuslan Ermilov mtod(m, char *), ETHER_HDR_LEN); 397407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 398407ea290SRuslan Ermilov evl->evl_proto = evl->evl_encap_proto; 399407ea290SRuslan Ermilov evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 400407ea290SRuslan Ermilov evl->evl_tag = htons(f->vlan); 401407ea290SRuslan Ermilov } 402407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, priv->downstream_hook, m); 403407ea290SRuslan Ermilov } 404407ea290SRuslan Ermilov return (error); 405407ea290SRuslan Ermilov } 406407ea290SRuslan Ermilov 407407ea290SRuslan Ermilov static int 408407ea290SRuslan Ermilov ng_vlan_shutdown(node_p node) 409407ea290SRuslan Ermilov { 410407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 411407ea290SRuslan Ermilov 412407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, NULL); 413407ea290SRuslan Ermilov NG_NODE_UNREF(node); 414407ea290SRuslan Ermilov FREE(priv, M_NETGRAPH); 415407ea290SRuslan Ermilov return (0); 416407ea290SRuslan Ermilov } 417407ea290SRuslan Ermilov 418407ea290SRuslan Ermilov static int 419407ea290SRuslan Ermilov ng_vlan_disconnect(hook_p hook) 420407ea290SRuslan Ermilov { 421407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 422407ea290SRuslan Ermilov struct filter *f; 423407ea290SRuslan Ermilov 424407ea290SRuslan Ermilov if (hook == priv->downstream_hook) 425407ea290SRuslan Ermilov priv->downstream_hook = NULL; 426407ea290SRuslan Ermilov else if (hook == priv->nomatch_hook) 427407ea290SRuslan Ermilov priv->nomatch_hook = NULL; 428407ea290SRuslan Ermilov else { 429407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 430407ea290SRuslan Ermilov if ((f = NG_HOOK_PRIVATE(hook)) != NULL) { 431407ea290SRuslan Ermilov LIST_REMOVE(f, next); 432407ea290SRuslan Ermilov priv->nent--; 433407ea290SRuslan Ermilov FREE(f, M_NETGRAPH); 434407ea290SRuslan Ermilov } 435407ea290SRuslan Ermilov } 436407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 437407ea290SRuslan Ermilov if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 438407ea290SRuslan Ermilov (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 439407ea290SRuslan Ermilov ng_rmnode_self(NG_HOOK_NODE(hook)); 440407ea290SRuslan Ermilov return (0); 441407ea290SRuslan Ermilov } 442