xref: /freebsd/sys/netgraph/ng_patch.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1d359a62dSAndrey V. Elsukov /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
4bf909fc9SJulian Elischer  * Copyright (c) 2010 Maxim Ignatenko <gelraen.ua@gmail.com>
5426b3d04SJulian Elischer  * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru>
6d359a62dSAndrey V. Elsukov  * All rights reserved.
7d359a62dSAndrey V. Elsukov  *
8d359a62dSAndrey V. Elsukov  * Redistribution and use in source and binary forms, with or without
9d359a62dSAndrey V. Elsukov  * modification, are permitted provided that the following conditions
10d359a62dSAndrey V. Elsukov  * are met:
11d359a62dSAndrey V. Elsukov  * 1. Redistributions of source code must retain the above copyright
12d359a62dSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer.
13d359a62dSAndrey V. Elsukov  * 2. Redistributions in binary form must reproduce the above copyright
14d359a62dSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer in the
15d359a62dSAndrey V. Elsukov  *    documentation and/or other materials provided with the distribution.
16d359a62dSAndrey V. Elsukov  *
17d359a62dSAndrey V. Elsukov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18d359a62dSAndrey V. Elsukov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19d359a62dSAndrey V. Elsukov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20d359a62dSAndrey V. Elsukov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21d359a62dSAndrey V. Elsukov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22d359a62dSAndrey V. Elsukov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23d359a62dSAndrey V. Elsukov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24d359a62dSAndrey V. Elsukov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25d359a62dSAndrey V. Elsukov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26d359a62dSAndrey V. Elsukov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27d359a62dSAndrey V. Elsukov  * SUCH DAMAGE.
28d359a62dSAndrey V. Elsukov  *
29d359a62dSAndrey V. Elsukov  */
30d359a62dSAndrey V. Elsukov 
31d359a62dSAndrey V. Elsukov #include <sys/param.h>
32cac2fe69SAndrey V. Elsukov #include <sys/systm.h>
33d359a62dSAndrey V. Elsukov #include <sys/kernel.h>
34cac2fe69SAndrey V. Elsukov #include <sys/endian.h>
356c0a2eb1SAndrey V. Elsukov #include <sys/malloc.h>
366c0a2eb1SAndrey V. Elsukov #include <sys/mbuf.h>
37426b3d04SJulian Elischer 
38426b3d04SJulian Elischer #include <net/bpf.h>
39426b3d04SJulian Elischer #include <net/ethernet.h>
40426b3d04SJulian Elischer 
41d359a62dSAndrey V. Elsukov #include <netgraph/ng_message.h>
42d359a62dSAndrey V. Elsukov #include <netgraph/ng_parse.h>
43d359a62dSAndrey V. Elsukov #include <netgraph/netgraph.h>
44d359a62dSAndrey V. Elsukov 
45426b3d04SJulian Elischer #include <netgraph/ng_patch.h>
46426b3d04SJulian Elischer 
47426b3d04SJulian Elischer /* private data */
48426b3d04SJulian Elischer struct ng_patch_priv {
49426b3d04SJulian Elischer 	hook_p		in;
50426b3d04SJulian Elischer 	hook_p		out;
51426b3d04SJulian Elischer 	uint8_t		dlt;	/* DLT_XXX from bpf.h */
52426b3d04SJulian Elischer 	struct ng_patch_stats stats;
53426b3d04SJulian Elischer 	struct ng_patch_config *conf;
54426b3d04SJulian Elischer };
55426b3d04SJulian Elischer 
56426b3d04SJulian Elischer typedef struct ng_patch_priv *priv_p;
57426b3d04SJulian Elischer 
58426b3d04SJulian Elischer /* Netgraph methods */
59d359a62dSAndrey V. Elsukov static ng_constructor_t	ng_patch_constructor;
60d359a62dSAndrey V. Elsukov static ng_rcvmsg_t	ng_patch_rcvmsg;
61d359a62dSAndrey V. Elsukov static ng_shutdown_t	ng_patch_shutdown;
62d359a62dSAndrey V. Elsukov static ng_newhook_t	ng_patch_newhook;
63d359a62dSAndrey V. Elsukov static ng_rcvdata_t	ng_patch_rcvdata;
64d359a62dSAndrey V. Elsukov static ng_disconnect_t	ng_patch_disconnect;
65426b3d04SJulian Elischer #define ERROUT(x) { error = (x); goto done; }
66426b3d04SJulian Elischer 
67d359a62dSAndrey V. Elsukov static int
ng_patch_config_getlen(const struct ng_parse_type * type,const u_char * start,const u_char * buf)686c0a2eb1SAndrey V. Elsukov ng_patch_config_getlen(const struct ng_parse_type *type,
696c0a2eb1SAndrey V. Elsukov     const u_char *start, const u_char *buf)
70d359a62dSAndrey V. Elsukov {
71bf909fc9SJulian Elischer 	const struct ng_patch_config *conf;
72d359a62dSAndrey V. Elsukov 
73bf909fc9SJulian Elischer 	conf = (const struct ng_patch_config *)(buf -
74cac2fe69SAndrey V. Elsukov 	    offsetof(struct ng_patch_config, ops));
75bf909fc9SJulian Elischer 
76bf909fc9SJulian Elischer 	return (conf->count);
77d359a62dSAndrey V. Elsukov }
78d359a62dSAndrey V. Elsukov 
79d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_op_type_fields[]
80426b3d04SJulian Elischer 	= NG_PATCH_OP_TYPE;
81d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_op_type = {
82d359a62dSAndrey V. Elsukov 	&ng_parse_struct_type,
83d359a62dSAndrey V. Elsukov 	&ng_patch_op_type_fields
84d359a62dSAndrey V. Elsukov };
85d359a62dSAndrey V. Elsukov 
86bf909fc9SJulian Elischer static const struct ng_parse_array_info ng_patch_ops_array_info = {
87d359a62dSAndrey V. Elsukov 	&ng_patch_op_type,
88d359a62dSAndrey V. Elsukov 	&ng_patch_config_getlen
89d359a62dSAndrey V. Elsukov };
90bf909fc9SJulian Elischer static const struct ng_parse_type ng_patch_ops_array_type = {
91d359a62dSAndrey V. Elsukov 	&ng_parse_array_type,
92bf909fc9SJulian Elischer 	&ng_patch_ops_array_info
93d359a62dSAndrey V. Elsukov };
94d359a62dSAndrey V. Elsukov 
95d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_config_type_fields[]
96426b3d04SJulian Elischer 	= NG_PATCH_CONFIG_TYPE;
97d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_config_type = {
98d359a62dSAndrey V. Elsukov 	&ng_parse_struct_type,
99d359a62dSAndrey V. Elsukov 	&ng_patch_config_type_fields
100d359a62dSAndrey V. Elsukov };
101d359a62dSAndrey V. Elsukov 
102d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_stats_fields[]
103426b3d04SJulian Elischer 	= NG_PATCH_STATS_TYPE;
104d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_stats_type = {
105d359a62dSAndrey V. Elsukov 	&ng_parse_struct_type,
106d359a62dSAndrey V. Elsukov 	&ng_patch_stats_fields
107d359a62dSAndrey V. Elsukov };
108d359a62dSAndrey V. Elsukov 
109d359a62dSAndrey V. Elsukov static const struct ng_cmdlist ng_patch_cmdlist[] = {
110d359a62dSAndrey V. Elsukov 	{
111d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
112426b3d04SJulian Elischer 		NGM_PATCH_GETDLT,
113426b3d04SJulian Elischer 		"getdlt",
114426b3d04SJulian Elischer 		NULL,
115426b3d04SJulian Elischer 		&ng_parse_uint8_type
116426b3d04SJulian Elischer 	},
117426b3d04SJulian Elischer 	{
118426b3d04SJulian Elischer 		NGM_PATCH_COOKIE,
119426b3d04SJulian Elischer 		NGM_PATCH_SETDLT,
120426b3d04SJulian Elischer 		"setdlt",
121426b3d04SJulian Elischer 		&ng_parse_uint8_type,
122426b3d04SJulian Elischer 		NULL
123426b3d04SJulian Elischer 	},
124426b3d04SJulian Elischer 	{
125426b3d04SJulian Elischer 		NGM_PATCH_COOKIE,
126d359a62dSAndrey V. Elsukov 		NGM_PATCH_GETCONFIG,
127d359a62dSAndrey V. Elsukov 		"getconfig",
128d359a62dSAndrey V. Elsukov 		NULL,
129d359a62dSAndrey V. Elsukov 		&ng_patch_config_type
130d359a62dSAndrey V. Elsukov 	},
131d359a62dSAndrey V. Elsukov 	{
132d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
133d359a62dSAndrey V. Elsukov 		NGM_PATCH_SETCONFIG,
134d359a62dSAndrey V. Elsukov 		"setconfig",
135d359a62dSAndrey V. Elsukov 		&ng_patch_config_type,
136d359a62dSAndrey V. Elsukov 		NULL
137d359a62dSAndrey V. Elsukov 	},
138d359a62dSAndrey V. Elsukov 	{
139d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
140d359a62dSAndrey V. Elsukov 		NGM_PATCH_GET_STATS,
141d359a62dSAndrey V. Elsukov 		"getstats",
142d359a62dSAndrey V. Elsukov 		NULL,
143d359a62dSAndrey V. Elsukov 		&ng_patch_stats_type
144d359a62dSAndrey V. Elsukov 	},
145d359a62dSAndrey V. Elsukov 	{
146d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
147d359a62dSAndrey V. Elsukov 		NGM_PATCH_CLR_STATS,
148d359a62dSAndrey V. Elsukov 		"clrstats",
149d359a62dSAndrey V. Elsukov 		NULL,
150d359a62dSAndrey V. Elsukov 		NULL
151d359a62dSAndrey V. Elsukov 	},
152d359a62dSAndrey V. Elsukov 	{
153d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
154d359a62dSAndrey V. Elsukov 		NGM_PATCH_GETCLR_STATS,
155d359a62dSAndrey V. Elsukov 		"getclrstats",
156d359a62dSAndrey V. Elsukov 		NULL,
157d359a62dSAndrey V. Elsukov 		&ng_patch_stats_type
158d359a62dSAndrey V. Elsukov 	},
159d359a62dSAndrey V. Elsukov 	{ 0 }
160d359a62dSAndrey V. Elsukov };
161d359a62dSAndrey V. Elsukov 
162d359a62dSAndrey V. Elsukov static struct ng_type typestruct = {
163d359a62dSAndrey V. Elsukov 	.version =	NG_ABI_VERSION,
164d359a62dSAndrey V. Elsukov 	.name =		NG_PATCH_NODE_TYPE,
165d359a62dSAndrey V. Elsukov 	.constructor =	ng_patch_constructor,
166d359a62dSAndrey V. Elsukov 	.rcvmsg =	ng_patch_rcvmsg,
167d359a62dSAndrey V. Elsukov 	.shutdown =	ng_patch_shutdown,
168d359a62dSAndrey V. Elsukov 	.newhook =	ng_patch_newhook,
169d359a62dSAndrey V. Elsukov 	.rcvdata =	ng_patch_rcvdata,
170d359a62dSAndrey V. Elsukov 	.disconnect =	ng_patch_disconnect,
171d359a62dSAndrey V. Elsukov 	.cmdlist =	ng_patch_cmdlist,
172d359a62dSAndrey V. Elsukov };
173bf909fc9SJulian Elischer 
174d359a62dSAndrey V. Elsukov NETGRAPH_INIT(patch, &typestruct);
175d359a62dSAndrey V. Elsukov 
176d359a62dSAndrey V. Elsukov static int
ng_patch_constructor(node_p node)177d359a62dSAndrey V. Elsukov ng_patch_constructor(node_p node)
178d359a62dSAndrey V. Elsukov {
179d359a62dSAndrey V. Elsukov 	priv_p privdata;
180d359a62dSAndrey V. Elsukov 
181ffbfc0aaSAndrey V. Elsukov 	privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO);
182426b3d04SJulian Elischer 	privdata->dlt = DLT_RAW;
183426b3d04SJulian Elischer 
184d359a62dSAndrey V. Elsukov 	NG_NODE_SET_PRIVATE(node, privdata);
185426b3d04SJulian Elischer 
186d359a62dSAndrey V. Elsukov 	return (0);
187d359a62dSAndrey V. Elsukov }
188d359a62dSAndrey V. Elsukov 
189d359a62dSAndrey V. Elsukov static int
ng_patch_newhook(node_p node,hook_p hook,const char * name)190d359a62dSAndrey V. Elsukov ng_patch_newhook(node_p node, hook_p hook, const char *name)
191d359a62dSAndrey V. Elsukov {
192d359a62dSAndrey V. Elsukov 	const priv_p privp = NG_NODE_PRIVATE(node);
193d359a62dSAndrey V. Elsukov 
194d359a62dSAndrey V. Elsukov 	if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) {
195d359a62dSAndrey V. Elsukov 		privp->in = hook;
196d359a62dSAndrey V. Elsukov 	} else if (strncmp(name, NG_PATCH_HOOK_OUT,
197d359a62dSAndrey V. Elsukov 	    strlen(NG_PATCH_HOOK_OUT)) == 0) {
198d359a62dSAndrey V. Elsukov 		privp->out = hook;
199d359a62dSAndrey V. Elsukov 	} else
200d359a62dSAndrey V. Elsukov 		return (EINVAL);
201426b3d04SJulian Elischer 
202d359a62dSAndrey V. Elsukov 	return (0);
203d359a62dSAndrey V. Elsukov }
204d359a62dSAndrey V. Elsukov 
205d359a62dSAndrey V. Elsukov static int
ng_patch_rcvmsg(node_p node,item_p item,hook_p lasthook)206d359a62dSAndrey V. Elsukov ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook)
207d359a62dSAndrey V. Elsukov {
208d359a62dSAndrey V. Elsukov 	const priv_p privp = NG_NODE_PRIVATE(node);
2096c0a2eb1SAndrey V. Elsukov 	struct ng_patch_config *conf, *newconf;
210d359a62dSAndrey V. Elsukov 	struct ng_mesg *msg;
211426b3d04SJulian Elischer 	struct ng_mesg *resp = NULL;
212426b3d04SJulian Elischer 	int i, error = 0;
213d359a62dSAndrey V. Elsukov 
214d359a62dSAndrey V. Elsukov 	NGI_GET_MSG(item, msg);
215426b3d04SJulian Elischer 
216426b3d04SJulian Elischer 	if  (msg->header.typecookie != NGM_PATCH_COOKIE)
217426b3d04SJulian Elischer 		ERROUT(EINVAL);
218426b3d04SJulian Elischer 
219426b3d04SJulian Elischer 	switch (msg->header.cmd)
220d359a62dSAndrey V. Elsukov 	{
221426b3d04SJulian Elischer 		case NGM_PATCH_GETCONFIG:
222426b3d04SJulian Elischer 			if (privp->conf == NULL)
223426b3d04SJulian Elischer 				ERROUT(0);
224426b3d04SJulian Elischer 
225426b3d04SJulian Elischer 			NG_MKRESPONSE(resp, msg,
226426b3d04SJulian Elischer 			    NG_PATCH_CONF_SIZE(privp->conf->count), M_WAITOK);
227426b3d04SJulian Elischer 
228426b3d04SJulian Elischer 			if (resp == NULL)
229426b3d04SJulian Elischer 				ERROUT(ENOMEM);
230426b3d04SJulian Elischer 
231426b3d04SJulian Elischer 			bcopy(privp->conf, resp->data,
232426b3d04SJulian Elischer 			    NG_PATCH_CONF_SIZE(privp->conf->count));
233426b3d04SJulian Elischer 
234426b3d04SJulian Elischer 			conf = (struct ng_patch_config *) resp->data;
235426b3d04SJulian Elischer 
236426b3d04SJulian Elischer 			for (i = 0; i < conf->count; i++) {
237426b3d04SJulian Elischer 				switch (conf->ops[i].length)
238426b3d04SJulian Elischer 				{
239426b3d04SJulian Elischer 					case 1:
240426b3d04SJulian Elischer 						conf->ops[i].val.v8 = conf->ops[i].val.v1;
241426b3d04SJulian Elischer 						break;
242426b3d04SJulian Elischer 					case 2:
243426b3d04SJulian Elischer 						conf->ops[i].val.v8 = conf->ops[i].val.v2;
244426b3d04SJulian Elischer 						break;
245426b3d04SJulian Elischer 					case 4:
246426b3d04SJulian Elischer 						conf->ops[i].val.v8 = conf->ops[i].val.v4;
247426b3d04SJulian Elischer 						break;
248426b3d04SJulian Elischer 					case 8:
249d359a62dSAndrey V. Elsukov 						break;
250d359a62dSAndrey V. Elsukov 				}
251426b3d04SJulian Elischer 			}
252d359a62dSAndrey V. Elsukov 
253426b3d04SJulian Elischer 			break;
254426b3d04SJulian Elischer 
255426b3d04SJulian Elischer 		case NGM_PATCH_SETCONFIG:
256d359a62dSAndrey V. Elsukov 			conf = (struct ng_patch_config *) msg->data;
257426b3d04SJulian Elischer 
258426b3d04SJulian Elischer 			if (msg->header.arglen < sizeof(struct ng_patch_config) ||
259426b3d04SJulian Elischer 			    msg->header.arglen < NG_PATCH_CONF_SIZE(conf->count))
260426b3d04SJulian Elischer 				ERROUT(EINVAL);
261d359a62dSAndrey V. Elsukov 
262d359a62dSAndrey V. Elsukov 			for (i = 0; i < conf->count; i++) {
263426b3d04SJulian Elischer 				switch (conf->ops[i].length)
264426b3d04SJulian Elischer 				{
265d359a62dSAndrey V. Elsukov 					case 1:
266426b3d04SJulian Elischer 						conf->ops[i].val.v1 = (uint8_t) conf->ops[i].val.v8;
267426b3d04SJulian Elischer 						break;
268d359a62dSAndrey V. Elsukov 					case 2:
269426b3d04SJulian Elischer 						conf->ops[i].val.v2 = (uint16_t) conf->ops[i].val.v8;
270426b3d04SJulian Elischer 						break;
271d359a62dSAndrey V. Elsukov 					case 4:
272426b3d04SJulian Elischer 						conf->ops[i].val.v4 = (uint32_t) conf->ops[i].val.v8;
273426b3d04SJulian Elischer 						break;
274d359a62dSAndrey V. Elsukov 					case 8:
275d359a62dSAndrey V. Elsukov 						break;
276d359a62dSAndrey V. Elsukov 					default:
277426b3d04SJulian Elischer 						ERROUT(EINVAL);
278d359a62dSAndrey V. Elsukov 				}
279d359a62dSAndrey V. Elsukov 			}
280d359a62dSAndrey V. Elsukov 
281426b3d04SJulian Elischer 			conf->csum_flags &= NG_PATCH_CSUM_IPV4|NG_PATCH_CSUM_IPV6;
282426b3d04SJulian Elischer 			conf->relative_offset = !!conf->relative_offset;
283d359a62dSAndrey V. Elsukov 
284426b3d04SJulian Elischer 			newconf = malloc(NG_PATCH_CONF_SIZE(conf->count), M_NETGRAPH, M_WAITOK | M_ZERO);
285426b3d04SJulian Elischer 
286426b3d04SJulian Elischer 			bcopy(conf, newconf, NG_PATCH_CONF_SIZE(conf->count));
287426b3d04SJulian Elischer 
288426b3d04SJulian Elischer 			if (privp->conf)
289426b3d04SJulian Elischer 				free(privp->conf, M_NETGRAPH);
290426b3d04SJulian Elischer 
291426b3d04SJulian Elischer 			privp->conf = newconf;
292426b3d04SJulian Elischer 
293d359a62dSAndrey V. Elsukov 			break;
294426b3d04SJulian Elischer 
295d359a62dSAndrey V. Elsukov 		case NGM_PATCH_GET_STATS:
296d359a62dSAndrey V. Elsukov 		case NGM_PATCH_CLR_STATS:
297426b3d04SJulian Elischer 		case NGM_PATCH_GETCLR_STATS:
298426b3d04SJulian Elischer 			if (msg->header.cmd != NGM_PATCH_CLR_STATS) {
299426b3d04SJulian Elischer 				NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), M_WAITOK);
300426b3d04SJulian Elischer 
301426b3d04SJulian Elischer 				if (resp == NULL)
302426b3d04SJulian Elischer 					ERROUT(ENOMEM);
303426b3d04SJulian Elischer 
304426b3d04SJulian Elischer 				bcopy(&(privp->stats), resp->data, sizeof(struct ng_patch_stats));
305d359a62dSAndrey V. Elsukov 			}
306d359a62dSAndrey V. Elsukov 
307426b3d04SJulian Elischer 			if (msg->header.cmd != NGM_PATCH_GET_STATS)
308426b3d04SJulian Elischer 				bzero(&(privp->stats), sizeof(struct ng_patch_stats));
309426b3d04SJulian Elischer 
310426b3d04SJulian Elischer 			break;
311426b3d04SJulian Elischer 
312426b3d04SJulian Elischer 		case NGM_PATCH_GETDLT:
313426b3d04SJulian Elischer 			NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
314426b3d04SJulian Elischer 
315426b3d04SJulian Elischer 			if (resp == NULL)
316426b3d04SJulian Elischer 				ERROUT(ENOMEM);
317426b3d04SJulian Elischer 
318426b3d04SJulian Elischer 			*((uint8_t *) resp->data) = privp->dlt;
319426b3d04SJulian Elischer 
320426b3d04SJulian Elischer 			break;
321426b3d04SJulian Elischer 
322426b3d04SJulian Elischer 		case NGM_PATCH_SETDLT:
323426b3d04SJulian Elischer 			if (msg->header.arglen != sizeof(uint8_t))
324426b3d04SJulian Elischer 				ERROUT(EINVAL);
325426b3d04SJulian Elischer 
326426b3d04SJulian Elischer 			switch (*(uint8_t *) msg->data)
327426b3d04SJulian Elischer 			{
328426b3d04SJulian Elischer 				case DLT_EN10MB:
329426b3d04SJulian Elischer 				case DLT_RAW:
330426b3d04SJulian Elischer 					privp->dlt = *(uint8_t *) msg->data;
331426b3d04SJulian Elischer 					break;
332426b3d04SJulian Elischer 
333426b3d04SJulian Elischer 				default:
334426b3d04SJulian Elischer 					ERROUT(EINVAL);
335426b3d04SJulian Elischer 			}
336426b3d04SJulian Elischer 
337426b3d04SJulian Elischer 			break;
338426b3d04SJulian Elischer 
339426b3d04SJulian Elischer 		default:
340426b3d04SJulian Elischer 			ERROUT(EINVAL);
341426b3d04SJulian Elischer 	}
342426b3d04SJulian Elischer 
343426b3d04SJulian Elischer done:
344d359a62dSAndrey V. Elsukov 	NG_RESPOND_MSG(error, node, item, resp);
345d359a62dSAndrey V. Elsukov 	NG_FREE_MSG(msg);
346426b3d04SJulian Elischer 
347d359a62dSAndrey V. Elsukov 	return (error);
348d359a62dSAndrey V. Elsukov }
349d359a62dSAndrey V. Elsukov 
350d359a62dSAndrey V. Elsukov static void
do_patch(priv_p privp,struct mbuf * m,int global_offset)351426b3d04SJulian Elischer do_patch(priv_p privp, struct mbuf *m, int global_offset)
352d359a62dSAndrey V. Elsukov {
353426b3d04SJulian Elischer 	int i, offset, patched = 0;
354426b3d04SJulian Elischer 	union ng_patch_op_val val;
355d359a62dSAndrey V. Elsukov 
356426b3d04SJulian Elischer 	for (i = 0; i < privp->conf->count; i++) {
357426b3d04SJulian Elischer 		offset = global_offset + privp->conf->ops[i].offset;
358426b3d04SJulian Elischer 
359426b3d04SJulian Elischer 		if (offset + privp->conf->ops[i].length > m->m_pkthdr.len)
360d359a62dSAndrey V. Elsukov 			continue;
361d359a62dSAndrey V. Elsukov 
362d359a62dSAndrey V. Elsukov 		/* for "=" operation we don't need to copy data from mbuf */
363426b3d04SJulian Elischer 		if (privp->conf->ops[i].mode != NG_PATCH_MODE_SET)
364426b3d04SJulian Elischer 			m_copydata(m, offset, privp->conf->ops[i].length, (caddr_t) &val);
365d359a62dSAndrey V. Elsukov 
366426b3d04SJulian Elischer 		switch (privp->conf->ops[i].length)
367426b3d04SJulian Elischer 		{
368d359a62dSAndrey V. Elsukov 			case 1:
369426b3d04SJulian Elischer 				switch (privp->conf->ops[i].mode)
370426b3d04SJulian Elischer 				{
371d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SET:
372426b3d04SJulian Elischer 						val.v1 = privp->conf->ops[i].val.v1;
373d359a62dSAndrey V. Elsukov 						break;
374d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_ADD:
375426b3d04SJulian Elischer 						val.v1 += privp->conf->ops[i].val.v1;
376d359a62dSAndrey V. Elsukov 						break;
377d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SUB:
378426b3d04SJulian Elischer 						val.v1 -= privp->conf->ops[i].val.v1;
379d359a62dSAndrey V. Elsukov 						break;
380d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_MUL:
381426b3d04SJulian Elischer 						val.v1 *= privp->conf->ops[i].val.v1;
382d359a62dSAndrey V. Elsukov 						break;
383d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_DIV:
384426b3d04SJulian Elischer 						val.v1 /= privp->conf->ops[i].val.v1;
385d359a62dSAndrey V. Elsukov 						break;
386d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_NEG:
387426b3d04SJulian Elischer 						*((int8_t *) &val) = - *((int8_t *) &val);
388d359a62dSAndrey V. Elsukov 						break;
389d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_AND:
390426b3d04SJulian Elischer 						val.v1 &= privp->conf->ops[i].val.v1;
391d359a62dSAndrey V. Elsukov 						break;
392d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_OR:
393426b3d04SJulian Elischer 						val.v1 |= privp->conf->ops[i].val.v1;
394d359a62dSAndrey V. Elsukov 						break;
395d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_XOR:
396426b3d04SJulian Elischer 						val.v1 ^= privp->conf->ops[i].val.v1;
397d359a62dSAndrey V. Elsukov 						break;
398d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SHL:
399426b3d04SJulian Elischer 						val.v1 <<= privp->conf->ops[i].val.v1;
400d359a62dSAndrey V. Elsukov 						break;
401d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SHR:
402426b3d04SJulian Elischer 						val.v1 >>= privp->conf->ops[i].val.v1;
403d359a62dSAndrey V. Elsukov 						break;
404d359a62dSAndrey V. Elsukov 				}
405d359a62dSAndrey V. Elsukov 				break;
406426b3d04SJulian Elischer 
407d359a62dSAndrey V. Elsukov 			case 2:
408426b3d04SJulian Elischer 				val.v2 = ntohs(val.v2);
409426b3d04SJulian Elischer 
410426b3d04SJulian Elischer 				switch (privp->conf->ops[i].mode)
411426b3d04SJulian Elischer 				{
412d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SET:
413426b3d04SJulian Elischer 						val.v2 = privp->conf->ops[i].val.v2;
414d359a62dSAndrey V. Elsukov 						break;
415d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_ADD:
416426b3d04SJulian Elischer 						val.v2 += privp->conf->ops[i].val.v2;
417d359a62dSAndrey V. Elsukov 						break;
418d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SUB:
419426b3d04SJulian Elischer 						val.v2 -= privp->conf->ops[i].val.v2;
420d359a62dSAndrey V. Elsukov 						break;
421d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_MUL:
422426b3d04SJulian Elischer 						val.v2 *= privp->conf->ops[i].val.v2;
423d359a62dSAndrey V. Elsukov 						break;
424d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_DIV:
425426b3d04SJulian Elischer 						val.v2 /= privp->conf->ops[i].val.v2;
426d359a62dSAndrey V. Elsukov 						break;
427d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_NEG:
428426b3d04SJulian Elischer 						*((int16_t *) &val) = - *((int16_t *) &val);
429d359a62dSAndrey V. Elsukov 						break;
430d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_AND:
431426b3d04SJulian Elischer 						val.v2 &= privp->conf->ops[i].val.v2;
432d359a62dSAndrey V. Elsukov 						break;
433d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_OR:
434426b3d04SJulian Elischer 						val.v2 |= privp->conf->ops[i].val.v2;
435d359a62dSAndrey V. Elsukov 						break;
436d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_XOR:
437426b3d04SJulian Elischer 						val.v2 ^= privp->conf->ops[i].val.v2;
438d359a62dSAndrey V. Elsukov 						break;
439d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SHL:
440426b3d04SJulian Elischer 						val.v2 <<= privp->conf->ops[i].val.v2;
441d359a62dSAndrey V. Elsukov 						break;
442d359a62dSAndrey V. Elsukov 					case NG_PATCH_MODE_SHR:
443426b3d04SJulian Elischer 						val.v2 >>= privp->conf->ops[i].val.v2;
444d359a62dSAndrey V. Elsukov 						break;
445d359a62dSAndrey V. Elsukov 				}
446d359a62dSAndrey V. Elsukov 
447426b3d04SJulian Elischer 				val.v2 = htons(val.v2);
448426b3d04SJulian Elischer 
449426b3d04SJulian Elischer 				break;
450426b3d04SJulian Elischer 
451426b3d04SJulian Elischer 			case 4:
452426b3d04SJulian Elischer 				val.v4 = ntohl(val.v4);
453426b3d04SJulian Elischer 
454426b3d04SJulian Elischer 				switch (privp->conf->ops[i].mode)
455426b3d04SJulian Elischer 				{
456426b3d04SJulian Elischer 					case NG_PATCH_MODE_SET:
457426b3d04SJulian Elischer 						val.v4 = privp->conf->ops[i].val.v4;
458426b3d04SJulian Elischer 						break;
459426b3d04SJulian Elischer 					case NG_PATCH_MODE_ADD:
460426b3d04SJulian Elischer 						val.v4 += privp->conf->ops[i].val.v4;
461426b3d04SJulian Elischer 						break;
462426b3d04SJulian Elischer 					case NG_PATCH_MODE_SUB:
463426b3d04SJulian Elischer 						val.v4 -= privp->conf->ops[i].val.v4;
464426b3d04SJulian Elischer 						break;
465426b3d04SJulian Elischer 					case NG_PATCH_MODE_MUL:
466426b3d04SJulian Elischer 						val.v4 *= privp->conf->ops[i].val.v4;
467426b3d04SJulian Elischer 						break;
468426b3d04SJulian Elischer 					case NG_PATCH_MODE_DIV:
469426b3d04SJulian Elischer 						val.v4 /= privp->conf->ops[i].val.v4;
470426b3d04SJulian Elischer 						break;
471426b3d04SJulian Elischer 					case NG_PATCH_MODE_NEG:
472426b3d04SJulian Elischer 						*((int32_t *) &val) = - *((int32_t *) &val);
473426b3d04SJulian Elischer 						break;
474426b3d04SJulian Elischer 					case NG_PATCH_MODE_AND:
475426b3d04SJulian Elischer 						val.v4 &= privp->conf->ops[i].val.v4;
476426b3d04SJulian Elischer 						break;
477426b3d04SJulian Elischer 					case NG_PATCH_MODE_OR:
478426b3d04SJulian Elischer 						val.v4 |= privp->conf->ops[i].val.v4;
479426b3d04SJulian Elischer 						break;
480426b3d04SJulian Elischer 					case NG_PATCH_MODE_XOR:
481426b3d04SJulian Elischer 						val.v4 ^= privp->conf->ops[i].val.v4;
482426b3d04SJulian Elischer 						break;
483426b3d04SJulian Elischer 					case NG_PATCH_MODE_SHL:
484426b3d04SJulian Elischer 						val.v4 <<= privp->conf->ops[i].val.v4;
485426b3d04SJulian Elischer 						break;
486426b3d04SJulian Elischer 					case NG_PATCH_MODE_SHR:
487426b3d04SJulian Elischer 						val.v4 >>= privp->conf->ops[i].val.v4;
488426b3d04SJulian Elischer 						break;
489426b3d04SJulian Elischer 				}
490426b3d04SJulian Elischer 
491426b3d04SJulian Elischer 				val.v4 = htonl(val.v4);
492426b3d04SJulian Elischer 
493426b3d04SJulian Elischer 				break;
494426b3d04SJulian Elischer 
495426b3d04SJulian Elischer 			case 8:
496426b3d04SJulian Elischer 				val.v8 = be64toh(val.v8);
497426b3d04SJulian Elischer 
498426b3d04SJulian Elischer 				switch (privp->conf->ops[i].mode)
499426b3d04SJulian Elischer 				{
500426b3d04SJulian Elischer 					case NG_PATCH_MODE_SET:
501426b3d04SJulian Elischer 						val.v8 = privp->conf->ops[i].val.v8;
502426b3d04SJulian Elischer 						break;
503426b3d04SJulian Elischer 					case NG_PATCH_MODE_ADD:
504426b3d04SJulian Elischer 						val.v8 += privp->conf->ops[i].val.v8;
505426b3d04SJulian Elischer 						break;
506426b3d04SJulian Elischer 					case NG_PATCH_MODE_SUB:
507426b3d04SJulian Elischer 						val.v8 -= privp->conf->ops[i].val.v8;
508426b3d04SJulian Elischer 						break;
509426b3d04SJulian Elischer 					case NG_PATCH_MODE_MUL:
510426b3d04SJulian Elischer 						val.v8 *= privp->conf->ops[i].val.v8;
511426b3d04SJulian Elischer 						break;
512426b3d04SJulian Elischer 					case NG_PATCH_MODE_DIV:
513426b3d04SJulian Elischer 						val.v8 /= privp->conf->ops[i].val.v8;
514426b3d04SJulian Elischer 						break;
515426b3d04SJulian Elischer 					case NG_PATCH_MODE_NEG:
516426b3d04SJulian Elischer 						*((int64_t *) &val) = - *((int64_t *) &val);
517426b3d04SJulian Elischer 						break;
518426b3d04SJulian Elischer 					case NG_PATCH_MODE_AND:
519426b3d04SJulian Elischer 						val.v8 &= privp->conf->ops[i].val.v8;
520426b3d04SJulian Elischer 						break;
521426b3d04SJulian Elischer 					case NG_PATCH_MODE_OR:
522426b3d04SJulian Elischer 						val.v8 |= privp->conf->ops[i].val.v8;
523426b3d04SJulian Elischer 						break;
524426b3d04SJulian Elischer 					case NG_PATCH_MODE_XOR:
525426b3d04SJulian Elischer 						val.v8 ^= privp->conf->ops[i].val.v8;
526426b3d04SJulian Elischer 						break;
527426b3d04SJulian Elischer 					case NG_PATCH_MODE_SHL:
528426b3d04SJulian Elischer 						val.v8 <<= privp->conf->ops[i].val.v8;
529426b3d04SJulian Elischer 						break;
530426b3d04SJulian Elischer 					case NG_PATCH_MODE_SHR:
531426b3d04SJulian Elischer 						val.v8 >>= privp->conf->ops[i].val.v8;
532426b3d04SJulian Elischer 						break;
533426b3d04SJulian Elischer 				}
534426b3d04SJulian Elischer 
535426b3d04SJulian Elischer 				val.v8 = htobe64(val.v8);
536426b3d04SJulian Elischer 
537426b3d04SJulian Elischer 				break;
538426b3d04SJulian Elischer 		}
539426b3d04SJulian Elischer 
540426b3d04SJulian Elischer 		m_copyback(m, offset, privp->conf->ops[i].length, (caddr_t) &val);
541d359a62dSAndrey V. Elsukov 		patched = 1;
542d359a62dSAndrey V. Elsukov 	}
543426b3d04SJulian Elischer 
544426b3d04SJulian Elischer 	if (patched)
545d359a62dSAndrey V. Elsukov 		privp->stats.patched++;
546d359a62dSAndrey V. Elsukov }
547d359a62dSAndrey V. Elsukov 
548d359a62dSAndrey V. Elsukov static int
ng_patch_rcvdata(hook_p hook,item_p item)549d359a62dSAndrey V. Elsukov ng_patch_rcvdata(hook_p hook, item_p item)
550d359a62dSAndrey V. Elsukov {
551d359a62dSAndrey V. Elsukov 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
552d359a62dSAndrey V. Elsukov 	struct mbuf *m;
553426b3d04SJulian Elischer 	hook_p out;
554426b3d04SJulian Elischer 	int pullup_len = 0;
555426b3d04SJulian Elischer 	int error = 0;
556d359a62dSAndrey V. Elsukov 
557d359a62dSAndrey V. Elsukov 	priv->stats.received++;
558426b3d04SJulian Elischer 
559d359a62dSAndrey V. Elsukov 	NGI_GET_M(item, m);
560426b3d04SJulian Elischer 
561426b3d04SJulian Elischer #define	PULLUP_CHECK(mbuf, length) do {					\
562426b3d04SJulian Elischer 	pullup_len += length;						\
563426b3d04SJulian Elischer 	if (((mbuf)->m_pkthdr.len < pullup_len) ||			\
564426b3d04SJulian Elischer 	    (pullup_len > MHLEN)) {					\
565426b3d04SJulian Elischer 		error = EINVAL;						\
566426b3d04SJulian Elischer 		goto bypass;						\
567426b3d04SJulian Elischer 	}								\
568426b3d04SJulian Elischer 	if ((mbuf)->m_len < pullup_len &&				\
569426b3d04SJulian Elischer 	    (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) {	\
570426b3d04SJulian Elischer 		error = ENOBUFS;					\
571426b3d04SJulian Elischer 		goto drop;						\
572426b3d04SJulian Elischer 	}								\
573426b3d04SJulian Elischer } while (0)
574426b3d04SJulian Elischer 
575426b3d04SJulian Elischer 	if (priv->conf && hook == priv->in &&
576426b3d04SJulian Elischer 	    m && (m->m_flags & M_PKTHDR)) {
577d359a62dSAndrey V. Elsukov 		m = m_unshare(m, M_NOWAIT);
578426b3d04SJulian Elischer 
579426b3d04SJulian Elischer 		if (m == NULL)
580426b3d04SJulian Elischer 			ERROUT(ENOMEM);
581426b3d04SJulian Elischer 
582426b3d04SJulian Elischer 		if (priv->conf->relative_offset) {
583426b3d04SJulian Elischer 			struct ether_header *eh;
584426b3d04SJulian Elischer 			struct ng_patch_vlan_header *vh;
585426b3d04SJulian Elischer 			uint16_t etype;
586426b3d04SJulian Elischer 
587426b3d04SJulian Elischer 			switch (priv->dlt)
588426b3d04SJulian Elischer 			{
589426b3d04SJulian Elischer 				case DLT_EN10MB:
590426b3d04SJulian Elischer 					PULLUP_CHECK(m, sizeof(struct ether_header));
591426b3d04SJulian Elischer 					eh = mtod(m, struct ether_header *);
592426b3d04SJulian Elischer 					etype = ntohs(eh->ether_type);
593426b3d04SJulian Elischer 
594426b3d04SJulian Elischer 					for (;;) {	/* QinQ support */
595426b3d04SJulian Elischer 						switch (etype)
596426b3d04SJulian Elischer 						{
597426b3d04SJulian Elischer 							case 0x8100:
598426b3d04SJulian Elischer 							case 0x88A8:
599426b3d04SJulian Elischer 							case 0x9100:
600426b3d04SJulian Elischer 								PULLUP_CHECK(m, sizeof(struct ng_patch_vlan_header));
601426b3d04SJulian Elischer 								vh = (struct ng_patch_vlan_header *) mtodo(m,
602426b3d04SJulian Elischer 								    pullup_len - sizeof(struct ng_patch_vlan_header));
603426b3d04SJulian Elischer 								etype = ntohs(vh->etype);
604426b3d04SJulian Elischer 								break;
605426b3d04SJulian Elischer 
606426b3d04SJulian Elischer 							default:
607426b3d04SJulian Elischer 								goto loopend;
608d359a62dSAndrey V. Elsukov 						}
609426b3d04SJulian Elischer 					}
610426b3d04SJulian Elischer loopend:
611426b3d04SJulian Elischer 					break;
612426b3d04SJulian Elischer 
613426b3d04SJulian Elischer 				case DLT_RAW:
614426b3d04SJulian Elischer 					break;
615426b3d04SJulian Elischer 
616426b3d04SJulian Elischer 				default:
617426b3d04SJulian Elischer 					ERROUT(EINVAL);
618426b3d04SJulian Elischer 			}
619d359a62dSAndrey V. Elsukov 		}
620d359a62dSAndrey V. Elsukov 
621426b3d04SJulian Elischer 		do_patch(priv, m, pullup_len);
622426b3d04SJulian Elischer 
623426b3d04SJulian Elischer 		m->m_pkthdr.csum_flags |= priv->conf->csum_flags;
624426b3d04SJulian Elischer 	}
625426b3d04SJulian Elischer 
626426b3d04SJulian Elischer #undef	PULLUP_CHECK
627426b3d04SJulian Elischer 
628426b3d04SJulian Elischer bypass:
629426b3d04SJulian Elischer 	out = NULL;
630426b3d04SJulian Elischer 
631d359a62dSAndrey V. Elsukov 	if (hook == priv->in) {
632d359a62dSAndrey V. Elsukov 		/* return frames on 'in' hook if 'out' not connected */
633426b3d04SJulian Elischer 		out = priv->out ? priv->out : priv->in;
634426b3d04SJulian Elischer 	} else if (hook == priv->out && priv->in) {
635426b3d04SJulian Elischer 		/* pass frames on 'out' hook if 'in' connected */
636426b3d04SJulian Elischer 		out = priv->in;
637d359a62dSAndrey V. Elsukov 	}
638d359a62dSAndrey V. Elsukov 
639426b3d04SJulian Elischer 	if (out == NULL)
640426b3d04SJulian Elischer 		ERROUT(0);
641426b3d04SJulian Elischer 
642426b3d04SJulian Elischer 	NG_FWD_NEW_DATA(error, item, out, m);
643426b3d04SJulian Elischer 
644426b3d04SJulian Elischer 	return (error);
645426b3d04SJulian Elischer 
646426b3d04SJulian Elischer done:
647426b3d04SJulian Elischer drop:
648d359a62dSAndrey V. Elsukov 	NG_FREE_ITEM(item);
649d359a62dSAndrey V. Elsukov 	NG_FREE_M(m);
650426b3d04SJulian Elischer 
651426b3d04SJulian Elischer 	priv->stats.dropped++;
652426b3d04SJulian Elischer 
653d359a62dSAndrey V. Elsukov 	return (error);
654d359a62dSAndrey V. Elsukov }
655d359a62dSAndrey V. Elsukov 
656d359a62dSAndrey V. Elsukov static int
ng_patch_shutdown(node_p node)657d359a62dSAndrey V. Elsukov ng_patch_shutdown(node_p node)
658d359a62dSAndrey V. Elsukov {
659d359a62dSAndrey V. Elsukov 	const priv_p privdata = NG_NODE_PRIVATE(node);
660d359a62dSAndrey V. Elsukov 
661d359a62dSAndrey V. Elsukov 	NG_NODE_SET_PRIVATE(node, NULL);
662d359a62dSAndrey V. Elsukov 	NG_NODE_UNREF(node);
663426b3d04SJulian Elischer 
664426b3d04SJulian Elischer 	if (privdata->conf != NULL)
665426b3d04SJulian Elischer 		free(privdata->conf, M_NETGRAPH);
666426b3d04SJulian Elischer 
667d359a62dSAndrey V. Elsukov 	free(privdata, M_NETGRAPH);
668426b3d04SJulian Elischer 
669d359a62dSAndrey V. Elsukov 	return (0);
670d359a62dSAndrey V. Elsukov }
671d359a62dSAndrey V. Elsukov 
672d359a62dSAndrey V. Elsukov static int
ng_patch_disconnect(hook_p hook)673d359a62dSAndrey V. Elsukov ng_patch_disconnect(hook_p hook)
674d359a62dSAndrey V. Elsukov {
6756c0a2eb1SAndrey V. Elsukov 	priv_p priv;
676d359a62dSAndrey V. Elsukov 
6776c0a2eb1SAndrey V. Elsukov 	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
678426b3d04SJulian Elischer 
679d359a62dSAndrey V. Elsukov 	if (hook == priv->in) {
680d359a62dSAndrey V. Elsukov 		priv->in = NULL;
681d359a62dSAndrey V. Elsukov 	}
682426b3d04SJulian Elischer 
683d359a62dSAndrey V. Elsukov 	if (hook == priv->out) {
684d359a62dSAndrey V. Elsukov 		priv->out = NULL;
685d359a62dSAndrey V. Elsukov 	}
686426b3d04SJulian Elischer 
6876c0a2eb1SAndrey V. Elsukov 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
6886c0a2eb1SAndrey V. Elsukov 	    NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
689d359a62dSAndrey V. Elsukov 		ng_rmnode_self(NG_HOOK_NODE(hook));
690426b3d04SJulian Elischer 
691d359a62dSAndrey V. Elsukov 	return (0);
692d359a62dSAndrey V. Elsukov }
693