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; 3023b1c41c5SGleb Smirnoff case NGM_FLOW_COOKIE: 3033b1c41c5SGleb Smirnoff { 3043b1c41c5SGleb Smirnoff struct ng_mesg *copy; 3053b1c41c5SGleb Smirnoff struct filterhead *chain; 3063b1c41c5SGleb Smirnoff struct filter *f; 3073b1c41c5SGleb Smirnoff 3083b1c41c5SGleb Smirnoff /* 3093b1c41c5SGleb Smirnoff * Flow control messages should come only 3103b1c41c5SGleb Smirnoff * from downstream. 3113b1c41c5SGleb Smirnoff */ 3123b1c41c5SGleb Smirnoff 3133b1c41c5SGleb Smirnoff if (lasthook == NULL) 3143b1c41c5SGleb Smirnoff break; 3153b1c41c5SGleb Smirnoff if (lasthook != priv->downstream_hook) 3163b1c41c5SGleb Smirnoff break; 3173b1c41c5SGleb Smirnoff 3183b1c41c5SGleb Smirnoff /* Broadcast the event to all uplinks. */ 3193b1c41c5SGleb Smirnoff for (i = 0, chain = priv->hashtable; i < HASHSIZE; 3203b1c41c5SGleb Smirnoff i++, chain++) 3213b1c41c5SGleb Smirnoff LIST_FOREACH(f, chain, next) { 3223b1c41c5SGleb Smirnoff NG_COPYMESSAGE(copy, msg, M_NOWAIT); 3233b1c41c5SGleb Smirnoff if (copy == NULL) 3243b1c41c5SGleb Smirnoff continue; 3253b1c41c5SGleb Smirnoff NG_SEND_MSG_HOOK(error, node, copy, f->hook, 0); 3263b1c41c5SGleb Smirnoff } 3273b1c41c5SGleb Smirnoff 3283b1c41c5SGleb Smirnoff break; 3293b1c41c5SGleb Smirnoff } 330407ea290SRuslan Ermilov default: /* Unknown type cookie. */ 331407ea290SRuslan Ermilov error = EINVAL; 332407ea290SRuslan Ermilov break; 333407ea290SRuslan Ermilov } 334407ea290SRuslan Ermilov NG_RESPOND_MSG(error, node, item, resp); 335407ea290SRuslan Ermilov NG_FREE_MSG(msg); 336407ea290SRuslan Ermilov return (error); 337407ea290SRuslan Ermilov } 338407ea290SRuslan Ermilov 339407ea290SRuslan Ermilov static int 340407ea290SRuslan Ermilov ng_vlan_rcvdata(hook_p hook, item_p item) 341407ea290SRuslan Ermilov { 342407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 343407ea290SRuslan Ermilov struct ether_header *eh; 344407ea290SRuslan Ermilov struct ether_vlan_header *evl; 345407ea290SRuslan Ermilov int error; 346407ea290SRuslan Ermilov u_int16_t vlan; 347407ea290SRuslan Ermilov struct mbuf *m; 348407ea290SRuslan Ermilov struct filter *f; 349407ea290SRuslan Ermilov 350407ea290SRuslan Ermilov /* Make sure we have an entire header. */ 351407ea290SRuslan Ermilov NGI_GET_M(item, m); 352407ea290SRuslan Ermilov if (m->m_len < sizeof(*eh) && 353407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*eh))) == NULL) { 354407ea290SRuslan Ermilov NG_FREE_ITEM(item); 355407ea290SRuslan Ermilov return (EINVAL); 356407ea290SRuslan Ermilov } 357407ea290SRuslan Ermilov eh = mtod(m, struct ether_header *); 358407ea290SRuslan Ermilov if (hook == priv->downstream_hook) { 359407ea290SRuslan Ermilov /* 360407ea290SRuslan Ermilov * If from downstream, select between a match hook 361407ea290SRuslan Ermilov * or the nomatch hook. 362407ea290SRuslan Ermilov */ 36378ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG || 36478ba57b9SAndre Oppermann eh->ether_type == htons(ETHERTYPE_VLAN)) { 36578ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG) { 366407ea290SRuslan Ermilov /* 367407ea290SRuslan Ermilov * Packet is tagged, m contains a normal 368407ea290SRuslan Ermilov * Ethernet frame; tag is stored out-of-band. 369407ea290SRuslan Ermilov */ 37078ba57b9SAndre Oppermann vlan = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 371407ea290SRuslan Ermilov (void)&evl; /* XXX silence GCC */ 372407ea290SRuslan Ermilov } else { 373407ea290SRuslan Ermilov if (m->m_len < sizeof(*evl) && 374407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*evl))) == NULL) { 375407ea290SRuslan Ermilov NG_FREE_ITEM(item); 376407ea290SRuslan Ermilov return (EINVAL); 377407ea290SRuslan Ermilov } 378407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 379407ea290SRuslan Ermilov vlan = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 380407ea290SRuslan Ermilov } 381407ea290SRuslan Ermilov if ((f = ng_vlan_findentry(priv, vlan)) != NULL) { 38278ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG) { 38378ba57b9SAndre Oppermann m->m_pkthdr.ether_vtag = 0; 38478ba57b9SAndre Oppermann m->m_flags &= ~M_VLANTAG; 38578ba57b9SAndre Oppermann } else { 386407ea290SRuslan Ermilov evl->evl_encap_proto = evl->evl_proto; 387407ea290SRuslan Ermilov bcopy(mtod(m, caddr_t), 388407ea290SRuslan Ermilov mtod(m, caddr_t) + 389407ea290SRuslan Ermilov ETHER_VLAN_ENCAP_LEN, 390407ea290SRuslan Ermilov ETHER_HDR_LEN); 391407ea290SRuslan Ermilov m_adj(m, ETHER_VLAN_ENCAP_LEN); 392407ea290SRuslan Ermilov } 393407ea290SRuslan Ermilov } 394407ea290SRuslan Ermilov } else 395407ea290SRuslan Ermilov f = NULL; 396407ea290SRuslan Ermilov if (f != NULL) 397407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, f->hook, m); 398407ea290SRuslan Ermilov else 399407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, priv->nomatch_hook, m); 400407ea290SRuslan Ermilov } else { 401407ea290SRuslan Ermilov /* 402407ea290SRuslan Ermilov * It is heading towards the downstream. 403407ea290SRuslan Ermilov * If from nomatch, pass it unmodified. 404407ea290SRuslan Ermilov * Otherwise, do the VLAN encapsulation. 405407ea290SRuslan Ermilov */ 406407ea290SRuslan Ermilov if (hook != priv->nomatch_hook) { 407407ea290SRuslan Ermilov if ((f = NG_HOOK_PRIVATE(hook)) == NULL) { 408407ea290SRuslan Ermilov NG_FREE_ITEM(item); 409407ea290SRuslan Ermilov NG_FREE_M(m); 410407ea290SRuslan Ermilov return (EOPNOTSUPP); 411407ea290SRuslan Ermilov } 412407ea290SRuslan Ermilov M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT); 413407ea290SRuslan Ermilov /* M_PREPEND takes care of m_len and m_pkthdr.len. */ 414407ea290SRuslan Ermilov if (m == NULL || (m->m_len < sizeof(*evl) && 415407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*evl))) == NULL)) { 416407ea290SRuslan Ermilov NG_FREE_ITEM(item); 417407ea290SRuslan Ermilov return (ENOMEM); 418407ea290SRuslan Ermilov } 419407ea290SRuslan Ermilov /* 420407ea290SRuslan Ermilov * Transform the Ethernet header into an Ethernet header 421407ea290SRuslan Ermilov * with 802.1Q encapsulation. 422407ea290SRuslan Ermilov */ 423407ea290SRuslan Ermilov bcopy(mtod(m, char *) + ETHER_VLAN_ENCAP_LEN, 424407ea290SRuslan Ermilov mtod(m, char *), ETHER_HDR_LEN); 425407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 426407ea290SRuslan Ermilov evl->evl_proto = evl->evl_encap_proto; 427407ea290SRuslan Ermilov evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 428407ea290SRuslan Ermilov evl->evl_tag = htons(f->vlan); 429407ea290SRuslan Ermilov } 430407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, priv->downstream_hook, m); 431407ea290SRuslan Ermilov } 432407ea290SRuslan Ermilov return (error); 433407ea290SRuslan Ermilov } 434407ea290SRuslan Ermilov 435407ea290SRuslan Ermilov static int 436407ea290SRuslan Ermilov ng_vlan_shutdown(node_p node) 437407ea290SRuslan Ermilov { 438407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 439407ea290SRuslan Ermilov 440407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, NULL); 441407ea290SRuslan Ermilov NG_NODE_UNREF(node); 442407ea290SRuslan Ermilov FREE(priv, M_NETGRAPH); 443407ea290SRuslan Ermilov return (0); 444407ea290SRuslan Ermilov } 445407ea290SRuslan Ermilov 446407ea290SRuslan Ermilov static int 447407ea290SRuslan Ermilov ng_vlan_disconnect(hook_p hook) 448407ea290SRuslan Ermilov { 449407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 450407ea290SRuslan Ermilov struct filter *f; 451407ea290SRuslan Ermilov 452407ea290SRuslan Ermilov if (hook == priv->downstream_hook) 453407ea290SRuslan Ermilov priv->downstream_hook = NULL; 454407ea290SRuslan Ermilov else if (hook == priv->nomatch_hook) 455407ea290SRuslan Ermilov priv->nomatch_hook = NULL; 456407ea290SRuslan Ermilov else { 457407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 458407ea290SRuslan Ermilov if ((f = NG_HOOK_PRIVATE(hook)) != NULL) { 459407ea290SRuslan Ermilov LIST_REMOVE(f, next); 460407ea290SRuslan Ermilov priv->nent--; 461407ea290SRuslan Ermilov FREE(f, M_NETGRAPH); 462407ea290SRuslan Ermilov } 463407ea290SRuslan Ermilov } 464407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 465407ea290SRuslan Ermilov if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 466407ea290SRuslan Ermilov (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 467407ea290SRuslan Ermilov ng_rmnode_self(NG_HOOK_NODE(hook)); 468407ea290SRuslan Ermilov return (0); 469407ea290SRuslan Ermilov } 470