1407ea290SRuslan Ermilov /*- 2407ea290SRuslan Ermilov * Copyright (c) 2003 IPNET Internet Communication Company 3bbf53c35SAdrian Chadd * Copyright (c) 2011 - 2012 Rozhuk Ivan <rozhuk.im@gmail.com> 4407ea290SRuslan Ermilov * All rights reserved. 5407ea290SRuslan Ermilov * 6407ea290SRuslan Ermilov * Redistribution and use in source and binary forms, with or without 7407ea290SRuslan Ermilov * modification, are permitted provided that the following conditions 8407ea290SRuslan Ermilov * are met: 9407ea290SRuslan Ermilov * 1. Redistributions of source code must retain the above copyright 10407ea290SRuslan Ermilov * notice, this list of conditions and the following disclaimer. 11407ea290SRuslan Ermilov * 2. Redistributions in binary form must reproduce the above copyright 12407ea290SRuslan Ermilov * notice, this list of conditions and the following disclaimer in the 13407ea290SRuslan Ermilov * documentation and/or other materials provided with the distribution. 14407ea290SRuslan Ermilov * 15407ea290SRuslan Ermilov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16407ea290SRuslan Ermilov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17407ea290SRuslan Ermilov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18407ea290SRuslan Ermilov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19407ea290SRuslan Ermilov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20407ea290SRuslan Ermilov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21407ea290SRuslan Ermilov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22407ea290SRuslan Ermilov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23407ea290SRuslan Ermilov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24407ea290SRuslan Ermilov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25407ea290SRuslan Ermilov * SUCH DAMAGE. 26407ea290SRuslan Ermilov * 27407ea290SRuslan Ermilov * Author: Ruslan Ermilov <ru@FreeBSD.org> 28407ea290SRuslan Ermilov * 29407ea290SRuslan Ermilov * $FreeBSD$ 30407ea290SRuslan Ermilov */ 31407ea290SRuslan Ermilov 32407ea290SRuslan Ermilov #include <sys/param.h> 33407ea290SRuslan Ermilov #include <sys/errno.h> 34407ea290SRuslan Ermilov #include <sys/kernel.h> 35407ea290SRuslan Ermilov #include <sys/malloc.h> 36407ea290SRuslan Ermilov #include <sys/mbuf.h> 37407ea290SRuslan Ermilov #include <sys/queue.h> 38407ea290SRuslan Ermilov #include <sys/socket.h> 39407ea290SRuslan Ermilov #include <sys/systm.h> 40407ea290SRuslan Ermilov 41407ea290SRuslan Ermilov #include <net/ethernet.h> 42407ea290SRuslan Ermilov #include <net/if.h> 43407ea290SRuslan Ermilov #include <net/if_vlan_var.h> 44407ea290SRuslan Ermilov 45407ea290SRuslan Ermilov #include <netgraph/ng_message.h> 46407ea290SRuslan Ermilov #include <netgraph/ng_parse.h> 47407ea290SRuslan Ermilov #include <netgraph/ng_vlan.h> 48407ea290SRuslan Ermilov #include <netgraph/netgraph.h> 49407ea290SRuslan Ermilov 50bbf53c35SAdrian Chadd struct ng_vlan_private { 51bbf53c35SAdrian Chadd hook_p downstream_hook; 52bbf53c35SAdrian Chadd hook_p nomatch_hook; 53bbf53c35SAdrian Chadd uint32_t decap_enable; 54bbf53c35SAdrian Chadd uint32_t encap_enable; 55bbf53c35SAdrian Chadd uint16_t encap_proto; 56bbf53c35SAdrian Chadd hook_p vlan_hook[(EVL_VLID_MASK + 1)]; 57bbf53c35SAdrian Chadd }; 58bbf53c35SAdrian Chadd typedef struct ng_vlan_private *priv_p; 59bbf53c35SAdrian Chadd 60bbf53c35SAdrian Chadd #define ETHER_VLAN_HDR_LEN (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN) 61bbf53c35SAdrian Chadd #define VLAN_TAG_MASK 0xFFFF 62bbf53c35SAdrian Chadd #define HOOK_VLAN_TAG_SET_MASK ((uintptr_t)((~0) & ~(VLAN_TAG_MASK))) 63bbf53c35SAdrian Chadd #define IS_HOOK_VLAN_SET(hdata) \ 64bbf53c35SAdrian Chadd ((((uintptr_t)hdata) & HOOK_VLAN_TAG_SET_MASK) == HOOK_VLAN_TAG_SET_MASK) 65bbf53c35SAdrian Chadd 66407ea290SRuslan Ermilov static ng_constructor_t ng_vlan_constructor; 67407ea290SRuslan Ermilov static ng_rcvmsg_t ng_vlan_rcvmsg; 68407ea290SRuslan Ermilov static ng_shutdown_t ng_vlan_shutdown; 69407ea290SRuslan Ermilov static ng_newhook_t ng_vlan_newhook; 70407ea290SRuslan Ermilov static ng_rcvdata_t ng_vlan_rcvdata; 71407ea290SRuslan Ermilov static ng_disconnect_t ng_vlan_disconnect; 72407ea290SRuslan Ermilov 73407ea290SRuslan Ermilov /* Parse type for struct ng_vlan_filter. */ 74407ea290SRuslan Ermilov static const struct ng_parse_struct_field ng_vlan_filter_fields[] = 75407ea290SRuslan Ermilov NG_VLAN_FILTER_FIELDS; 76407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_filter_type = { 77407ea290SRuslan Ermilov &ng_parse_struct_type, 78407ea290SRuslan Ermilov &ng_vlan_filter_fields 79407ea290SRuslan Ermilov }; 80407ea290SRuslan Ermilov 81407ea290SRuslan Ermilov static int 82407ea290SRuslan Ermilov ng_vlan_getTableLength(const struct ng_parse_type *type, 83407ea290SRuslan Ermilov const u_char *start, const u_char *buf) 84407ea290SRuslan Ermilov { 85407ea290SRuslan Ermilov const struct ng_vlan_table *const table = 86407ea290SRuslan Ermilov (const struct ng_vlan_table *)(buf - sizeof(u_int32_t)); 87407ea290SRuslan Ermilov 88407ea290SRuslan Ermilov return table->n; 89407ea290SRuslan Ermilov } 90407ea290SRuslan Ermilov 91407ea290SRuslan Ermilov /* Parse type for struct ng_vlan_table. */ 92407ea290SRuslan Ermilov static const struct ng_parse_array_info ng_vlan_table_array_info = { 93407ea290SRuslan Ermilov &ng_vlan_filter_type, 94407ea290SRuslan Ermilov ng_vlan_getTableLength 95407ea290SRuslan Ermilov }; 96407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_table_array_type = { 97407ea290SRuslan Ermilov &ng_parse_array_type, 98407ea290SRuslan Ermilov &ng_vlan_table_array_info 99407ea290SRuslan Ermilov }; 100407ea290SRuslan Ermilov static const struct ng_parse_struct_field ng_vlan_table_fields[] = 101407ea290SRuslan Ermilov NG_VLAN_TABLE_FIELDS; 102407ea290SRuslan Ermilov static const struct ng_parse_type ng_vlan_table_type = { 103407ea290SRuslan Ermilov &ng_parse_struct_type, 104407ea290SRuslan Ermilov &ng_vlan_table_fields 105407ea290SRuslan Ermilov }; 106407ea290SRuslan Ermilov 107407ea290SRuslan Ermilov /* List of commands and how to convert arguments to/from ASCII. */ 108407ea290SRuslan Ermilov static const struct ng_cmdlist ng_vlan_cmdlist[] = { 109407ea290SRuslan Ermilov { 110407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 111407ea290SRuslan Ermilov NGM_VLAN_ADD_FILTER, 112407ea290SRuslan Ermilov "addfilter", 113407ea290SRuslan Ermilov &ng_vlan_filter_type, 114407ea290SRuslan Ermilov NULL 115407ea290SRuslan Ermilov }, 116407ea290SRuslan Ermilov { 117407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 118407ea290SRuslan Ermilov NGM_VLAN_DEL_FILTER, 119407ea290SRuslan Ermilov "delfilter", 120407ea290SRuslan Ermilov &ng_parse_hookbuf_type, 121407ea290SRuslan Ermilov NULL 122407ea290SRuslan Ermilov }, 123407ea290SRuslan Ermilov { 124407ea290SRuslan Ermilov NGM_VLAN_COOKIE, 125407ea290SRuslan Ermilov NGM_VLAN_GET_TABLE, 126407ea290SRuslan Ermilov "gettable", 127407ea290SRuslan Ermilov NULL, 128407ea290SRuslan Ermilov &ng_vlan_table_type 129407ea290SRuslan Ermilov }, 130bbf53c35SAdrian Chadd { 131bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 132bbf53c35SAdrian Chadd NGM_VLAN_DEL_VID_FLT, 133bbf53c35SAdrian Chadd "delvidflt", 134bbf53c35SAdrian Chadd &ng_parse_uint16_type, 135bbf53c35SAdrian Chadd NULL 136bbf53c35SAdrian Chadd }, 137bbf53c35SAdrian Chadd { 138bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 139bbf53c35SAdrian Chadd NGM_VLAN_GET_DECAP, 140bbf53c35SAdrian Chadd "getdecap", 141bbf53c35SAdrian Chadd NULL, 142bbf53c35SAdrian Chadd &ng_parse_hint32_type 143bbf53c35SAdrian Chadd }, 144bbf53c35SAdrian Chadd { 145bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 146bbf53c35SAdrian Chadd NGM_VLAN_SET_DECAP, 147bbf53c35SAdrian Chadd "setdecap", 148bbf53c35SAdrian Chadd &ng_parse_hint32_type, 149bbf53c35SAdrian Chadd NULL 150bbf53c35SAdrian Chadd }, 151bbf53c35SAdrian Chadd { 152bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 153bbf53c35SAdrian Chadd NGM_VLAN_GET_ENCAP, 154bbf53c35SAdrian Chadd "getencap", 155bbf53c35SAdrian Chadd NULL, 156bbf53c35SAdrian Chadd &ng_parse_hint32_type 157bbf53c35SAdrian Chadd }, 158bbf53c35SAdrian Chadd { 159bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 160bbf53c35SAdrian Chadd NGM_VLAN_SET_ENCAP, 161bbf53c35SAdrian Chadd "setencap", 162bbf53c35SAdrian Chadd &ng_parse_hint32_type, 163bbf53c35SAdrian Chadd NULL 164bbf53c35SAdrian Chadd }, 165bbf53c35SAdrian Chadd { 166bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 167bbf53c35SAdrian Chadd NGM_VLAN_GET_ENCAP_PROTO, 168bbf53c35SAdrian Chadd "getencapproto", 169bbf53c35SAdrian Chadd NULL, 170bbf53c35SAdrian Chadd &ng_parse_hint16_type 171bbf53c35SAdrian Chadd }, 172bbf53c35SAdrian Chadd { 173bbf53c35SAdrian Chadd NGM_VLAN_COOKIE, 174bbf53c35SAdrian Chadd NGM_VLAN_SET_ENCAP_PROTO, 175bbf53c35SAdrian Chadd "setencapproto", 176bbf53c35SAdrian Chadd &ng_parse_hint16_type, 177bbf53c35SAdrian Chadd NULL 178bbf53c35SAdrian Chadd }, 179407ea290SRuslan Ermilov { 0 } 180407ea290SRuslan Ermilov }; 181407ea290SRuslan Ermilov 182407ea290SRuslan Ermilov static struct ng_type ng_vlan_typestruct = { 183f8aae777SJulian Elischer .version = NG_ABI_VERSION, 184f8aae777SJulian Elischer .name = NG_VLAN_NODE_TYPE, 185f8aae777SJulian Elischer .constructor = ng_vlan_constructor, 186f8aae777SJulian Elischer .rcvmsg = ng_vlan_rcvmsg, 187f8aae777SJulian Elischer .shutdown = ng_vlan_shutdown, 188f8aae777SJulian Elischer .newhook = ng_vlan_newhook, 189f8aae777SJulian Elischer .rcvdata = ng_vlan_rcvdata, 190f8aae777SJulian Elischer .disconnect = ng_vlan_disconnect, 191f8aae777SJulian Elischer .cmdlist = ng_vlan_cmdlist, 192407ea290SRuslan Ermilov }; 193407ea290SRuslan Ermilov NETGRAPH_INIT(vlan, &ng_vlan_typestruct); 194407ea290SRuslan Ermilov 195407ea290SRuslan Ermilov 196bbf53c35SAdrian Chadd /* 197bbf53c35SAdrian Chadd * Helper functions. 198bbf53c35SAdrian Chadd */ 199407ea290SRuslan Ermilov 200bbf53c35SAdrian Chadd static __inline int 201bbf53c35SAdrian Chadd m_chk(struct mbuf **mp, int len) 202407ea290SRuslan Ermilov { 203407ea290SRuslan Ermilov 204bbf53c35SAdrian Chadd if ((*mp)->m_pkthdr.len < len) { 205bbf53c35SAdrian Chadd m_freem((*mp)); 206bbf53c35SAdrian Chadd (*mp) = NULL; 207bbf53c35SAdrian Chadd return (EINVAL); 208407ea290SRuslan Ermilov } 209bbf53c35SAdrian Chadd if ((*mp)->m_len < len && ((*mp) = m_pullup((*mp), len)) == NULL) 210bbf53c35SAdrian Chadd return (ENOBUFS); 211bbf53c35SAdrian Chadd 212bbf53c35SAdrian Chadd return (0); 213bbf53c35SAdrian Chadd } 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 390*053359b7SPedro 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 398*053359b7SPedro 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 526bbf53c35SAdrian Chadd NGI_GET_M(item, m); 527407ea290SRuslan Ermilov 528407ea290SRuslan Ermilov /* Make sure we have an entire header. */ 529bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_HDR_LEN); 530bbf53c35SAdrian Chadd if (error != 0) 531bbf53c35SAdrian Chadd goto mchk_err; 532bbf53c35SAdrian Chadd 533407ea290SRuslan Ermilov eh = mtod(m, struct ether_header *); 534407ea290SRuslan Ermilov if (hook == priv->downstream_hook) { 535407ea290SRuslan Ermilov /* 536407ea290SRuslan Ermilov * If from downstream, select between a match hook 537407ea290SRuslan Ermilov * or the nomatch hook. 538407ea290SRuslan Ermilov */ 539bbf53c35SAdrian Chadd 540bbf53c35SAdrian Chadd dst_hook = priv->nomatch_hook; 541bbf53c35SAdrian Chadd 542bbf53c35SAdrian Chadd /* Skip packets without tag. */ 543bbf53c35SAdrian Chadd if ((m->m_flags & M_VLANTAG) == 0 && 544bbf53c35SAdrian Chadd eh->ether_type != priv->encap_proto) { 545bbf53c35SAdrian Chadd if (dst_hook == NULL) 546bbf53c35SAdrian Chadd goto net_down; 547bbf53c35SAdrian Chadd goto send_packet; 548bbf53c35SAdrian Chadd } 549bbf53c35SAdrian Chadd 550bbf53c35SAdrian Chadd /* Process packets with tag. */ 55178ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG) { 552407ea290SRuslan Ermilov /* 553407ea290SRuslan Ermilov * Packet is tagged, m contains a normal 554407ea290SRuslan Ermilov * Ethernet frame; tag is stored out-of-band. 555407ea290SRuslan Ermilov */ 556bbf53c35SAdrian Chadd evl = NULL; 557bbf53c35SAdrian Chadd vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 558bbf53c35SAdrian Chadd } else { /* eh->ether_type == priv->encap_proto */ 559bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_VLAN_HDR_LEN); 560bbf53c35SAdrian Chadd if (error != 0) 561bbf53c35SAdrian Chadd goto mchk_err; 562407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 563bbf53c35SAdrian Chadd vid = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 564407ea290SRuslan Ermilov } 565bbf53c35SAdrian Chadd 566bbf53c35SAdrian Chadd if (priv->vlan_hook[vid] != NULL) { 567bbf53c35SAdrian Chadd /* 568*053359b7SPedro F. Giffuni * VLAN filter: always remove vlan tags and 569bbf53c35SAdrian Chadd * decapsulate packet. 570bbf53c35SAdrian Chadd */ 571bbf53c35SAdrian Chadd dst_hook = priv->vlan_hook[vid]; 572bbf53c35SAdrian Chadd if (evl == NULL) { /* m->m_flags & M_VLANTAG */ 57378ba57b9SAndre Oppermann m->m_pkthdr.ether_vtag = 0; 57478ba57b9SAndre Oppermann m->m_flags &= ~M_VLANTAG; 575bbf53c35SAdrian Chadd goto send_packet; 576bbf53c35SAdrian Chadd } 577bbf53c35SAdrian Chadd } else { /* nomatch_hook */ 578bbf53c35SAdrian Chadd if (dst_hook == NULL) 579bbf53c35SAdrian Chadd goto net_down; 580bbf53c35SAdrian Chadd if (evl == NULL || priv->decap_enable == 0) 581bbf53c35SAdrian Chadd goto send_packet; 582bbf53c35SAdrian Chadd /* Save tag out-of-band. */ 583bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag); 584bbf53c35SAdrian Chadd m->m_flags |= M_VLANTAG; 585bbf53c35SAdrian Chadd } 586bbf53c35SAdrian Chadd 587bbf53c35SAdrian Chadd /* 588bbf53c35SAdrian Chadd * Decapsulate: 589bbf53c35SAdrian Chadd * TPID = ether type encap 590bbf53c35SAdrian Chadd * Move DstMAC and SrcMAC to ETHER_TYPE. 591bbf53c35SAdrian Chadd * Before: 592bbf53c35SAdrian Chadd * [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload] 593bbf53c35SAdrian Chadd * |-----------| >>>>>>>>>>>>>>>>>>>> |--------------------| 594bbf53c35SAdrian Chadd * After: 595bbf53c35SAdrian Chadd * [free space ] [dmac] [smac] [ether_type] [payload] 596bbf53c35SAdrian Chadd * |-----------| |--------------------| 597bbf53c35SAdrian Chadd */ 598bbf53c35SAdrian Chadd bcopy((char *)evl, ((char *)evl + ETHER_VLAN_ENCAP_LEN), 599bbf53c35SAdrian Chadd (ETHER_ADDR_LEN * 2)); 600407ea290SRuslan Ermilov m_adj(m, ETHER_VLAN_ENCAP_LEN); 601407ea290SRuslan Ermilov } else { 602407ea290SRuslan Ermilov /* 603407ea290SRuslan Ermilov * It is heading towards the downstream. 604407ea290SRuslan Ermilov * If from nomatch, pass it unmodified. 605407ea290SRuslan Ermilov * Otherwise, do the VLAN encapsulation. 606407ea290SRuslan Ermilov */ 607bbf53c35SAdrian Chadd dst_hook = priv->downstream_hook; 608bbf53c35SAdrian Chadd if (dst_hook == NULL) 609bbf53c35SAdrian Chadd goto net_down; 610bbf53c35SAdrian Chadd if (hook != priv->nomatch_hook) {/* Filter hook. */ 611bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); 612bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) { 613bbf53c35SAdrian Chadd /* 614bbf53c35SAdrian Chadd * Packet from hook not in filter 615bbf53c35SAdrian Chadd * call addfilter for this hook to fix. 616bbf53c35SAdrian Chadd */ 617bbf53c35SAdrian Chadd error = EOPNOTSUPP; 618bbf53c35SAdrian Chadd goto drop; 619407ea290SRuslan Ermilov } 620bbf53c35SAdrian Chadd eth_vtag = (hook_data & VLAN_TAG_MASK); 621bbf53c35SAdrian Chadd if ((priv->encap_enable & VLAN_ENCAP_FROM_FILTER) == 0) { 622bbf53c35SAdrian Chadd /* Just set packet header tag and send. */ 623bbf53c35SAdrian Chadd m->m_flags |= M_VLANTAG; 624bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = eth_vtag; 625bbf53c35SAdrian Chadd goto send_packet; 626407ea290SRuslan Ermilov } 627bbf53c35SAdrian Chadd } else { /* nomatch_hook */ 628bbf53c35SAdrian Chadd if ((priv->encap_enable & VLAN_ENCAP_FROM_NOMATCH) == 0 || 629bbf53c35SAdrian Chadd (m->m_flags & M_VLANTAG) == 0) 630bbf53c35SAdrian Chadd goto send_packet; 631bbf53c35SAdrian Chadd /* Encapsulate tagged packet. */ 632bbf53c35SAdrian Chadd eth_vtag = m->m_pkthdr.ether_vtag; 633bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = 0; 634bbf53c35SAdrian Chadd m->m_flags &= ~M_VLANTAG; 635bbf53c35SAdrian Chadd } 636bbf53c35SAdrian Chadd 637407ea290SRuslan Ermilov /* 638407ea290SRuslan Ermilov * Transform the Ethernet header into an Ethernet header 639407ea290SRuslan Ermilov * with 802.1Q encapsulation. 640bbf53c35SAdrian Chadd * Mod of: ether_vlanencap. 641bbf53c35SAdrian Chadd * 642bbf53c35SAdrian Chadd * TPID = ether type encap 643bbf53c35SAdrian Chadd * Move DstMAC and SrcMAC from ETHER_TYPE. 644bbf53c35SAdrian Chadd * Before: 645bbf53c35SAdrian Chadd * [free space ] [dmac] [smac] [ether_type] [payload] 646bbf53c35SAdrian Chadd * <<<<<<<<<<<<< |-----------| |--------------------| 647bbf53c35SAdrian Chadd * After: 648bbf53c35SAdrian Chadd * [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload] 649bbf53c35SAdrian Chadd * |-----------| |-- inserted tag --| |--------------------| 650407ea290SRuslan Ermilov */ 651eb1b1807SGleb Smirnoff M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT); 652bbf53c35SAdrian Chadd if (m == NULL) 653bbf53c35SAdrian Chadd error = ENOMEM; 654bbf53c35SAdrian Chadd else 655bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_VLAN_HDR_LEN); 656bbf53c35SAdrian Chadd if (error != 0) 657bbf53c35SAdrian Chadd goto mchk_err; 658bbf53c35SAdrian Chadd 659407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *); 660bbf53c35SAdrian Chadd bcopy(((char *)evl + ETHER_VLAN_ENCAP_LEN), 661bbf53c35SAdrian Chadd (char *)evl, (ETHER_ADDR_LEN * 2)); 662bbf53c35SAdrian Chadd evl->evl_encap_proto = priv->encap_proto; 663bbf53c35SAdrian Chadd evl->evl_tag = htons(eth_vtag); 664407ea290SRuslan Ermilov } 665bbf53c35SAdrian Chadd 666bbf53c35SAdrian Chadd send_packet: 667bbf53c35SAdrian Chadd NG_FWD_NEW_DATA(error, item, dst_hook, m); 668bbf53c35SAdrian Chadd return (error); 669bbf53c35SAdrian Chadd net_down: 670bbf53c35SAdrian Chadd error = ENETDOWN; 671bbf53c35SAdrian Chadd drop: 672bbf53c35SAdrian Chadd m_freem(m); 673bbf53c35SAdrian Chadd mchk_err: 674bbf53c35SAdrian Chadd NG_FREE_ITEM(item); 675407ea290SRuslan Ermilov return (error); 676407ea290SRuslan Ermilov } 677407ea290SRuslan Ermilov 678407ea290SRuslan Ermilov static int 679407ea290SRuslan Ermilov ng_vlan_shutdown(node_p node) 680407ea290SRuslan Ermilov { 681407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node); 682407ea290SRuslan Ermilov 683407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, NULL); 684407ea290SRuslan Ermilov NG_NODE_UNREF(node); 6851ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH); 686407ea290SRuslan Ermilov return (0); 687407ea290SRuslan Ermilov } 688407ea290SRuslan Ermilov 689407ea290SRuslan Ermilov static int 690407ea290SRuslan Ermilov ng_vlan_disconnect(hook_p hook) 691407ea290SRuslan Ermilov { 692407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 693bbf53c35SAdrian Chadd uintptr_t hook_data; 694407ea290SRuslan Ermilov 695407ea290SRuslan Ermilov if (hook == priv->downstream_hook) 696407ea290SRuslan Ermilov priv->downstream_hook = NULL; 697407ea290SRuslan Ermilov else if (hook == priv->nomatch_hook) 698407ea290SRuslan Ermilov priv->nomatch_hook = NULL; 699407ea290SRuslan Ermilov else { 700407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */ 701bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook); 702bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data)) 703bbf53c35SAdrian Chadd priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL; 704407ea290SRuslan Ermilov } 705407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL); 706407ea290SRuslan Ermilov if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && 707407ea290SRuslan Ermilov (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 708407ea290SRuslan Ermilov ng_rmnode_self(NG_HOOK_NODE(hook)); 709407ea290SRuslan Ermilov return (0); 710407ea290SRuslan Ermilov } 711