1208946Sae/*-
2208946Sae * Copyright (C) 2010 by Maxim Ignatenko <gelraen.ua@gmail.com>
3208946Sae * All rights reserved.
4208946Sae *
5208946Sae * Redistribution and use in source and binary forms, with or without
6208946Sae * modification, are permitted provided that the following conditions
7208946Sae * are met:
8208946Sae * 1. Redistributions of source code must retain the above copyright
9208946Sae *    notice, this list of conditions and the following disclaimer.
10208946Sae * 2. Redistributions in binary form must reproduce the above copyright
11208946Sae *    notice, this list of conditions and the following disclaimer in the
12208946Sae *    documentation and/or other materials provided with the distribution.
13208946Sae *
14208946Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15208946Sae * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16208946Sae * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17208946Sae * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18208946Sae * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19208946Sae * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20208946Sae * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21208946Sae * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22208946Sae * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23208946Sae * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24208946Sae * SUCH DAMAGE.
25208946Sae *
26208946Sae */
27208946Sae
28208946Sae#include <sys/cdefs.h>
29208946Sae__FBSDID("$FreeBSD$");
30208946Sae
31208946Sae#include <sys/param.h>
32209194Sae#include <sys/systm.h>
33208946Sae#include <sys/kernel.h>
34209194Sae#include <sys/endian.h>
35208989Sae#include <sys/malloc.h>
36208989Sae#include <sys/mbuf.h>
37208946Sae#include <netgraph/ng_message.h>
38208946Sae#include <netgraph/ng_parse.h>
39208946Sae#include <netgraph/ng_patch.h>
40208946Sae#include <netgraph/netgraph.h>
41208946Sae
42208946Saestatic ng_constructor_t	ng_patch_constructor;
43208946Saestatic ng_rcvmsg_t	ng_patch_rcvmsg;
44208946Saestatic ng_shutdown_t	ng_patch_shutdown;
45208946Saestatic ng_newhook_t	ng_patch_newhook;
46208946Saestatic ng_rcvdata_t	ng_patch_rcvdata;
47208946Saestatic ng_disconnect_t	ng_patch_disconnect;
48208946Sae
49208946Saestatic int
50208989Saeng_patch_config_getlen(const struct ng_parse_type *type,
51208989Sae    const u_char *start, const u_char *buf)
52208946Sae{
53208946Sae	const struct ng_patch_config *p;
54208946Sae
55208946Sae	p = (const struct ng_patch_config *)(buf -
56209194Sae	    offsetof(struct ng_patch_config, ops));
57208946Sae	return (p->count);
58208946Sae}
59208946Sae
60208946Saestatic const struct ng_parse_struct_field ng_patch_op_type_fields[]
61208946Sae	= NG_PATCH_OP_TYPE_INFO;
62208946Saestatic const struct ng_parse_type ng_patch_op_type = {
63208946Sae	&ng_parse_struct_type,
64208946Sae	&ng_patch_op_type_fields
65208946Sae};
66208946Sae
67208946Saestatic const struct ng_parse_array_info ng_patch_confarr_info = {
68208946Sae	&ng_patch_op_type,
69208946Sae	&ng_patch_config_getlen
70208946Sae};
71208946Saestatic const struct ng_parse_type ng_patch_confarr_type = {
72208946Sae	&ng_parse_array_type,
73208946Sae	&ng_patch_confarr_info
74208946Sae};
75208946Sae
76208946Saestatic const struct ng_parse_struct_field ng_patch_config_type_fields[]
77208946Sae	= NG_PATCH_CONFIG_TYPE_INFO;
78208946Saestatic const struct ng_parse_type ng_patch_config_type = {
79208946Sae	&ng_parse_struct_type,
80208946Sae	&ng_patch_config_type_fields
81208946Sae};
82208946Sae
83208946Saestatic const struct ng_parse_struct_field ng_patch_stats_fields[]
84208946Sae	= NG_PATCH_STATS_TYPE_INFO;
85208946Saestatic const struct ng_parse_type ng_patch_stats_type = {
86208946Sae	&ng_parse_struct_type,
87208946Sae	&ng_patch_stats_fields
88208946Sae};
89208946Sae
90208946Saestatic const struct ng_cmdlist ng_patch_cmdlist[] = {
91208946Sae	{
92208946Sae		NGM_PATCH_COOKIE,
93208946Sae		NGM_PATCH_GETCONFIG,
94208946Sae		"getconfig",
95208946Sae		NULL,
96208946Sae		&ng_patch_config_type
97208946Sae	},
98208946Sae	{
99208946Sae		NGM_PATCH_COOKIE,
100208946Sae		NGM_PATCH_SETCONFIG,
101208946Sae		"setconfig",
102208946Sae		&ng_patch_config_type,
103208946Sae		NULL
104208946Sae	},
105208946Sae	{
106208946Sae		NGM_PATCH_COOKIE,
107208946Sae		NGM_PATCH_GET_STATS,
108208946Sae		"getstats",
109208946Sae		NULL,
110208946Sae		&ng_patch_stats_type
111208946Sae	},
112208946Sae	{
113208946Sae		NGM_PATCH_COOKIE,
114208946Sae		NGM_PATCH_CLR_STATS,
115208946Sae		"clrstats",
116208946Sae		NULL,
117208946Sae		NULL
118208946Sae	},
119208946Sae	{
120208946Sae		NGM_PATCH_COOKIE,
121208946Sae		NGM_PATCH_GETCLR_STATS,
122208946Sae		"getclrstats",
123208946Sae		NULL,
124208946Sae		&ng_patch_stats_type
125208946Sae	},
126208946Sae	{ 0 }
127208946Sae};
128208946Sae
129208946Saestatic struct ng_type typestruct = {
130208946Sae	.version =	NG_ABI_VERSION,
131208946Sae	.name =		NG_PATCH_NODE_TYPE,
132208946Sae	.constructor =	ng_patch_constructor,
133208946Sae	.rcvmsg =	ng_patch_rcvmsg,
134208946Sae	.shutdown =	ng_patch_shutdown,
135208946Sae	.newhook =	ng_patch_newhook,
136208946Sae	.rcvdata =	ng_patch_rcvdata,
137208946Sae	.disconnect =	ng_patch_disconnect,
138208946Sae	.cmdlist =	ng_patch_cmdlist,
139208946Sae};
140208946SaeNETGRAPH_INIT(patch, &typestruct);
141208946Sae
142208946Saeunion patch_val {
143208946Sae	uint8_t		v1;
144208946Sae	uint16_t	v2;
145208946Sae	uint32_t	v4;
146208946Sae	uint64_t	v8;
147208946Sae};
148208946Sae
149208946Saestruct ng_patch_priv {
150208946Sae	hook_p		in;
151208946Sae	hook_p		out;
152208946Sae	struct ng_patch_config *config;
153208946Sae	union patch_val *val;
154208946Sae	struct ng_patch_stats stats;
155208946Sae};
156208946Saetypedef struct ng_patch_priv *priv_p;
157208946Sae
158208989Sae#define	NG_PATCH_CONF_SIZE(count)	(sizeof(struct ng_patch_config) + \
159208946Sae		(count) * sizeof(struct ng_patch_op))
160208946Sae
161208946Saestatic void do_patch(priv_p conf, struct mbuf *m);
162208946Sae
163208946Saestatic int
164208946Saeng_patch_constructor(node_p node)
165208946Sae{
166208946Sae	priv_p privdata;
167208946Sae
168220767Sae	privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO);
169208946Sae	NG_NODE_SET_PRIVATE(node, privdata);
170208946Sae	privdata->in = NULL;
171208946Sae	privdata->out = NULL;
172208946Sae	privdata->config = NULL;
173208946Sae	return (0);
174208946Sae}
175208946Sae
176208946Saestatic int
177208946Saeng_patch_newhook(node_p node, hook_p hook, const char *name)
178208946Sae{
179208946Sae	const priv_p privp = NG_NODE_PRIVATE(node);
180208946Sae
181208946Sae	if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) {
182208946Sae		privp->in = hook;
183208946Sae	} else if (strncmp(name, NG_PATCH_HOOK_OUT,
184208946Sae	    strlen(NG_PATCH_HOOK_OUT)) == 0) {
185208946Sae		privp->out = hook;
186208946Sae	} else
187208946Sae		return (EINVAL);
188208946Sae	return(0);
189208946Sae}
190208946Sae
191208946Saestatic int
192208946Saeng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook)
193208946Sae{
194208946Sae	const priv_p privp = NG_NODE_PRIVATE(node);
195208989Sae	struct ng_patch_config *conf, *newconf;
196208989Sae	union patch_val *newval;
197208946Sae	struct ng_mesg *msg;
198208989Sae	struct ng_mesg *resp;
199208989Sae	int i, clear, error;
200208946Sae
201208989Sae	clear = error = 0;
202208989Sae	resp = NULL;
203208946Sae	NGI_GET_MSG(item, msg);
204208946Sae	switch (msg->header.typecookie) {
205208946Sae	case NGM_PATCH_COOKIE:
206208946Sae		switch (msg->header.cmd) {
207208946Sae		case NGM_PATCH_GETCONFIG:
208208946Sae			if (privp->config == NULL)
209208946Sae				break;
210208946Sae			NG_MKRESPONSE(resp, msg,
211220767Sae			    NG_PATCH_CONF_SIZE(privp->config->count),
212220767Sae			    M_WAITOK);
213208946Sae			bcopy(privp->config, resp->data,
214208946Sae			    NG_PATCH_CONF_SIZE(privp->config->count));
215208946Sae			break;
216208946Sae		case NGM_PATCH_SETCONFIG:
217208946Sae		    {
218208989Sae			if (msg->header.arglen <
219208989Sae			    sizeof(struct ng_patch_config)) {
220208946Sae				error = EINVAL;
221208946Sae				break;
222208946Sae			}
223208946Sae
224208946Sae			conf = (struct ng_patch_config *)msg->data;
225208989Sae			if (msg->header.arglen <
226208989Sae			    NG_PATCH_CONF_SIZE(conf->count)) {
227208946Sae				error = EINVAL;
228208946Sae				break;
229208946Sae			}
230208946Sae
231208946Sae			for(i = 0; i < conf->count; i++) {
232208946Sae				switch(conf->ops[i].length) {
233208946Sae				case 1:
234208946Sae				case 2:
235208946Sae				case 4:
236208946Sae				case 8:
237208946Sae					break;
238208946Sae				default:
239208946Sae					error = EINVAL;
240208946Sae					break;
241208946Sae				}
242208946Sae				if (error != 0)
243208946Sae					break;
244208946Sae			}
245208946Sae
246208946Sae			conf->csum_flags &= CSUM_IP | CSUM_TCP | CSUM_UDP |
247208946Sae			    CSUM_SCTP;
248208946Sae
249208946Sae			if (error == 0) {
250208989Sae				newconf = malloc(
251208989Sae				    NG_PATCH_CONF_SIZE(conf->count),
252220767Sae				    M_NETGRAPH, M_WAITOK);
253208989Sae				newval = malloc(conf->count *
254208989Sae				    sizeof(union patch_val), M_NETGRAPH,
255220767Sae				    M_WAITOK);
256208946Sae				for(i = 0; i < conf->count; i++) {
257208946Sae					switch (conf->ops[i].length) {
258208946Sae					case 1:
259208989Sae						newval[i].v1 =
260208989Sae						    conf->ops[i].value;
261208946Sae						break;
262208946Sae					case 2:
263208989Sae						newval[i].v2 =
264208989Sae						    conf->ops[i].value;
265208946Sae						break;
266208946Sae					case 4:
267208989Sae						newval[i].v4 =
268208989Sae						    conf->ops[i].value;
269208946Sae						break;
270208946Sae					case 8:
271208989Sae						newval[i].v8 =
272208989Sae						    conf->ops[i].value;
273208946Sae						break;
274208946Sae					}
275208946Sae				}
276208989Sae				bcopy(conf, newconf,
277208989Sae				    NG_PATCH_CONF_SIZE(conf->count));
278208946Sae				if (privp->val != NULL)
279208946Sae					free(privp->val, M_NETGRAPH);
280208946Sae				privp->val = newval;
281208946Sae				if (privp->config != NULL)
282208946Sae					free(privp->config, M_NETGRAPH);
283208946Sae				privp->config = newconf;
284208946Sae			}
285208946Sae			break;
286208946Sae		    }
287208946Sae		case NGM_PATCH_GETCLR_STATS:
288208946Sae			clear = 1;
289208946Sae			/* FALLTHROUGH */
290208946Sae		case NGM_PATCH_GET_STATS:
291208946Sae			NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats),
292220767Sae			    M_WAITOK);
293208946Sae			bcopy(&(privp->stats), resp->data,
294208946Sae			    sizeof(struct ng_patch_stats));
295208946Sae			if (clear == 0)
296208946Sae				break;
297208946Sae			/* else FALLTHROUGH */
298208946Sae		case NGM_PATCH_CLR_STATS:
299208946Sae			bzero(&(privp->stats), sizeof(struct ng_patch_stats));
300208946Sae			break;
301208946Sae		default:
302208946Sae			error = EINVAL;
303208946Sae			break;
304208946Sae		}
305208946Sae		break;
306208946Sae	default:
307208946Sae		error = EINVAL;
308208946Sae		break;
309208946Sae	}
310208946Sae
311208946Sae	NG_RESPOND_MSG(error, node, item, resp);
312208946Sae	NG_FREE_MSG(msg);
313208946Sae	return(error);
314208946Sae}
315208946Sae
316208946Saestatic void
317208946Saedo_patch(priv_p privp, struct mbuf *m)
318208946Sae{
319208989Sae	struct ng_patch_config *conf;
320208946Sae	uint64_t buf;
321208989Sae	int i, patched;
322208946Sae
323208989Sae	conf = privp->config;
324208989Sae	patched = 0;
325208946Sae	for(i = 0; i < conf->count; i++) {
326208989Sae		if (conf->ops[i].offset + conf->ops[i].length >
327208989Sae		    m->m_pkthdr.len)
328208946Sae			continue;
329208946Sae
330208946Sae		/* for "=" operation we don't need to copy data from mbuf */
331208946Sae		if (conf->ops[i].mode != NG_PATCH_MODE_SET) {
332208946Sae			m_copydata(m, conf->ops[i].offset,
333208946Sae			    conf->ops[i].length, (caddr_t)&buf);
334208946Sae		}
335208989Sae
336208946Sae		switch (conf->ops[i].length) {
337208946Sae		case 1:
338208946Sae			switch (conf->ops[i].mode) {
339208946Sae			case NG_PATCH_MODE_SET:
340208946Sae				*((uint8_t *)&buf) = privp->val[i].v1;
341208946Sae				break;
342208946Sae			case NG_PATCH_MODE_ADD:
343208946Sae				*((uint8_t *)&buf) += privp->val[i].v1;
344208946Sae				break;
345208946Sae			case NG_PATCH_MODE_SUB:
346208946Sae				*((uint8_t *)&buf) -= privp->val[i].v1;
347208946Sae				break;
348208946Sae			case NG_PATCH_MODE_MUL:
349208946Sae				*((uint8_t *)&buf) *= privp->val[i].v1;
350208946Sae				break;
351208946Sae			case NG_PATCH_MODE_DIV:
352208946Sae				*((uint8_t *)&buf) /= privp->val[i].v1;
353208946Sae				break;
354208946Sae			case NG_PATCH_MODE_NEG:
355208946Sae				*((int8_t *)&buf) = - *((int8_t *)&buf);
356208946Sae				break;
357208946Sae			case NG_PATCH_MODE_AND:
358208946Sae				*((uint8_t *)&buf) &= privp->val[i].v1;
359208946Sae				break;
360208946Sae			case NG_PATCH_MODE_OR:
361208946Sae				*((uint8_t *)&buf) |= privp->val[i].v1;
362208946Sae				break;
363208946Sae			case NG_PATCH_MODE_XOR:
364208946Sae				*((uint8_t *)&buf) ^= privp->val[i].v1;
365208946Sae				break;
366208946Sae			case NG_PATCH_MODE_SHL:
367208946Sae				*((uint8_t *)&buf) <<= privp->val[i].v1;
368208946Sae				break;
369208946Sae			case NG_PATCH_MODE_SHR:
370208946Sae				*((uint8_t *)&buf) >>= privp->val[i].v1;
371208946Sae				break;
372208946Sae			}
373208946Sae			break;
374208946Sae		case 2:
375208946Sae			*((int16_t *)&buf) =  ntohs(*((int16_t *)&buf));
376208946Sae			switch (conf->ops[i].mode) {
377208946Sae			case NG_PATCH_MODE_SET:
378208946Sae				*((uint16_t *)&buf) = privp->val[i].v2;
379208946Sae				break;
380208946Sae			case NG_PATCH_MODE_ADD:
381208946Sae				*((uint16_t *)&buf) += privp->val[i].v2;
382208946Sae				break;
383208946Sae			case NG_PATCH_MODE_SUB:
384208946Sae				*((uint16_t *)&buf) -= privp->val[i].v2;
385208946Sae				break;
386208946Sae			case NG_PATCH_MODE_MUL:
387208946Sae				*((uint16_t *)&buf) *= privp->val[i].v2;
388208946Sae				break;
389208946Sae			case NG_PATCH_MODE_DIV:
390208946Sae				*((uint16_t *)&buf) /= privp->val[i].v2;
391208946Sae				break;
392208946Sae			case NG_PATCH_MODE_NEG:
393208946Sae				*((int16_t *)&buf) = - *((int16_t *)&buf);
394208946Sae				break;
395208946Sae			case NG_PATCH_MODE_AND:
396208946Sae				*((uint16_t *)&buf) &= privp->val[i].v2;
397208946Sae				break;
398208946Sae			case NG_PATCH_MODE_OR:
399208946Sae				*((uint16_t *)&buf) |= privp->val[i].v2;
400208946Sae				break;
401208946Sae			case NG_PATCH_MODE_XOR:
402208946Sae				*((uint16_t *)&buf) ^= privp->val[i].v2;
403208946Sae				break;
404208946Sae			case NG_PATCH_MODE_SHL:
405208946Sae				*((uint16_t *)&buf) <<= privp->val[i].v2;
406208946Sae				break;
407208946Sae			case NG_PATCH_MODE_SHR:
408208946Sae				*((uint16_t *)&buf) >>= privp->val[i].v2;
409208946Sae				break;
410208946Sae			}
411208946Sae			*((int16_t *)&buf) =  htons(*((int16_t *)&buf));
412208946Sae			break;
413208946Sae		case 4:
414208946Sae			*((int32_t *)&buf) =  ntohl(*((int32_t *)&buf));
415208946Sae			switch (conf->ops[i].mode) {
416208946Sae			case NG_PATCH_MODE_SET:
417208946Sae				*((uint32_t *)&buf) = privp->val[i].v4;
418208946Sae				break;
419208946Sae			case NG_PATCH_MODE_ADD:
420208946Sae				*((uint32_t *)&buf) += privp->val[i].v4;
421208946Sae				break;
422208946Sae			case NG_PATCH_MODE_SUB:
423208946Sae				*((uint32_t *)&buf) -= privp->val[i].v4;
424208946Sae				break;
425208946Sae			case NG_PATCH_MODE_MUL:
426208946Sae				*((uint32_t *)&buf) *= privp->val[i].v4;
427208946Sae				break;
428208946Sae			case NG_PATCH_MODE_DIV:
429208946Sae				*((uint32_t *)&buf) /= privp->val[i].v4;
430208946Sae				break;
431208946Sae			case NG_PATCH_MODE_NEG:
432208946Sae				*((int32_t *)&buf) = - *((int32_t *)&buf);
433208946Sae				break;
434208946Sae			case NG_PATCH_MODE_AND:
435208946Sae				*((uint32_t *)&buf) &= privp->val[i].v4;
436208946Sae				break;
437208946Sae			case NG_PATCH_MODE_OR:
438208946Sae				*((uint32_t *)&buf) |= privp->val[i].v4;
439208946Sae				break;
440208946Sae			case NG_PATCH_MODE_XOR:
441208946Sae				*((uint32_t *)&buf) ^= privp->val[i].v4;
442208946Sae				break;
443208946Sae			case NG_PATCH_MODE_SHL:
444208946Sae				*((uint32_t *)&buf) <<= privp->val[i].v4;
445208946Sae				break;
446208946Sae			case NG_PATCH_MODE_SHR:
447208946Sae				*((uint32_t *)&buf) >>= privp->val[i].v4;
448208946Sae				break;
449208946Sae			}
450208946Sae			*((int32_t *)&buf) =  htonl(*((int32_t *)&buf));
451208946Sae			break;
452208946Sae		case 8:
453208946Sae			*((int64_t *)&buf) =  be64toh(*((int64_t *)&buf));
454208946Sae			switch (conf->ops[i].mode) {
455208946Sae			case NG_PATCH_MODE_SET:
456208946Sae				*((uint64_t *)&buf) = privp->val[i].v8;
457208946Sae				break;
458208946Sae			case NG_PATCH_MODE_ADD:
459208946Sae				*((uint64_t *)&buf) += privp->val[i].v8;
460208946Sae				break;
461208946Sae			case NG_PATCH_MODE_SUB:
462208946Sae				*((uint64_t *)&buf) -= privp->val[i].v8;
463208946Sae				break;
464208946Sae			case NG_PATCH_MODE_MUL:
465208946Sae				*((uint64_t *)&buf) *= privp->val[i].v8;
466208946Sae				break;
467208946Sae			case NG_PATCH_MODE_DIV:
468208946Sae				*((uint64_t *)&buf) /= privp->val[i].v8;
469208946Sae				break;
470208946Sae			case NG_PATCH_MODE_NEG:
471208946Sae				*((int64_t *)&buf) = - *((int64_t *)&buf);
472208946Sae				break;
473208946Sae			case NG_PATCH_MODE_AND:
474208946Sae				*((uint64_t *)&buf) &= privp->val[i].v8;
475208946Sae				break;
476208946Sae			case NG_PATCH_MODE_OR:
477208946Sae				*((uint64_t *)&buf) |= privp->val[i].v8;
478208946Sae				break;
479208946Sae			case NG_PATCH_MODE_XOR:
480208946Sae				*((uint64_t *)&buf) ^= privp->val[i].v8;
481208946Sae				break;
482208946Sae			case NG_PATCH_MODE_SHL:
483208946Sae				*((uint64_t *)&buf) <<= privp->val[i].v8;
484208946Sae				break;
485208946Sae			case NG_PATCH_MODE_SHR:
486208946Sae				*((uint64_t *)&buf) >>= privp->val[i].v8;
487208946Sae				break;
488208946Sae			}
489208946Sae			*((int64_t *)&buf) =  htobe64(*((int64_t *)&buf));
490208946Sae			break;
491208946Sae		}
492208946Sae
493208946Sae		m_copyback(m, conf->ops[i].offset, conf->ops[i].length,
494208946Sae		    (caddr_t)&buf);
495208946Sae		patched = 1;
496208946Sae	}
497208946Sae	if (patched > 0)
498208946Sae		privp->stats.patched++;
499208946Sae}
500208946Sae
501208946Saestatic int
502208946Saeng_patch_rcvdata(hook_p hook, item_p item)
503208946Sae{
504208946Sae	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
505208946Sae	struct mbuf *m;
506208946Sae	hook_p target;
507208946Sae	int error;
508208946Sae
509208946Sae	priv->stats.received++;
510208946Sae	NGI_GET_M(item, m);
511208946Sae	if (priv->config != NULL && hook == priv->in &&
512208946Sae	    (m->m_flags & M_PKTHDR) != 0) {
513208946Sae		m = m_unshare(m,M_NOWAIT);
514208946Sae		if (m == NULL) {
515208946Sae			priv->stats.dropped++;
516208946Sae			NG_FREE_ITEM(item);
517208946Sae			return (ENOMEM);
518208946Sae		}
519208946Sae		do_patch(priv, m);
520234574Smelifaro		m->m_pkthdr.csum_flags |= priv->config->csum_flags;
521208946Sae	}
522208946Sae
523208946Sae	target = NULL;
524208946Sae	if (hook == priv->in) {
525208946Sae		/* return frames on 'in' hook if 'out' not connected */
526208946Sae		if (priv->out != NULL)
527208946Sae			target = priv->out;
528208946Sae		else
529208946Sae			target = priv->in;
530208946Sae	}
531208946Sae	if (hook == priv->out && priv->in != NULL)
532208946Sae		target = priv->in;
533208946Sae
534208946Sae	if (target == NULL) {
535208946Sae		priv->stats.dropped++;
536208946Sae		NG_FREE_ITEM(item);
537208946Sae		NG_FREE_M(m);
538208946Sae		return (0);
539208946Sae	}
540208946Sae	NG_FWD_NEW_DATA(error, item, target, m);
541208946Sae	return (error);
542208946Sae}
543208946Sae
544208946Saestatic int
545208946Saeng_patch_shutdown(node_p node)
546208946Sae{
547208946Sae	const priv_p privdata = NG_NODE_PRIVATE(node);
548208946Sae
549208946Sae	if (privdata->val != NULL)
550208946Sae		free(privdata->val, M_NETGRAPH);
551208946Sae	if (privdata->config != NULL)
552208946Sae		free(privdata->config, M_NETGRAPH);
553208946Sae	NG_NODE_SET_PRIVATE(node, NULL);
554208946Sae	NG_NODE_UNREF(node);
555208946Sae	free(privdata, M_NETGRAPH);
556208946Sae	return (0);
557208946Sae}
558208946Sae
559208946Saestatic int
560208946Saeng_patch_disconnect(hook_p hook)
561208946Sae{
562208989Sae	priv_p priv;
563208946Sae
564208989Sae	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
565208946Sae	if (hook == priv->in) {
566208946Sae		priv->in = NULL;
567208946Sae	}
568208946Sae	if (hook == priv->out) {
569208946Sae		priv->out = NULL;
570208946Sae	}
571208989Sae	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
572208989Sae	    NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
573208946Sae		ng_rmnode_self(NG_HOOK_NODE(hook));
574208946Sae	return (0);
575208946Sae}
576208946Sae
577