xref: /freebsd/sys/netgraph/ng_checksum.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1d7373c82SJulian Elischer /*-
2d7373c82SJulian Elischer  * Copyright (c) 2015 Dmitry Vagin <daemon.hammer@ya.ru>
3d7373c82SJulian Elischer  * All rights reserved.
4d7373c82SJulian Elischer  *
5d7373c82SJulian Elischer  * Redistribution and use in source and binary forms, with or without
6d7373c82SJulian Elischer  * modification, are permitted provided that the following conditions
7d7373c82SJulian Elischer  * are met:
8d7373c82SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
9d7373c82SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
10d7373c82SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
11d7373c82SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
12d7373c82SJulian Elischer  *    documentation and/or other materials provided with the distribution.
13d7373c82SJulian Elischer  *
14d7373c82SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15d7373c82SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d7373c82SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17d7373c82SJulian Elischer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18d7373c82SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d7373c82SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d7373c82SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d7373c82SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d7373c82SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d7373c82SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d7373c82SJulian Elischer  * SUCH DAMAGE.
25d7373c82SJulian Elischer  *
26d7373c82SJulian Elischer  */
27d7373c82SJulian Elischer 
28d7373c82SJulian Elischer #include <sys/cdefs.h>
29d7373c82SJulian Elischer #include "opt_inet.h"
30d7373c82SJulian Elischer #include "opt_inet6.h"
31d7373c82SJulian Elischer 
32d7373c82SJulian Elischer #include <sys/param.h>
33d7373c82SJulian Elischer #include <sys/systm.h>
34d7373c82SJulian Elischer #include <sys/kernel.h>
35d7373c82SJulian Elischer #include <sys/endian.h>
36d7373c82SJulian Elischer #include <sys/malloc.h>
37d7373c82SJulian Elischer #include <sys/mbuf.h>
38d7373c82SJulian Elischer #include <sys/socket.h>
39d7373c82SJulian Elischer 
40d7373c82SJulian Elischer #include <net/bpf.h>
41d7373c82SJulian Elischer #include <net/ethernet.h>
42d7373c82SJulian Elischer #include <net/if.h>
43d7373c82SJulian Elischer #include <net/if_vlan_var.h>
44d7373c82SJulian Elischer 
45d7373c82SJulian Elischer #include <netinet/in.h>
46d7373c82SJulian Elischer #include <netinet/ip.h>
47d7373c82SJulian Elischer #include <netinet/ip6.h>
48d7373c82SJulian Elischer #include <netinet/tcp.h>
49d7373c82SJulian Elischer #include <netinet/udp.h>
50d7373c82SJulian Elischer #include <machine/in_cksum.h>
51d7373c82SJulian Elischer 
52d7373c82SJulian Elischer #include <netgraph/ng_message.h>
53d7373c82SJulian Elischer #include <netgraph/ng_parse.h>
54d7373c82SJulian Elischer #include <netgraph/netgraph.h>
55d7373c82SJulian Elischer 
56d7373c82SJulian Elischer #include <netgraph/ng_checksum.h>
57d7373c82SJulian Elischer 
58d7373c82SJulian Elischer /* private data */
59d7373c82SJulian Elischer struct ng_checksum_priv {
60d7373c82SJulian Elischer 	hook_p in;
61d7373c82SJulian Elischer 	hook_p out;
62d7373c82SJulian Elischer 	uint8_t dlt;	/* DLT_XXX from bpf.h */
63d7373c82SJulian Elischer 	struct ng_checksum_config *conf;
64d7373c82SJulian Elischer 	struct ng_checksum_stats stats;
65d7373c82SJulian Elischer };
66d7373c82SJulian Elischer 
67d7373c82SJulian Elischer typedef struct ng_checksum_priv *priv_p;
68d7373c82SJulian Elischer 
69d7373c82SJulian Elischer /* Netgraph methods */
70d7373c82SJulian Elischer static ng_constructor_t	ng_checksum_constructor;
71d7373c82SJulian Elischer static ng_rcvmsg_t	ng_checksum_rcvmsg;
72d7373c82SJulian Elischer static ng_shutdown_t	ng_checksum_shutdown;
73d7373c82SJulian Elischer static ng_newhook_t	ng_checksum_newhook;
74d7373c82SJulian Elischer static ng_rcvdata_t	ng_checksum_rcvdata;
75d7373c82SJulian Elischer static ng_disconnect_t	ng_checksum_disconnect;
76d7373c82SJulian Elischer #define ERROUT(x) { error = (x); goto done; }
77d7373c82SJulian Elischer 
78d7373c82SJulian Elischer static const struct ng_parse_struct_field ng_checksum_config_type_fields[]
79d7373c82SJulian Elischer 	= NG_CHECKSUM_CONFIG_TYPE;
80d7373c82SJulian Elischer static const struct ng_parse_type ng_checksum_config_type = {
81d7373c82SJulian Elischer 	&ng_parse_struct_type,
82d7373c82SJulian Elischer 	&ng_checksum_config_type_fields
83d7373c82SJulian Elischer };
84d7373c82SJulian Elischer 
85d7373c82SJulian Elischer static const struct ng_parse_struct_field ng_checksum_stats_fields[]
86d7373c82SJulian Elischer 	= NG_CHECKSUM_STATS_TYPE;
87d7373c82SJulian Elischer static const struct ng_parse_type ng_checksum_stats_type = {
88d7373c82SJulian Elischer 	&ng_parse_struct_type,
89d7373c82SJulian Elischer 	&ng_checksum_stats_fields
90d7373c82SJulian Elischer };
91d7373c82SJulian Elischer 
92d7373c82SJulian Elischer static const struct ng_cmdlist ng_checksum_cmdlist[] = {
93d7373c82SJulian Elischer 	{
94d7373c82SJulian Elischer 		NGM_CHECKSUM_COOKIE,
95d7373c82SJulian Elischer 		NGM_CHECKSUM_GETDLT,
96d7373c82SJulian Elischer 		"getdlt",
97d7373c82SJulian Elischer 		NULL,
98d7373c82SJulian Elischer 		&ng_parse_uint8_type
99d7373c82SJulian Elischer 	},
100d7373c82SJulian Elischer 	{
101d7373c82SJulian Elischer 		NGM_CHECKSUM_COOKIE,
102d7373c82SJulian Elischer 		NGM_CHECKSUM_SETDLT,
103d7373c82SJulian Elischer 		"setdlt",
104d7373c82SJulian Elischer 		&ng_parse_uint8_type,
105d7373c82SJulian Elischer 		NULL
106d7373c82SJulian Elischer 	},
107d7373c82SJulian Elischer 	{
108d7373c82SJulian Elischer 		NGM_CHECKSUM_COOKIE,
109d7373c82SJulian Elischer 		NGM_CHECKSUM_GETCONFIG,
110d7373c82SJulian Elischer 		"getconfig",
111d7373c82SJulian Elischer 		NULL,
112d7373c82SJulian Elischer 		&ng_checksum_config_type
113d7373c82SJulian Elischer 	},
114d7373c82SJulian Elischer 	{
115d7373c82SJulian Elischer 		NGM_CHECKSUM_COOKIE,
116d7373c82SJulian Elischer 		NGM_CHECKSUM_SETCONFIG,
117d7373c82SJulian Elischer 		"setconfig",
118d7373c82SJulian Elischer 		&ng_checksum_config_type,
119d7373c82SJulian Elischer 		NULL
120d7373c82SJulian Elischer 	},
121d7373c82SJulian Elischer 	{
122d7373c82SJulian Elischer 		NGM_CHECKSUM_COOKIE,
123d7373c82SJulian Elischer 		NGM_CHECKSUM_GET_STATS,
124d7373c82SJulian Elischer 		"getstats",
125d7373c82SJulian Elischer 		NULL,
126d7373c82SJulian Elischer 		&ng_checksum_stats_type
127d7373c82SJulian Elischer 	},
128d7373c82SJulian Elischer 	{
129d7373c82SJulian Elischer 		NGM_CHECKSUM_COOKIE,
130d7373c82SJulian Elischer 		NGM_CHECKSUM_CLR_STATS,
131d7373c82SJulian Elischer 		"clrstats",
132d7373c82SJulian Elischer 		NULL,
133d7373c82SJulian Elischer 		NULL
134d7373c82SJulian Elischer 	},
135d7373c82SJulian Elischer 	{
136d7373c82SJulian Elischer 		NGM_CHECKSUM_COOKIE,
137d7373c82SJulian Elischer 		NGM_CHECKSUM_GETCLR_STATS,
138d7373c82SJulian Elischer 		"getclrstats",
139d7373c82SJulian Elischer 		NULL,
140d7373c82SJulian Elischer 		&ng_checksum_stats_type
141d7373c82SJulian Elischer 	},
142d7373c82SJulian Elischer 	{ 0 }
143d7373c82SJulian Elischer };
144d7373c82SJulian Elischer 
145d7373c82SJulian Elischer static struct ng_type typestruct = {
146d7373c82SJulian Elischer 	.version =	NG_ABI_VERSION,
147d7373c82SJulian Elischer 	.name =		NG_CHECKSUM_NODE_TYPE,
148d7373c82SJulian Elischer 	.constructor =	ng_checksum_constructor,
149d7373c82SJulian Elischer 	.rcvmsg =	ng_checksum_rcvmsg,
150d7373c82SJulian Elischer 	.shutdown =	ng_checksum_shutdown,
151d7373c82SJulian Elischer 	.newhook =	ng_checksum_newhook,
152d7373c82SJulian Elischer 	.rcvdata =	ng_checksum_rcvdata,
153d7373c82SJulian Elischer 	.disconnect =	ng_checksum_disconnect,
154d7373c82SJulian Elischer 	.cmdlist =	ng_checksum_cmdlist,
155d7373c82SJulian Elischer };
156d7373c82SJulian Elischer 
157d7373c82SJulian Elischer NETGRAPH_INIT(checksum, &typestruct);
158d7373c82SJulian Elischer 
159d7373c82SJulian Elischer static int
ng_checksum_constructor(node_p node)160d7373c82SJulian Elischer ng_checksum_constructor(node_p node)
161d7373c82SJulian Elischer {
162d7373c82SJulian Elischer 	priv_p priv;
163d7373c82SJulian Elischer 
164d7373c82SJulian Elischer 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_WAITOK|M_ZERO);
165d7373c82SJulian Elischer 	priv->dlt = DLT_RAW;
166d7373c82SJulian Elischer 
167d7373c82SJulian Elischer 	NG_NODE_SET_PRIVATE(node, priv);
168d7373c82SJulian Elischer 
169d7373c82SJulian Elischer 	return (0);
170d7373c82SJulian Elischer }
171d7373c82SJulian Elischer 
172d7373c82SJulian Elischer static int
ng_checksum_newhook(node_p node,hook_p hook,const char * name)173d7373c82SJulian Elischer ng_checksum_newhook(node_p node, hook_p hook, const char *name)
174d7373c82SJulian Elischer {
175d7373c82SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
176d7373c82SJulian Elischer 
177d7373c82SJulian Elischer 	if (strncmp(name, NG_CHECKSUM_HOOK_IN, strlen(NG_CHECKSUM_HOOK_IN)) == 0) {
178d7373c82SJulian Elischer 		priv->in = hook;
179d7373c82SJulian Elischer 	} else if (strncmp(name, NG_CHECKSUM_HOOK_OUT, strlen(NG_CHECKSUM_HOOK_OUT)) == 0) {
180d7373c82SJulian Elischer 		priv->out = hook;
181d7373c82SJulian Elischer 	} else
182d7373c82SJulian Elischer 		return (EINVAL);
183d7373c82SJulian Elischer 
184d7373c82SJulian Elischer 	return (0);
185d7373c82SJulian Elischer }
186d7373c82SJulian Elischer 
187d7373c82SJulian Elischer static int
ng_checksum_rcvmsg(node_p node,item_p item,hook_p lasthook)188d7373c82SJulian Elischer ng_checksum_rcvmsg(node_p node, item_p item, hook_p lasthook)
189d7373c82SJulian Elischer {
190d7373c82SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
191d7373c82SJulian Elischer 	struct ng_checksum_config *conf, *newconf;
192d7373c82SJulian Elischer 	struct ng_mesg *msg;
193d7373c82SJulian Elischer 	struct ng_mesg *resp = NULL;
194d7373c82SJulian Elischer 	int error = 0;
195d7373c82SJulian Elischer 
196d7373c82SJulian Elischer 	NGI_GET_MSG(item, msg);
197d7373c82SJulian Elischer 
198d7373c82SJulian Elischer 	if  (msg->header.typecookie != NGM_CHECKSUM_COOKIE)
199d7373c82SJulian Elischer 		ERROUT(EINVAL);
200d7373c82SJulian Elischer 
201d7373c82SJulian Elischer 	switch (msg->header.cmd)
202d7373c82SJulian Elischer 	{
203d7373c82SJulian Elischer 		case NGM_CHECKSUM_GETDLT:
204d7373c82SJulian Elischer 			NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
205d7373c82SJulian Elischer 
206d7373c82SJulian Elischer 			if (resp == NULL)
207d7373c82SJulian Elischer 				ERROUT(ENOMEM);
208d7373c82SJulian Elischer 
209d7373c82SJulian Elischer 			*((uint8_t *) resp->data) = priv->dlt;
210d7373c82SJulian Elischer 
211d7373c82SJulian Elischer 			break;
212d7373c82SJulian Elischer 
213d7373c82SJulian Elischer 		case NGM_CHECKSUM_SETDLT:
214d7373c82SJulian Elischer 			if (msg->header.arglen != sizeof(uint8_t))
215d7373c82SJulian Elischer 				ERROUT(EINVAL);
216d7373c82SJulian Elischer 
217d7373c82SJulian Elischer 			switch (*(uint8_t *) msg->data)
218d7373c82SJulian Elischer 			{
219d7373c82SJulian Elischer 				case DLT_EN10MB:
220d7373c82SJulian Elischer 				case DLT_RAW:
221d7373c82SJulian Elischer 					priv->dlt = *(uint8_t *) msg->data;
222d7373c82SJulian Elischer 					break;
223d7373c82SJulian Elischer 
224d7373c82SJulian Elischer 				default:
225d7373c82SJulian Elischer 					ERROUT(EINVAL);
226d7373c82SJulian Elischer 			}
227d7373c82SJulian Elischer 
228d7373c82SJulian Elischer 			break;
229d7373c82SJulian Elischer 
230d7373c82SJulian Elischer 		case NGM_CHECKSUM_GETCONFIG:
231d7373c82SJulian Elischer 			if (priv->conf == NULL)
232d7373c82SJulian Elischer 				ERROUT(0);
233d7373c82SJulian Elischer 
234d7373c82SJulian Elischer 			NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_config), M_WAITOK);
235d7373c82SJulian Elischer 
236d7373c82SJulian Elischer 			if (resp == NULL)
237d7373c82SJulian Elischer 				ERROUT(ENOMEM);
238d7373c82SJulian Elischer 
239d7373c82SJulian Elischer 			bcopy(priv->conf, resp->data, sizeof(struct ng_checksum_config));
240d7373c82SJulian Elischer 
241d7373c82SJulian Elischer 			break;
242d7373c82SJulian Elischer 
243d7373c82SJulian Elischer 		case NGM_CHECKSUM_SETCONFIG:
244d7373c82SJulian Elischer 			conf = (struct ng_checksum_config *) msg->data;
245d7373c82SJulian Elischer 
246d7373c82SJulian Elischer 			if (msg->header.arglen != sizeof(struct ng_checksum_config))
247d7373c82SJulian Elischer 				ERROUT(EINVAL);
248d7373c82SJulian Elischer 
249d7373c82SJulian Elischer 			conf->csum_flags &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6;
250d7373c82SJulian Elischer 			conf->csum_offload &= NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6;
251d7373c82SJulian Elischer 
252d7373c82SJulian Elischer 			newconf = malloc(sizeof(struct ng_checksum_config), M_NETGRAPH, M_WAITOK|M_ZERO);
253d7373c82SJulian Elischer 
254d7373c82SJulian Elischer 			bcopy(conf, newconf, sizeof(struct ng_checksum_config));
255d7373c82SJulian Elischer 
256d7373c82SJulian Elischer 			if (priv->conf)
257d7373c82SJulian Elischer 				free(priv->conf, M_NETGRAPH);
258d7373c82SJulian Elischer 
259d7373c82SJulian Elischer 			priv->conf = newconf;
260d7373c82SJulian Elischer 
261d7373c82SJulian Elischer 			break;
262d7373c82SJulian Elischer 
263d7373c82SJulian Elischer 		case NGM_CHECKSUM_GET_STATS:
264d7373c82SJulian Elischer 		case NGM_CHECKSUM_CLR_STATS:
265d7373c82SJulian Elischer 		case NGM_CHECKSUM_GETCLR_STATS:
266d7373c82SJulian Elischer 			if (msg->header.cmd != NGM_CHECKSUM_CLR_STATS) {
267d7373c82SJulian Elischer 				NG_MKRESPONSE(resp, msg, sizeof(struct ng_checksum_stats), M_WAITOK);
268d7373c82SJulian Elischer 
269d7373c82SJulian Elischer 				if (resp == NULL)
270d7373c82SJulian Elischer 					ERROUT(ENOMEM);
271d7373c82SJulian Elischer 
272d7373c82SJulian Elischer 				bcopy(&(priv->stats), resp->data, sizeof(struct ng_checksum_stats));
273d7373c82SJulian Elischer 			}
274d7373c82SJulian Elischer 
275d7373c82SJulian Elischer 			if (msg->header.cmd != NGM_CHECKSUM_GET_STATS)
276d7373c82SJulian Elischer 				bzero(&(priv->stats), sizeof(struct ng_checksum_stats));
277d7373c82SJulian Elischer 
278d7373c82SJulian Elischer 			break;
279d7373c82SJulian Elischer 
280d7373c82SJulian Elischer 		default:
281d7373c82SJulian Elischer 			ERROUT(EINVAL);
282d7373c82SJulian Elischer 	}
283d7373c82SJulian Elischer 
284d7373c82SJulian Elischer done:
285d7373c82SJulian Elischer 	NG_RESPOND_MSG(error, node, item, resp);
286d7373c82SJulian Elischer 	NG_FREE_MSG(msg);
287d7373c82SJulian Elischer 
288d7373c82SJulian Elischer 	return (error);
289d7373c82SJulian Elischer }
290d7373c82SJulian Elischer 
291d7373c82SJulian Elischer #define	PULLUP_CHECK(mbuf, length) do {					\
292d7373c82SJulian Elischer 	pullup_len += length;						\
293d7373c82SJulian Elischer 	if (((mbuf)->m_pkthdr.len < pullup_len) ||			\
294d7373c82SJulian Elischer 	    (pullup_len > MHLEN)) {					\
295d7373c82SJulian Elischer 		return (EINVAL);					\
296d7373c82SJulian Elischer 	}								\
297d7373c82SJulian Elischer 	if ((mbuf)->m_len < pullup_len &&				\
298d7373c82SJulian Elischer 	    (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) {	\
299d7373c82SJulian Elischer 		return (ENOBUFS);					\
300d7373c82SJulian Elischer 	}								\
301d7373c82SJulian Elischer } while (0)
302d7373c82SJulian Elischer 
303d7373c82SJulian Elischer #ifdef INET
304d7373c82SJulian Elischer static int
checksum_ipv4(priv_p priv,struct mbuf * m,int l3_offset)305d7373c82SJulian Elischer checksum_ipv4(priv_p priv, struct mbuf *m, int l3_offset)
306d7373c82SJulian Elischer {
307d7373c82SJulian Elischer 	struct ip *ip4;
308d7373c82SJulian Elischer 	int pullup_len;
309d7373c82SJulian Elischer 	int hlen, plen;
310d7373c82SJulian Elischer 	int processed = 0;
311d7373c82SJulian Elischer 
312d7373c82SJulian Elischer 	pullup_len = l3_offset;
313d7373c82SJulian Elischer 
314d7373c82SJulian Elischer 	PULLUP_CHECK(m, sizeof(struct ip));
315d7373c82SJulian Elischer 	ip4 = (struct ip *) mtodo(m, l3_offset);
316d7373c82SJulian Elischer 
317d7373c82SJulian Elischer 	if (ip4->ip_v != IPVERSION)
318d7373c82SJulian Elischer 		return (EOPNOTSUPP);
319d7373c82SJulian Elischer 
320d7373c82SJulian Elischer 	hlen = ip4->ip_hl << 2;
321d7373c82SJulian Elischer 	plen = ntohs(ip4->ip_len);
322d7373c82SJulian Elischer 
323d7373c82SJulian Elischer 	if (hlen < sizeof(struct ip) || m->m_pkthdr.len < l3_offset + plen)
324d7373c82SJulian Elischer 		return (EINVAL);
325d7373c82SJulian Elischer 
326d7373c82SJulian Elischer 	if (m->m_pkthdr.csum_flags & CSUM_IP) {
327d7373c82SJulian Elischer 		ip4->ip_sum = 0;
328d7373c82SJulian Elischer 
329d7373c82SJulian Elischer 		if ((priv->conf->csum_offload & CSUM_IP) == 0) {
330d7373c82SJulian Elischer 			if (hlen == sizeof(struct ip))
331d7373c82SJulian Elischer 				ip4->ip_sum = in_cksum_hdr(ip4);
332d7373c82SJulian Elischer 			else
333d7373c82SJulian Elischer 				ip4->ip_sum = in_cksum_skip(m, l3_offset + hlen, l3_offset);
334d7373c82SJulian Elischer 
335d7373c82SJulian Elischer 			m->m_pkthdr.csum_flags &= ~CSUM_IP;
336d7373c82SJulian Elischer 		}
337d7373c82SJulian Elischer 
338d7373c82SJulian Elischer 		processed = 1;
339d7373c82SJulian Elischer 	}
340d7373c82SJulian Elischer 
341d7373c82SJulian Elischer 	pullup_len = l3_offset + hlen;
342d7373c82SJulian Elischer 
343d7373c82SJulian Elischer 	/* We can not calculate a checksum fragmented packets */
344d7373c82SJulian Elischer 	if (ip4->ip_off & htons(IP_MF|IP_OFFMASK)) {
345d7373c82SJulian Elischer 		m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP);
346d7373c82SJulian Elischer 		return (0);
347d7373c82SJulian Elischer 	}
348d7373c82SJulian Elischer 
349d7373c82SJulian Elischer 	switch (ip4->ip_p)
350d7373c82SJulian Elischer 	{
351d7373c82SJulian Elischer 		case IPPROTO_TCP:
352d7373c82SJulian Elischer 			if (m->m_pkthdr.csum_flags & CSUM_TCP) {
353d7373c82SJulian Elischer 				struct tcphdr *th;
354d7373c82SJulian Elischer 
355d7373c82SJulian Elischer 				PULLUP_CHECK(m, sizeof(struct tcphdr));
356d7373c82SJulian Elischer 				th = (struct tcphdr *) mtodo(m, l3_offset + hlen);
357d7373c82SJulian Elischer 
358d7373c82SJulian Elischer 				th->th_sum = in_pseudo(ip4->ip_src.s_addr,
359d7373c82SJulian Elischer 				    ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen));
360d7373c82SJulian Elischer 
361d7373c82SJulian Elischer 				if ((priv->conf->csum_offload & CSUM_TCP) == 0) {
362d7373c82SJulian Elischer 					th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen);
363d7373c82SJulian Elischer 					m->m_pkthdr.csum_flags &= ~CSUM_TCP;
364d7373c82SJulian Elischer 				}
365d7373c82SJulian Elischer 
366d7373c82SJulian Elischer 				processed = 1;
367d7373c82SJulian Elischer 			}
368d7373c82SJulian Elischer 
369d7373c82SJulian Elischer 			m->m_pkthdr.csum_flags &= ~CSUM_UDP;
370d7373c82SJulian Elischer 			break;
371d7373c82SJulian Elischer 
372d7373c82SJulian Elischer 		case IPPROTO_UDP:
373d7373c82SJulian Elischer 			if (m->m_pkthdr.csum_flags & CSUM_UDP) {
374d7373c82SJulian Elischer 				struct udphdr *uh;
375d7373c82SJulian Elischer 
376d7373c82SJulian Elischer 				PULLUP_CHECK(m, sizeof(struct udphdr));
377d7373c82SJulian Elischer 				uh = (struct udphdr *) mtodo(m, l3_offset + hlen);
378d7373c82SJulian Elischer 
379d7373c82SJulian Elischer 				uh->uh_sum = in_pseudo(ip4->ip_src.s_addr,
380d7373c82SJulian Elischer 				    ip4->ip_dst.s_addr, htons(ip4->ip_p + plen - hlen));
381d7373c82SJulian Elischer 
382d7373c82SJulian Elischer 				if ((priv->conf->csum_offload & CSUM_UDP) == 0) {
383d7373c82SJulian Elischer 					uh->uh_sum = in_cksum_skip(m,
384d7373c82SJulian Elischer 					    l3_offset + plen, l3_offset + hlen);
385d7373c82SJulian Elischer 
386d7373c82SJulian Elischer 					if (uh->uh_sum == 0)
387d7373c82SJulian Elischer 						uh->uh_sum = 0xffff;
388d7373c82SJulian Elischer 
389d7373c82SJulian Elischer 					m->m_pkthdr.csum_flags &= ~CSUM_UDP;
390d7373c82SJulian Elischer 				}
391d7373c82SJulian Elischer 
392d7373c82SJulian Elischer 				processed = 1;
393d7373c82SJulian Elischer 			}
394d7373c82SJulian Elischer 
395d7373c82SJulian Elischer 			m->m_pkthdr.csum_flags &= ~CSUM_TCP;
396d7373c82SJulian Elischer 			break;
397d7373c82SJulian Elischer 
398d7373c82SJulian Elischer 		default:
399d7373c82SJulian Elischer 			m->m_pkthdr.csum_flags &= ~(CSUM_TCP|CSUM_UDP);
400d7373c82SJulian Elischer 			break;
401d7373c82SJulian Elischer 	}
402d7373c82SJulian Elischer 
403d7373c82SJulian Elischer 	m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV6;
404d7373c82SJulian Elischer 
405d7373c82SJulian Elischer 	if (processed)
406d7373c82SJulian Elischer 		priv->stats.processed++;
407d7373c82SJulian Elischer 
408d7373c82SJulian Elischer 	return (0);
409d7373c82SJulian Elischer }
410d7373c82SJulian Elischer #endif /* INET */
411d7373c82SJulian Elischer 
412d7373c82SJulian Elischer #ifdef INET6
413d7373c82SJulian Elischer static int
checksum_ipv6(priv_p priv,struct mbuf * m,int l3_offset)414d7373c82SJulian Elischer checksum_ipv6(priv_p priv, struct mbuf *m, int l3_offset)
415d7373c82SJulian Elischer {
416d7373c82SJulian Elischer 	struct ip6_hdr *ip6;
417d7373c82SJulian Elischer 	struct ip6_ext *ip6e = NULL;
418d7373c82SJulian Elischer 	int pullup_len;
419d7373c82SJulian Elischer 	int hlen, plen;
420d7373c82SJulian Elischer 	int nxt;
421d7373c82SJulian Elischer 	int processed = 0;
422d7373c82SJulian Elischer 
423d7373c82SJulian Elischer 	pullup_len = l3_offset;
424d7373c82SJulian Elischer 
425d7373c82SJulian Elischer 	PULLUP_CHECK(m, sizeof(struct ip6_hdr));
426d7373c82SJulian Elischer 	ip6 = (struct ip6_hdr *) mtodo(m, l3_offset);
427d7373c82SJulian Elischer 
428d7373c82SJulian Elischer 	if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)
429d7373c82SJulian Elischer 		return (EOPNOTSUPP);
430d7373c82SJulian Elischer 
431d7373c82SJulian Elischer 	hlen = sizeof(struct ip6_hdr);
432d7373c82SJulian Elischer 	plen = ntohs(ip6->ip6_plen) + hlen;
433d7373c82SJulian Elischer 
434d7373c82SJulian Elischer 	if (m->m_pkthdr.len < l3_offset + plen)
435d7373c82SJulian Elischer 		return (EINVAL);
436d7373c82SJulian Elischer 
437d7373c82SJulian Elischer 	nxt = ip6->ip6_nxt;
438d7373c82SJulian Elischer 
439d7373c82SJulian Elischer 	for (;;) {
440d7373c82SJulian Elischer 		switch (nxt)
441d7373c82SJulian Elischer 		{
442d7373c82SJulian Elischer 			case IPPROTO_DSTOPTS:
443d7373c82SJulian Elischer 			case IPPROTO_HOPOPTS:
444d7373c82SJulian Elischer 			case IPPROTO_ROUTING:
445d7373c82SJulian Elischer 				PULLUP_CHECK(m, sizeof(struct ip6_ext));
446d7373c82SJulian Elischer 				ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen);
447d7373c82SJulian Elischer 				nxt = ip6e->ip6e_nxt;
448d7373c82SJulian Elischer 				hlen += (ip6e->ip6e_len + 1) << 3;
449d7373c82SJulian Elischer 				pullup_len = l3_offset + hlen;
450d7373c82SJulian Elischer 				break;
451d7373c82SJulian Elischer 
452d7373c82SJulian Elischer 			case IPPROTO_AH:
453d7373c82SJulian Elischer 				PULLUP_CHECK(m, sizeof(struct ip6_ext));
454d7373c82SJulian Elischer 				ip6e = (struct ip6_ext *) mtodo(m, l3_offset + hlen);
455d7373c82SJulian Elischer 				nxt = ip6e->ip6e_nxt;
456d7373c82SJulian Elischer 				hlen += (ip6e->ip6e_len + 2) << 2;
457d7373c82SJulian Elischer 				pullup_len = l3_offset + hlen;
458d7373c82SJulian Elischer 				break;
459d7373c82SJulian Elischer 
460d7373c82SJulian Elischer 			case IPPROTO_FRAGMENT:
461d7373c82SJulian Elischer 				/* We can not calculate a checksum fragmented packets */
462d7373c82SJulian Elischer 				m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6);
463d7373c82SJulian Elischer 				return (0);
464d7373c82SJulian Elischer 
465d7373c82SJulian Elischer 			default:
466d7373c82SJulian Elischer 				goto loopend;
467d7373c82SJulian Elischer 		}
468d7373c82SJulian Elischer 
469d7373c82SJulian Elischer 		if (nxt == 0)
470d7373c82SJulian Elischer 			return (EINVAL);
471d7373c82SJulian Elischer 	}
472d7373c82SJulian Elischer 
473d7373c82SJulian Elischer loopend:
474d7373c82SJulian Elischer 
475d7373c82SJulian Elischer 	switch (nxt)
476d7373c82SJulian Elischer 	{
477d7373c82SJulian Elischer 		case IPPROTO_TCP:
478d7373c82SJulian Elischer 			if (m->m_pkthdr.csum_flags & CSUM_TCP_IPV6) {
479d7373c82SJulian Elischer 				struct tcphdr *th;
480d7373c82SJulian Elischer 
481d7373c82SJulian Elischer 				PULLUP_CHECK(m, sizeof(struct tcphdr));
482d7373c82SJulian Elischer 				th = (struct tcphdr *) mtodo(m, l3_offset + hlen);
483d7373c82SJulian Elischer 
484d7373c82SJulian Elischer 				th->th_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0);
485d7373c82SJulian Elischer 
486d7373c82SJulian Elischer 				if ((priv->conf->csum_offload & CSUM_TCP_IPV6) == 0) {
487d7373c82SJulian Elischer 					th->th_sum = in_cksum_skip(m, l3_offset + plen, l3_offset + hlen);
488d7373c82SJulian Elischer 					m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6;
489d7373c82SJulian Elischer 				}
490d7373c82SJulian Elischer 
491d7373c82SJulian Elischer 				processed = 1;
492d7373c82SJulian Elischer 			}
493d7373c82SJulian Elischer 
494d7373c82SJulian Elischer 			m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6;
495d7373c82SJulian Elischer 			break;
496d7373c82SJulian Elischer 
497d7373c82SJulian Elischer 		case IPPROTO_UDP:
498d7373c82SJulian Elischer 			if (m->m_pkthdr.csum_flags & CSUM_UDP_IPV6) {
499d7373c82SJulian Elischer 				struct udphdr *uh;
500d7373c82SJulian Elischer 
501d7373c82SJulian Elischer 				PULLUP_CHECK(m, sizeof(struct udphdr));
502d7373c82SJulian Elischer 				uh = (struct udphdr *) mtodo(m, l3_offset + hlen);
503d7373c82SJulian Elischer 
504d7373c82SJulian Elischer 				uh->uh_sum = in6_cksum_pseudo(ip6, plen - hlen, nxt, 0);
505d7373c82SJulian Elischer 
506d7373c82SJulian Elischer 				if ((priv->conf->csum_offload & CSUM_UDP_IPV6) == 0) {
507d7373c82SJulian Elischer 					uh->uh_sum = in_cksum_skip(m,
508d7373c82SJulian Elischer 					    l3_offset + plen, l3_offset + hlen);
509d7373c82SJulian Elischer 
510d7373c82SJulian Elischer 					if (uh->uh_sum == 0)
511d7373c82SJulian Elischer 						uh->uh_sum = 0xffff;
512d7373c82SJulian Elischer 
513d7373c82SJulian Elischer 					m->m_pkthdr.csum_flags &= ~CSUM_UDP_IPV6;
514d7373c82SJulian Elischer 				}
515d7373c82SJulian Elischer 
516d7373c82SJulian Elischer 				processed = 1;
517d7373c82SJulian Elischer 			}
518d7373c82SJulian Elischer 
519d7373c82SJulian Elischer 			m->m_pkthdr.csum_flags &= ~CSUM_TCP_IPV6;
520d7373c82SJulian Elischer 			break;
521d7373c82SJulian Elischer 
522d7373c82SJulian Elischer 		default:
523d7373c82SJulian Elischer 			m->m_pkthdr.csum_flags &= ~(CSUM_TCP_IPV6|CSUM_UDP_IPV6);
524d7373c82SJulian Elischer 			break;
525d7373c82SJulian Elischer 	}
526d7373c82SJulian Elischer 
527d7373c82SJulian Elischer 	m->m_pkthdr.csum_flags &= ~NG_CHECKSUM_CSUM_IPV4;
528d7373c82SJulian Elischer 
529d7373c82SJulian Elischer 	if (processed)
530d7373c82SJulian Elischer 		priv->stats.processed++;
531d7373c82SJulian Elischer 
532d7373c82SJulian Elischer 	return (0);
533d7373c82SJulian Elischer }
534d7373c82SJulian Elischer #endif /* INET6 */
535d7373c82SJulian Elischer 
536d7373c82SJulian Elischer #undef	PULLUP_CHECK
537d7373c82SJulian Elischer 
538d7373c82SJulian Elischer static int
ng_checksum_rcvdata(hook_p hook,item_p item)539d7373c82SJulian Elischer ng_checksum_rcvdata(hook_p hook, item_p item)
540d7373c82SJulian Elischer {
541d7373c82SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
542d7373c82SJulian Elischer 	struct mbuf *m;
543d7373c82SJulian Elischer 	hook_p out;
544d7373c82SJulian Elischer 	int error = 0;
545d7373c82SJulian Elischer 
546d7373c82SJulian Elischer 	priv->stats.received++;
547d7373c82SJulian Elischer 
548d7373c82SJulian Elischer 	NGI_GET_M(item, m);
549d7373c82SJulian Elischer 
550d7373c82SJulian Elischer #define	PULLUP_CHECK(mbuf, length) do {					\
551d7373c82SJulian Elischer 	pullup_len += length;						\
552d7373c82SJulian Elischer 	if (((mbuf)->m_pkthdr.len < pullup_len) ||			\
553d7373c82SJulian Elischer 	    (pullup_len > MHLEN)) {					\
554d7373c82SJulian Elischer 		error = EINVAL;						\
555d7373c82SJulian Elischer 		goto bypass;						\
556d7373c82SJulian Elischer 	}								\
557d7373c82SJulian Elischer 	if ((mbuf)->m_len < pullup_len &&				\
558d7373c82SJulian Elischer 	    (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) {	\
559d7373c82SJulian Elischer 		error = ENOBUFS;					\
560d7373c82SJulian Elischer 		goto drop;						\
561d7373c82SJulian Elischer 	}								\
562d7373c82SJulian Elischer } while (0)
563d7373c82SJulian Elischer 
564d7373c82SJulian Elischer 	if (!(priv->conf && hook == priv->in && m && (m->m_flags & M_PKTHDR)))
565d7373c82SJulian Elischer 		goto bypass;
566d7373c82SJulian Elischer 
567d7373c82SJulian Elischer 	m->m_pkthdr.csum_flags |= priv->conf->csum_flags;
568d7373c82SJulian Elischer 
569d7373c82SJulian Elischer 	if (m->m_pkthdr.csum_flags & (NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6))
570d7373c82SJulian Elischer 	{
571d7373c82SJulian Elischer 		struct ether_header *eh;
572d7373c82SJulian Elischer 		struct ng_checksum_vlan_header *vh;
573d7373c82SJulian Elischer 		int pullup_len = 0;
574d7373c82SJulian Elischer 		uint16_t etype;
575d7373c82SJulian Elischer 
576d7373c82SJulian Elischer 		m = m_unshare(m, M_NOWAIT);
577d7373c82SJulian Elischer 
578d7373c82SJulian Elischer 		if (m == NULL)
579d7373c82SJulian Elischer 			ERROUT(ENOMEM);
580d7373c82SJulian Elischer 
581d7373c82SJulian Elischer 		switch (priv->dlt)
582d7373c82SJulian Elischer 		{
583d7373c82SJulian Elischer 			case DLT_EN10MB:
584d7373c82SJulian Elischer 				PULLUP_CHECK(m, sizeof(struct ether_header));
585d7373c82SJulian Elischer 				eh = mtod(m, struct ether_header *);
586d7373c82SJulian Elischer 				etype = ntohs(eh->ether_type);
587d7373c82SJulian Elischer 
588d7373c82SJulian Elischer 				for (;;) {	/* QinQ support */
589d7373c82SJulian Elischer 					switch (etype)
590d7373c82SJulian Elischer 					{
591d7373c82SJulian Elischer 						case 0x8100:
592d7373c82SJulian Elischer 						case 0x88A8:
593d7373c82SJulian Elischer 						case 0x9100:
594d7373c82SJulian Elischer 							PULLUP_CHECK(m, sizeof(struct ng_checksum_vlan_header));
595d7373c82SJulian Elischer 							vh = (struct ng_checksum_vlan_header *) mtodo(m,
596d7373c82SJulian Elischer 							    pullup_len - sizeof(struct ng_checksum_vlan_header));
597d7373c82SJulian Elischer 							etype = ntohs(vh->etype);
598d7373c82SJulian Elischer 							break;
599d7373c82SJulian Elischer 
600d7373c82SJulian Elischer 						default:
601d7373c82SJulian Elischer 							goto loopend;
602d7373c82SJulian Elischer 					}
603d7373c82SJulian Elischer 				}
604d7373c82SJulian Elischer loopend:
605d7373c82SJulian Elischer #ifdef INET
606d7373c82SJulian Elischer 				if (etype == ETHERTYPE_IP &&
607d7373c82SJulian Elischer 				    (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)) {
608d7373c82SJulian Elischer 					error = checksum_ipv4(priv, m, pullup_len);
609d7373c82SJulian Elischer 					if (error == ENOBUFS)
610d7373c82SJulian Elischer 						goto drop;
611d7373c82SJulian Elischer 				} else
612d7373c82SJulian Elischer #endif
613d7373c82SJulian Elischer #ifdef INET6
614d7373c82SJulian Elischer 				if (etype == ETHERTYPE_IPV6 &&
615d7373c82SJulian Elischer 				    (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)) {
616d7373c82SJulian Elischer 					error = checksum_ipv6(priv, m, pullup_len);
617d7373c82SJulian Elischer 					if (error == ENOBUFS)
618d7373c82SJulian Elischer 						goto drop;
619d7373c82SJulian Elischer 				} else
620d7373c82SJulian Elischer #endif
621d7373c82SJulian Elischer 				{
622d7373c82SJulian Elischer 					m->m_pkthdr.csum_flags &=
623d7373c82SJulian Elischer 					    ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6);
624d7373c82SJulian Elischer 				}
625d7373c82SJulian Elischer 
626d7373c82SJulian Elischer 				break;
627d7373c82SJulian Elischer 
628d7373c82SJulian Elischer 			case DLT_RAW:
629d7373c82SJulian Elischer #ifdef INET
630d7373c82SJulian Elischer 				if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV4)
631d7373c82SJulian Elischer 				{
632d7373c82SJulian Elischer 					error = checksum_ipv4(priv, m, pullup_len);
633d7373c82SJulian Elischer 
634d7373c82SJulian Elischer 					if (error == 0)
635d7373c82SJulian Elischer 						goto bypass;
636d7373c82SJulian Elischer 					else if (error == ENOBUFS)
637d7373c82SJulian Elischer 						goto drop;
638d7373c82SJulian Elischer 				}
639d7373c82SJulian Elischer #endif
640d7373c82SJulian Elischer #ifdef INET6
641d7373c82SJulian Elischer 				if (m->m_pkthdr.csum_flags & NG_CHECKSUM_CSUM_IPV6)
642d7373c82SJulian Elischer 				{
643d7373c82SJulian Elischer 					error = checksum_ipv6(priv, m, pullup_len);
644d7373c82SJulian Elischer 
645d7373c82SJulian Elischer 					if (error == 0)
646d7373c82SJulian Elischer 						goto bypass;
647d7373c82SJulian Elischer 					else if (error == ENOBUFS)
648d7373c82SJulian Elischer 						goto drop;
649d7373c82SJulian Elischer 				}
650d7373c82SJulian Elischer #endif
651d7373c82SJulian Elischer 				if (error)
652d7373c82SJulian Elischer 					m->m_pkthdr.csum_flags &=
653d7373c82SJulian Elischer 					    ~(NG_CHECKSUM_CSUM_IPV4|NG_CHECKSUM_CSUM_IPV6);
654d7373c82SJulian Elischer 
655d7373c82SJulian Elischer 				break;
656d7373c82SJulian Elischer 
657d7373c82SJulian Elischer 			default:
658d7373c82SJulian Elischer 				ERROUT(EINVAL);
659d7373c82SJulian Elischer 		}
660d7373c82SJulian Elischer 	}
661d7373c82SJulian Elischer 
662d7373c82SJulian Elischer #undef	PULLUP_CHECK
663d7373c82SJulian Elischer 
664d7373c82SJulian Elischer bypass:
665d7373c82SJulian Elischer 	out = NULL;
666d7373c82SJulian Elischer 
667d7373c82SJulian Elischer 	if (hook == priv->in) {
668d7373c82SJulian Elischer 		/* return frames on 'in' hook if 'out' not connected */
669d7373c82SJulian Elischer 		out = priv->out ? priv->out : priv->in;
670d7373c82SJulian Elischer 	} else if (hook == priv->out && priv->in) {
671d7373c82SJulian Elischer 		/* pass frames on 'out' hook if 'in' connected */
672d7373c82SJulian Elischer 		out = priv->in;
673d7373c82SJulian Elischer 	}
674d7373c82SJulian Elischer 
675d7373c82SJulian Elischer 	if (out == NULL)
676d7373c82SJulian Elischer 		ERROUT(0);
677d7373c82SJulian Elischer 
678d7373c82SJulian Elischer 	NG_FWD_NEW_DATA(error, item, out, m);
679d7373c82SJulian Elischer 
680d7373c82SJulian Elischer 	return (error);
681d7373c82SJulian Elischer 
682d7373c82SJulian Elischer done:
683*687e510eSLutz Donnerhacke 	NG_FREE_M(m);
684d7373c82SJulian Elischer drop:
685d7373c82SJulian Elischer 	NG_FREE_ITEM(item);
686d7373c82SJulian Elischer 
687d7373c82SJulian Elischer 	priv->stats.dropped++;
688d7373c82SJulian Elischer 
689d7373c82SJulian Elischer 	return (error);
690d7373c82SJulian Elischer }
691d7373c82SJulian Elischer 
692d7373c82SJulian Elischer static int
ng_checksum_shutdown(node_p node)693d7373c82SJulian Elischer ng_checksum_shutdown(node_p node)
694d7373c82SJulian Elischer {
695d7373c82SJulian Elischer 	const priv_p priv = NG_NODE_PRIVATE(node);
696d7373c82SJulian Elischer 
697d7373c82SJulian Elischer 	NG_NODE_SET_PRIVATE(node, NULL);
698d7373c82SJulian Elischer 	NG_NODE_UNREF(node);
699d7373c82SJulian Elischer 
700d7373c82SJulian Elischer 	if (priv->conf)
701d7373c82SJulian Elischer 		free(priv->conf, M_NETGRAPH);
702d7373c82SJulian Elischer 
703d7373c82SJulian Elischer 	free(priv, M_NETGRAPH);
704d7373c82SJulian Elischer 
705d7373c82SJulian Elischer 	return (0);
706d7373c82SJulian Elischer }
707d7373c82SJulian Elischer 
708d7373c82SJulian Elischer static int
ng_checksum_disconnect(hook_p hook)709d7373c82SJulian Elischer ng_checksum_disconnect(hook_p hook)
710d7373c82SJulian Elischer {
711d7373c82SJulian Elischer 	priv_p priv;
712d7373c82SJulian Elischer 
713d7373c82SJulian Elischer 	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
714d7373c82SJulian Elischer 
715d7373c82SJulian Elischer 	if (hook == priv->in)
716d7373c82SJulian Elischer 		priv->in = NULL;
717d7373c82SJulian Elischer 
718d7373c82SJulian Elischer 	if (hook == priv->out)
719d7373c82SJulian Elischer 		priv->out = NULL;
720d7373c82SJulian Elischer 
721d7373c82SJulian Elischer 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
722d7373c82SJulian Elischer 	    NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
723d7373c82SJulian Elischer 		ng_rmnode_self(NG_HOOK_NODE(hook));
724d7373c82SJulian Elischer 
725d7373c82SJulian Elischer 	return (0);
726d7373c82SJulian Elischer }
727