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