xref: /freebsd/sys/netgraph/ng_patch.c (revision bf909fc9a43ea8d560214e4bae6344bc454074fb)
1d359a62dSAndrey V. Elsukov /*-
2*bf909fc9SJulian Elischer  * Copyright (c) 2010  Maxim Ignatenko <gelraen.ua@gmail.com>
3d359a62dSAndrey V. Elsukov  * All rights reserved.
4d359a62dSAndrey V. Elsukov  *
5d359a62dSAndrey V. Elsukov  * Redistribution and use in source and binary forms, with or without
6d359a62dSAndrey V. Elsukov  * modification, are permitted provided that the following conditions
7d359a62dSAndrey V. Elsukov  * are met:
8d359a62dSAndrey V. Elsukov  * 1. Redistributions of source code must retain the above copyright
9d359a62dSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer.
10d359a62dSAndrey V. Elsukov  * 2. Redistributions in binary form must reproduce the above copyright
11d359a62dSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer in the
12d359a62dSAndrey V. Elsukov  *    documentation and/or other materials provided with the distribution.
13d359a62dSAndrey V. Elsukov  *
14d359a62dSAndrey V. Elsukov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15d359a62dSAndrey V. Elsukov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d359a62dSAndrey V. Elsukov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17d359a62dSAndrey V. Elsukov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18d359a62dSAndrey V. Elsukov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d359a62dSAndrey V. Elsukov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d359a62dSAndrey V. Elsukov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d359a62dSAndrey V. Elsukov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d359a62dSAndrey V. Elsukov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d359a62dSAndrey V. Elsukov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d359a62dSAndrey V. Elsukov  * SUCH DAMAGE.
25d359a62dSAndrey V. Elsukov  *
26d359a62dSAndrey V. Elsukov  */
27d359a62dSAndrey V. Elsukov 
28d359a62dSAndrey V. Elsukov #include <sys/cdefs.h>
29d359a62dSAndrey V. Elsukov __FBSDID("$FreeBSD$");
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>
37d359a62dSAndrey V. Elsukov #include <netgraph/ng_message.h>
38d359a62dSAndrey V. Elsukov #include <netgraph/ng_parse.h>
39d359a62dSAndrey V. Elsukov #include <netgraph/ng_patch.h>
40d359a62dSAndrey V. Elsukov #include <netgraph/netgraph.h>
41d359a62dSAndrey V. Elsukov 
42d359a62dSAndrey V. Elsukov static ng_constructor_t	ng_patch_constructor;
43d359a62dSAndrey V. Elsukov static ng_rcvmsg_t	ng_patch_rcvmsg;
44d359a62dSAndrey V. Elsukov static ng_shutdown_t	ng_patch_shutdown;
45d359a62dSAndrey V. Elsukov static ng_newhook_t	ng_patch_newhook;
46d359a62dSAndrey V. Elsukov static ng_rcvdata_t	ng_patch_rcvdata;
47d359a62dSAndrey V. Elsukov static ng_disconnect_t	ng_patch_disconnect;
48d359a62dSAndrey V. Elsukov 
49d359a62dSAndrey V. Elsukov static int
506c0a2eb1SAndrey V. Elsukov ng_patch_config_getlen(const struct ng_parse_type *type,
516c0a2eb1SAndrey V. Elsukov     const u_char *start, const u_char *buf)
52d359a62dSAndrey V. Elsukov {
53*bf909fc9SJulian Elischer 	const struct ng_patch_config *conf;
54d359a62dSAndrey V. Elsukov 
55*bf909fc9SJulian Elischer 	conf = (const struct ng_patch_config *)(buf -
56cac2fe69SAndrey V. Elsukov 	    offsetof(struct ng_patch_config, ops));
57*bf909fc9SJulian Elischer 
58*bf909fc9SJulian Elischer 	return (conf->count);
59d359a62dSAndrey V. Elsukov }
60d359a62dSAndrey V. Elsukov 
61d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_op_type_fields[]
62d359a62dSAndrey V. Elsukov 	= NG_PATCH_OP_TYPE_INFO;
63d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_op_type = {
64d359a62dSAndrey V. Elsukov 	&ng_parse_struct_type,
65d359a62dSAndrey V. Elsukov 	&ng_patch_op_type_fields
66d359a62dSAndrey V. Elsukov };
67d359a62dSAndrey V. Elsukov 
68*bf909fc9SJulian Elischer static const struct ng_parse_array_info ng_patch_ops_array_info = {
69d359a62dSAndrey V. Elsukov 	&ng_patch_op_type,
70d359a62dSAndrey V. Elsukov 	&ng_patch_config_getlen
71d359a62dSAndrey V. Elsukov };
72*bf909fc9SJulian Elischer static const struct ng_parse_type ng_patch_ops_array_type = {
73d359a62dSAndrey V. Elsukov 	&ng_parse_array_type,
74*bf909fc9SJulian Elischer 	&ng_patch_ops_array_info
75d359a62dSAndrey V. Elsukov };
76d359a62dSAndrey V. Elsukov 
77d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_config_type_fields[]
78d359a62dSAndrey V. Elsukov 	= NG_PATCH_CONFIG_TYPE_INFO;
79d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_config_type = {
80d359a62dSAndrey V. Elsukov 	&ng_parse_struct_type,
81d359a62dSAndrey V. Elsukov 	&ng_patch_config_type_fields
82d359a62dSAndrey V. Elsukov };
83d359a62dSAndrey V. Elsukov 
84d359a62dSAndrey V. Elsukov static const struct ng_parse_struct_field ng_patch_stats_fields[]
85d359a62dSAndrey V. Elsukov 	= NG_PATCH_STATS_TYPE_INFO;
86d359a62dSAndrey V. Elsukov static const struct ng_parse_type ng_patch_stats_type = {
87d359a62dSAndrey V. Elsukov 	&ng_parse_struct_type,
88d359a62dSAndrey V. Elsukov 	&ng_patch_stats_fields
89d359a62dSAndrey V. Elsukov };
90d359a62dSAndrey V. Elsukov 
91d359a62dSAndrey V. Elsukov static const struct ng_cmdlist ng_patch_cmdlist[] = {
92d359a62dSAndrey V. Elsukov 	{
93d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
94d359a62dSAndrey V. Elsukov 		NGM_PATCH_GETCONFIG,
95d359a62dSAndrey V. Elsukov 		"getconfig",
96d359a62dSAndrey V. Elsukov 		NULL,
97d359a62dSAndrey V. Elsukov 		&ng_patch_config_type
98d359a62dSAndrey V. Elsukov 	},
99d359a62dSAndrey V. Elsukov 	{
100d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
101d359a62dSAndrey V. Elsukov 		NGM_PATCH_SETCONFIG,
102d359a62dSAndrey V. Elsukov 		"setconfig",
103d359a62dSAndrey V. Elsukov 		&ng_patch_config_type,
104d359a62dSAndrey V. Elsukov 		NULL
105d359a62dSAndrey V. Elsukov 	},
106d359a62dSAndrey V. Elsukov 	{
107d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
108d359a62dSAndrey V. Elsukov 		NGM_PATCH_GET_STATS,
109d359a62dSAndrey V. Elsukov 		"getstats",
110d359a62dSAndrey V. Elsukov 		NULL,
111d359a62dSAndrey V. Elsukov 		&ng_patch_stats_type
112d359a62dSAndrey V. Elsukov 	},
113d359a62dSAndrey V. Elsukov 	{
114d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
115d359a62dSAndrey V. Elsukov 		NGM_PATCH_CLR_STATS,
116d359a62dSAndrey V. Elsukov 		"clrstats",
117d359a62dSAndrey V. Elsukov 		NULL,
118d359a62dSAndrey V. Elsukov 		NULL
119d359a62dSAndrey V. Elsukov 	},
120d359a62dSAndrey V. Elsukov 	{
121d359a62dSAndrey V. Elsukov 		NGM_PATCH_COOKIE,
122d359a62dSAndrey V. Elsukov 		NGM_PATCH_GETCLR_STATS,
123d359a62dSAndrey V. Elsukov 		"getclrstats",
124d359a62dSAndrey V. Elsukov 		NULL,
125d359a62dSAndrey V. Elsukov 		&ng_patch_stats_type
126d359a62dSAndrey V. Elsukov 	},
127d359a62dSAndrey V. Elsukov 	{ 0 }
128d359a62dSAndrey V. Elsukov };
129d359a62dSAndrey V. Elsukov 
130d359a62dSAndrey V. Elsukov static struct ng_type typestruct = {
131d359a62dSAndrey V. Elsukov 	.version =	NG_ABI_VERSION,
132d359a62dSAndrey V. Elsukov 	.name =		NG_PATCH_NODE_TYPE,
133d359a62dSAndrey V. Elsukov 	.constructor =	ng_patch_constructor,
134d359a62dSAndrey V. Elsukov 	.rcvmsg =	ng_patch_rcvmsg,
135d359a62dSAndrey V. Elsukov 	.shutdown =	ng_patch_shutdown,
136d359a62dSAndrey V. Elsukov 	.newhook =	ng_patch_newhook,
137d359a62dSAndrey V. Elsukov 	.rcvdata =	ng_patch_rcvdata,
138d359a62dSAndrey V. Elsukov 	.disconnect =	ng_patch_disconnect,
139d359a62dSAndrey V. Elsukov 	.cmdlist =	ng_patch_cmdlist,
140d359a62dSAndrey V. Elsukov };
141*bf909fc9SJulian Elischer 
142d359a62dSAndrey V. Elsukov NETGRAPH_INIT(patch, &typestruct);
143d359a62dSAndrey V. Elsukov 
144d359a62dSAndrey V. Elsukov union patch_val {
145d359a62dSAndrey V. Elsukov 	uint8_t		v1;
146d359a62dSAndrey V. Elsukov 	uint16_t	v2;
147d359a62dSAndrey V. Elsukov 	uint32_t	v4;
148d359a62dSAndrey V. Elsukov 	uint64_t	v8;
149d359a62dSAndrey V. Elsukov };
150d359a62dSAndrey V. Elsukov 
151*bf909fc9SJulian Elischer /* private data */
152d359a62dSAndrey V. Elsukov struct ng_patch_priv {
153d359a62dSAndrey V. Elsukov 	hook_p		in;
154d359a62dSAndrey V. Elsukov 	hook_p		out;
155d359a62dSAndrey V. Elsukov 	struct ng_patch_config *config;
156d359a62dSAndrey V. Elsukov 	union patch_val *val;
157d359a62dSAndrey V. Elsukov 	struct ng_patch_stats stats;
158d359a62dSAndrey V. Elsukov };
159d359a62dSAndrey V. Elsukov typedef struct ng_patch_priv *priv_p;
160d359a62dSAndrey V. Elsukov 
161d359a62dSAndrey V. Elsukov #define	NG_PATCH_CONF_SIZE(count)	(sizeof(struct ng_patch_config) + \
162d359a62dSAndrey V. Elsukov 		(count) * sizeof(struct ng_patch_op))
163d359a62dSAndrey V. Elsukov 
164d359a62dSAndrey V. Elsukov static void do_patch(priv_p conf, struct mbuf *m);
165d359a62dSAndrey V. Elsukov 
166d359a62dSAndrey V. Elsukov static int
167d359a62dSAndrey V. Elsukov ng_patch_constructor(node_p node)
168d359a62dSAndrey V. Elsukov {
169d359a62dSAndrey V. Elsukov 	priv_p privdata;
170d359a62dSAndrey V. Elsukov 
171ffbfc0aaSAndrey V. Elsukov 	privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO);
172d359a62dSAndrey V. Elsukov 	NG_NODE_SET_PRIVATE(node, privdata);
173d359a62dSAndrey V. Elsukov 	privdata->in = NULL;
174d359a62dSAndrey V. Elsukov 	privdata->out = NULL;
175d359a62dSAndrey V. Elsukov 	privdata->config = NULL;
176d359a62dSAndrey V. Elsukov 	return (0);
177d359a62dSAndrey V. Elsukov }
178d359a62dSAndrey V. Elsukov 
179d359a62dSAndrey V. Elsukov static int
180d359a62dSAndrey V. Elsukov ng_patch_newhook(node_p node, hook_p hook, const char *name)
181d359a62dSAndrey V. Elsukov {
182d359a62dSAndrey V. Elsukov 	const priv_p privp = NG_NODE_PRIVATE(node);
183d359a62dSAndrey V. Elsukov 
184d359a62dSAndrey V. Elsukov 	if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) {
185d359a62dSAndrey V. Elsukov 		privp->in = hook;
186d359a62dSAndrey V. Elsukov 	} else if (strncmp(name, NG_PATCH_HOOK_OUT,
187d359a62dSAndrey V. Elsukov 	    strlen(NG_PATCH_HOOK_OUT)) == 0) {
188d359a62dSAndrey V. Elsukov 		privp->out = hook;
189d359a62dSAndrey V. Elsukov 	} else
190d359a62dSAndrey V. Elsukov 		return (EINVAL);
191d359a62dSAndrey V. Elsukov 	return(0);
192d359a62dSAndrey V. Elsukov }
193d359a62dSAndrey V. Elsukov 
194d359a62dSAndrey V. Elsukov static int
195d359a62dSAndrey V. Elsukov ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook)
196d359a62dSAndrey V. Elsukov {
197d359a62dSAndrey V. Elsukov 	const priv_p privp = NG_NODE_PRIVATE(node);
1986c0a2eb1SAndrey V. Elsukov 	struct ng_patch_config *conf, *newconf;
1996c0a2eb1SAndrey V. Elsukov 	union patch_val *newval;
200d359a62dSAndrey V. Elsukov 	struct ng_mesg *msg;
2016c0a2eb1SAndrey V. Elsukov 	struct ng_mesg *resp;
2026c0a2eb1SAndrey V. Elsukov 	int i, clear, error;
203d359a62dSAndrey V. Elsukov 
2046c0a2eb1SAndrey V. Elsukov 	clear = error = 0;
2056c0a2eb1SAndrey V. Elsukov 	resp = NULL;
206d359a62dSAndrey V. Elsukov 	NGI_GET_MSG(item, msg);
207d359a62dSAndrey V. Elsukov 	switch (msg->header.typecookie) {
208d359a62dSAndrey V. Elsukov 	case NGM_PATCH_COOKIE:
209d359a62dSAndrey V. Elsukov 		switch (msg->header.cmd) {
210d359a62dSAndrey V. Elsukov 		case NGM_PATCH_GETCONFIG:
211d359a62dSAndrey V. Elsukov 			if (privp->config == NULL)
212d359a62dSAndrey V. Elsukov 				break;
213d359a62dSAndrey V. Elsukov 			NG_MKRESPONSE(resp, msg,
214ffbfc0aaSAndrey V. Elsukov 			    NG_PATCH_CONF_SIZE(privp->config->count),
215ffbfc0aaSAndrey V. Elsukov 			    M_WAITOK);
216d359a62dSAndrey V. Elsukov 			bcopy(privp->config, resp->data,
217d359a62dSAndrey V. Elsukov 			    NG_PATCH_CONF_SIZE(privp->config->count));
218d359a62dSAndrey V. Elsukov 			break;
219d359a62dSAndrey V. Elsukov 		case NGM_PATCH_SETCONFIG:
220d359a62dSAndrey V. Elsukov 		    {
2216c0a2eb1SAndrey V. Elsukov 			if (msg->header.arglen <
2226c0a2eb1SAndrey V. Elsukov 			    sizeof(struct ng_patch_config)) {
223d359a62dSAndrey V. Elsukov 				error = EINVAL;
224d359a62dSAndrey V. Elsukov 				break;
225d359a62dSAndrey V. Elsukov 			}
226d359a62dSAndrey V. Elsukov 
227d359a62dSAndrey V. Elsukov 			conf = (struct ng_patch_config *)msg->data;
2286c0a2eb1SAndrey V. Elsukov 			if (msg->header.arglen <
2296c0a2eb1SAndrey V. Elsukov 			    NG_PATCH_CONF_SIZE(conf->count)) {
230d359a62dSAndrey V. Elsukov 				error = EINVAL;
231d359a62dSAndrey V. Elsukov 				break;
232d359a62dSAndrey V. Elsukov 			}
233d359a62dSAndrey V. Elsukov 
234d359a62dSAndrey V. Elsukov 			for(i = 0; i < conf->count; i++) {
235d359a62dSAndrey V. Elsukov 				switch(conf->ops[i].length) {
236d359a62dSAndrey V. Elsukov 				case 1:
237d359a62dSAndrey V. Elsukov 				case 2:
238d359a62dSAndrey V. Elsukov 				case 4:
239d359a62dSAndrey V. Elsukov 				case 8:
240d359a62dSAndrey V. Elsukov 					break;
241d359a62dSAndrey V. Elsukov 				default:
242d359a62dSAndrey V. Elsukov 					error = EINVAL;
243d359a62dSAndrey V. Elsukov 					break;
244d359a62dSAndrey V. Elsukov 				}
245d359a62dSAndrey V. Elsukov 				if (error != 0)
246d359a62dSAndrey V. Elsukov 					break;
247d359a62dSAndrey V. Elsukov 			}
248d359a62dSAndrey V. Elsukov 
249d359a62dSAndrey V. Elsukov 			conf->csum_flags &= CSUM_IP | CSUM_TCP | CSUM_UDP |
250d359a62dSAndrey V. Elsukov 			    CSUM_SCTP;
251d359a62dSAndrey V. Elsukov 
252d359a62dSAndrey V. Elsukov 			if (error == 0) {
2536c0a2eb1SAndrey V. Elsukov 				newconf = malloc(
2546c0a2eb1SAndrey V. Elsukov 				    NG_PATCH_CONF_SIZE(conf->count),
255ffbfc0aaSAndrey V. Elsukov 				    M_NETGRAPH, M_WAITOK);
2566c0a2eb1SAndrey V. Elsukov 				newval = malloc(conf->count *
2576c0a2eb1SAndrey V. Elsukov 				    sizeof(union patch_val), M_NETGRAPH,
258ffbfc0aaSAndrey V. Elsukov 				    M_WAITOK);
259d359a62dSAndrey V. Elsukov 				for(i = 0; i < conf->count; i++) {
260d359a62dSAndrey V. Elsukov 					switch (conf->ops[i].length) {
261d359a62dSAndrey V. Elsukov 					case 1:
2626c0a2eb1SAndrey V. Elsukov 						newval[i].v1 =
2636c0a2eb1SAndrey V. Elsukov 						    conf->ops[i].value;
264d359a62dSAndrey V. Elsukov 						break;
265d359a62dSAndrey V. Elsukov 					case 2:
2666c0a2eb1SAndrey V. Elsukov 						newval[i].v2 =
2676c0a2eb1SAndrey V. Elsukov 						    conf->ops[i].value;
268d359a62dSAndrey V. Elsukov 						break;
269d359a62dSAndrey V. Elsukov 					case 4:
2706c0a2eb1SAndrey V. Elsukov 						newval[i].v4 =
2716c0a2eb1SAndrey V. Elsukov 						    conf->ops[i].value;
272d359a62dSAndrey V. Elsukov 						break;
273d359a62dSAndrey V. Elsukov 					case 8:
2746c0a2eb1SAndrey V. Elsukov 						newval[i].v8 =
2756c0a2eb1SAndrey V. Elsukov 						    conf->ops[i].value;
276d359a62dSAndrey V. Elsukov 						break;
277d359a62dSAndrey V. Elsukov 					}
278d359a62dSAndrey V. Elsukov 				}
2796c0a2eb1SAndrey V. Elsukov 				bcopy(conf, newconf,
2806c0a2eb1SAndrey V. Elsukov 				    NG_PATCH_CONF_SIZE(conf->count));
281d359a62dSAndrey V. Elsukov 				if (privp->val != NULL)
282d359a62dSAndrey V. Elsukov 					free(privp->val, M_NETGRAPH);
283d359a62dSAndrey V. Elsukov 				privp->val = newval;
284d359a62dSAndrey V. Elsukov 				if (privp->config != NULL)
285d359a62dSAndrey V. Elsukov 					free(privp->config, M_NETGRAPH);
286d359a62dSAndrey V. Elsukov 				privp->config = newconf;
287d359a62dSAndrey V. Elsukov 			}
288d359a62dSAndrey V. Elsukov 			break;
289d359a62dSAndrey V. Elsukov 		    }
290d359a62dSAndrey V. Elsukov 		case NGM_PATCH_GETCLR_STATS:
291d359a62dSAndrey V. Elsukov 			clear = 1;
292d359a62dSAndrey V. Elsukov 			/* FALLTHROUGH */
293d359a62dSAndrey V. Elsukov 		case NGM_PATCH_GET_STATS:
294d359a62dSAndrey V. Elsukov 			NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats),
295ffbfc0aaSAndrey V. Elsukov 			    M_WAITOK);
296d359a62dSAndrey V. Elsukov 			bcopy(&(privp->stats), resp->data,
297d359a62dSAndrey V. Elsukov 			    sizeof(struct ng_patch_stats));
298d359a62dSAndrey V. Elsukov 			if (clear == 0)
299d359a62dSAndrey V. Elsukov 				break;
300d359a62dSAndrey V. Elsukov 			/* else FALLTHROUGH */
301d359a62dSAndrey V. Elsukov 		case NGM_PATCH_CLR_STATS:
302d359a62dSAndrey V. Elsukov 			bzero(&(privp->stats), sizeof(struct ng_patch_stats));
303d359a62dSAndrey V. Elsukov 			break;
304d359a62dSAndrey V. Elsukov 		default:
305d359a62dSAndrey V. Elsukov 			error = EINVAL;
306d359a62dSAndrey V. Elsukov 			break;
307d359a62dSAndrey V. Elsukov 		}
308d359a62dSAndrey V. Elsukov 		break;
309d359a62dSAndrey V. Elsukov 	default:
310d359a62dSAndrey V. Elsukov 		error = EINVAL;
311d359a62dSAndrey V. Elsukov 		break;
312d359a62dSAndrey V. Elsukov 	}
313d359a62dSAndrey V. Elsukov 
314d359a62dSAndrey V. Elsukov 	NG_RESPOND_MSG(error, node, item, resp);
315d359a62dSAndrey V. Elsukov 	NG_FREE_MSG(msg);
316d359a62dSAndrey V. Elsukov 	return(error);
317d359a62dSAndrey V. Elsukov }
318d359a62dSAndrey V. Elsukov 
319d359a62dSAndrey V. Elsukov static void
320d359a62dSAndrey V. Elsukov do_patch(priv_p privp, struct mbuf *m)
321d359a62dSAndrey V. Elsukov {
3226c0a2eb1SAndrey V. Elsukov 	struct ng_patch_config *conf;
323d359a62dSAndrey V. Elsukov 	uint64_t buf;
3246c0a2eb1SAndrey V. Elsukov 	int i, patched;
325d359a62dSAndrey V. Elsukov 
3266c0a2eb1SAndrey V. Elsukov 	conf = privp->config;
3276c0a2eb1SAndrey V. Elsukov 	patched = 0;
328d359a62dSAndrey V. Elsukov 	for(i = 0; i < conf->count; i++) {
3296c0a2eb1SAndrey V. Elsukov 		if (conf->ops[i].offset + conf->ops[i].length >
3306c0a2eb1SAndrey V. Elsukov 		    m->m_pkthdr.len)
331d359a62dSAndrey V. Elsukov 			continue;
332d359a62dSAndrey V. Elsukov 
333d359a62dSAndrey V. Elsukov 		/* for "=" operation we don't need to copy data from mbuf */
334d359a62dSAndrey V. Elsukov 		if (conf->ops[i].mode != NG_PATCH_MODE_SET) {
335d359a62dSAndrey V. Elsukov 			m_copydata(m, conf->ops[i].offset,
336d359a62dSAndrey V. Elsukov 			    conf->ops[i].length, (caddr_t)&buf);
337d359a62dSAndrey V. Elsukov 		}
338d359a62dSAndrey V. Elsukov 
339d359a62dSAndrey V. Elsukov 		switch (conf->ops[i].length) {
340d359a62dSAndrey V. Elsukov 		case 1:
341d359a62dSAndrey V. Elsukov 			switch (conf->ops[i].mode) {
342d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SET:
343d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) = privp->val[i].v1;
344d359a62dSAndrey V. Elsukov 				break;
345d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_ADD:
346d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) += privp->val[i].v1;
347d359a62dSAndrey V. Elsukov 				break;
348d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SUB:
349d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) -= privp->val[i].v1;
350d359a62dSAndrey V. Elsukov 				break;
351d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_MUL:
352d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) *= privp->val[i].v1;
353d359a62dSAndrey V. Elsukov 				break;
354d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_DIV:
355d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) /= privp->val[i].v1;
356d359a62dSAndrey V. Elsukov 				break;
357d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_NEG:
358d359a62dSAndrey V. Elsukov 				*((int8_t *)&buf) = - *((int8_t *)&buf);
359d359a62dSAndrey V. Elsukov 				break;
360d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_AND:
361d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) &= privp->val[i].v1;
362d359a62dSAndrey V. Elsukov 				break;
363d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_OR:
364d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) |= privp->val[i].v1;
365d359a62dSAndrey V. Elsukov 				break;
366d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_XOR:
367d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) ^= privp->val[i].v1;
368d359a62dSAndrey V. Elsukov 				break;
369d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHL:
370d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) <<= privp->val[i].v1;
371d359a62dSAndrey V. Elsukov 				break;
372d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHR:
373d359a62dSAndrey V. Elsukov 				*((uint8_t *)&buf) >>= privp->val[i].v1;
374d359a62dSAndrey V. Elsukov 				break;
375d359a62dSAndrey V. Elsukov 			}
376d359a62dSAndrey V. Elsukov 			break;
377d359a62dSAndrey V. Elsukov 		case 2:
378d359a62dSAndrey V. Elsukov 			*((int16_t *)&buf) =  ntohs(*((int16_t *)&buf));
379d359a62dSAndrey V. Elsukov 			switch (conf->ops[i].mode) {
380d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SET:
381d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) = privp->val[i].v2;
382d359a62dSAndrey V. Elsukov 				break;
383d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_ADD:
384d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) += privp->val[i].v2;
385d359a62dSAndrey V. Elsukov 				break;
386d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SUB:
387d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) -= privp->val[i].v2;
388d359a62dSAndrey V. Elsukov 				break;
389d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_MUL:
390d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) *= privp->val[i].v2;
391d359a62dSAndrey V. Elsukov 				break;
392d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_DIV:
393d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) /= privp->val[i].v2;
394d359a62dSAndrey V. Elsukov 				break;
395d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_NEG:
396d359a62dSAndrey V. Elsukov 				*((int16_t *)&buf) = - *((int16_t *)&buf);
397d359a62dSAndrey V. Elsukov 				break;
398d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_AND:
399d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) &= privp->val[i].v2;
400d359a62dSAndrey V. Elsukov 				break;
401d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_OR:
402d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) |= privp->val[i].v2;
403d359a62dSAndrey V. Elsukov 				break;
404d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_XOR:
405d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) ^= privp->val[i].v2;
406d359a62dSAndrey V. Elsukov 				break;
407d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHL:
408d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) <<= privp->val[i].v2;
409d359a62dSAndrey V. Elsukov 				break;
410d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHR:
411d359a62dSAndrey V. Elsukov 				*((uint16_t *)&buf) >>= privp->val[i].v2;
412d359a62dSAndrey V. Elsukov 				break;
413d359a62dSAndrey V. Elsukov 			}
414d359a62dSAndrey V. Elsukov 			*((int16_t *)&buf) =  htons(*((int16_t *)&buf));
415d359a62dSAndrey V. Elsukov 			break;
416d359a62dSAndrey V. Elsukov 		case 4:
417d359a62dSAndrey V. Elsukov 			*((int32_t *)&buf) =  ntohl(*((int32_t *)&buf));
418d359a62dSAndrey V. Elsukov 			switch (conf->ops[i].mode) {
419d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SET:
420d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) = privp->val[i].v4;
421d359a62dSAndrey V. Elsukov 				break;
422d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_ADD:
423d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) += privp->val[i].v4;
424d359a62dSAndrey V. Elsukov 				break;
425d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SUB:
426d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) -= privp->val[i].v4;
427d359a62dSAndrey V. Elsukov 				break;
428d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_MUL:
429d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) *= privp->val[i].v4;
430d359a62dSAndrey V. Elsukov 				break;
431d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_DIV:
432d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) /= privp->val[i].v4;
433d359a62dSAndrey V. Elsukov 				break;
434d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_NEG:
435d359a62dSAndrey V. Elsukov 				*((int32_t *)&buf) = - *((int32_t *)&buf);
436d359a62dSAndrey V. Elsukov 				break;
437d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_AND:
438d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) &= privp->val[i].v4;
439d359a62dSAndrey V. Elsukov 				break;
440d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_OR:
441d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) |= privp->val[i].v4;
442d359a62dSAndrey V. Elsukov 				break;
443d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_XOR:
444d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) ^= privp->val[i].v4;
445d359a62dSAndrey V. Elsukov 				break;
446d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHL:
447d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) <<= privp->val[i].v4;
448d359a62dSAndrey V. Elsukov 				break;
449d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHR:
450d359a62dSAndrey V. Elsukov 				*((uint32_t *)&buf) >>= privp->val[i].v4;
451d359a62dSAndrey V. Elsukov 				break;
452d359a62dSAndrey V. Elsukov 			}
453d359a62dSAndrey V. Elsukov 			*((int32_t *)&buf) =  htonl(*((int32_t *)&buf));
454d359a62dSAndrey V. Elsukov 			break;
455d359a62dSAndrey V. Elsukov 		case 8:
456d359a62dSAndrey V. Elsukov 			*((int64_t *)&buf) =  be64toh(*((int64_t *)&buf));
457d359a62dSAndrey V. Elsukov 			switch (conf->ops[i].mode) {
458d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SET:
459d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) = privp->val[i].v8;
460d359a62dSAndrey V. Elsukov 				break;
461d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_ADD:
462d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) += privp->val[i].v8;
463d359a62dSAndrey V. Elsukov 				break;
464d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SUB:
465d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) -= privp->val[i].v8;
466d359a62dSAndrey V. Elsukov 				break;
467d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_MUL:
468d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) *= privp->val[i].v8;
469d359a62dSAndrey V. Elsukov 				break;
470d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_DIV:
471d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) /= privp->val[i].v8;
472d359a62dSAndrey V. Elsukov 				break;
473d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_NEG:
474d359a62dSAndrey V. Elsukov 				*((int64_t *)&buf) = - *((int64_t *)&buf);
475d359a62dSAndrey V. Elsukov 				break;
476d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_AND:
477d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) &= privp->val[i].v8;
478d359a62dSAndrey V. Elsukov 				break;
479d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_OR:
480d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) |= privp->val[i].v8;
481d359a62dSAndrey V. Elsukov 				break;
482d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_XOR:
483d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) ^= privp->val[i].v8;
484d359a62dSAndrey V. Elsukov 				break;
485d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHL:
486d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) <<= privp->val[i].v8;
487d359a62dSAndrey V. Elsukov 				break;
488d359a62dSAndrey V. Elsukov 			case NG_PATCH_MODE_SHR:
489d359a62dSAndrey V. Elsukov 				*((uint64_t *)&buf) >>= privp->val[i].v8;
490d359a62dSAndrey V. Elsukov 				break;
491d359a62dSAndrey V. Elsukov 			}
492d359a62dSAndrey V. Elsukov 			*((int64_t *)&buf) =  htobe64(*((int64_t *)&buf));
493d359a62dSAndrey V. Elsukov 			break;
494d359a62dSAndrey V. Elsukov 		}
495d359a62dSAndrey V. Elsukov 
496d359a62dSAndrey V. Elsukov 		m_copyback(m, conf->ops[i].offset, conf->ops[i].length,
497d359a62dSAndrey V. Elsukov 		    (caddr_t)&buf);
498d359a62dSAndrey V. Elsukov 		patched = 1;
499d359a62dSAndrey V. Elsukov 	}
500d359a62dSAndrey V. Elsukov 	if (patched > 0)
501d359a62dSAndrey V. Elsukov 		privp->stats.patched++;
502d359a62dSAndrey V. Elsukov }
503d359a62dSAndrey V. Elsukov 
504d359a62dSAndrey V. Elsukov static int
505d359a62dSAndrey V. Elsukov ng_patch_rcvdata(hook_p hook, item_p item)
506d359a62dSAndrey V. Elsukov {
507d359a62dSAndrey V. Elsukov 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
508d359a62dSAndrey V. Elsukov 	struct mbuf *m;
509d359a62dSAndrey V. Elsukov 	hook_p target;
510d359a62dSAndrey V. Elsukov 	int error;
511d359a62dSAndrey V. Elsukov 
512d359a62dSAndrey V. Elsukov 	priv->stats.received++;
513d359a62dSAndrey V. Elsukov 	NGI_GET_M(item, m);
514d359a62dSAndrey V. Elsukov 	if (priv->config != NULL && hook == priv->in &&
515d359a62dSAndrey V. Elsukov 	    (m->m_flags & M_PKTHDR) != 0) {
516d359a62dSAndrey V. Elsukov 		m = m_unshare(m,M_NOWAIT);
517d359a62dSAndrey V. Elsukov 		if (m == NULL) {
518d359a62dSAndrey V. Elsukov 			priv->stats.dropped++;
519d359a62dSAndrey V. Elsukov 			NG_FREE_ITEM(item);
520d359a62dSAndrey V. Elsukov 			return (ENOMEM);
521d359a62dSAndrey V. Elsukov 		}
522d359a62dSAndrey V. Elsukov 		do_patch(priv, m);
5233a0cd8dbSAlexander V. Chernikov 		m->m_pkthdr.csum_flags |= priv->config->csum_flags;
524d359a62dSAndrey V. Elsukov 	}
525d359a62dSAndrey V. Elsukov 
526d359a62dSAndrey V. Elsukov 	target = NULL;
527d359a62dSAndrey V. Elsukov 	if (hook == priv->in) {
528d359a62dSAndrey V. Elsukov 		/* return frames on 'in' hook if 'out' not connected */
529d359a62dSAndrey V. Elsukov 		if (priv->out != NULL)
530d359a62dSAndrey V. Elsukov 			target = priv->out;
531d359a62dSAndrey V. Elsukov 		else
532d359a62dSAndrey V. Elsukov 			target = priv->in;
533d359a62dSAndrey V. Elsukov 	}
534d359a62dSAndrey V. Elsukov 	if (hook == priv->out && priv->in != NULL)
535d359a62dSAndrey V. Elsukov 		target = priv->in;
536d359a62dSAndrey V. Elsukov 
537d359a62dSAndrey V. Elsukov 	if (target == NULL) {
538d359a62dSAndrey V. Elsukov 		priv->stats.dropped++;
539d359a62dSAndrey V. Elsukov 		NG_FREE_ITEM(item);
540d359a62dSAndrey V. Elsukov 		NG_FREE_M(m);
541d359a62dSAndrey V. Elsukov 		return (0);
542d359a62dSAndrey V. Elsukov 	}
543d359a62dSAndrey V. Elsukov 	NG_FWD_NEW_DATA(error, item, target, m);
544d359a62dSAndrey V. Elsukov 	return (error);
545d359a62dSAndrey V. Elsukov }
546d359a62dSAndrey V. Elsukov 
547d359a62dSAndrey V. Elsukov static int
548d359a62dSAndrey V. Elsukov ng_patch_shutdown(node_p node)
549d359a62dSAndrey V. Elsukov {
550d359a62dSAndrey V. Elsukov 	const priv_p privdata = NG_NODE_PRIVATE(node);
551d359a62dSAndrey V. Elsukov 
552d359a62dSAndrey V. Elsukov 	if (privdata->val != NULL)
553d359a62dSAndrey V. Elsukov 		free(privdata->val, M_NETGRAPH);
554d359a62dSAndrey V. Elsukov 	if (privdata->config != NULL)
555d359a62dSAndrey V. Elsukov 		free(privdata->config, M_NETGRAPH);
556d359a62dSAndrey V. Elsukov 	NG_NODE_SET_PRIVATE(node, NULL);
557d359a62dSAndrey V. Elsukov 	NG_NODE_UNREF(node);
558d359a62dSAndrey V. Elsukov 	free(privdata, M_NETGRAPH);
559d359a62dSAndrey V. Elsukov 	return (0);
560d359a62dSAndrey V. Elsukov }
561d359a62dSAndrey V. Elsukov 
562d359a62dSAndrey V. Elsukov static int
563d359a62dSAndrey V. Elsukov ng_patch_disconnect(hook_p hook)
564d359a62dSAndrey V. Elsukov {
5656c0a2eb1SAndrey V. Elsukov 	priv_p priv;
566d359a62dSAndrey V. Elsukov 
5676c0a2eb1SAndrey V. Elsukov 	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
568d359a62dSAndrey V. Elsukov 	if (hook == priv->in) {
569d359a62dSAndrey V. Elsukov 		priv->in = NULL;
570d359a62dSAndrey V. Elsukov 	}
571d359a62dSAndrey V. Elsukov 	if (hook == priv->out) {
572d359a62dSAndrey V. Elsukov 		priv->out = NULL;
573d359a62dSAndrey V. Elsukov 	}
5746c0a2eb1SAndrey V. Elsukov 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
5756c0a2eb1SAndrey V. Elsukov 	    NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
576d359a62dSAndrey V. Elsukov 		ng_rmnode_self(NG_HOOK_NODE(hook));
577d359a62dSAndrey V. Elsukov 	return (0);
578d359a62dSAndrey V. Elsukov }
579d359a62dSAndrey V. Elsukov 
580