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
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
ng_vlan_getTableLength(const struct ng_parse_type * type,const u_char * start,const u_char * buf)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
195bbf53c35SAdrian Chadd /*
196bbf53c35SAdrian Chadd * Helper functions.
197bbf53c35SAdrian Chadd */
198407ea290SRuslan Ermilov
199bbf53c35SAdrian Chadd static __inline int
m_chk(struct mbuf ** mp,int len)200bbf53c35SAdrian Chadd m_chk(struct mbuf **mp, int len)
201407ea290SRuslan Ermilov {
202407ea290SRuslan Ermilov
203bbf53c35SAdrian Chadd if ((*mp)->m_pkthdr.len < len) {
204bbf53c35SAdrian Chadd m_freem((*mp));
205bbf53c35SAdrian Chadd (*mp) = NULL;
206bbf53c35SAdrian Chadd return (EINVAL);
207407ea290SRuslan Ermilov }
208bbf53c35SAdrian Chadd if ((*mp)->m_len < len && ((*mp) = m_pullup((*mp), len)) == NULL)
209bbf53c35SAdrian Chadd return (ENOBUFS);
210bbf53c35SAdrian Chadd
211bbf53c35SAdrian Chadd return (0);
212bbf53c35SAdrian Chadd }
213bbf53c35SAdrian Chadd
214bbf53c35SAdrian Chadd /*
215bbf53c35SAdrian Chadd * Netgraph node functions.
216bbf53c35SAdrian Chadd */
217407ea290SRuslan Ermilov
218407ea290SRuslan Ermilov static int
ng_vlan_constructor(node_p node)219407ea290SRuslan Ermilov ng_vlan_constructor(node_p node)
220407ea290SRuslan Ermilov {
221407ea290SRuslan Ermilov priv_p priv;
222407ea290SRuslan Ermilov
223674d86bfSGleb Smirnoff priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK | M_ZERO);
224bbf53c35SAdrian Chadd priv->decap_enable = 0;
225bbf53c35SAdrian Chadd priv->encap_enable = VLAN_ENCAP_FROM_FILTER;
226bbf53c35SAdrian Chadd priv->encap_proto = htons(ETHERTYPE_VLAN);
227407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, priv);
228407ea290SRuslan Ermilov return (0);
229407ea290SRuslan Ermilov }
230407ea290SRuslan Ermilov
231407ea290SRuslan Ermilov static int
ng_vlan_newhook(node_p node,hook_p hook,const char * name)232407ea290SRuslan Ermilov ng_vlan_newhook(node_p node, hook_p hook, const char *name)
233407ea290SRuslan Ermilov {
234407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node);
235407ea290SRuslan Ermilov
236407ea290SRuslan Ermilov if (strcmp(name, NG_VLAN_HOOK_DOWNSTREAM) == 0)
237407ea290SRuslan Ermilov priv->downstream_hook = hook;
238407ea290SRuslan Ermilov else if (strcmp(name, NG_VLAN_HOOK_NOMATCH) == 0)
239407ea290SRuslan Ermilov priv->nomatch_hook = hook;
240407ea290SRuslan Ermilov else {
241407ea290SRuslan Ermilov /*
242407ea290SRuslan Ermilov * Any other hook name is valid and can
243407ea290SRuslan Ermilov * later be associated with a filter rule.
244407ea290SRuslan Ermilov */
245407ea290SRuslan Ermilov }
246407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL);
247407ea290SRuslan Ermilov return (0);
248407ea290SRuslan Ermilov }
249407ea290SRuslan Ermilov
250407ea290SRuslan Ermilov static int
ng_vlan_rcvmsg(node_p node,item_p item,hook_p lasthook)251407ea290SRuslan Ermilov ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook)
252407ea290SRuslan Ermilov {
253407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node);
254407ea290SRuslan Ermilov struct ng_mesg *msg, *resp = NULL;
255407ea290SRuslan Ermilov struct ng_vlan_filter *vf;
256407ea290SRuslan Ermilov hook_p hook;
257407ea290SRuslan Ermilov struct ng_vlan_table *t;
258bbf53c35SAdrian Chadd uintptr_t hook_data;
259bbf53c35SAdrian Chadd int i, vlan_count;
260bbf53c35SAdrian Chadd uint16_t vid;
261bbf53c35SAdrian Chadd int error = 0;
262407ea290SRuslan Ermilov
263407ea290SRuslan Ermilov NGI_GET_MSG(item, msg);
264407ea290SRuslan Ermilov /* Deal with message according to cookie and command. */
265407ea290SRuslan Ermilov switch (msg->header.typecookie) {
266407ea290SRuslan Ermilov case NGM_VLAN_COOKIE:
267407ea290SRuslan Ermilov switch (msg->header.cmd) {
268407ea290SRuslan Ermilov case NGM_VLAN_ADD_FILTER:
269407ea290SRuslan Ermilov /* Check that message is long enough. */
270407ea290SRuslan Ermilov if (msg->header.arglen != sizeof(*vf)) {
271407ea290SRuslan Ermilov error = EINVAL;
272407ea290SRuslan Ermilov break;
273407ea290SRuslan Ermilov }
274407ea290SRuslan Ermilov vf = (struct ng_vlan_filter *)msg->data;
275407ea290SRuslan Ermilov /* Sanity check the VLAN ID value. */
276bbf53c35SAdrian Chadd #ifdef NG_VLAN_USE_OLD_VLAN_NAME
277bbf53c35SAdrian Chadd if (vf->vid == 0 && vf->vid != vf->vlan) {
278bbf53c35SAdrian Chadd vf->vid = vf->vlan;
279bbf53c35SAdrian Chadd } else if (vf->vid != 0 && vf->vlan != 0 &&
280bbf53c35SAdrian Chadd vf->vid != vf->vlan) {
281bbf53c35SAdrian Chadd error = EINVAL;
282bbf53c35SAdrian Chadd break;
283bbf53c35SAdrian Chadd }
284bbf53c35SAdrian Chadd #endif
285bbf53c35SAdrian Chadd if (vf->vid & ~EVL_VLID_MASK ||
286bbf53c35SAdrian Chadd vf->pcp & ~7 ||
287bbf53c35SAdrian Chadd vf->cfi & ~1) {
288407ea290SRuslan Ermilov error = EINVAL;
289407ea290SRuslan Ermilov break;
290407ea290SRuslan Ermilov }
291407ea290SRuslan Ermilov /* Check that a referenced hook exists. */
292bbf53c35SAdrian Chadd hook = ng_findhook(node, vf->hook_name);
293407ea290SRuslan Ermilov if (hook == NULL) {
294407ea290SRuslan Ermilov error = ENOENT;
295407ea290SRuslan Ermilov break;
296407ea290SRuslan Ermilov }
297407ea290SRuslan Ermilov /* And is not one of the special hooks. */
298407ea290SRuslan Ermilov if (hook == priv->downstream_hook ||
299407ea290SRuslan Ermilov hook == priv->nomatch_hook) {
300407ea290SRuslan Ermilov error = EINVAL;
301407ea290SRuslan Ermilov break;
302407ea290SRuslan Ermilov }
303407ea290SRuslan Ermilov /* And is not already in service. */
304bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) {
305407ea290SRuslan Ermilov error = EEXIST;
306407ea290SRuslan Ermilov break;
307407ea290SRuslan Ermilov }
308407ea290SRuslan Ermilov /* Check we don't already trap this VLAN. */
309bbf53c35SAdrian Chadd if (priv->vlan_hook[vf->vid] != NULL) {
310407ea290SRuslan Ermilov error = EEXIST;
311407ea290SRuslan Ermilov break;
312407ea290SRuslan Ermilov }
313bbf53c35SAdrian Chadd /* Link vlan and hook together. */
314bbf53c35SAdrian Chadd NG_HOOK_SET_PRIVATE(hook,
315bbf53c35SAdrian Chadd (void *)(HOOK_VLAN_TAG_SET_MASK |
316bbf53c35SAdrian Chadd EVL_MAKETAG(vf->vid, vf->pcp, vf->cfi)));
317bbf53c35SAdrian Chadd priv->vlan_hook[vf->vid] = hook;
318407ea290SRuslan Ermilov break;
319407ea290SRuslan Ermilov case NGM_VLAN_DEL_FILTER:
320407ea290SRuslan Ermilov /* Check that message is long enough. */
321407ea290SRuslan Ermilov if (msg->header.arglen != NG_HOOKSIZ) {
322407ea290SRuslan Ermilov error = EINVAL;
323407ea290SRuslan Ermilov break;
324407ea290SRuslan Ermilov }
325407ea290SRuslan Ermilov /* Check that hook exists and is active. */
326407ea290SRuslan Ermilov hook = ng_findhook(node, (char *)msg->data);
327bbf53c35SAdrian Chadd if (hook == NULL) {
328407ea290SRuslan Ermilov error = ENOENT;
329407ea290SRuslan Ermilov break;
330407ea290SRuslan Ermilov }
331bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
332bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) {
333bbf53c35SAdrian Chadd error = ENOENT;
334bbf53c35SAdrian Chadd break;
335bbf53c35SAdrian Chadd }
336bbf53c35SAdrian Chadd
337bbf53c35SAdrian Chadd KASSERT(priv->vlan_hook[EVL_VLANOFTAG(hook_data)] == hook,
338bbf53c35SAdrian Chadd ("%s: NGM_VLAN_DEL_FILTER: Invalid VID for Hook = %s\n",
339bbf53c35SAdrian Chadd __func__, (char *)msg->data));
340bbf53c35SAdrian Chadd
341407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */
342bbf53c35SAdrian Chadd priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
343407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL);
344bbf53c35SAdrian Chadd break;
345bbf53c35SAdrian Chadd case NGM_VLAN_DEL_VID_FLT:
346bbf53c35SAdrian Chadd /* Check that message is long enough. */
347bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint16_t)) {
348bbf53c35SAdrian Chadd error = EINVAL;
349bbf53c35SAdrian Chadd break;
350bbf53c35SAdrian Chadd }
351bbf53c35SAdrian Chadd vid = (*((uint16_t *)msg->data));
352bbf53c35SAdrian Chadd /* Sanity check the VLAN ID value. */
353bbf53c35SAdrian Chadd if (vid & ~EVL_VLID_MASK) {
354bbf53c35SAdrian Chadd error = EINVAL;
355bbf53c35SAdrian Chadd break;
356bbf53c35SAdrian Chadd }
357bbf53c35SAdrian Chadd /* Check that hook exists and is active. */
358bbf53c35SAdrian Chadd hook = priv->vlan_hook[vid];
359bbf53c35SAdrian Chadd if (hook == NULL) {
360bbf53c35SAdrian Chadd error = ENOENT;
361bbf53c35SAdrian Chadd break;
362bbf53c35SAdrian Chadd }
363bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
364bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) {
365bbf53c35SAdrian Chadd error = ENOENT;
366bbf53c35SAdrian Chadd break;
367bbf53c35SAdrian Chadd }
368bbf53c35SAdrian Chadd
369bbf53c35SAdrian Chadd KASSERT(EVL_VLANOFTAG(hook_data) == vid,
370bbf53c35SAdrian Chadd ("%s: NGM_VLAN_DEL_VID_FLT:"
371bbf53c35SAdrian Chadd " Invalid VID Hook = %us, must be: %us\n",
372bbf53c35SAdrian Chadd __func__, (uint16_t )EVL_VLANOFTAG(hook_data),
373bbf53c35SAdrian Chadd vid));
374bbf53c35SAdrian Chadd
375bbf53c35SAdrian Chadd /* Purge a rule that refers to this hook. */
376bbf53c35SAdrian Chadd priv->vlan_hook[vid] = NULL;
377bbf53c35SAdrian Chadd NG_HOOK_SET_PRIVATE(hook, NULL);
378407ea290SRuslan Ermilov break;
379407ea290SRuslan Ermilov case NGM_VLAN_GET_TABLE:
380bbf53c35SAdrian Chadd /* Calculate vlans. */
381bbf53c35SAdrian Chadd vlan_count = 0;
382bbf53c35SAdrian Chadd for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
383bbf53c35SAdrian Chadd if (priv->vlan_hook[i] != NULL &&
384bbf53c35SAdrian Chadd NG_HOOK_IS_VALID(priv->vlan_hook[i]))
385bbf53c35SAdrian Chadd vlan_count ++;
386bbf53c35SAdrian Chadd }
387bbf53c35SAdrian Chadd
388053359b7SPedro F. Giffuni /* Allocate memory for response. */
389407ea290SRuslan Ermilov NG_MKRESPONSE(resp, msg, sizeof(*t) +
390bbf53c35SAdrian Chadd vlan_count * sizeof(*t->filter), M_NOWAIT);
391407ea290SRuslan Ermilov if (resp == NULL) {
392407ea290SRuslan Ermilov error = ENOMEM;
393407ea290SRuslan Ermilov break;
394407ea290SRuslan Ermilov }
395bbf53c35SAdrian Chadd
396053359b7SPedro F. Giffuni /* Pack data to response. */
397407ea290SRuslan Ermilov t = (struct ng_vlan_table *)resp->data;
398bbf53c35SAdrian Chadd t->n = 0;
399407ea290SRuslan Ermilov vf = &t->filter[0];
400bbf53c35SAdrian Chadd for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
401bbf53c35SAdrian Chadd hook = priv->vlan_hook[i];
402bbf53c35SAdrian Chadd if (hook == NULL || NG_HOOK_NOT_VALID(hook))
403bbf53c35SAdrian Chadd continue;
404bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
405bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0)
406bbf53c35SAdrian Chadd continue;
407bbf53c35SAdrian Chadd
408bbf53c35SAdrian Chadd KASSERT(EVL_VLANOFTAG(hook_data) == i,
409bbf53c35SAdrian Chadd ("%s: NGM_VLAN_GET_TABLE:"
410bbf53c35SAdrian Chadd " hook %s VID = %us, must be: %i\n",
411bbf53c35SAdrian Chadd __func__, NG_HOOK_NAME(hook),
412bbf53c35SAdrian Chadd (uint16_t)EVL_VLANOFTAG(hook_data), i));
413bbf53c35SAdrian Chadd
414bbf53c35SAdrian Chadd #ifdef NG_VLAN_USE_OLD_VLAN_NAME
415bbf53c35SAdrian Chadd vf->vlan = i;
416bbf53c35SAdrian Chadd #endif
417bbf53c35SAdrian Chadd vf->vid = i;
418bbf53c35SAdrian Chadd vf->pcp = EVL_PRIOFTAG(hook_data);
419bbf53c35SAdrian Chadd vf->cfi = EVL_CFIOFTAG(hook_data);
420bbf53c35SAdrian Chadd strncpy(vf->hook_name,
421bbf53c35SAdrian Chadd NG_HOOK_NAME(hook), NG_HOOKSIZ);
422407ea290SRuslan Ermilov vf ++;
423bbf53c35SAdrian Chadd t->n ++;
424407ea290SRuslan Ermilov }
425bbf53c35SAdrian Chadd break;
426bbf53c35SAdrian Chadd case NGM_VLAN_GET_DECAP:
427bbf53c35SAdrian Chadd NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
428bbf53c35SAdrian Chadd if (resp == NULL) {
429bbf53c35SAdrian Chadd error = ENOMEM;
430bbf53c35SAdrian Chadd break;
431407ea290SRuslan Ermilov }
432bbf53c35SAdrian Chadd (*((uint32_t *)resp->data)) = priv->decap_enable;
433bbf53c35SAdrian Chadd break;
434bbf53c35SAdrian Chadd case NGM_VLAN_SET_DECAP:
435bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint32_t)) {
436bbf53c35SAdrian Chadd error = EINVAL;
437bbf53c35SAdrian Chadd break;
438bbf53c35SAdrian Chadd }
439bbf53c35SAdrian Chadd priv->decap_enable = (*((uint32_t *)msg->data));
440bbf53c35SAdrian Chadd break;
441bbf53c35SAdrian Chadd case NGM_VLAN_GET_ENCAP:
442bbf53c35SAdrian Chadd NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT);
443bbf53c35SAdrian Chadd if (resp == NULL) {
444bbf53c35SAdrian Chadd error = ENOMEM;
445bbf53c35SAdrian Chadd break;
446bbf53c35SAdrian Chadd }
447bbf53c35SAdrian Chadd (*((uint32_t *)resp->data)) = priv->encap_enable;
448bbf53c35SAdrian Chadd break;
449bbf53c35SAdrian Chadd case NGM_VLAN_SET_ENCAP:
450bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint32_t)) {
451bbf53c35SAdrian Chadd error = EINVAL;
452bbf53c35SAdrian Chadd break;
453bbf53c35SAdrian Chadd }
454bbf53c35SAdrian Chadd priv->encap_enable = (*((uint32_t *)msg->data));
455bbf53c35SAdrian Chadd break;
456bbf53c35SAdrian Chadd case NGM_VLAN_GET_ENCAP_PROTO:
457bbf53c35SAdrian Chadd NG_MKRESPONSE(resp, msg, sizeof(uint16_t), M_NOWAIT);
458bbf53c35SAdrian Chadd if (resp == NULL) {
459bbf53c35SAdrian Chadd error = ENOMEM;
460bbf53c35SAdrian Chadd break;
461bbf53c35SAdrian Chadd }
462bbf53c35SAdrian Chadd (*((uint16_t *)resp->data)) = ntohs(priv->encap_proto);
463bbf53c35SAdrian Chadd break;
464bbf53c35SAdrian Chadd case NGM_VLAN_SET_ENCAP_PROTO:
465bbf53c35SAdrian Chadd if (msg->header.arglen != sizeof(uint16_t)) {
466bbf53c35SAdrian Chadd error = EINVAL;
467bbf53c35SAdrian Chadd break;
468bbf53c35SAdrian Chadd }
469bbf53c35SAdrian Chadd priv->encap_proto = htons((*((uint16_t *)msg->data)));
470407ea290SRuslan Ermilov break;
471407ea290SRuslan Ermilov default: /* Unknown command. */
472407ea290SRuslan Ermilov error = EINVAL;
473407ea290SRuslan Ermilov break;
474407ea290SRuslan Ermilov }
475407ea290SRuslan Ermilov break;
4763b1c41c5SGleb Smirnoff case NGM_FLOW_COOKIE:
4773b1c41c5SGleb Smirnoff {
4783b1c41c5SGleb Smirnoff struct ng_mesg *copy;
4793b1c41c5SGleb Smirnoff
4803b1c41c5SGleb Smirnoff /*
4813b1c41c5SGleb Smirnoff * Flow control messages should come only
4823b1c41c5SGleb Smirnoff * from downstream.
4833b1c41c5SGleb Smirnoff */
4843b1c41c5SGleb Smirnoff
4853b1c41c5SGleb Smirnoff if (lasthook == NULL)
4863b1c41c5SGleb Smirnoff break;
4873b1c41c5SGleb Smirnoff if (lasthook != priv->downstream_hook)
4883b1c41c5SGleb Smirnoff break;
4893b1c41c5SGleb Smirnoff /* Broadcast the event to all uplinks. */
490bbf53c35SAdrian Chadd for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
491bbf53c35SAdrian Chadd if (priv->vlan_hook[i] == NULL)
492bbf53c35SAdrian Chadd continue;
493bbf53c35SAdrian Chadd
4943b1c41c5SGleb Smirnoff NG_COPYMESSAGE(copy, msg, M_NOWAIT);
4953b1c41c5SGleb Smirnoff if (copy == NULL)
4963b1c41c5SGleb Smirnoff continue;
497bbf53c35SAdrian Chadd NG_SEND_MSG_HOOK(error, node, copy,
498bbf53c35SAdrian Chadd priv->vlan_hook[i], 0);
4993b1c41c5SGleb Smirnoff }
5003b1c41c5SGleb Smirnoff break;
5013b1c41c5SGleb Smirnoff }
502407ea290SRuslan Ermilov default: /* Unknown type cookie. */
503407ea290SRuslan Ermilov error = EINVAL;
504407ea290SRuslan Ermilov break;
505407ea290SRuslan Ermilov }
506407ea290SRuslan Ermilov NG_RESPOND_MSG(error, node, item, resp);
507407ea290SRuslan Ermilov NG_FREE_MSG(msg);
508407ea290SRuslan Ermilov return (error);
509407ea290SRuslan Ermilov }
510407ea290SRuslan Ermilov
511407ea290SRuslan Ermilov static int
ng_vlan_rcvdata(hook_p hook,item_p item)512407ea290SRuslan Ermilov ng_vlan_rcvdata(hook_p hook, item_p item)
513407ea290SRuslan Ermilov {
514407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
515407ea290SRuslan Ermilov struct ether_header *eh;
516bbf53c35SAdrian Chadd struct ether_vlan_header *evl;
517407ea290SRuslan Ermilov int error;
518bbf53c35SAdrian Chadd uintptr_t hook_data;
519bbf53c35SAdrian Chadd uint16_t vid, eth_vtag;
520407ea290SRuslan Ermilov struct mbuf *m;
521bbf53c35SAdrian Chadd hook_p dst_hook;
522bbf53c35SAdrian Chadd
523bbf53c35SAdrian Chadd NGI_GET_M(item, m);
524407ea290SRuslan Ermilov
525407ea290SRuslan Ermilov /* Make sure we have an entire header. */
526bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_HDR_LEN);
527bbf53c35SAdrian Chadd if (error != 0)
528bbf53c35SAdrian Chadd goto mchk_err;
529bbf53c35SAdrian Chadd
530407ea290SRuslan Ermilov eh = mtod(m, struct ether_header *);
531407ea290SRuslan Ermilov if (hook == priv->downstream_hook) {
532407ea290SRuslan Ermilov /*
533407ea290SRuslan Ermilov * If from downstream, select between a match hook
534407ea290SRuslan Ermilov * or the nomatch hook.
535407ea290SRuslan Ermilov */
536bbf53c35SAdrian Chadd
537bbf53c35SAdrian Chadd dst_hook = priv->nomatch_hook;
538bbf53c35SAdrian Chadd
539bbf53c35SAdrian Chadd /* Skip packets without tag. */
540bbf53c35SAdrian Chadd if ((m->m_flags & M_VLANTAG) == 0 &&
541bbf53c35SAdrian Chadd eh->ether_type != priv->encap_proto) {
542bbf53c35SAdrian Chadd if (dst_hook == NULL)
543bbf53c35SAdrian Chadd goto net_down;
544bbf53c35SAdrian Chadd goto send_packet;
545bbf53c35SAdrian Chadd }
546bbf53c35SAdrian Chadd
547bbf53c35SAdrian Chadd /* Process packets with tag. */
54878ba57b9SAndre Oppermann if (m->m_flags & M_VLANTAG) {
549407ea290SRuslan Ermilov /*
550407ea290SRuslan Ermilov * Packet is tagged, m contains a normal
551407ea290SRuslan Ermilov * Ethernet frame; tag is stored out-of-band.
552407ea290SRuslan Ermilov */
553bbf53c35SAdrian Chadd evl = NULL;
554bbf53c35SAdrian Chadd vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
555bbf53c35SAdrian Chadd } else { /* eh->ether_type == priv->encap_proto */
556bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_VLAN_HDR_LEN);
557bbf53c35SAdrian Chadd if (error != 0)
558bbf53c35SAdrian Chadd goto mchk_err;
559407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *);
560bbf53c35SAdrian Chadd vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
561407ea290SRuslan Ermilov }
562bbf53c35SAdrian Chadd
563bbf53c35SAdrian Chadd if (priv->vlan_hook[vid] != NULL) {
564bbf53c35SAdrian Chadd /*
565053359b7SPedro F. Giffuni * VLAN filter: always remove vlan tags and
566bbf53c35SAdrian Chadd * decapsulate packet.
567bbf53c35SAdrian Chadd */
568bbf53c35SAdrian Chadd dst_hook = priv->vlan_hook[vid];
569bbf53c35SAdrian Chadd if (evl == NULL) { /* m->m_flags & M_VLANTAG */
57078ba57b9SAndre Oppermann m->m_pkthdr.ether_vtag = 0;
57178ba57b9SAndre Oppermann m->m_flags &= ~M_VLANTAG;
572bbf53c35SAdrian Chadd goto send_packet;
573bbf53c35SAdrian Chadd }
574bbf53c35SAdrian Chadd } else { /* nomatch_hook */
575bbf53c35SAdrian Chadd if (dst_hook == NULL)
576bbf53c35SAdrian Chadd goto net_down;
577bbf53c35SAdrian Chadd if (evl == NULL || priv->decap_enable == 0)
578bbf53c35SAdrian Chadd goto send_packet;
579bbf53c35SAdrian Chadd /* Save tag out-of-band. */
580bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
581bbf53c35SAdrian Chadd m->m_flags |= M_VLANTAG;
582bbf53c35SAdrian Chadd }
583bbf53c35SAdrian Chadd
584bbf53c35SAdrian Chadd /*
585bbf53c35SAdrian Chadd * Decapsulate:
586bbf53c35SAdrian Chadd * TPID = ether type encap
587bbf53c35SAdrian Chadd * Move DstMAC and SrcMAC to ETHER_TYPE.
588bbf53c35SAdrian Chadd * Before:
589bbf53c35SAdrian Chadd * [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload]
590bbf53c35SAdrian Chadd * |-----------| >>>>>>>>>>>>>>>>>>>> |--------------------|
591bbf53c35SAdrian Chadd * After:
592bbf53c35SAdrian Chadd * [free space ] [dmac] [smac] [ether_type] [payload]
593bbf53c35SAdrian Chadd * |-----------| |--------------------|
594bbf53c35SAdrian Chadd */
595bbf53c35SAdrian Chadd bcopy((char *)evl, ((char *)evl + ETHER_VLAN_ENCAP_LEN),
596bbf53c35SAdrian Chadd (ETHER_ADDR_LEN * 2));
597407ea290SRuslan Ermilov m_adj(m, ETHER_VLAN_ENCAP_LEN);
598407ea290SRuslan Ermilov } else {
599407ea290SRuslan Ermilov /*
600407ea290SRuslan Ermilov * It is heading towards the downstream.
601407ea290SRuslan Ermilov * If from nomatch, pass it unmodified.
602407ea290SRuslan Ermilov * Otherwise, do the VLAN encapsulation.
603407ea290SRuslan Ermilov */
604bbf53c35SAdrian Chadd dst_hook = priv->downstream_hook;
605bbf53c35SAdrian Chadd if (dst_hook == NULL)
606bbf53c35SAdrian Chadd goto net_down;
607bbf53c35SAdrian Chadd if (hook != priv->nomatch_hook) {/* Filter hook. */
608bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
609bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data) == 0) {
610bbf53c35SAdrian Chadd /*
611bbf53c35SAdrian Chadd * Packet from hook not in filter
612bbf53c35SAdrian Chadd * call addfilter for this hook to fix.
613bbf53c35SAdrian Chadd */
614bbf53c35SAdrian Chadd error = EOPNOTSUPP;
615bbf53c35SAdrian Chadd goto drop;
616407ea290SRuslan Ermilov }
617bbf53c35SAdrian Chadd eth_vtag = (hook_data & VLAN_TAG_MASK);
618bbf53c35SAdrian Chadd if ((priv->encap_enable & VLAN_ENCAP_FROM_FILTER) == 0) {
619bbf53c35SAdrian Chadd /* Just set packet header tag and send. */
620bbf53c35SAdrian Chadd m->m_flags |= M_VLANTAG;
621bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = eth_vtag;
622bbf53c35SAdrian Chadd goto send_packet;
623407ea290SRuslan Ermilov }
624bbf53c35SAdrian Chadd } else { /* nomatch_hook */
625bbf53c35SAdrian Chadd if ((priv->encap_enable & VLAN_ENCAP_FROM_NOMATCH) == 0 ||
626bbf53c35SAdrian Chadd (m->m_flags & M_VLANTAG) == 0)
627bbf53c35SAdrian Chadd goto send_packet;
628bbf53c35SAdrian Chadd /* Encapsulate tagged packet. */
629bbf53c35SAdrian Chadd eth_vtag = m->m_pkthdr.ether_vtag;
630bbf53c35SAdrian Chadd m->m_pkthdr.ether_vtag = 0;
631bbf53c35SAdrian Chadd m->m_flags &= ~M_VLANTAG;
632bbf53c35SAdrian Chadd }
633bbf53c35SAdrian Chadd
634407ea290SRuslan Ermilov /*
635407ea290SRuslan Ermilov * Transform the Ethernet header into an Ethernet header
636407ea290SRuslan Ermilov * with 802.1Q encapsulation.
637bbf53c35SAdrian Chadd * Mod of: ether_vlanencap.
638bbf53c35SAdrian Chadd *
639bbf53c35SAdrian Chadd * TPID = ether type encap
640bbf53c35SAdrian Chadd * Move DstMAC and SrcMAC from ETHER_TYPE.
641bbf53c35SAdrian Chadd * Before:
642bbf53c35SAdrian Chadd * [free space ] [dmac] [smac] [ether_type] [payload]
643bbf53c35SAdrian Chadd * <<<<<<<<<<<<< |-----------| |--------------------|
644bbf53c35SAdrian Chadd * After:
645bbf53c35SAdrian Chadd * [dmac] [smac] [TPID] [PCP/CFI/VID] [ether_type] [payload]
646bbf53c35SAdrian Chadd * |-----------| |-- inserted tag --| |--------------------|
647407ea290SRuslan Ermilov */
648eb1b1807SGleb Smirnoff M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_NOWAIT);
649bbf53c35SAdrian Chadd if (m == NULL)
650bbf53c35SAdrian Chadd error = ENOMEM;
651bbf53c35SAdrian Chadd else
652bbf53c35SAdrian Chadd error = m_chk(&m, ETHER_VLAN_HDR_LEN);
653bbf53c35SAdrian Chadd if (error != 0)
654bbf53c35SAdrian Chadd goto mchk_err;
655bbf53c35SAdrian Chadd
656407ea290SRuslan Ermilov evl = mtod(m, struct ether_vlan_header *);
657bbf53c35SAdrian Chadd bcopy(((char *)evl + ETHER_VLAN_ENCAP_LEN),
658bbf53c35SAdrian Chadd (char *)evl, (ETHER_ADDR_LEN * 2));
659bbf53c35SAdrian Chadd evl->evl_encap_proto = priv->encap_proto;
660bbf53c35SAdrian Chadd evl->evl_tag = htons(eth_vtag);
661407ea290SRuslan Ermilov }
662bbf53c35SAdrian Chadd
663bbf53c35SAdrian Chadd send_packet:
664bbf53c35SAdrian Chadd NG_FWD_NEW_DATA(error, item, dst_hook, m);
665bbf53c35SAdrian Chadd return (error);
666bbf53c35SAdrian Chadd net_down:
667bbf53c35SAdrian Chadd error = ENETDOWN;
668bbf53c35SAdrian Chadd drop:
669bbf53c35SAdrian Chadd m_freem(m);
670bbf53c35SAdrian Chadd mchk_err:
671bbf53c35SAdrian Chadd NG_FREE_ITEM(item);
672407ea290SRuslan Ermilov return (error);
673407ea290SRuslan Ermilov }
674407ea290SRuslan Ermilov
675407ea290SRuslan Ermilov static int
ng_vlan_shutdown(node_p node)676407ea290SRuslan Ermilov ng_vlan_shutdown(node_p node)
677407ea290SRuslan Ermilov {
678407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(node);
679407ea290SRuslan Ermilov
680407ea290SRuslan Ermilov NG_NODE_SET_PRIVATE(node, NULL);
681407ea290SRuslan Ermilov NG_NODE_UNREF(node);
6821ede983cSDag-Erling Smørgrav free(priv, M_NETGRAPH);
683407ea290SRuslan Ermilov return (0);
684407ea290SRuslan Ermilov }
685407ea290SRuslan Ermilov
686407ea290SRuslan Ermilov static int
ng_vlan_disconnect(hook_p hook)687407ea290SRuslan Ermilov ng_vlan_disconnect(hook_p hook)
688407ea290SRuslan Ermilov {
689407ea290SRuslan Ermilov const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
690bbf53c35SAdrian Chadd uintptr_t hook_data;
691407ea290SRuslan Ermilov
692407ea290SRuslan Ermilov if (hook == priv->downstream_hook)
693407ea290SRuslan Ermilov priv->downstream_hook = NULL;
694407ea290SRuslan Ermilov else if (hook == priv->nomatch_hook)
695407ea290SRuslan Ermilov priv->nomatch_hook = NULL;
696407ea290SRuslan Ermilov else {
697407ea290SRuslan Ermilov /* Purge a rule that refers to this hook. */
698bbf53c35SAdrian Chadd hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
699bbf53c35SAdrian Chadd if (IS_HOOK_VLAN_SET(hook_data))
700bbf53c35SAdrian Chadd priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
701407ea290SRuslan Ermilov }
702407ea290SRuslan Ermilov NG_HOOK_SET_PRIVATE(hook, NULL);
703407ea290SRuslan Ermilov if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
704407ea290SRuslan Ermilov (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
705407ea290SRuslan Ermilov ng_rmnode_self(NG_HOOK_NODE(hook));
706407ea290SRuslan Ermilov return (0);
707407ea290SRuslan Ermilov }
708