xref: /freebsd/sys/netgraph/ng_patch.c (revision 30c90f019fae2636046f3789dab92794a138d19a)
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$");
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 
42 static ng_constructor_t	ng_patch_constructor;
43 static ng_rcvmsg_t	ng_patch_rcvmsg;
44 static ng_shutdown_t	ng_patch_shutdown;
45 static ng_newhook_t	ng_patch_newhook;
46 static ng_rcvdata_t	ng_patch_rcvdata;
47 static ng_disconnect_t	ng_patch_disconnect;
48 
49 static int
50 ng_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 
61 static const struct ng_parse_struct_field ng_patch_op_type_fields[]
62 	= NG_PATCH_OP_TYPE_INFO;
63 static const struct ng_parse_type ng_patch_op_type = {
64 	&ng_parse_struct_type,
65 	&ng_patch_op_type_fields
66 };
67 
68 static const struct ng_parse_array_info ng_patch_ops_array_info = {
69 	&ng_patch_op_type,
70 	&ng_patch_config_getlen
71 };
72 static const struct ng_parse_type ng_patch_ops_array_type = {
73 	&ng_parse_array_type,
74 	&ng_patch_ops_array_info
75 };
76 
77 static const struct ng_parse_struct_field ng_patch_config_type_fields[]
78 	= NG_PATCH_CONFIG_TYPE_INFO;
79 static const struct ng_parse_type ng_patch_config_type = {
80 	&ng_parse_struct_type,
81 	&ng_patch_config_type_fields
82 };
83 
84 static const struct ng_parse_struct_field ng_patch_stats_fields[]
85 	= NG_PATCH_STATS_TYPE_INFO;
86 static const struct ng_parse_type ng_patch_stats_type = {
87 	&ng_parse_struct_type,
88 	&ng_patch_stats_fields
89 };
90 
91 static 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 
130 static 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 
142 NETGRAPH_INIT(patch, &typestruct);
143 
144 union patch_val {
145 	uint8_t		v1;
146 	uint16_t	v2;
147 	uint32_t	v4;
148 	uint64_t	v8;
149 };
150 
151 /* private data */
152 struct 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 };
159 typedef 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 
164 static void do_patch(priv_p conf, struct mbuf *m);
165 
166 static int
167 ng_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 
179 static int
180 ng_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 
194 static int
195 ng_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 
319 static void
320 do_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 
504 static int
505 ng_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 
547 static int
548 ng_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 
562 static int
563 ng_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