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: releng/10.4/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
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
Cache object: c1a4857150db521ccbf9967e649b1da9
|