1407ea290SRuslan Ermilov /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3fe267a55SPedro F. Giffuni * 4407ea290SRuslan Ermilov * Copyright (c) 2003 IPNET Internet Communication Company 5bbf53c35SAdrian Chadd * Copyright (c) 2011 - 2012 Rozhuk Ivan <rozhuk.im@gmail.com> 6407ea290SRuslan Ermilov * All rights reserved. 7407ea290SRuslan Ermilov * 8407ea290SRuslan Ermilov * Redistribution and use in source and binary forms, with or without 9407ea290SRuslan Ermilov * modification, are permitted provided that the following conditions 10407ea290SRuslan Ermilov * are met: 11407ea290SRuslan Ermilov * 1. Redistributions of source code must retain the above copyright 12407ea290SRuslan Ermilov * notice, this list of conditions and the following disclaimer. 13407ea290SRuslan Ermilov * 2. Redistributions in binary form must reproduce the above copyright 14407ea290SRuslan Ermilov * notice, this list of conditions and the following disclaimer in the 15407ea290SRuslan Ermilov * documentation and/or other materials provided with the distribution. 16407ea290SRuslan Ermilov * 17407ea290SRuslan Ermilov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18407ea290SRuslan Ermilov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19407ea290SRuslan Ermilov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20407ea290SRuslan Ermilov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21407ea290SRuslan Ermilov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22407ea290SRuslan Ermilov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23407ea290SRuslan Ermilov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24407ea290SRuslan Ermilov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25407ea290SRuslan Ermilov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26407ea290SRuslan Ermilov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27407ea290SRuslan Ermilov * SUCH DAMAGE. 28407ea290SRuslan Ermilov * 29407ea290SRuslan Ermilov * Author: Ruslan Ermilov <ru@FreeBSD.org> 30407ea290SRuslan Ermilov * 31407ea290SRuslan Ermilov * $FreeBSD$ 32407ea290SRuslan Ermilov */ 33407ea290SRuslan Ermilov 34407ea290SRuslan Ermilov #include <sys/param.h> 35407ea290SRuslan Ermilov #include <sys/errno.h> 36407ea290SRuslan Ermilov #include <sys/kernel.h> 37407ea290SRuslan Ermilov #include <sys/malloc.h> 38407ea290SRuslan Ermilov #include <sys/mbuf.h> 39407ea290SRuslan Ermilov #include <sys/queue.h> 40407ea290SRuslan Ermilov #include <sys/socket.h> 41407ea290SRuslan Ermilov #include <sys/systm.h> 42407ea290SRuslan Ermilov 43407ea290SRuslan Ermilov #include <net/ethernet.h> 44407ea290SRuslan Ermilov #include <net/if.h> 45407ea290SRuslan Ermilov #include <net/if_vlan_var.h> 46407ea290SRuslan Ermilov 47407ea290SRuslan Ermilov #include <netgraph/ng_message.h> 48407ea290SRuslan Ermilov #include <netgraph/ng_parse.h> 49407ea290SRuslan Ermilov #include <netgraph/ng_vlan.h> 50407ea290SRuslan Ermilov #include <netgraph/netgraph.h> 51407ea290SRuslan Ermilov 52bbf53c35SAdrian Chadd struct ng_vlan_private { 53bbf53c35SAdrian Chadd hook_p downstream_hook; 54bbf53c35SAdrian Chadd hook_p nomatch_hook; 55bbf53c35SAdrian Chadd uint32_t decap_enable; 56bbf53c35SAdrian Chadd uint32_t encap_enable; 57bbf53c35SAdrian Chadd uint16_t encap_proto; 58bbf53c35SAdrian Chadd hook_p vlan_hook[(EVL_VLID_MASK + 1)]; 59bbf53c35SAdrian Chadd }; 60bbf53c35SAdrian Chadd typedef struct ng_vlan_private *priv_p; 61bbf53c35SAdrian Chadd 62bbf53c35SAdrian Chadd #define ETHER_VLAN_HDR_LEN (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN) 63bbf53c35SAdrian Chadd #define VLAN_TAG_MASK 0xFFFF 64bbf53c35SAdrian Chadd #define HOOK_VLAN_TAG_SET_MASK ((uintptr_t)((~0) & ~(VLAN_TAG_MASK))) 65bbf53c35SAdrian Chadd #define IS_HOOK_VLAN_SET(hdata) \ 66bbf53c35SAdrian Chadd ((((uintptr_t)hdata) & HOOK_VLAN_TAG_SET_MASK) == HOOK_VLAN_TAG_SET_MASK) 67bbf53c35SAdrian Chadd 68407ea290SRuslan Ermilov static ng_constructor_t ng_vlan_constructor; 69407ea290SRuslan Ermilov static ng_rcvmsg_t ng_vlan_rcvmsg; 70407ea290SRuslan Ermilov static ng_shutdown_t ng_vlan_shutdown; 71407ea290SRuslan Ermilov static ng_newhook_t ng_vlan_newhook; 72407ea290SRuslan Ermilov static ng_rcvdata_t ng_vlan_rcvdata; 73407ea290SRuslan Ermilov static ng_disconnect_t ng_vlan_disconnect; 74407ea290SRuslan Ermilov 75407ea290SRuslan Ermilov /* Parse type for struct ng_vlan_filter. */ 76407ea290SRuslan Ermilov static const struct ng_parse_struct_field ng_vlan_filter_fields[] = 77407ea290SRuslan Ermilov NG_VLAN_FILTER_FIELDS; 78407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_filter_type = { 79407ea290SRuslan Ermilov &ng_parse_struct_type, 80407ea290SRuslan Ermilov &ng_vlan_filter_fields 81407ea290SRuslan Ermilov }; 82407ea290SRuslan Ermilov 83407ea290SRuslan Ermilov static int 84407ea290SRuslan Ermilov ng_vlan_getTableLength(const struct ng_parse_type *type, 85407ea290SRuslan Ermilov const u_char *start, const u_char *buf) 86407ea290SRuslan Ermilov { 87407ea290SRuslan Ermilov const struct ng_vlan_table *const table = 88407ea290SRuslan Ermilov (const struct ng_vlan_table *)(buf - sizeof(u_int32_t)); 89407ea290SRuslan Ermilov 90407ea290SRuslan Ermilov return table->n; 91407ea290SRuslan Ermilov } 92407ea290SRuslan Ermilov 93407ea290SRuslan Ermilov /* Parse type for struct ng_vlan_table. */ 94407ea290SRuslan Ermilov static const struct ng_parse_array_info ng_vlan_table_array_info = { 95407ea290SRuslan Ermilov &ng_vlan_filter_type, 96407ea290SRuslan Ermilov ng_vlan_getTableLength 97407ea290SRuslan Ermilov }; 98407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_table_array_type = { 99407ea290SRuslan Ermilov &ng_parse_array_type, 100407ea290SRuslan Ermilov &ng_vlan_table_array_info 101407ea290SRuslan Ermilov }; 102407ea290SRuslan Ermilov static const struct ng_parse_struct_field ng_vlan_table_fields[] = 103407ea290SRuslan Ermilov NG_VLAN_TABLE_FIELDS; 104407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_table_type = { 105407ea290SRuslan Ermilov &ng_parse_struct_type, 106407ea290SRuslan Ermilov &ng_vlan_table_fields 107407ea290SRuslan Ermilov }; 108407ea290SRuslan Ermilov 109407ea290SRuslan Ermilov /* List of commands and how to convert arguments to/from ASCII. */ 110407ea290SRuslan Ermilov static const struct ng_cmdlist ng_vlan_cmdlist[] = { 111407ea290SRuslan Ermilov { 112407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 113407ea290SRuslan Ermilov NGM_VLAN_ADD_FILTER, 114407ea290SRuslan Ermilov "addfilter", 115407ea290SRuslan Ermilov &ng_vlan_filter_type, 116407ea290SRuslan Ermilov NULL 117407ea290SRuslan Ermilov }, 118407ea290SRuslan Ermilov { 119407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 120407ea290SRuslan Ermilov NGM_VLAN_DEL_FILTER, 121407ea290SRuslan Ermilov "delfilter", 122407ea290SRuslan Ermilov &ng_parse_hookbuf_type, 123407ea290SRuslan Ermilov NULL 124407ea290SRuslan Ermilov }, 125407ea290SRuslan Ermilov { 126407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 127407ea290SRuslan Ermilov NGM_VLAN_GET_TABLE, 128407ea290SRuslan Ermilov "gettable", 129407ea290SRuslan Ermilov NULL, 130407ea290SRuslan Ermilov &ng_vlan_table_type 131407ea290SRuslan Ermilov }, 132bbf53c35SAdrian Chadd { 133bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 134bbf53c35SAdrian Chadd NGM_VLAN_DEL_VID_FLT, 135bbf53c35SAdrian Chadd "delvidflt", 136bbf53c35SAdrian Chadd &ng_parse_uint16_type, 137bbf53c35SAdrian Chadd NULL 138bbf53c35SAdrian Chadd }, 139bbf53c35SAdrian Chadd { 140bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 141bbf53c35SAdrian Chadd NGM_VLAN_GET_DECAP, 142bbf53c35SAdrian Chadd "getdecap", 143bbf53c35SAdrian Chadd NULL, 144bbf53c35SAdrian Chadd &ng_parse_hint32_type 145bbf53c35SAdrian Chadd }, 146bbf53c35SAdrian Chadd { 147bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 148bbf53c35SAdrian Chadd NGM_VLAN_SET_DECAP, 149bbf53c35SAdrian Chadd "setdecap", 150bbf53c35SAdrian Chadd &ng_parse_hint32_type, 151bbf53c35SAdrian Chadd NULL 152bbf53c35SAdrian Chadd }, 153bbf53c35SAdrian Chadd { 154bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 155bbf53c35SAdrian Chadd NGM_VLAN_GET_ENCAP, 156bbf53c35SAdrian Chadd "getencap", 157bbf53c35SAdrian Chadd NULL, 158bbf53c35SAdrian Chadd &ng_parse_hint32_type 159bbf53c35SAdrian Chadd }, 160bbf53c35SAdrian Chadd { 161bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 162bbf53c35SAdrian Chadd NGM_VLAN_SET_ENCAP, 163bbf53c35SAdrian Chadd "setencap", 164bbf53c35SAdrian Chadd &ng_parse_hint32_type, 165bbf53c35SAdrian Chadd NULL 166bbf53c35SAdrian Chadd }, 167bbf53c35SAdrian Chadd { 168bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 169bbf53c35SAdrian Chadd NGM_VLAN_GET_ENCAP_PROTO, 170bbf53c35SAdrian Chadd "getencapproto", 171bbf53c35SAdrian Chadd NULL, 172bbf53c35SAdrian Chadd &ng_parse_hint16_type 173bbf53c35SAdrian Chadd }, 174bbf53c35SAdrian Chadd { 175bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 176bbf53c35SAdrian Chadd NGM_VLAN_SET_ENCAP_PROTO, 177bbf53c35SAdrian Chadd "setencapproto", 178bbf53c35SAdrian Chadd &ng_parse_hint16_type, 179bbf53c35SAdrian Chadd NULL 180bbf53c35SAdrian Chadd }, 181407ea290SRuslan Ermilov { 0 } 182407ea290SRuslan Ermilov }; 183407ea290SRuslan Ermilov 184407ea290SRuslan Ermilov static struct ng_type ng_vlan_typestruct = { 185f8aae777SJulian Elischer .version = NG_ABI_VERSION, 186f8aae777SJulian Elischer .name = NG_VLAN_NODE_TYPE, 187f8aae777SJulian Elischer .constructor = ng_vlan_constructor, 188f8aae777SJulian Elischer .rcvmsg = ng_vlan_rcvmsg, 189f8aae777SJulian Elischer .shutdown = ng_vlan_shutdown, 190f8aae777SJulian Elischer .newhook = ng_vlan_newhook, 191f8aae777SJulian Elischer .rcvdata = ng_vlan_rcvdata, 192f8aae777SJulian Elischer .disconnect = ng_vlan_disconnect, 193f8aae777SJulian Elischer .cmdlist = ng_vlan_cmdlist, 194407ea290SRuslan Ermilov }; 195407ea290SRuslan Ermilov NETGRAPH_INIT(vlan, &ng_vlan_typestruct); 196407ea290SRuslan Ermilov 197bbf53c35SAdrian Chadd /* 198bbf53c35SAdrian Chadd * Helper functions. 199bbf53c35SAdrian Chadd */ 200407ea290SRuslan Ermilov 201bbf53c35SAdrian Chadd static __inline int 202bbf53c35SAdrian Chadd m_chk(struct mbuf **mp, int len) 203407ea290SRuslan Ermilov { 204407ea290SRuslan Ermilov 205bbf53c35SAdrian Chadd if ((*mp)->m_pkthdr.len < len) { 206bbf53c35SAdrian Chadd m_freem((*mp)); 207bbf53c35SAdrian Chadd (*mp) = NULL; 208bbf53c35SAdrian Chadd return (EINVAL); 209407ea290SRuslan Ermilov } 210bbf53c35SAdrian Chadd if ((*mp)->m_len < len && ((*mp) = m_pullup((*mp), len)) == NULL) 211bbf53c35SAdrian Chadd return (ENOBUFS); 212bbf53c35SAdrian Chadd 213bbf53c35SAdrian Chadd return (0); 214bbf53c35SAdrian Chadd } 215bbf53c35SAdrian Chadd 216bbf53c35SAdrian Chadd /* 217bbf53c35SAdrian Chadd * Netgraph node functions. 218bbf53c35SAdrian Chadd */ 219407ea290SRuslan Ermilov 220407ea290SRuslan Ermilov static int 221407ea290SRuslan Ermilov ng_vlan_constructor(node_p node) 222407ea290SRuslan Ermilov { 223407ea290SRuslan Ermilov priv_p priv; 224407ea290SRuslan Ermilov 225674d86bfSGleb Smirnoff priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO); 226bbf53c35SAdrian Chadd priv->decap_enable = 0; 227bbf53c35SAdrian Chadd priv->encap_enable = VLAN_ENCAP_FROM_FILTER; 228bbf53c35SAdrian Chadd priv->encap_proto = htons(ETHERTYPE_VLAN); 229407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, priv); 230407ea290SRuslan Ermilov return (0); 231407ea290SRuslan Ermilov } 232407ea290SRuslan Ermilov 233407ea290SRuslan Ermilov static int 234407ea290SRuslan Ermilov ng_vlan_newhook(node_p node, hook_p hook, const char *name) 235407ea290SRuslan Ermilov { 236407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 237407ea290SRuslan Ermilov 238407ea290SRuslan Ermilov if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0) 239407ea290SRuslan Ermilov priv->downstream_hook = hook; 240407ea290SRuslan Ermilov else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0) 241407ea290SRuslan Ermilov priv->nomatch_hook = hook; 242407ea290SRuslan Ermilov else { 243407ea290SRuslan Ermilov /* 244407ea290SRuslan Ermilov * Any other hook name is valid and can 245407ea290SRuslan Ermilov * later be associated with a filter rule. 246407ea290SRuslan Ermilov */ 247407ea290SRuslan Ermilov } 248407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 249407ea290SRuslan Ermilov return (0); 250407ea290SRuslan Ermilov } 251407ea290SRuslan Ermilov 252407ea290SRuslan Ermilov static int 253407ea290SRuslan Ermilov ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook) 254407ea290SRuslan Ermilov { 255407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 256407ea290SRuslan Ermilov struct ng_mesg *msg, *resp = NULL; 257407ea290SRuslan Ermilov struct ng_vlan_filter *vf; 258407ea290SRuslan Ermilov hook_p hook; 259407ea290SRuslan Ermilov struct ng_vlan_table *t; 260bbf53c35SAdrian Chadd uintptr_t hook_data; 261bbf53c35SAdrian Chadd int i, vlan_count; 262bbf53c35SAdrian Chadd uint16_t vid; 263bbf53c35SAdrian Chadd int error = 0; 264407ea290SRuslan Ermilov 265407ea290SRuslan Ermilov NGI_GET_MSG(item, msg); 266407ea290SRuslan Ermilov /* Deal with message according to cookie and command. */ 267407ea290SRuslan Ermilov switch (msg->header.typecookie) { 268407ea290SRuslan Ermilov case NGM_VLAN_COOKIE: 269407ea290SRuslan Ermilov switch (msg->header.cmd) { 270407ea290SRuslan Ermilov case NGM_VLAN_ADD_FILTER: 271407ea290SRuslan Ermilov /* Check that message is long enough. */ 272407ea290SRuslan Ermilov if (msg->header.arglen != sizeof(*vf)) { 273407ea290SRuslan Ermilov error = EINVAL; 274407ea290SRuslan Ermilov break; 275407ea290SRuslan Ermilov } 276407ea290SRuslan Ermilov vf = (struct ng_vlan_filter *)msg->data; 277407ea290SRuslan Ermilov /* Sanity check the VLAN ID value. */ 278bbf53c35SAdrian Chadd #ifdef NG_VLAN_USE_OLD_VLAN_NAME 279bbf53c35SAdrian Chadd if (vf->vid == 0 && vf->vid != vf->vlan) { 280bbf53c35SAdrian Chadd vf->vid = vf->vlan; 281bbf53c35SAdrian Chadd } else if (vf->vid != 0 && vf->vlan != 0 && 282bbf53c35SAdrian Chadd vf->vid != vf->vlan) { 283bbf53c35SAdrian Chadd error = EINVAL; 284bbf53c35SAdrian Chadd break; 285bbf53c35SAdrian Chadd } 286bbf53c35SAdrian Chadd #endif 287bbf53c35SAdrian Chadd if (vf->vid & ~EVL_VLID_MASK || 288bbf53c35SAdrian Chadd vf->pcp & ~7 || 289bbf53c35SAdrian Chadd vf->cfi & ~1) { 290407ea290SRuslan Ermilov error = EINVAL; 291407ea290SRuslan Ermilov break; 292407ea290SRuslan Ermilov } 293407ea290SRuslan Ermilov /* Check that a referenced hook exists. */ 294bbf53c35SAdrian Chadd hook = ng_findhook(node, vf->hook_name); 295407ea290SRuslan Ermilov if (hook == NULL) { 296407ea290SRuslan Ermilov error = ENOENT; 297407ea290SRuslan Ermilov break; 298407ea290SRuslan Ermilov } 299407ea290SRuslan Ermilov /* And is not one of the special hooks. */ 300407ea290SRuslan Ermilov if (hook == priv->downstream_hook || 301407ea290SRuslan Ermilov hook == priv->nomatch_hook) { 302407ea290SRuslan Ermilov error = EINVAL; 303407ea290SRuslan Ermilov break; 304407ea290SRuslan Ermilov } 305407ea290SRuslan Ermilov /* And is not already in service. */ 306bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) { 307407ea290SRuslan Ermilov error = EEXIST; 308407ea290SRuslan Ermilov break; 309407ea290SRuslan Ermilov } 310407ea290SRuslan Ermilov /* Check we don't already trap this VLAN. */ 311bbf53c35SAdrian Chadd if (priv->vlan_hook[vf->vid] != NULL) { 312407ea290SRuslan Ermilov error = EEXIST; 313407ea290SRuslan Ermilov break; 314407ea290SRuslan Ermilov } 315bbf53c35SAdrian Chadd /* Link vlan and hook together. */ 316bbf53c35SAdrian Chadd NG_HOOK_SET_PRIVATE(hook, 317bbf53c35SAdrian Chadd (void *)(HOOK_VLAN_TAG_SET_MASK | 318bbf53c35SAdrian Chadd EVL_MAKETAG(vf->vid, vf->pcp, vf->cfi))); 319bbf53c35SAdrian Chadd priv->vlan_hook[vf->vid] = hook; 320407ea290SRuslan Ermilov break; 321407ea290SRuslan Ermilov case NGM_VLAN_DEL_FILTER: 322407ea290SRuslan Ermilov /* Check that message is long enough. */ 323407ea290SRuslan Ermilov if (msg->header.arglen != NG_HOOKSIZ) { 324407ea290SRuslan Ermilov error = EINVAL; 325407ea290SRuslan Ermilov break; 326407ea290SRuslan Ermilov } 327407ea290SRuslan Ermilov /* Check that hook exists and is active. */ 328407ea290SRuslan Ermilov hook = ng_findhook(node, (char *)msg->data); 329bbf53c35SAdrian Chadd if (hook == NULL) { 330407ea290SRuslan Ermilov error = ENOENT; 331407ea290SRuslan Ermilov break; 332407ea290SRuslan Ermilov } 333bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); 334bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) { 335bbf53c35SAdrian Chadd error = ENOENT; 336bbf53c35SAdrian Chadd break; 337bbf53c35SAdrian Chadd } 338bbf53c35SAdrian Chadd 339bbf53c35SAdrian Chadd KASSERT(priv->vlan_hook[EVL_VLANOFTAG(hook_data)] == hook, 340bbf53c35SAdrian Chadd ("%s: NGM_VLAN_DEL_FILTER: Invalid VID for Hook = %s\n", 341bbf53c35SAdrian Chadd __func__, (char *)msg->data)); 342bbf53c35SAdrian Chadd 343407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 344bbf53c35SAdrian Chadd priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL; 345407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 346bbf53c35SAdrian Chadd break; 347bbf53c35SAdrian Chadd case NGM_VLAN_DEL_VID_FLT: 348bbf53c35SAdrian Chadd /* Check that message is long enough. */ 349bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint16_t)) { 350bbf53c35SAdrian Chadd error = EINVAL; 351bbf53c35SAdrian Chadd break; 352bbf53c35SAdrian Chadd } 353bbf53c35SAdrian Chadd vid = (*((uint16_t *)msg->data)); 354bbf53c35SAdrian Chadd /* Sanity check the VLAN ID value. */ 355bbf53c35SAdrian Chadd if (vid & ~EVL_VLID_MASK) { 356bbf53c35SAdrian Chadd error = EINVAL; 357bbf53c35SAdrian Chadd break; 358bbf53c35SAdrian Chadd } 359bbf53c35SAdrian Chadd /* Check that hook exists and is active. */ 360bbf53c35SAdrian Chadd hook = priv->vlan_hook[vid]; 361bbf53c35SAdrian Chadd if (hook == NULL) { 362bbf53c35SAdrian Chadd error = ENOENT; 363bbf53c35SAdrian Chadd break; 364bbf53c35SAdrian Chadd } 365bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); 366bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) { 367bbf53c35SAdrian Chadd error = ENOENT; 368bbf53c35SAdrian Chadd break; 369bbf53c35SAdrian Chadd } 370bbf53c35SAdrian Chadd 371bbf53c35SAdrian Chadd KASSERT(EVL_VLANOFTAG(hook_data) == vid, 372bbf53c35SAdrian Chadd ("%s: NGM_VLAN_DEL_VID_FLT:" 373bbf53c35SAdrian Chadd " Invalid VID Hook = %us, must be: %us\n", 374bbf53c35SAdrian Chadd __func__, (uint16_t )EVL_VLANOFTAG(hook_data), 375bbf53c35SAdrian Chadd vid)); 376bbf53c35SAdrian Chadd 377bbf53c35SAdrian Chadd /* Purge a rule that refers to this hook. */ 378bbf53c35SAdrian Chadd priv->vlan_hook[vid] = NULL; 379bbf53c35SAdrian Chadd NG_HOOK_SET_PRIVATE(hook, NULL); 380407ea290SRuslan Ermilov break; 381407ea290SRuslan Ermilov case NGM_VLAN_GET_TABLE: 382bbf53c35SAdrian Chadd /* Calculate vlans. */ 383bbf53c35SAdrian Chadd vlan_count = 0; 384bbf53c35SAdrian Chadd for (i = 0; i < (EVL_VLID_MASK + 1); i ++) { 385bbf53c35SAdrian Chadd if (priv->vlan_hook[i] != NULL && 386bbf53c35SAdrian Chadd NG_HOOK_IS_VALID(priv->vlan_hook[i])) 387bbf53c35SAdrian Chadd vlan_count ++; 388bbf53c35SAdrian Chadd } 389bbf53c35SAdrian Chadd 390053359b7SPedro F. Giffuni /* Allocate memory for response. */ 391407ea290SRuslan Ermilov NG_MKRESPONSE(resp, msg, sizeof(*t) + 392bbf53c35SAdrian Chadd vlan_count * sizeof(*t->filter), M_NOWAIT); 393407ea290SRuslan Ermilov if (resp == NULL) { 394407ea290SRuslan Ermilov error = ENOMEM; 395407ea290SRuslan Ermilov break; 396407ea290SRuslan Ermilov } 397bbf53c35SAdrian Chadd 398053359b7SPedro F. Giffuni /* Pack data to response. */ 399407ea290SRuslan Ermilov t = (struct ng_vlan_table *)resp->data; 400bbf53c35SAdrian Chadd t->n = 0; 401407ea290SRuslan Ermilov vf = &t->filter[0]; 402bbf53c35SAdrian Chadd for (i = 0; i < (EVL_VLID_MASK + 1); i ++) { 403bbf53c35SAdrian Chadd hook = priv->vlan_hook[i]; 404bbf53c35SAdrian Chadd if (hook == NULL || NG_HOOK_NOT_VALID(hook)) 405bbf53c35SAdrian Chadd continue; 406bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); 407bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) 408bbf53c35SAdrian Chadd continue; 409bbf53c35SAdrian Chadd 410bbf53c35SAdrian Chadd KASSERT(EVL_VLANOFTAG(hook_data) == i, 411bbf53c35SAdrian Chadd ("%s: NGM_VLAN_GET_TABLE:" 412bbf53c35SAdrian Chadd " hook %s VID = %us, must be: %i\n", 413bbf53c35SAdrian Chadd __func__, NG_HOOK_NAME(hook), 414bbf53c35SAdrian Chadd (uint16_t)EVL_VLANOFTAG(hook_data), i)); 415bbf53c35SAdrian Chadd 416bbf53c35SAdrian Chadd #ifdef NG_VLAN_USE_OLD_VLAN_NAME 417bbf53c35SAdrian Chadd vf->vlan = i; 418bbf53c35SAdrian Chadd #endif 419bbf53c35SAdrian Chadd vf->vid = i; 420bbf53c35SAdrian Chadd vf->pcp = EVL_PRIOFTAG(hook_data); 421bbf53c35SAdrian Chadd vf->cfi = EVL_CFIOFTAG(hook_data); 422bbf53c35SAdrian Chadd strncpy(vf->hook_name, 423bbf53c35SAdrian Chadd NG_HOOK_NAME(hook), NG_HOOKSIZ); 424407ea290SRuslan Ermilov vf ++; 425bbf53c35SAdrian Chadd t->n ++; 426407ea290SRuslan Ermilov } 427bbf53c35SAdrian Chadd break; 428bbf53c35SAdrian Chadd case NGM_VLAN_GET_DECAP: 429bbf53c35SAdrian Chadd NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); 430bbf53c35SAdrian Chadd if (resp == NULL) { 431bbf53c35SAdrian Chadd error = ENOMEM; 432bbf53c35SAdrian Chadd break; 433407ea290SRuslan Ermilov } 434bbf53c35SAdrian Chadd (*((uint32_t *)resp->data)) = priv->decap_enable; 435bbf53c35SAdrian Chadd break; 436bbf53c35SAdrian Chadd case NGM_VLAN_SET_DECAP: 437bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint32_t)) { 438bbf53c35SAdrian Chadd error = EINVAL; 439bbf53c35SAdrian Chadd break; 440bbf53c35SAdrian Chadd } 441bbf53c35SAdrian Chadd priv->decap_enable = (*((uint32_t *)msg->data)); 442bbf53c35SAdrian Chadd break; 443bbf53c35SAdrian Chadd case NGM_VLAN_GET_ENCAP: 444bbf53c35SAdrian Chadd NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); 445bbf53c35SAdrian Chadd if (resp == NULL) { 446bbf53c35SAdrian Chadd error = ENOMEM; 447bbf53c35SAdrian Chadd break; 448bbf53c35SAdrian Chadd } 449bbf53c35SAdrian Chadd (*((uint32_t *)resp->data)) = priv->encap_enable; 450bbf53c35SAdrian Chadd break; 451bbf53c35SAdrian Chadd case NGM_VLAN_SET_ENCAP: 452bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint32_t)) { 453bbf53c35SAdrian Chadd error = EINVAL; 454bbf53c35SAdrian Chadd break; 455bbf53c35SAdrian Chadd } 456bbf53c35SAdrian Chadd priv->encap_enable = (*((uint32_t *)msg->data)); 457bbf53c35SAdrian Chadd break; 458bbf53c35SAdrian Chadd case NGM_VLAN_GET_ENCAP_PROTO: 459bbf53c35SAdrian Chadd NG_MKRESPONSE(resp, msg, sizeof(uint16_t), M_NOWAIT); 460bbf53c35SAdrian Chadd if (resp == NULL) { 461bbf53c35SAdrian Chadd error = ENOMEM; 462bbf53c35SAdrian Chadd break; 463bbf53c35SAdrian Chadd } 464bbf53c35SAdrian Chadd (*((uint16_t *)resp->data)) = ntohs(priv->encap_proto); 465bbf53c35SAdrian Chadd break; 466bbf53c35SAdrian Chadd case NGM_VLAN_SET_ENCAP_PROTO: 467bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint16_t)) { 468bbf53c35SAdrian Chadd error = EINVAL; 469bbf53c35SAdrian Chadd break; 470bbf53c35SAdrian Chadd } 471bbf53c35SAdrian Chadd priv->encap_proto = htons((*((uint16_t *)msg->data))); 472407ea290SRuslan Ermilov break; 473407ea290SRuslan Ermilov default: /* Unknown command. */ 474407ea290SRuslan Ermilov error = EINVAL; 475407ea290SRuslan Ermilov break; 476407ea290SRuslan Ermilov } 477407ea290SRuslan Ermilov break; 4783b1c41c5SGleb Smirnoff case NGM_FLOW_COOKIE: 4793b1c41c5SGleb Smirnoff { 4803b1c41c5SGleb Smirnoff struct ng_mesg *copy; 4813b1c41c5SGleb Smirnoff 4823b1c41c5SGleb Smirnoff /* 4833b1c41c5SGleb Smirnoff * Flow control messages should come only 4843b1c41c5SGleb Smirnoff * from downstream. 4853b1c41c5SGleb Smirnoff */ 4863b1c41c5SGleb Smirnoff 4873b1c41c5SGleb Smirnoff if (lasthook == NULL) 4883b1c41c5SGleb Smirnoff break; 4893b1c41c5SGleb Smirnoff if (lasthook != priv->downstream_hook) 4903b1c41c5SGleb Smirnoff break; 4913b1c41c5SGleb Smirnoff /* Broadcast the event to all uplinks. */ 492bbf53c35SAdrian Chadd for (i = 0; i < (EVL_VLID_MASK + 1); i ++) { 493bbf53c35SAdrian Chadd if (priv->vlan_hook[i] == NULL) 494bbf53c35SAdrian Chadd continue; 495bbf53c35SAdrian Chadd 4963b1c41c5SGleb Smirnoff NG_COPYMESSAGE(copy, msg, M_NOWAIT); 4973b1c41c5SGleb Smirnoff if (copy == NULL) 4983b1c41c5SGleb Smirnoff continue; 499bbf53c35SAdrian Chadd NG_SEND_MSG_HOOK(error, node, copy, 500bbf53c35SAdrian Chadd priv->vlan_hook[i], 0); 5013b1c41c5SGleb Smirnoff } 5023b1c41c5SGleb Smirnoff break; 5033b1c41c5SGleb Smirnoff } 504407ea290SRuslan Ermilov default: /* Unknown type cookie. */ 505407ea290SRuslan Ermilov error = EINVAL; 506407ea290SRuslan Ermilov break; 507407ea290SRuslan Ermilov } 508407ea290SRuslan Ermilov NG_RESPOND_MSG(error, node, item, resp); 509407ea290SRuslan Ermilov NG_FREE_MSG(msg); 510407ea290SRuslan Ermilov return (error); 511407ea290SRuslan Ermilov } 512407ea290SRuslan Ermilov 513407ea290SRuslan Ermilov static int 514407ea290SRuslan Ermilov ng_vlan_rcvdata(hook_p hook, item_p item) 515407ea290SRuslan Ermilov { 516407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 517407ea290SRuslan Ermilov struct ether_header *eh; 518bbf53c35SAdrian Chadd struct ether_vlan_header *evl; 519407ea290SRuslan Ermilov int error; 520bbf53c35SAdrian Chadd uintptr_t hook_data; 521bbf53c35SAdrian Chadd uint16_t vid, eth_vtag; 522407ea290SRuslan Ermilov struct mbuf *m; 523bbf53c35SAdrian Chadd hook_p dst_hook; 524bbf53c35SAdrian Chadd 525bbf53c35SAdrian Chadd NGI_GET_M(item, m); 526407ea290SRuslan Ermilov 527407ea290SRuslan Ermilov /* Make sure we have an entire header. */ 528bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_HDR_LEN); 529bbf53c35SAdrian Chadd if (error != 0) 530bbf53c35SAdrian Chadd goto mchk_err; 531bbf53c35SAdrian Chadd 532407ea290SRuslan Ermilov eh = mtod(m, struct ether_header *); 533407ea290SRuslan Ermilov if (hook == priv->downstream_hook) { 534407ea290SRuslan Ermilov /* 535407ea290SRuslan Ermilov * If from downstream, select between a match hook 536407ea290SRuslan Ermilov * or the nomatch hook. 537407ea290SRuslan Ermilov */ 538bbf53c35SAdrian Chadd 539bbf53c35SAdrian Chadd dst_hook = priv->nomatch_hook; 540bbf53c35SAdrian Chadd 541bbf53c35SAdrian Chadd /* Skip packets without tag. */ 542bbf53c35SAdrian Chadd if ((m->m_flags & M_VLANTAG) == 0 && 543bbf53c35SAdrian Chadd eh->ether_type != priv->encap_proto) { 544bbf53c35SAdrian Chadd if (dst_hook == NULL) 545bbf53c35SAdrian Chadd goto net_down; 546bbf53c35SAdrian Chadd goto send_packet; 547bbf53c35SAdrian Chadd } 548bbf53c35SAdrian Chadd 549bbf53c35SAdrian Chadd /* Process packets with tag. */ 55078ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG) { 551407ea290SRuslan Ermilov /* 552407ea290SRuslan Ermilov * Packet is tagged, m contains a normal 553407ea290SRuslan Ermilov * Ethernet frame; tag is stored out-of-band. 554407ea290SRuslan Ermilov */ 555bbf53c35SAdrian Chadd evl = NULL; 556bbf53c35SAdrian Chadd vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 557bbf53c35SAdrian Chadd } else { /* eh->ether_type == priv->encap_proto */ 558bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_VLAN_HDR_LEN); 559bbf53c35SAdrian Chadd if (error != 0) 560bbf53c35SAdrian Chadd goto mchk_err; 561407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 562bbf53c35SAdrian Chadd vid = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 563407ea290SRuslan Ermilov } 564bbf53c35SAdrian Chadd 565bbf53c35SAdrian Chadd if (priv->vlan_hook[vid] != NULL) { 566bbf53c35SAdrian Chadd /* 567053359b7SPedro F. Giffuni * VLAN filter: always remove vlan tags and 568bbf53c35SAdrian Chadd * decapsulate packet. 569bbf53c35SAdrian Chadd */ 570bbf53c35SAdrian Chadd dst_hook = priv->vlan_hook[vid]; 571bbf53c35SAdrian Chadd if (evl == NULL) { /* m->m_flags & M_VLANTAG */ 57278ba57b9SAndre Oppermann m->m_pkthdr.ether_vtag = 0; 57378ba57b9SAndre Oppermann m->m_flags &= ~M_VLANTAG; 574bbf53c35SAdrian Chadd goto send_packet; 575bbf53c35SAdrian Chadd } 576bbf53c35SAdrian Chadd } else { /* nomatch_hook */ 577bbf53c35SAdrian Chadd if (dst_hook == NULL) 578bbf53c35SAdrian Chadd goto net_down; 579bbf53c35SAdrian Chadd if (evl == NULL || priv->decap_enable == 0) 580bbf53c35SAdrian Chadd goto send_packet; 581bbf53c35SAdrian Chadd /* Save tag out-of-band. */ 582bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); 583bbf53c35SAdrian Chadd m->m_flags |= M_VLANTAG; 584bbf53c35SAdrian Chadd } 585bbf53c35SAdrian Chadd 586bbf53c35SAdrian Chadd /* 587bbf53c35SAdrian Chadd * Decapsulate: 588bbf53c35SAdrian Chadd * TPID = ether type encap 589bbf53c35SAdrian Chadd * Move DstMAC and SrcMAC to ETHER_TYPE. 590bbf53c35SAdrian Chadd * Before: 591bbf53c35SAdrian Chadd * [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload] 592bbf53c35SAdrian Chadd * |-----------| >>>>>>>>>>>>>>>>>>>> |--------------------| 593bbf53c35SAdrian Chadd * After: 594bbf53c35SAdrian Chadd * [free space ] [dmac] [smac] [ether_type] [payload] 595bbf53c35SAdrian Chadd * |-----------| |--------------------| 596bbf53c35SAdrian Chadd */ 597bbf53c35SAdrian Chadd bcopy((char *)evl, ((char *)evl + ETHER_VLAN_ENCAP_LEN), 598bbf53c35SAdrian Chadd (ETHER_ADDR_LEN * 2)); 599407ea290SRuslan Ermilov m_adj(m, ETHER_VLAN_ENCAP_LEN); 600407ea290SRuslan Ermilov } else { 601407ea290SRuslan Ermilov /* 602407ea290SRuslan Ermilov * It is heading towards the downstream. 603407ea290SRuslan Ermilov * If from nomatch, pass it unmodified. 604407ea290SRuslan Ermilov * Otherwise, do the VLAN encapsulation. 605407ea290SRuslan Ermilov */ 606bbf53c35SAdrian Chadd dst_hook = priv->downstream_hook; 607bbf53c35SAdrian Chadd if (dst_hook == NULL) 608bbf53c35SAdrian Chadd goto net_down; 609bbf53c35SAdrian Chadd if (hook != priv->nomatch_hook) {/* Filter hook. */ 610bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); 611bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) { 612bbf53c35SAdrian Chadd /* 613bbf53c35SAdrian Chadd * Packet from hook not in filter 614bbf53c35SAdrian Chadd * call addfilter for this hook to fix. 615bbf53c35SAdrian Chadd */ 616bbf53c35SAdrian Chadd error = EOPNOTSUPP; 617bbf53c35SAdrian Chadd goto drop; 618407ea290SRuslan Ermilov } 619bbf53c35SAdrian Chadd eth_vtag = (hook_data & VLAN_TAG_MASK); 620bbf53c35SAdrian Chadd if ((priv->encap_enable & VLAN_ENCAP_FROM_FILTER) == 0) { 621bbf53c35SAdrian Chadd /* Just set packet header tag and send. */ 622bbf53c35SAdrian Chadd m->m_flags |= M_VLANTAG; 623bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = eth_vtag; 624bbf53c35SAdrian Chadd goto send_packet; 625407ea290SRuslan Ermilov } 626bbf53c35SAdrian Chadd } else { /* nomatch_hook */ 627bbf53c35SAdrian Chadd if ((priv->encap_enable & VLAN_ENCAP_FROM_NOMATCH) == 0 || 628bbf53c35SAdrian Chadd (m->m_flags & M_VLANTAG) == 0) 629bbf53c35SAdrian Chadd goto send_packet; 630bbf53c35SAdrian Chadd /* Encapsulate tagged packet. */ 631bbf53c35SAdrian Chadd eth_vtag = m->m_pkthdr.ether_vtag; 632bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = 0; 633bbf53c35SAdrian Chadd m->m_flags &= ~M_VLANTAG; 634bbf53c35SAdrian Chadd } 635bbf53c35SAdrian Chadd 636407ea290SRuslan Ermilov /* 637407ea290SRuslan Ermilov * Transform the Ethernet header into an Ethernet header 638407ea290SRuslan Ermilov * with 802.1Q encapsulation. 639bbf53c35SAdrian Chadd * Mod of: ether_vlanencap. 640bbf53c35SAdrian Chadd * 641bbf53c35SAdrian Chadd * TPID = ether type encap 642bbf53c35SAdrian Chadd * Move DstMAC and SrcMAC from ETHER_TYPE. 643bbf53c35SAdrian Chadd * Before: 644bbf53c35SAdrian Chadd * [free space ] [dmac] [smac] [ether_type] [payload] 645bbf53c35SAdrian Chadd * <<<<<<<<<<<<< |-----------| |--------------------| 646bbf53c35SAdrian Chadd * After: 647bbf53c35SAdrian Chadd * [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload] 648bbf53c35SAdrian Chadd * |-----------| |-- inserted tag --| |--------------------| 649407ea290SRuslan Ermilov */ 650eb1b1807SGleb Smirnoff M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT); 651bbf53c35SAdrian Chadd if (m == NULL) 652bbf53c35SAdrian Chadd error = ENOMEM; 653bbf53c35SAdrian Chadd else 654bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_VLAN_HDR_LEN); 655bbf53c35SAdrian Chadd if (error != 0) 656bbf53c35SAdrian Chadd goto mchk_err; 657bbf53c35SAdrian Chadd 658407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 659bbf53c35SAdrian Chadd bcopy(((char *)evl + ETHER_VLAN_ENCAP_LEN), 660bbf53c35SAdrian Chadd (char *)evl, (ETHER_ADDR_LEN * 2)); 661bbf53c35SAdrian Chadd evl->evl_encap_proto = priv->encap_proto; 662bbf53c35SAdrian Chadd evl->evl_tag = htons(eth_vtag); 663407ea290SRuslan Ermilov } 664bbf53c35SAdrian Chadd 665bbf53c35SAdrian Chadd send_packet: 666bbf53c35SAdrian Chadd NG_FWD_NEW_DATA(error, item, dst_hook, m); 667bbf53c35SAdrian Chadd return (error); 668bbf53c35SAdrian Chadd net_down: 669bbf53c35SAdrian Chadd error = ENETDOWN; 670bbf53c35SAdrian Chadd drop: 671bbf53c35SAdrian Chadd m_freem(m); 672bbf53c35SAdrian Chadd mchk_err: 673bbf53c35SAdrian Chadd NG_FREE_ITEM(item); 674407ea290SRuslan Ermilov return (error); 675407ea290SRuslan Ermilov } 676407ea290SRuslan Ermilov 677407ea290SRuslan Ermilov static int 678407ea290SRuslan Ermilov ng_vlan_shutdown(node_p node) 679407ea290SRuslan Ermilov { 680407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 681407ea290SRuslan Ermilov 682407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, NULL); 683407ea290SRuslan Ermilov NG_NODE_UNREF(node); 6841ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH); 685407ea290SRuslan Ermilov return (0); 686407ea290SRuslan Ermilov } 687407ea290SRuslan Ermilov 688407ea290SRuslan Ermilov static int 689407ea290SRuslan Ermilov ng_vlan_disconnect(hook_p hook) 690407ea290SRuslan Ermilov { 691407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 692bbf53c35SAdrian Chadd uintptr_t hook_data; 693407ea290SRuslan Ermilov 694407ea290SRuslan Ermilov if (hook == priv->downstream_hook) 695407ea290SRuslan Ermilov priv->downstream_hook = NULL; 696407ea290SRuslan Ermilov else if (hook == priv->nomatch_hook) 697407ea290SRuslan Ermilov priv->nomatch_hook = NULL; 698407ea290SRuslan Ermilov else { 699407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 700bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); 701bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data)) 702bbf53c35SAdrian Chadd priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL; 703407ea290SRuslan Ermilov } 704407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 705407ea290SRuslan Ermilov if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 706407ea290SRuslan Ermilov (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 707407ea290SRuslan Ermilov ng_rmnode_self(NG_HOOK_NODE(hook)); 708407ea290SRuslan Ermilov return (0); 709407ea290SRuslan Ermilov } 710