xref: /f-stack/freebsd/netgraph/ng_patch.c (revision 22ce4aff)
1a1fd9364Slogwang /*-
2*22ce4affSfengbojiang  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang  *
4*22ce4affSfengbojiang  * Copyright (c) 2010 Maxim Ignatenko <[email protected]>
5*22ce4affSfengbojiang  * Copyright (c) 2015 Dmitry Vagin <[email protected]>
6a1fd9364Slogwang  * All rights reserved.
7a1fd9364Slogwang  *
8a1fd9364Slogwang  * Redistribution and use in source and binary forms, with or without
9a1fd9364Slogwang  * modification, are permitted provided that the following conditions
10a1fd9364Slogwang  * are met:
11a1fd9364Slogwang  * 1. Redistributions of source code must retain the above copyright
12a1fd9364Slogwang  *    notice, this list of conditions and the following disclaimer.
13a1fd9364Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
14a1fd9364Slogwang  *    notice, this list of conditions and the following disclaimer in the
15a1fd9364Slogwang  *    documentation and/or other materials provided with the distribution.
16a1fd9364Slogwang  *
17a1fd9364Slogwang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18a1fd9364Slogwang  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a1fd9364Slogwang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a1fd9364Slogwang  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21a1fd9364Slogwang  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a1fd9364Slogwang  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a1fd9364Slogwang  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a1fd9364Slogwang  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a1fd9364Slogwang  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a1fd9364Slogwang  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a1fd9364Slogwang  * SUCH DAMAGE.
28a1fd9364Slogwang  *
29a1fd9364Slogwang  */
30a1fd9364Slogwang 
31a1fd9364Slogwang #include <sys/cdefs.h>
32a1fd9364Slogwang __FBSDID("$FreeBSD$");
33a1fd9364Slogwang 
34a1fd9364Slogwang #include <sys/param.h>
35a1fd9364Slogwang #include <sys/systm.h>
36a1fd9364Slogwang #include <sys/kernel.h>
37a1fd9364Slogwang #include <sys/endian.h>
38a1fd9364Slogwang #include <sys/malloc.h>
39a1fd9364Slogwang #include <sys/mbuf.h>
40*22ce4affSfengbojiang 
41*22ce4affSfengbojiang #include <net/bpf.h>
42*22ce4affSfengbojiang #include <net/ethernet.h>
43*22ce4affSfengbojiang 
44a1fd9364Slogwang #include <netgraph/ng_message.h>
45a1fd9364Slogwang #include <netgraph/ng_parse.h>
46a1fd9364Slogwang #include <netgraph/netgraph.h>
47a1fd9364Slogwang 
48*22ce4affSfengbojiang #include <netgraph/ng_patch.h>
49*22ce4affSfengbojiang 
50*22ce4affSfengbojiang /* private data */
51*22ce4affSfengbojiang struct ng_patch_priv {
52*22ce4affSfengbojiang 	hook_p		in;
53*22ce4affSfengbojiang 	hook_p		out;
54*22ce4affSfengbojiang 	uint8_t		dlt;	/* DLT_XXX from bpf.h */
55*22ce4affSfengbojiang 	struct ng_patch_stats stats;
56*22ce4affSfengbojiang 	struct ng_patch_config *conf;
57*22ce4affSfengbojiang };
58*22ce4affSfengbojiang 
59*22ce4affSfengbojiang typedef struct ng_patch_priv *priv_p;
60*22ce4affSfengbojiang 
61*22ce4affSfengbojiang /* Netgraph methods */
62a1fd9364Slogwang static ng_constructor_t	ng_patch_constructor;
63a1fd9364Slogwang static ng_rcvmsg_t	ng_patch_rcvmsg;
64a1fd9364Slogwang static ng_shutdown_t	ng_patch_shutdown;
65a1fd9364Slogwang static ng_newhook_t	ng_patch_newhook;
66a1fd9364Slogwang static ng_rcvdata_t	ng_patch_rcvdata;
67a1fd9364Slogwang static ng_disconnect_t	ng_patch_disconnect;
68*22ce4affSfengbojiang #define ERROUT(x) { error = (x); goto done; }
69a1fd9364Slogwang 
70a1fd9364Slogwang static int
ng_patch_config_getlen(const struct ng_parse_type * type,const u_char * start,const u_char * buf)71a1fd9364Slogwang ng_patch_config_getlen(const struct ng_parse_type *type,
72a1fd9364Slogwang     const u_char *start, const u_char *buf)
73a1fd9364Slogwang {
74*22ce4affSfengbojiang 	const struct ng_patch_config *conf;
75a1fd9364Slogwang 
76*22ce4affSfengbojiang 	conf = (const struct ng_patch_config *)(buf -
77a1fd9364Slogwang 	    offsetof(struct ng_patch_config, ops));
78*22ce4affSfengbojiang 
79*22ce4affSfengbojiang 	return (conf->count);
80a1fd9364Slogwang }
81a1fd9364Slogwang 
82a1fd9364Slogwang static const struct ng_parse_struct_field ng_patch_op_type_fields[]
83*22ce4affSfengbojiang 	= NG_PATCH_OP_TYPE;
84a1fd9364Slogwang static const struct ng_parse_type ng_patch_op_type = {
85a1fd9364Slogwang 	&ng_parse_struct_type,
86a1fd9364Slogwang 	&ng_patch_op_type_fields
87a1fd9364Slogwang };
88a1fd9364Slogwang 
89*22ce4affSfengbojiang static const struct ng_parse_array_info ng_patch_ops_array_info = {
90a1fd9364Slogwang 	&ng_patch_op_type,
91a1fd9364Slogwang 	&ng_patch_config_getlen
92a1fd9364Slogwang };
93*22ce4affSfengbojiang static const struct ng_parse_type ng_patch_ops_array_type = {
94a1fd9364Slogwang 	&ng_parse_array_type,
95*22ce4affSfengbojiang 	&ng_patch_ops_array_info
96a1fd9364Slogwang };
97a1fd9364Slogwang 
98a1fd9364Slogwang static const struct ng_parse_struct_field ng_patch_config_type_fields[]
99*22ce4affSfengbojiang 	= NG_PATCH_CONFIG_TYPE;
100a1fd9364Slogwang static const struct ng_parse_type ng_patch_config_type = {
101a1fd9364Slogwang 	&ng_parse_struct_type,
102a1fd9364Slogwang 	&ng_patch_config_type_fields
103a1fd9364Slogwang };
104a1fd9364Slogwang 
105a1fd9364Slogwang static const struct ng_parse_struct_field ng_patch_stats_fields[]
106*22ce4affSfengbojiang 	= NG_PATCH_STATS_TYPE;
107a1fd9364Slogwang static const struct ng_parse_type ng_patch_stats_type = {
108a1fd9364Slogwang 	&ng_parse_struct_type,
109a1fd9364Slogwang 	&ng_patch_stats_fields
110a1fd9364Slogwang };
111a1fd9364Slogwang 
112a1fd9364Slogwang static const struct ng_cmdlist ng_patch_cmdlist[] = {
113a1fd9364Slogwang 	{
114a1fd9364Slogwang 		NGM_PATCH_COOKIE,
115*22ce4affSfengbojiang 		NGM_PATCH_GETDLT,
116*22ce4affSfengbojiang 		"getdlt",
117*22ce4affSfengbojiang 		NULL,
118*22ce4affSfengbojiang 		&ng_parse_uint8_type
119*22ce4affSfengbojiang 	},
120*22ce4affSfengbojiang 	{
121*22ce4affSfengbojiang 		NGM_PATCH_COOKIE,
122*22ce4affSfengbojiang 		NGM_PATCH_SETDLT,
123*22ce4affSfengbojiang 		"setdlt",
124*22ce4affSfengbojiang 		&ng_parse_uint8_type,
125*22ce4affSfengbojiang 		NULL
126*22ce4affSfengbojiang 	},
127*22ce4affSfengbojiang 	{
128*22ce4affSfengbojiang 		NGM_PATCH_COOKIE,
129a1fd9364Slogwang 		NGM_PATCH_GETCONFIG,
130a1fd9364Slogwang 		"getconfig",
131a1fd9364Slogwang 		NULL,
132a1fd9364Slogwang 		&ng_patch_config_type
133a1fd9364Slogwang 	},
134a1fd9364Slogwang 	{
135a1fd9364Slogwang 		NGM_PATCH_COOKIE,
136a1fd9364Slogwang 		NGM_PATCH_SETCONFIG,
137a1fd9364Slogwang 		"setconfig",
138a1fd9364Slogwang 		&ng_patch_config_type,
139a1fd9364Slogwang 		NULL
140a1fd9364Slogwang 	},
141a1fd9364Slogwang 	{
142a1fd9364Slogwang 		NGM_PATCH_COOKIE,
143a1fd9364Slogwang 		NGM_PATCH_GET_STATS,
144a1fd9364Slogwang 		"getstats",
145a1fd9364Slogwang 		NULL,
146a1fd9364Slogwang 		&ng_patch_stats_type
147a1fd9364Slogwang 	},
148a1fd9364Slogwang 	{
149a1fd9364Slogwang 		NGM_PATCH_COOKIE,
150a1fd9364Slogwang 		NGM_PATCH_CLR_STATS,
151a1fd9364Slogwang 		"clrstats",
152a1fd9364Slogwang 		NULL,
153a1fd9364Slogwang 		NULL
154a1fd9364Slogwang 	},
155a1fd9364Slogwang 	{
156a1fd9364Slogwang 		NGM_PATCH_COOKIE,
157a1fd9364Slogwang 		NGM_PATCH_GETCLR_STATS,
158a1fd9364Slogwang 		"getclrstats",
159a1fd9364Slogwang 		NULL,
160a1fd9364Slogwang 		&ng_patch_stats_type
161a1fd9364Slogwang 	},
162a1fd9364Slogwang 	{ 0 }
163a1fd9364Slogwang };
164a1fd9364Slogwang 
165a1fd9364Slogwang static struct ng_type typestruct = {
166a1fd9364Slogwang 	.version =	NG_ABI_VERSION,
167a1fd9364Slogwang 	.name =		NG_PATCH_NODE_TYPE,
168a1fd9364Slogwang 	.constructor =	ng_patch_constructor,
169a1fd9364Slogwang 	.rcvmsg =	ng_patch_rcvmsg,
170a1fd9364Slogwang 	.shutdown =	ng_patch_shutdown,
171a1fd9364Slogwang 	.newhook =	ng_patch_newhook,
172a1fd9364Slogwang 	.rcvdata =	ng_patch_rcvdata,
173a1fd9364Slogwang 	.disconnect =	ng_patch_disconnect,
174a1fd9364Slogwang 	.cmdlist =	ng_patch_cmdlist,
175a1fd9364Slogwang };
176*22ce4affSfengbojiang 
177a1fd9364Slogwang NETGRAPH_INIT(patch, &typestruct);
178a1fd9364Slogwang 
179a1fd9364Slogwang static int
ng_patch_constructor(node_p node)180a1fd9364Slogwang ng_patch_constructor(node_p node)
181a1fd9364Slogwang {
182a1fd9364Slogwang 	priv_p privdata;
183a1fd9364Slogwang 
184a1fd9364Slogwang 	privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO);
185*22ce4affSfengbojiang 	privdata->dlt = DLT_RAW;
186*22ce4affSfengbojiang 
187a1fd9364Slogwang 	NG_NODE_SET_PRIVATE(node, privdata);
188*22ce4affSfengbojiang 
189a1fd9364Slogwang 	return (0);
190a1fd9364Slogwang }
191a1fd9364Slogwang 
192a1fd9364Slogwang static int
ng_patch_newhook(node_p node,hook_p hook,const char * name)193a1fd9364Slogwang ng_patch_newhook(node_p node, hook_p hook, const char *name)
194a1fd9364Slogwang {
195a1fd9364Slogwang 	const priv_p privp = NG_NODE_PRIVATE(node);
196a1fd9364Slogwang 
197a1fd9364Slogwang 	if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) {
198a1fd9364Slogwang 		privp->in = hook;
199a1fd9364Slogwang 	} else if (strncmp(name, NG_PATCH_HOOK_OUT,
200a1fd9364Slogwang 	    strlen(NG_PATCH_HOOK_OUT)) == 0) {
201a1fd9364Slogwang 		privp->out = hook;
202a1fd9364Slogwang 	} else
203a1fd9364Slogwang 		return (EINVAL);
204*22ce4affSfengbojiang 
205a1fd9364Slogwang 	return (0);
206a1fd9364Slogwang }
207a1fd9364Slogwang 
208a1fd9364Slogwang static int
ng_patch_rcvmsg(node_p node,item_p item,hook_p lasthook)209a1fd9364Slogwang ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook)
210a1fd9364Slogwang {
211a1fd9364Slogwang 	const priv_p privp = NG_NODE_PRIVATE(node);
212a1fd9364Slogwang 	struct ng_patch_config *conf, *newconf;
213a1fd9364Slogwang 	struct ng_mesg *msg;
214*22ce4affSfengbojiang 	struct ng_mesg *resp = NULL;
215*22ce4affSfengbojiang 	int i, error = 0;
216a1fd9364Slogwang 
217a1fd9364Slogwang 	NGI_GET_MSG(item, msg);
218*22ce4affSfengbojiang 
219*22ce4affSfengbojiang 	if  (msg->header.typecookie != NGM_PATCH_COOKIE)
220*22ce4affSfengbojiang 		ERROUT(EINVAL);
221*22ce4affSfengbojiang 
222*22ce4affSfengbojiang 	switch (msg->header.cmd)
223a1fd9364Slogwang 	{
224*22ce4affSfengbojiang 		case NGM_PATCH_GETCONFIG:
225*22ce4affSfengbojiang 			if (privp->conf == NULL)
226*22ce4affSfengbojiang 				ERROUT(0);
227*22ce4affSfengbojiang 
228*22ce4affSfengbojiang 			NG_MKRESPONSE(resp, msg,
229*22ce4affSfengbojiang 			    NG_PATCH_CONF_SIZE(privp->conf->count), M_WAITOK);
230*22ce4affSfengbojiang 
231*22ce4affSfengbojiang 			if (resp == NULL)
232*22ce4affSfengbojiang 				ERROUT(ENOMEM);
233*22ce4affSfengbojiang 
234*22ce4affSfengbojiang 			bcopy(privp->conf, resp->data,
235*22ce4affSfengbojiang 			    NG_PATCH_CONF_SIZE(privp->conf->count));
236*22ce4affSfengbojiang 
237*22ce4affSfengbojiang 			conf = (struct ng_patch_config *) resp->data;
238*22ce4affSfengbojiang 
239*22ce4affSfengbojiang 			for (i = 0; i < conf->count; i++) {
240*22ce4affSfengbojiang 				switch (conf->ops[i].length)
241*22ce4affSfengbojiang 				{
242*22ce4affSfengbojiang 					case 1:
243*22ce4affSfengbojiang 						conf->ops[i].val.v8 = conf->ops[i].val.v1;
244*22ce4affSfengbojiang 						break;
245*22ce4affSfengbojiang 					case 2:
246*22ce4affSfengbojiang 						conf->ops[i].val.v8 = conf->ops[i].val.v2;
247*22ce4affSfengbojiang 						break;
248*22ce4affSfengbojiang 					case 4:
249*22ce4affSfengbojiang 						conf->ops[i].val.v8 = conf->ops[i].val.v4;
250*22ce4affSfengbojiang 						break;
251*22ce4affSfengbojiang 					case 8:
252a1fd9364Slogwang 						break;
253a1fd9364Slogwang 				}
254*22ce4affSfengbojiang 			}
255a1fd9364Slogwang 
256*22ce4affSfengbojiang 			break;
257*22ce4affSfengbojiang 
258*22ce4affSfengbojiang 		case NGM_PATCH_SETCONFIG:
259a1fd9364Slogwang 			conf = (struct ng_patch_config *) msg->data;
260*22ce4affSfengbojiang 
261*22ce4affSfengbojiang 			if (msg->header.arglen < sizeof(struct ng_patch_config) ||
262*22ce4affSfengbojiang 			    msg->header.arglen < NG_PATCH_CONF_SIZE(conf->count))
263*22ce4affSfengbojiang 				ERROUT(EINVAL);
264a1fd9364Slogwang 
265a1fd9364Slogwang 			for (i = 0; i < conf->count; i++) {
266*22ce4affSfengbojiang 				switch (conf->ops[i].length)
267*22ce4affSfengbojiang 				{
268a1fd9364Slogwang 					case 1:
269*22ce4affSfengbojiang 						conf->ops[i].val.v1 = (uint8_t) conf->ops[i].val.v8;
270*22ce4affSfengbojiang 						break;
271a1fd9364Slogwang 					case 2:
272*22ce4affSfengbojiang 						conf->ops[i].val.v2 = (uint16_t) conf->ops[i].val.v8;
273*22ce4affSfengbojiang 						break;
274a1fd9364Slogwang 					case 4:
275*22ce4affSfengbojiang 						conf->ops[i].val.v4 = (uint32_t) conf->ops[i].val.v8;
276*22ce4affSfengbojiang 						break;
277a1fd9364Slogwang 					case 8:
278a1fd9364Slogwang 						break;
279a1fd9364Slogwang 					default:
280*22ce4affSfengbojiang 						ERROUT(EINVAL);
281a1fd9364Slogwang 				}
282a1fd9364Slogwang 			}
283a1fd9364Slogwang 
284*22ce4affSfengbojiang 			conf->csum_flags &= NG_PATCH_CSUM_IPV4|NG_PATCH_CSUM_IPV6;
285*22ce4affSfengbojiang 			conf->relative_offset = !!conf->relative_offset;
286a1fd9364Slogwang 
287*22ce4affSfengbojiang 			newconf = malloc(NG_PATCH_CONF_SIZE(conf->count), M_NETGRAPH, M_WAITOK | M_ZERO);
288*22ce4affSfengbojiang 
289*22ce4affSfengbojiang 			bcopy(conf, newconf, NG_PATCH_CONF_SIZE(conf->count));
290*22ce4affSfengbojiang 
291*22ce4affSfengbojiang 			if (privp->conf)
292*22ce4affSfengbojiang 				free(privp->conf, M_NETGRAPH);
293*22ce4affSfengbojiang 
294*22ce4affSfengbojiang 			privp->conf = newconf;
295*22ce4affSfengbojiang 
296a1fd9364Slogwang 			break;
297*22ce4affSfengbojiang 
298a1fd9364Slogwang 		case NGM_PATCH_GET_STATS:
299a1fd9364Slogwang 		case NGM_PATCH_CLR_STATS:
300*22ce4affSfengbojiang 		case NGM_PATCH_GETCLR_STATS:
301*22ce4affSfengbojiang 			if (msg->header.cmd != NGM_PATCH_CLR_STATS) {
302*22ce4affSfengbojiang 				NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), M_WAITOK);
303*22ce4affSfengbojiang 
304*22ce4affSfengbojiang 				if (resp == NULL)
305*22ce4affSfengbojiang 					ERROUT(ENOMEM);
306*22ce4affSfengbojiang 
307*22ce4affSfengbojiang 				bcopy(&(privp->stats), resp->data, sizeof(struct ng_patch_stats));
308a1fd9364Slogwang 			}
309a1fd9364Slogwang 
310*22ce4affSfengbojiang 			if (msg->header.cmd != NGM_PATCH_GET_STATS)
311*22ce4affSfengbojiang 				bzero(&(privp->stats), sizeof(struct ng_patch_stats));
312*22ce4affSfengbojiang 
313*22ce4affSfengbojiang 			break;
314*22ce4affSfengbojiang 
315*22ce4affSfengbojiang 		case NGM_PATCH_GETDLT:
316*22ce4affSfengbojiang 			NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
317*22ce4affSfengbojiang 
318*22ce4affSfengbojiang 			if (resp == NULL)
319*22ce4affSfengbojiang 				ERROUT(ENOMEM);
320*22ce4affSfengbojiang 
321*22ce4affSfengbojiang 			*((uint8_t *) resp->data) = privp->dlt;
322*22ce4affSfengbojiang 
323*22ce4affSfengbojiang 			break;
324*22ce4affSfengbojiang 
325*22ce4affSfengbojiang 		case NGM_PATCH_SETDLT:
326*22ce4affSfengbojiang 			if (msg->header.arglen != sizeof(uint8_t))
327*22ce4affSfengbojiang 				ERROUT(EINVAL);
328*22ce4affSfengbojiang 
329*22ce4affSfengbojiang 			switch (*(uint8_t *) msg->data)
330*22ce4affSfengbojiang 			{
331*22ce4affSfengbojiang 				case DLT_EN10MB:
332*22ce4affSfengbojiang 				case DLT_RAW:
333*22ce4affSfengbojiang 					privp->dlt = *(uint8_t *) msg->data;
334*22ce4affSfengbojiang 					break;
335*22ce4affSfengbojiang 
336*22ce4affSfengbojiang 				default:
337*22ce4affSfengbojiang 					ERROUT(EINVAL);
338*22ce4affSfengbojiang 			}
339*22ce4affSfengbojiang 
340*22ce4affSfengbojiang 			break;
341*22ce4affSfengbojiang 
342*22ce4affSfengbojiang 		default:
343*22ce4affSfengbojiang 			ERROUT(EINVAL);
344*22ce4affSfengbojiang 	}
345*22ce4affSfengbojiang 
346*22ce4affSfengbojiang done:
347a1fd9364Slogwang 	NG_RESPOND_MSG(error, node, item, resp);
348a1fd9364Slogwang 	NG_FREE_MSG(msg);
349*22ce4affSfengbojiang 
350a1fd9364Slogwang 	return (error);
351a1fd9364Slogwang }
352a1fd9364Slogwang 
353a1fd9364Slogwang static void
do_patch(priv_p privp,struct mbuf * m,int global_offset)354*22ce4affSfengbojiang do_patch(priv_p privp, struct mbuf *m, int global_offset)
355a1fd9364Slogwang {
356*22ce4affSfengbojiang 	int i, offset, patched = 0;
357*22ce4affSfengbojiang 	union ng_patch_op_val val;
358a1fd9364Slogwang 
359*22ce4affSfengbojiang 	for (i = 0; i < privp->conf->count; i++) {
360*22ce4affSfengbojiang 		offset = global_offset + privp->conf->ops[i].offset;
361*22ce4affSfengbojiang 
362*22ce4affSfengbojiang 		if (offset + privp->conf->ops[i].length > m->m_pkthdr.len)
363a1fd9364Slogwang 			continue;
364a1fd9364Slogwang 
365a1fd9364Slogwang 		/* for "=" operation we don't need to copy data from mbuf */
366*22ce4affSfengbojiang 		if (privp->conf->ops[i].mode != NG_PATCH_MODE_SET)
367*22ce4affSfengbojiang 			m_copydata(m, offset, privp->conf->ops[i].length, (caddr_t) &val);
368a1fd9364Slogwang 
369*22ce4affSfengbojiang 		switch (privp->conf->ops[i].length)
370*22ce4affSfengbojiang 		{
371a1fd9364Slogwang 			case 1:
372*22ce4affSfengbojiang 				switch (privp->conf->ops[i].mode)
373*22ce4affSfengbojiang 				{
374a1fd9364Slogwang 					case NG_PATCH_MODE_SET:
375*22ce4affSfengbojiang 						val.v1 = privp->conf->ops[i].val.v1;
376a1fd9364Slogwang 						break;
377a1fd9364Slogwang 					case NG_PATCH_MODE_ADD:
378*22ce4affSfengbojiang 						val.v1 += privp->conf->ops[i].val.v1;
379a1fd9364Slogwang 						break;
380a1fd9364Slogwang 					case NG_PATCH_MODE_SUB:
381*22ce4affSfengbojiang 						val.v1 -= privp->conf->ops[i].val.v1;
382a1fd9364Slogwang 						break;
383a1fd9364Slogwang 					case NG_PATCH_MODE_MUL:
384*22ce4affSfengbojiang 						val.v1 *= privp->conf->ops[i].val.v1;
385a1fd9364Slogwang 						break;
386a1fd9364Slogwang 					case NG_PATCH_MODE_DIV:
387*22ce4affSfengbojiang 						val.v1 /= privp->conf->ops[i].val.v1;
388a1fd9364Slogwang 						break;
389a1fd9364Slogwang 					case NG_PATCH_MODE_NEG:
390*22ce4affSfengbojiang 						*((int8_t *) &val) = - *((int8_t *) &val);
391a1fd9364Slogwang 						break;
392a1fd9364Slogwang 					case NG_PATCH_MODE_AND:
393*22ce4affSfengbojiang 						val.v1 &= privp->conf->ops[i].val.v1;
394a1fd9364Slogwang 						break;
395a1fd9364Slogwang 					case NG_PATCH_MODE_OR:
396*22ce4affSfengbojiang 						val.v1 |= privp->conf->ops[i].val.v1;
397a1fd9364Slogwang 						break;
398a1fd9364Slogwang 					case NG_PATCH_MODE_XOR:
399*22ce4affSfengbojiang 						val.v1 ^= privp->conf->ops[i].val.v1;
400a1fd9364Slogwang 						break;
401a1fd9364Slogwang 					case NG_PATCH_MODE_SHL:
402*22ce4affSfengbojiang 						val.v1 <<= privp->conf->ops[i].val.v1;
403a1fd9364Slogwang 						break;
404a1fd9364Slogwang 					case NG_PATCH_MODE_SHR:
405*22ce4affSfengbojiang 						val.v1 >>= privp->conf->ops[i].val.v1;
406a1fd9364Slogwang 						break;
407a1fd9364Slogwang 				}
408a1fd9364Slogwang 				break;
409*22ce4affSfengbojiang 
410a1fd9364Slogwang 			case 2:
411*22ce4affSfengbojiang 				val.v2 = ntohs(val.v2);
412*22ce4affSfengbojiang 
413*22ce4affSfengbojiang 				switch (privp->conf->ops[i].mode)
414*22ce4affSfengbojiang 				{
415a1fd9364Slogwang 					case NG_PATCH_MODE_SET:
416*22ce4affSfengbojiang 						val.v2 = privp->conf->ops[i].val.v2;
417a1fd9364Slogwang 						break;
418a1fd9364Slogwang 					case NG_PATCH_MODE_ADD:
419*22ce4affSfengbojiang 						val.v2 += privp->conf->ops[i].val.v2;
420a1fd9364Slogwang 						break;
421a1fd9364Slogwang 					case NG_PATCH_MODE_SUB:
422*22ce4affSfengbojiang 						val.v2 -= privp->conf->ops[i].val.v2;
423a1fd9364Slogwang 						break;
424a1fd9364Slogwang 					case NG_PATCH_MODE_MUL:
425*22ce4affSfengbojiang 						val.v2 *= privp->conf->ops[i].val.v2;
426a1fd9364Slogwang 						break;
427a1fd9364Slogwang 					case NG_PATCH_MODE_DIV:
428*22ce4affSfengbojiang 						val.v2 /= privp->conf->ops[i].val.v2;
429a1fd9364Slogwang 						break;
430a1fd9364Slogwang 					case NG_PATCH_MODE_NEG:
431*22ce4affSfengbojiang 						*((int16_t *) &val) = - *((int16_t *) &val);
432a1fd9364Slogwang 						break;
433a1fd9364Slogwang 					case NG_PATCH_MODE_AND:
434*22ce4affSfengbojiang 						val.v2 &= privp->conf->ops[i].val.v2;
435a1fd9364Slogwang 						break;
436a1fd9364Slogwang 					case NG_PATCH_MODE_OR:
437*22ce4affSfengbojiang 						val.v2 |= privp->conf->ops[i].val.v2;
438a1fd9364Slogwang 						break;
439a1fd9364Slogwang 					case NG_PATCH_MODE_XOR:
440*22ce4affSfengbojiang 						val.v2 ^= privp->conf->ops[i].val.v2;
441a1fd9364Slogwang 						break;
442a1fd9364Slogwang 					case NG_PATCH_MODE_SHL:
443*22ce4affSfengbojiang 						val.v2 <<= privp->conf->ops[i].val.v2;
444a1fd9364Slogwang 						break;
445a1fd9364Slogwang 					case NG_PATCH_MODE_SHR:
446*22ce4affSfengbojiang 						val.v2 >>= privp->conf->ops[i].val.v2;
447a1fd9364Slogwang 						break;
448a1fd9364Slogwang 				}
449a1fd9364Slogwang 
450*22ce4affSfengbojiang 				val.v2 = htons(val.v2);
451*22ce4affSfengbojiang 
452*22ce4affSfengbojiang 				break;
453*22ce4affSfengbojiang 
454*22ce4affSfengbojiang 			case 4:
455*22ce4affSfengbojiang 				val.v4 = ntohl(val.v4);
456*22ce4affSfengbojiang 
457*22ce4affSfengbojiang 				switch (privp->conf->ops[i].mode)
458*22ce4affSfengbojiang 				{
459*22ce4affSfengbojiang 					case NG_PATCH_MODE_SET:
460*22ce4affSfengbojiang 						val.v4 = privp->conf->ops[i].val.v4;
461*22ce4affSfengbojiang 						break;
462*22ce4affSfengbojiang 					case NG_PATCH_MODE_ADD:
463*22ce4affSfengbojiang 						val.v4 += privp->conf->ops[i].val.v4;
464*22ce4affSfengbojiang 						break;
465*22ce4affSfengbojiang 					case NG_PATCH_MODE_SUB:
466*22ce4affSfengbojiang 						val.v4 -= privp->conf->ops[i].val.v4;
467*22ce4affSfengbojiang 						break;
468*22ce4affSfengbojiang 					case NG_PATCH_MODE_MUL:
469*22ce4affSfengbojiang 						val.v4 *= privp->conf->ops[i].val.v4;
470*22ce4affSfengbojiang 						break;
471*22ce4affSfengbojiang 					case NG_PATCH_MODE_DIV:
472*22ce4affSfengbojiang 						val.v4 /= privp->conf->ops[i].val.v4;
473*22ce4affSfengbojiang 						break;
474*22ce4affSfengbojiang 					case NG_PATCH_MODE_NEG:
475*22ce4affSfengbojiang 						*((int32_t *) &val) = - *((int32_t *) &val);
476*22ce4affSfengbojiang 						break;
477*22ce4affSfengbojiang 					case NG_PATCH_MODE_AND:
478*22ce4affSfengbojiang 						val.v4 &= privp->conf->ops[i].val.v4;
479*22ce4affSfengbojiang 						break;
480*22ce4affSfengbojiang 					case NG_PATCH_MODE_OR:
481*22ce4affSfengbojiang 						val.v4 |= privp->conf->ops[i].val.v4;
482*22ce4affSfengbojiang 						break;
483*22ce4affSfengbojiang 					case NG_PATCH_MODE_XOR:
484*22ce4affSfengbojiang 						val.v4 ^= privp->conf->ops[i].val.v4;
485*22ce4affSfengbojiang 						break;
486*22ce4affSfengbojiang 					case NG_PATCH_MODE_SHL:
487*22ce4affSfengbojiang 						val.v4 <<= privp->conf->ops[i].val.v4;
488*22ce4affSfengbojiang 						break;
489*22ce4affSfengbojiang 					case NG_PATCH_MODE_SHR:
490*22ce4affSfengbojiang 						val.v4 >>= privp->conf->ops[i].val.v4;
491*22ce4affSfengbojiang 						break;
492*22ce4affSfengbojiang 				}
493*22ce4affSfengbojiang 
494*22ce4affSfengbojiang 				val.v4 = htonl(val.v4);
495*22ce4affSfengbojiang 
496*22ce4affSfengbojiang 				break;
497*22ce4affSfengbojiang 
498*22ce4affSfengbojiang 			case 8:
499*22ce4affSfengbojiang 				val.v8 = be64toh(val.v8);
500*22ce4affSfengbojiang 
501*22ce4affSfengbojiang 				switch (privp->conf->ops[i].mode)
502*22ce4affSfengbojiang 				{
503*22ce4affSfengbojiang 					case NG_PATCH_MODE_SET:
504*22ce4affSfengbojiang 						val.v8 = privp->conf->ops[i].val.v8;
505*22ce4affSfengbojiang 						break;
506*22ce4affSfengbojiang 					case NG_PATCH_MODE_ADD:
507*22ce4affSfengbojiang 						val.v8 += privp->conf->ops[i].val.v8;
508*22ce4affSfengbojiang 						break;
509*22ce4affSfengbojiang 					case NG_PATCH_MODE_SUB:
510*22ce4affSfengbojiang 						val.v8 -= privp->conf->ops[i].val.v8;
511*22ce4affSfengbojiang 						break;
512*22ce4affSfengbojiang 					case NG_PATCH_MODE_MUL:
513*22ce4affSfengbojiang 						val.v8 *= privp->conf->ops[i].val.v8;
514*22ce4affSfengbojiang 						break;
515*22ce4affSfengbojiang 					case NG_PATCH_MODE_DIV:
516*22ce4affSfengbojiang 						val.v8 /= privp->conf->ops[i].val.v8;
517*22ce4affSfengbojiang 						break;
518*22ce4affSfengbojiang 					case NG_PATCH_MODE_NEG:
519*22ce4affSfengbojiang 						*((int64_t *) &val) = - *((int64_t *) &val);
520*22ce4affSfengbojiang 						break;
521*22ce4affSfengbojiang 					case NG_PATCH_MODE_AND:
522*22ce4affSfengbojiang 						val.v8 &= privp->conf->ops[i].val.v8;
523*22ce4affSfengbojiang 						break;
524*22ce4affSfengbojiang 					case NG_PATCH_MODE_OR:
525*22ce4affSfengbojiang 						val.v8 |= privp->conf->ops[i].val.v8;
526*22ce4affSfengbojiang 						break;
527*22ce4affSfengbojiang 					case NG_PATCH_MODE_XOR:
528*22ce4affSfengbojiang 						val.v8 ^= privp->conf->ops[i].val.v8;
529*22ce4affSfengbojiang 						break;
530*22ce4affSfengbojiang 					case NG_PATCH_MODE_SHL:
531*22ce4affSfengbojiang 						val.v8 <<= privp->conf->ops[i].val.v8;
532*22ce4affSfengbojiang 						break;
533*22ce4affSfengbojiang 					case NG_PATCH_MODE_SHR:
534*22ce4affSfengbojiang 						val.v8 >>= privp->conf->ops[i].val.v8;
535*22ce4affSfengbojiang 						break;
536*22ce4affSfengbojiang 				}
537*22ce4affSfengbojiang 
538*22ce4affSfengbojiang 				val.v8 = htobe64(val.v8);
539*22ce4affSfengbojiang 
540*22ce4affSfengbojiang 				break;
541*22ce4affSfengbojiang 		}
542*22ce4affSfengbojiang 
543*22ce4affSfengbojiang 		m_copyback(m, offset, privp->conf->ops[i].length, (caddr_t) &val);
544a1fd9364Slogwang 		patched = 1;
545a1fd9364Slogwang 	}
546*22ce4affSfengbojiang 
547*22ce4affSfengbojiang 	if (patched)
548a1fd9364Slogwang 		privp->stats.patched++;
549a1fd9364Slogwang }
550a1fd9364Slogwang 
551a1fd9364Slogwang static int
ng_patch_rcvdata(hook_p hook,item_p item)552a1fd9364Slogwang ng_patch_rcvdata(hook_p hook, item_p item)
553a1fd9364Slogwang {
554a1fd9364Slogwang 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
555a1fd9364Slogwang 	struct mbuf *m;
556*22ce4affSfengbojiang 	hook_p out;
557*22ce4affSfengbojiang 	int pullup_len = 0;
558*22ce4affSfengbojiang 	int error = 0;
559a1fd9364Slogwang 
560a1fd9364Slogwang 	priv->stats.received++;
561*22ce4affSfengbojiang 
562a1fd9364Slogwang 	NGI_GET_M(item, m);
563*22ce4affSfengbojiang 
564*22ce4affSfengbojiang #define	PULLUP_CHECK(mbuf, length) do {					\
565*22ce4affSfengbojiang 	pullup_len += length;						\
566*22ce4affSfengbojiang 	if (((mbuf)->m_pkthdr.len < pullup_len) ||			\
567*22ce4affSfengbojiang 	    (pullup_len > MHLEN)) {					\
568*22ce4affSfengbojiang 		error = EINVAL;						\
569*22ce4affSfengbojiang 		goto bypass;						\
570*22ce4affSfengbojiang 	}								\
571*22ce4affSfengbojiang 	if ((mbuf)->m_len < pullup_len &&				\
572*22ce4affSfengbojiang 	    (((mbuf) = m_pullup((mbuf), pullup_len)) == NULL)) {	\
573*22ce4affSfengbojiang 		error = ENOBUFS;					\
574*22ce4affSfengbojiang 		goto drop;						\
575*22ce4affSfengbojiang 	}								\
576*22ce4affSfengbojiang } while (0)
577*22ce4affSfengbojiang 
578*22ce4affSfengbojiang 	if (priv->conf && hook == priv->in &&
579*22ce4affSfengbojiang 	    m && (m->m_flags & M_PKTHDR)) {
580a1fd9364Slogwang 		m = m_unshare(m, M_NOWAIT);
581*22ce4affSfengbojiang 
582*22ce4affSfengbojiang 		if (m == NULL)
583*22ce4affSfengbojiang 			ERROUT(ENOMEM);
584*22ce4affSfengbojiang 
585*22ce4affSfengbojiang 		if (priv->conf->relative_offset) {
586*22ce4affSfengbojiang 			struct ether_header *eh;
587*22ce4affSfengbojiang 			struct ng_patch_vlan_header *vh;
588*22ce4affSfengbojiang 			uint16_t etype;
589*22ce4affSfengbojiang 
590*22ce4affSfengbojiang 			switch (priv->dlt)
591*22ce4affSfengbojiang 			{
592*22ce4affSfengbojiang 				case DLT_EN10MB:
593*22ce4affSfengbojiang 					PULLUP_CHECK(m, sizeof(struct ether_header));
594*22ce4affSfengbojiang 					eh = mtod(m, struct ether_header *);
595*22ce4affSfengbojiang 					etype = ntohs(eh->ether_type);
596*22ce4affSfengbojiang 
597*22ce4affSfengbojiang 					for (;;) {	/* QinQ support */
598*22ce4affSfengbojiang 						switch (etype)
599*22ce4affSfengbojiang 						{
600*22ce4affSfengbojiang 							case 0x8100:
601*22ce4affSfengbojiang 							case 0x88A8:
602*22ce4affSfengbojiang 							case 0x9100:
603*22ce4affSfengbojiang 								PULLUP_CHECK(m, sizeof(struct ng_patch_vlan_header));
604*22ce4affSfengbojiang 								vh = (struct ng_patch_vlan_header *) mtodo(m,
605*22ce4affSfengbojiang 								    pullup_len - sizeof(struct ng_patch_vlan_header));
606*22ce4affSfengbojiang 								etype = ntohs(vh->etype);
607*22ce4affSfengbojiang 								break;
608*22ce4affSfengbojiang 
609*22ce4affSfengbojiang 							default:
610*22ce4affSfengbojiang 								goto loopend;
611a1fd9364Slogwang 						}
612*22ce4affSfengbojiang 					}
613*22ce4affSfengbojiang loopend:
614*22ce4affSfengbojiang 					break;
615*22ce4affSfengbojiang 
616*22ce4affSfengbojiang 				case DLT_RAW:
617*22ce4affSfengbojiang 					break;
618*22ce4affSfengbojiang 
619*22ce4affSfengbojiang 				default:
620*22ce4affSfengbojiang 					ERROUT(EINVAL);
621*22ce4affSfengbojiang 			}
622a1fd9364Slogwang 		}
623a1fd9364Slogwang 
624*22ce4affSfengbojiang 		do_patch(priv, m, pullup_len);
625*22ce4affSfengbojiang 
626*22ce4affSfengbojiang 		m->m_pkthdr.csum_flags |= priv->conf->csum_flags;
627*22ce4affSfengbojiang 	}
628*22ce4affSfengbojiang 
629*22ce4affSfengbojiang #undef	PULLUP_CHECK
630*22ce4affSfengbojiang 
631*22ce4affSfengbojiang bypass:
632*22ce4affSfengbojiang 	out = NULL;
633*22ce4affSfengbojiang 
634a1fd9364Slogwang 	if (hook == priv->in) {
635a1fd9364Slogwang 		/* return frames on 'in' hook if 'out' not connected */
636*22ce4affSfengbojiang 		out = priv->out ? priv->out : priv->in;
637*22ce4affSfengbojiang 	} else if (hook == priv->out && priv->in) {
638*22ce4affSfengbojiang 		/* pass frames on 'out' hook if 'in' connected */
639*22ce4affSfengbojiang 		out = priv->in;
640a1fd9364Slogwang 	}
641a1fd9364Slogwang 
642*22ce4affSfengbojiang 	if (out == NULL)
643*22ce4affSfengbojiang 		ERROUT(0);
644*22ce4affSfengbojiang 
645*22ce4affSfengbojiang 	NG_FWD_NEW_DATA(error, item, out, m);
646*22ce4affSfengbojiang 
647*22ce4affSfengbojiang 	return (error);
648*22ce4affSfengbojiang 
649*22ce4affSfengbojiang done:
650*22ce4affSfengbojiang drop:
651a1fd9364Slogwang 	NG_FREE_ITEM(item);
652a1fd9364Slogwang 	NG_FREE_M(m);
653*22ce4affSfengbojiang 
654*22ce4affSfengbojiang 	priv->stats.dropped++;
655*22ce4affSfengbojiang 
656a1fd9364Slogwang 	return (error);
657a1fd9364Slogwang }
658a1fd9364Slogwang 
659a1fd9364Slogwang static int
ng_patch_shutdown(node_p node)660a1fd9364Slogwang ng_patch_shutdown(node_p node)
661a1fd9364Slogwang {
662a1fd9364Slogwang 	const priv_p privdata = NG_NODE_PRIVATE(node);
663a1fd9364Slogwang 
664a1fd9364Slogwang 	NG_NODE_SET_PRIVATE(node, NULL);
665a1fd9364Slogwang 	NG_NODE_UNREF(node);
666*22ce4affSfengbojiang 
667*22ce4affSfengbojiang 	if (privdata->conf != NULL)
668*22ce4affSfengbojiang 		free(privdata->conf, M_NETGRAPH);
669*22ce4affSfengbojiang 
670a1fd9364Slogwang 	free(privdata, M_NETGRAPH);
671*22ce4affSfengbojiang 
672a1fd9364Slogwang 	return (0);
673a1fd9364Slogwang }
674a1fd9364Slogwang 
675a1fd9364Slogwang static int
ng_patch_disconnect(hook_p hook)676a1fd9364Slogwang ng_patch_disconnect(hook_p hook)
677a1fd9364Slogwang {
678a1fd9364Slogwang 	priv_p priv;
679a1fd9364Slogwang 
680a1fd9364Slogwang 	priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
681*22ce4affSfengbojiang 
682a1fd9364Slogwang 	if (hook == priv->in) {
683a1fd9364Slogwang 		priv->in = NULL;
684a1fd9364Slogwang 	}
685*22ce4affSfengbojiang 
686a1fd9364Slogwang 	if (hook == priv->out) {
687a1fd9364Slogwang 		priv->out = NULL;
688a1fd9364Slogwang 	}
689*22ce4affSfengbojiang 
690a1fd9364Slogwang 	if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
691a1fd9364Slogwang 	    NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
692a1fd9364Slogwang 		ng_rmnode_self(NG_HOOK_NODE(hook));
693*22ce4affSfengbojiang 
694a1fd9364Slogwang 	return (0);
695a1fd9364Slogwang }
696