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 164*674d86bfSGleb Smirnoff priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 165407ea290SRuslan Ermilov for (i = 0; i < HASHSIZE; i++) 166407ea290SRuslan Ermilov LIST_INIT(&priv->hashtable[i]); 167407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, priv); 168407ea290SRuslan Ermilov return (0); 169407ea290SRuslan Ermilov } 170407ea290SRuslan Ermilov 171407ea290SRuslan Ermilov static int 172407ea290SRuslan Ermilov ng_vlan_newhook(node_p node, hook_p hook, const char *name) 173407ea290SRuslan Ermilov { 174407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 175407ea290SRuslan Ermilov 176407ea290SRuslan Ermilov if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0) 177407ea290SRuslan Ermilov priv->downstream_hook = hook; 178407ea290SRuslan Ermilov else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0) 179407ea290SRuslan Ermilov priv->nomatch_hook = hook; 180407ea290SRuslan Ermilov else { 181407ea290SRuslan Ermilov /* 182407ea290SRuslan Ermilov * Any other hook name is valid and can 183407ea290SRuslan Ermilov * later be associated with a filter rule. 184407ea290SRuslan Ermilov */ 185407ea290SRuslan Ermilov } 186407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 187407ea290SRuslan Ermilov return (0); 188407ea290SRuslan Ermilov } 189407ea290SRuslan Ermilov 190407ea290SRuslan Ermilov static int 191407ea290SRuslan Ermilov ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook) 192407ea290SRuslan Ermilov { 193407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 194407ea290SRuslan Ermilov int error = 0; 195407ea290SRuslan Ermilov struct ng_mesg *msg, *resp = NULL; 196407ea290SRuslan Ermilov struct ng_vlan_filter *vf; 197407ea290SRuslan Ermilov struct filter *f; 198407ea290SRuslan Ermilov hook_p hook; 199407ea290SRuslan Ermilov struct ng_vlan_table *t; 200407ea290SRuslan Ermilov int i; 201407ea290SRuslan Ermilov 202407ea290SRuslan Ermilov NGI_GET_MSG(item, msg); 203407ea290SRuslan Ermilov /* Deal with message according to cookie and command. */ 204407ea290SRuslan Ermilov switch (msg->header.typecookie) { 205407ea290SRuslan Ermilov case NGM_VLAN_COOKIE: 206407ea290SRuslan Ermilov switch (msg->header.cmd) { 207407ea290SRuslan Ermilov case NGM_VLAN_ADD_FILTER: 208407ea290SRuslan Ermilov /* Check that message is long enough. */ 209407ea290SRuslan Ermilov if (msg->header.arglen != sizeof(*vf)) { 210407ea290SRuslan Ermilov error = EINVAL; 211407ea290SRuslan Ermilov break; 212407ea290SRuslan Ermilov } 213407ea290SRuslan Ermilov vf = (struct ng_vlan_filter *)msg->data; 214407ea290SRuslan Ermilov /* Sanity check the VLAN ID value. */ 215407ea290SRuslan Ermilov if (vf->vlan & ~EVL_VLID_MASK) { 216407ea290SRuslan Ermilov error = EINVAL; 217407ea290SRuslan Ermilov break; 218407ea290SRuslan Ermilov } 219407ea290SRuslan Ermilov /* Check that a referenced hook exists. */ 220407ea290SRuslan Ermilov hook = ng_findhook(node, vf->hook); 221407ea290SRuslan Ermilov if (hook == NULL) { 222407ea290SRuslan Ermilov error = ENOENT; 223407ea290SRuslan Ermilov break; 224407ea290SRuslan Ermilov } 225407ea290SRuslan Ermilov /* And is not one of the special hooks. */ 226407ea290SRuslan Ermilov if (hook == priv->downstream_hook || 227407ea290SRuslan Ermilov hook == priv->nomatch_hook) { 228407ea290SRuslan Ermilov error = EINVAL; 229407ea290SRuslan Ermilov break; 230407ea290SRuslan Ermilov } 231407ea290SRuslan Ermilov /* And is not already in service. */ 232407ea290SRuslan Ermilov if (NG_HOOK_PRIVATE(hook) != NULL) { 233407ea290SRuslan Ermilov error = EEXIST; 234407ea290SRuslan Ermilov break; 235407ea290SRuslan Ermilov } 236407ea290SRuslan Ermilov /* Check we don't already trap this VLAN. */ 237407ea290SRuslan Ermilov if (ng_vlan_findentry(priv, vf->vlan)) { 238407ea290SRuslan Ermilov error = EEXIST; 239407ea290SRuslan Ermilov break; 240407ea290SRuslan Ermilov } 241407ea290SRuslan Ermilov /* Create filter. */ 2421ede983cSDag-Erling Smørgrav f = malloc(sizeof(*f), 243407ea290SRuslan Ermilov M_NETGRAPH, M_NOWAIT | M_ZERO); 244407ea290SRuslan Ermilov if (f == NULL) { 245407ea290SRuslan Ermilov error = ENOMEM; 246407ea290SRuslan Ermilov break; 247407ea290SRuslan Ermilov } 248407ea290SRuslan Ermilov /* Link filter and hook together. */ 249407ea290SRuslan Ermilov f->hook = hook; 250407ea290SRuslan Ermilov f->vlan = vf->vlan; 251407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, f); 252407ea290SRuslan Ermilov /* Register filter in a hash table. */ 253407ea290SRuslan Ermilov LIST_INSERT_HEAD( 254407ea290SRuslan Ermilov &priv->hashtable[HASH(f->vlan)], f, next); 255407ea290SRuslan Ermilov priv->nent++; 256407ea290SRuslan Ermilov break; 257407ea290SRuslan Ermilov case NGM_VLAN_DEL_FILTER: 258407ea290SRuslan Ermilov /* Check that message is long enough. */ 259407ea290SRuslan Ermilov if (msg->header.arglen != NG_HOOKSIZ) { 260407ea290SRuslan Ermilov error = EINVAL; 261407ea290SRuslan Ermilov break; 262407ea290SRuslan Ermilov } 263407ea290SRuslan Ermilov /* Check that hook exists and is active. */ 264407ea290SRuslan Ermilov hook = ng_findhook(node, (char *)msg->data); 265407ea290SRuslan Ermilov if (hook == NULL || 266407ea290SRuslan Ermilov (f = NG_HOOK_PRIVATE(hook)) == NULL) { 267407ea290SRuslan Ermilov error = ENOENT; 268407ea290SRuslan Ermilov break; 269407ea290SRuslan Ermilov } 270407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 271407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 272407ea290SRuslan Ermilov LIST_REMOVE(f, next); 273407ea290SRuslan Ermilov priv->nent--; 2741ede983cSDag-Erling Smørgrav free(f, M_NETGRAPH); 275407ea290SRuslan Ermilov break; 276407ea290SRuslan Ermilov case NGM_VLAN_GET_TABLE: 277407ea290SRuslan Ermilov NG_MKRESPONSE(resp, msg, sizeof(*t) + 278407ea290SRuslan Ermilov priv->nent * sizeof(*t->filter), M_NOWAIT); 279407ea290SRuslan Ermilov if (resp == NULL) { 280407ea290SRuslan Ermilov error = ENOMEM; 281407ea290SRuslan Ermilov break; 282407ea290SRuslan Ermilov } 283407ea290SRuslan Ermilov t = (struct ng_vlan_table *)resp->data; 284407ea290SRuslan Ermilov t->n = priv->nent; 285407ea290SRuslan Ermilov vf = &t->filter[0]; 286407ea290SRuslan Ermilov for (i = 0; i < HASHSIZE; i++) { 287407ea290SRuslan Ermilov LIST_FOREACH(f, &priv->hashtable[i], next) { 288407ea290SRuslan Ermilov vf->vlan = f->vlan; 289407ea290SRuslan Ermilov strncpy(vf->hook, NG_HOOK_NAME(f->hook), 290407ea290SRuslan Ermilov NG_HOOKSIZ); 291407ea290SRuslan Ermilov vf++; 292407ea290SRuslan Ermilov } 293407ea290SRuslan Ermilov } 294407ea290SRuslan Ermilov break; 295407ea290SRuslan Ermilov default: /* Unknown command. */ 296407ea290SRuslan Ermilov error = EINVAL; 297407ea290SRuslan Ermilov break; 298407ea290SRuslan Ermilov } 299407ea290SRuslan Ermilov break; 3003b1c41c5SGleb Smirnoff case NGM_FLOW_COOKIE: 3013b1c41c5SGleb Smirnoff { 3023b1c41c5SGleb Smirnoff struct ng_mesg *copy; 3033b1c41c5SGleb Smirnoff struct filterhead *chain; 3043b1c41c5SGleb Smirnoff struct filter *f; 3053b1c41c5SGleb Smirnoff 3063b1c41c5SGleb Smirnoff /* 3073b1c41c5SGleb Smirnoff * Flow control messages should come only 3083b1c41c5SGleb Smirnoff * from downstream. 3093b1c41c5SGleb Smirnoff */ 3103b1c41c5SGleb Smirnoff 3113b1c41c5SGleb Smirnoff if (lasthook == NULL) 3123b1c41c5SGleb Smirnoff break; 3133b1c41c5SGleb Smirnoff if (lasthook != priv->downstream_hook) 3143b1c41c5SGleb Smirnoff break; 3153b1c41c5SGleb Smirnoff 3163b1c41c5SGleb Smirnoff /* Broadcast the event to all uplinks. */ 3173b1c41c5SGleb Smirnoff for (i = 0, chain = priv->hashtable; i < HASHSIZE; 3183b1c41c5SGleb Smirnoff i++, chain++) 3193b1c41c5SGleb Smirnoff LIST_FOREACH(f, chain, next) { 3203b1c41c5SGleb Smirnoff NG_COPYMESSAGE(copy, msg, M_NOWAIT); 3213b1c41c5SGleb Smirnoff if (copy == NULL) 3223b1c41c5SGleb Smirnoff continue; 3233b1c41c5SGleb Smirnoff NG_SEND_MSG_HOOK(error, node, copy, f->hook, 0); 3243b1c41c5SGleb Smirnoff } 3253b1c41c5SGleb Smirnoff 3263b1c41c5SGleb Smirnoff break; 3273b1c41c5SGleb Smirnoff } 328407ea290SRuslan Ermilov default: /* Unknown type cookie. */ 329407ea290SRuslan Ermilov error = EINVAL; 330407ea290SRuslan Ermilov break; 331407ea290SRuslan Ermilov } 332407ea290SRuslan Ermilov NG_RESPOND_MSG(error, node, item, resp); 333407ea290SRuslan Ermilov NG_FREE_MSG(msg); 334407ea290SRuslan Ermilov return (error); 335407ea290SRuslan Ermilov } 336407ea290SRuslan Ermilov 337407ea290SRuslan Ermilov static int 338407ea290SRuslan Ermilov ng_vlan_rcvdata(hook_p hook, item_p item) 339407ea290SRuslan Ermilov { 340407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 341407ea290SRuslan Ermilov struct ether_header *eh; 342c7b8e2f5SWarner Losh struct ether_vlan_header *evl = NULL; 343407ea290SRuslan Ermilov int error; 344407ea290SRuslan Ermilov u_int16_t vlan; 345407ea290SRuslan Ermilov struct mbuf *m; 346407ea290SRuslan Ermilov struct filter *f; 347407ea290SRuslan Ermilov 348407ea290SRuslan Ermilov /* Make sure we have an entire header. */ 349407ea290SRuslan Ermilov NGI_GET_M(item, m); 350407ea290SRuslan Ermilov if (m->m_len < sizeof(*eh) && 351407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*eh))) == NULL) { 352407ea290SRuslan Ermilov NG_FREE_ITEM(item); 353407ea290SRuslan Ermilov return (EINVAL); 354407ea290SRuslan Ermilov } 355407ea290SRuslan Ermilov eh = mtod(m, struct ether_header *); 356407ea290SRuslan Ermilov if (hook == priv->downstream_hook) { 357407ea290SRuslan Ermilov /* 358407ea290SRuslan Ermilov * If from downstream, select between a match hook 359407ea290SRuslan Ermilov * or the nomatch hook. 360407ea290SRuslan Ermilov */ 36178ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG || 36278ba57b9SAndre Oppermann eh->ether_type == htons(ETHERTYPE_VLAN)) { 36378ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG) { 364407ea290SRuslan Ermilov /* 365407ea290SRuslan Ermilov * Packet is tagged, m contains a normal 366407ea290SRuslan Ermilov * Ethernet frame; tag is stored out-of-band. 367407ea290SRuslan Ermilov */ 36878ba57b9SAndre Oppermann vlan = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 369407ea290SRuslan Ermilov } else { 370407ea290SRuslan Ermilov if (m->m_len < sizeof(*evl) && 371407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*evl))) == NULL) { 372407ea290SRuslan Ermilov NG_FREE_ITEM(item); 373407ea290SRuslan Ermilov return (EINVAL); 374407ea290SRuslan Ermilov } 375407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 376407ea290SRuslan Ermilov vlan = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 377407ea290SRuslan Ermilov } 378407ea290SRuslan Ermilov if ((f = ng_vlan_findentry(priv, vlan)) != NULL) { 37978ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG) { 38078ba57b9SAndre Oppermann m->m_pkthdr.ether_vtag = 0; 38178ba57b9SAndre Oppermann m->m_flags &= ~M_VLANTAG; 38278ba57b9SAndre Oppermann } else { 383407ea290SRuslan Ermilov evl->evl_encap_proto = evl->evl_proto; 384407ea290SRuslan Ermilov bcopy(mtod(m, caddr_t), 385407ea290SRuslan Ermilov mtod(m, caddr_t) + 386407ea290SRuslan Ermilov ETHER_VLAN_ENCAP_LEN, 387407ea290SRuslan Ermilov ETHER_HDR_LEN); 388407ea290SRuslan Ermilov m_adj(m, ETHER_VLAN_ENCAP_LEN); 389407ea290SRuslan Ermilov } 390407ea290SRuslan Ermilov } 391407ea290SRuslan Ermilov } else 392407ea290SRuslan Ermilov f = NULL; 393407ea290SRuslan Ermilov if (f != NULL) 394407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, f->hook, m); 395407ea290SRuslan Ermilov else 396407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, priv->nomatch_hook, m); 397407ea290SRuslan Ermilov } else { 398407ea290SRuslan Ermilov /* 399407ea290SRuslan Ermilov * It is heading towards the downstream. 400407ea290SRuslan Ermilov * If from nomatch, pass it unmodified. 401407ea290SRuslan Ermilov * Otherwise, do the VLAN encapsulation. 402407ea290SRuslan Ermilov */ 403407ea290SRuslan Ermilov if (hook != priv->nomatch_hook) { 404407ea290SRuslan Ermilov if ((f = NG_HOOK_PRIVATE(hook)) == NULL) { 405407ea290SRuslan Ermilov NG_FREE_ITEM(item); 406407ea290SRuslan Ermilov NG_FREE_M(m); 407407ea290SRuslan Ermilov return (EOPNOTSUPP); 408407ea290SRuslan Ermilov } 409407ea290SRuslan Ermilov M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT); 410407ea290SRuslan Ermilov /* M_PREPEND takes care of m_len and m_pkthdr.len. */ 411407ea290SRuslan Ermilov if (m == NULL || (m->m_len < sizeof(*evl) && 412407ea290SRuslan Ermilov (m = m_pullup(m, sizeof(*evl))) == NULL)) { 413407ea290SRuslan Ermilov NG_FREE_ITEM(item); 414407ea290SRuslan Ermilov return (ENOMEM); 415407ea290SRuslan Ermilov } 416407ea290SRuslan Ermilov /* 417407ea290SRuslan Ermilov * Transform the Ethernet header into an Ethernet header 418407ea290SRuslan Ermilov * with 802.1Q encapsulation. 419407ea290SRuslan Ermilov */ 420407ea290SRuslan Ermilov bcopy(mtod(m, char *) + ETHER_VLAN_ENCAP_LEN, 421407ea290SRuslan Ermilov mtod(m, char *), ETHER_HDR_LEN); 422407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 423407ea290SRuslan Ermilov evl->evl_proto = evl->evl_encap_proto; 424407ea290SRuslan Ermilov evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 425407ea290SRuslan Ermilov evl->evl_tag = htons(f->vlan); 426407ea290SRuslan Ermilov } 427407ea290SRuslan Ermilov NG_FWD_NEW_DATA(error, item, priv->downstream_hook, m); 428407ea290SRuslan Ermilov } 429407ea290SRuslan Ermilov return (error); 430407ea290SRuslan Ermilov } 431407ea290SRuslan Ermilov 432407ea290SRuslan Ermilov static int 433407ea290SRuslan Ermilov ng_vlan_shutdown(node_p node) 434407ea290SRuslan Ermilov { 435407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 436407ea290SRuslan Ermilov 437407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, NULL); 438407ea290SRuslan Ermilov NG_NODE_UNREF(node); 4391ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH); 440407ea290SRuslan Ermilov return (0); 441407ea290SRuslan Ermilov } 442407ea290SRuslan Ermilov 443407ea290SRuslan Ermilov static int 444407ea290SRuslan Ermilov ng_vlan_disconnect(hook_p hook) 445407ea290SRuslan Ermilov { 446407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 447407ea290SRuslan Ermilov struct filter *f; 448407ea290SRuslan Ermilov 449407ea290SRuslan Ermilov if (hook == priv->downstream_hook) 450407ea290SRuslan Ermilov priv->downstream_hook = NULL; 451407ea290SRuslan Ermilov else if (hook == priv->nomatch_hook) 452407ea290SRuslan Ermilov priv->nomatch_hook = NULL; 453407ea290SRuslan Ermilov else { 454407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 455407ea290SRuslan Ermilov if ((f = NG_HOOK_PRIVATE(hook)) != NULL) { 456407ea290SRuslan Ermilov LIST_REMOVE(f, next); 457407ea290SRuslan Ermilov priv->nent--; 4581ede983cSDag-Erling Smørgrav free(f, M_NETGRAPH); 459407ea290SRuslan Ermilov } 460407ea290SRuslan Ermilov } 461407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 462407ea290SRuslan Ermilov if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 463407ea290SRuslan Ermilov (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 464407ea290SRuslan Ermilov ng_rmnode_self(NG_HOOK_NODE(hook)); 465407ea290SRuslan Ermilov return (0); 466407ea290SRuslan Ermilov } 467