1 /*-
2 * Copyright (C) 2010 by 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/11.0/sys/netgraph/ng_patch.c 234574 2012-04-22 17:00:52Z melifaro $");
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 *p;
54
55 p = (const struct ng_patch_config *)(buf -
56 offsetof(struct ng_patch_config, ops));
57 return (p->count);
58 }
59
60 static const struct ng_parse_struct_field ng_patch_op_type_fields[]
61 = NG_PATCH_OP_TYPE_INFO;
62 static const struct ng_parse_type ng_patch_op_type = {
63 &ng_parse_struct_type,
64 &ng_patch_op_type_fields
65 };
66
67 static const struct ng_parse_array_info ng_patch_confarr_info = {
68 &ng_patch_op_type,
69 &ng_patch_config_getlen
70 };
71 static const struct ng_parse_type ng_patch_confarr_type = {
72 &ng_parse_array_type,
73 &ng_patch_confarr_info
74 };
75
76 static const struct ng_parse_struct_field ng_patch_config_type_fields[]
77 = NG_PATCH_CONFIG_TYPE_INFO;
78 static const struct ng_parse_type ng_patch_config_type = {
79 &ng_parse_struct_type,
80 &ng_patch_config_type_fields
81 };
82
83 static const struct ng_parse_struct_field ng_patch_stats_fields[]
84 = NG_PATCH_STATS_TYPE_INFO;
85 static const struct ng_parse_type ng_patch_stats_type = {
86 &ng_parse_struct_type,
87 &ng_patch_stats_fields
88 };
89
90 static const struct ng_cmdlist ng_patch_cmdlist[] = {
91 {
92 NGM_PATCH_COOKIE,
93 NGM_PATCH_GETCONFIG,
94 "getconfig",
95 NULL,
96 &ng_patch_config_type
97 },
98 {
99 NGM_PATCH_COOKIE,
100 NGM_PATCH_SETCONFIG,
101 "setconfig",
102 &ng_patch_config_type,
103 NULL
104 },
105 {
106 NGM_PATCH_COOKIE,
107 NGM_PATCH_GET_STATS,
108 "getstats",
109 NULL,
110 &ng_patch_stats_type
111 },
112 {
113 NGM_PATCH_COOKIE,
114 NGM_PATCH_CLR_STATS,
115 "clrstats",
116 NULL,
117 NULL
118 },
119 {
120 NGM_PATCH_COOKIE,
121 NGM_PATCH_GETCLR_STATS,
122 "getclrstats",
123 NULL,
124 &ng_patch_stats_type
125 },
126 { 0 }
127 };
128
129 static struct ng_type typestruct = {
130 .version = NG_ABI_VERSION,
131 .name = NG_PATCH_NODE_TYPE,
132 .constructor = ng_patch_constructor,
133 .rcvmsg = ng_patch_rcvmsg,
134 .shutdown = ng_patch_shutdown,
135 .newhook = ng_patch_newhook,
136 .rcvdata = ng_patch_rcvdata,
137 .disconnect = ng_patch_disconnect,
138 .cmdlist = ng_patch_cmdlist,
139 };
140 NETGRAPH_INIT(patch, &typestruct);
141
142 union patch_val {
143 uint8_t v1;
144 uint16_t v2;
145 uint32_t v4;
146 uint64_t v8;
147 };
148
149 struct ng_patch_priv {
150 hook_p in;
151 hook_p out;
152 struct ng_patch_config *config;
153 union patch_val *val;
154 struct ng_patch_stats stats;
155 };
156 typedef struct ng_patch_priv *priv_p;
157
158 #define NG_PATCH_CONF_SIZE(count) (sizeof(struct ng_patch_config) + \
159 (count) * sizeof(struct ng_patch_op))
160
161 static void do_patch(priv_p conf, struct mbuf *m);
162
163 static int
164 ng_patch_constructor(node_p node)
165 {
166 priv_p privdata;
167
168 privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO);
169 NG_NODE_SET_PRIVATE(node, privdata);
170 privdata->in = NULL;
171 privdata->out = NULL;
172 privdata->config = NULL;
173 return (0);
174 }
175
176 static int
177 ng_patch_newhook(node_p node, hook_p hook, const char *name)
178 {
179 const priv_p privp = NG_NODE_PRIVATE(node);
180
181 if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) {
182 privp->in = hook;
183 } else if (strncmp(name, NG_PATCH_HOOK_OUT,
184 strlen(NG_PATCH_HOOK_OUT)) == 0) {
185 privp->out = hook;
186 } else
187 return (EINVAL);
188 return(0);
189 }
190
191 static int
192 ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook)
193 {
194 const priv_p privp = NG_NODE_PRIVATE(node);
195 struct ng_patch_config *conf, *newconf;
196 union patch_val *newval;
197 struct ng_mesg *msg;
198 struct ng_mesg *resp;
199 int i, clear, error;
200
201 clear = error = 0;
202 resp = NULL;
203 NGI_GET_MSG(item, msg);
204 switch (msg->header.typecookie) {
205 case NGM_PATCH_COOKIE:
206 switch (msg->header.cmd) {
207 case NGM_PATCH_GETCONFIG:
208 if (privp->config == NULL)
209 break;
210 NG_MKRESPONSE(resp, msg,
211 NG_PATCH_CONF_SIZE(privp->config->count),
212 M_WAITOK);
213 bcopy(privp->config, resp->data,
214 NG_PATCH_CONF_SIZE(privp->config->count));
215 break;
216 case NGM_PATCH_SETCONFIG:
217 {
218 if (msg->header.arglen <
219 sizeof(struct ng_patch_config)) {
220 error = EINVAL;
221 break;
222 }
223
224 conf = (struct ng_patch_config *)msg->data;
225 if (msg->header.arglen <
226 NG_PATCH_CONF_SIZE(conf->count)) {
227 error = EINVAL;
228 break;
229 }
230
231 for(i = 0; i < conf->count; i++) {
232 switch(conf->ops[i].length) {
233 case 1:
234 case 2:
235 case 4:
236 case 8:
237 break;
238 default:
239 error = EINVAL;
240 break;
241 }
242 if (error != 0)
243 break;
244 }
245
246 conf->csum_flags &= CSUM_IP | CSUM_TCP | CSUM_UDP |
247 CSUM_SCTP;
248
249 if (error == 0) {
250 newconf = malloc(
251 NG_PATCH_CONF_SIZE(conf->count),
252 M_NETGRAPH, M_WAITOK);
253 newval = malloc(conf->count *
254 sizeof(union patch_val), M_NETGRAPH,
255 M_WAITOK);
256 for(i = 0; i < conf->count; i++) {
257 switch (conf->ops[i].length) {
258 case 1:
259 newval[i].v1 =
260 conf->ops[i].value;
261 break;
262 case 2:
263 newval[i].v2 =
264 conf->ops[i].value;
265 break;
266 case 4:
267 newval[i].v4 =
268 conf->ops[i].value;
269 break;
270 case 8:
271 newval[i].v8 =
272 conf->ops[i].value;
273 break;
274 }
275 }
276 bcopy(conf, newconf,
277 NG_PATCH_CONF_SIZE(conf->count));
278 if (privp->val != NULL)
279 free(privp->val, M_NETGRAPH);
280 privp->val = newval;
281 if (privp->config != NULL)
282 free(privp->config, M_NETGRAPH);
283 privp->config = newconf;
284 }
285 break;
286 }
287 case NGM_PATCH_GETCLR_STATS:
288 clear = 1;
289 /* FALLTHROUGH */
290 case NGM_PATCH_GET_STATS:
291 NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats),
292 M_WAITOK);
293 bcopy(&(privp->stats), resp->data,
294 sizeof(struct ng_patch_stats));
295 if (clear == 0)
296 break;
297 /* else FALLTHROUGH */
298 case NGM_PATCH_CLR_STATS:
299 bzero(&(privp->stats), sizeof(struct ng_patch_stats));
300 break;
301 default:
302 error = EINVAL;
303 break;
304 }
305 break;
306 default:
307 error = EINVAL;
308 break;
309 }
310
311 NG_RESPOND_MSG(error, node, item, resp);
312 NG_FREE_MSG(msg);
313 return(error);
314 }
315
316 static void
317 do_patch(priv_p privp, struct mbuf *m)
318 {
319 struct ng_patch_config *conf;
320 uint64_t buf;
321 int i, patched;
322
323 conf = privp->config;
324 patched = 0;
325 for(i = 0; i < conf->count; i++) {
326 if (conf->ops[i].offset + conf->ops[i].length >
327 m->m_pkthdr.len)
328 continue;
329
330 /* for "=" operation we don't need to copy data from mbuf */
331 if (conf->ops[i].mode != NG_PATCH_MODE_SET) {
332 m_copydata(m, conf->ops[i].offset,
333 conf->ops[i].length, (caddr_t)&buf);
334 }
335
336 switch (conf->ops[i].length) {
337 case 1:
338 switch (conf->ops[i].mode) {
339 case NG_PATCH_MODE_SET:
340 *((uint8_t *)&buf) = privp->val[i].v1;
341 break;
342 case NG_PATCH_MODE_ADD:
343 *((uint8_t *)&buf) += privp->val[i].v1;
344 break;
345 case NG_PATCH_MODE_SUB:
346 *((uint8_t *)&buf) -= privp->val[i].v1;
347 break;
348 case NG_PATCH_MODE_MUL:
349 *((uint8_t *)&buf) *= privp->val[i].v1;
350 break;
351 case NG_PATCH_MODE_DIV:
352 *((uint8_t *)&buf) /= privp->val[i].v1;
353 break;
354 case NG_PATCH_MODE_NEG:
355 *((int8_t *)&buf) = - *((int8_t *)&buf);
356 break;
357 case NG_PATCH_MODE_AND:
358 *((uint8_t *)&buf) &= privp->val[i].v1;
359 break;
360 case NG_PATCH_MODE_OR:
361 *((uint8_t *)&buf) |= privp->val[i].v1;
362 break;
363 case NG_PATCH_MODE_XOR:
364 *((uint8_t *)&buf) ^= privp->val[i].v1;
365 break;
366 case NG_PATCH_MODE_SHL:
367 *((uint8_t *)&buf) <<= privp->val[i].v1;
368 break;
369 case NG_PATCH_MODE_SHR:
370 *((uint8_t *)&buf) >>= privp->val[i].v1;
371 break;
372 }
373 break;
374 case 2:
375 *((int16_t *)&buf) = ntohs(*((int16_t *)&buf));
376 switch (conf->ops[i].mode) {
377 case NG_PATCH_MODE_SET:
378 *((uint16_t *)&buf) = privp->val[i].v2;
379 break;
380 case NG_PATCH_MODE_ADD:
381 *((uint16_t *)&buf) += privp->val[i].v2;
382 break;
383 case NG_PATCH_MODE_SUB:
384 *((uint16_t *)&buf) -= privp->val[i].v2;
385 break;
386 case NG_PATCH_MODE_MUL:
387 *((uint16_t *)&buf) *= privp->val[i].v2;
388 break;
389 case NG_PATCH_MODE_DIV:
390 *((uint16_t *)&buf) /= privp->val[i].v2;
391 break;
392 case NG_PATCH_MODE_NEG:
393 *((int16_t *)&buf) = - *((int16_t *)&buf);
394 break;
395 case NG_PATCH_MODE_AND:
396 *((uint16_t *)&buf) &= privp->val[i].v2;
397 break;
398 case NG_PATCH_MODE_OR:
399 *((uint16_t *)&buf) |= privp->val[i].v2;
400 break;
401 case NG_PATCH_MODE_XOR:
402 *((uint16_t *)&buf) ^= privp->val[i].v2;
403 break;
404 case NG_PATCH_MODE_SHL:
405 *((uint16_t *)&buf) <<= privp->val[i].v2;
406 break;
407 case NG_PATCH_MODE_SHR:
408 *((uint16_t *)&buf) >>= privp->val[i].v2;
409 break;
410 }
411 *((int16_t *)&buf) = htons(*((int16_t *)&buf));
412 break;
413 case 4:
414 *((int32_t *)&buf) = ntohl(*((int32_t *)&buf));
415 switch (conf->ops[i].mode) {
416 case NG_PATCH_MODE_SET:
417 *((uint32_t *)&buf) = privp->val[i].v4;
418 break;
419 case NG_PATCH_MODE_ADD:
420 *((uint32_t *)&buf) += privp->val[i].v4;
421 break;
422 case NG_PATCH_MODE_SUB:
423 *((uint32_t *)&buf) -= privp->val[i].v4;
424 break;
425 case NG_PATCH_MODE_MUL:
426 *((uint32_t *)&buf) *= privp->val[i].v4;
427 break;
428 case NG_PATCH_MODE_DIV:
429 *((uint32_t *)&buf) /= privp->val[i].v4;
430 break;
431 case NG_PATCH_MODE_NEG:
432 *((int32_t *)&buf) = - *((int32_t *)&buf);
433 break;
434 case NG_PATCH_MODE_AND:
435 *((uint32_t *)&buf) &= privp->val[i].v4;
436 break;
437 case NG_PATCH_MODE_OR:
438 *((uint32_t *)&buf) |= privp->val[i].v4;
439 break;
440 case NG_PATCH_MODE_XOR:
441 *((uint32_t *)&buf) ^= privp->val[i].v4;
442 break;
443 case NG_PATCH_MODE_SHL:
444 *((uint32_t *)&buf) <<= privp->val[i].v4;
445 break;
446 case NG_PATCH_MODE_SHR:
447 *((uint32_t *)&buf) >>= privp->val[i].v4;
448 break;
449 }
450 *((int32_t *)&buf) = htonl(*((int32_t *)&buf));
451 break;
452 case 8:
453 *((int64_t *)&buf) = be64toh(*((int64_t *)&buf));
454 switch (conf->ops[i].mode) {
455 case NG_PATCH_MODE_SET:
456 *((uint64_t *)&buf) = privp->val[i].v8;
457 break;
458 case NG_PATCH_MODE_ADD:
459 *((uint64_t *)&buf) += privp->val[i].v8;
460 break;
461 case NG_PATCH_MODE_SUB:
462 *((uint64_t *)&buf) -= privp->val[i].v8;
463 break;
464 case NG_PATCH_MODE_MUL:
465 *((uint64_t *)&buf) *= privp->val[i].v8;
466 break;
467 case NG_PATCH_MODE_DIV:
468 *((uint64_t *)&buf) /= privp->val[i].v8;
469 break;
470 case NG_PATCH_MODE_NEG:
471 *((int64_t *)&buf) = - *((int64_t *)&buf);
472 break;
473 case NG_PATCH_MODE_AND:
474 *((uint64_t *)&buf) &= privp->val[i].v8;
475 break;
476 case NG_PATCH_MODE_OR:
477 *((uint64_t *)&buf) |= privp->val[i].v8;
478 break;
479 case NG_PATCH_MODE_XOR:
480 *((uint64_t *)&buf) ^= privp->val[i].v8;
481 break;
482 case NG_PATCH_MODE_SHL:
483 *((uint64_t *)&buf) <<= privp->val[i].v8;
484 break;
485 case NG_PATCH_MODE_SHR:
486 *((uint64_t *)&buf) >>= privp->val[i].v8;
487 break;
488 }
489 *((int64_t *)&buf) = htobe64(*((int64_t *)&buf));
490 break;
491 }
492
493 m_copyback(m, conf->ops[i].offset, conf->ops[i].length,
494 (caddr_t)&buf);
495 patched = 1;
496 }
497 if (patched > 0)
498 privp->stats.patched++;
499 }
500
501 static int
502 ng_patch_rcvdata(hook_p hook, item_p item)
503 {
504 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
505 struct mbuf *m;
506 hook_p target;
507 int error;
508
509 priv->stats.received++;
510 NGI_GET_M(item, m);
511 if (priv->config != NULL && hook == priv->in &&
512 (m->m_flags & M_PKTHDR) != 0) {
513 m = m_unshare(m,M_NOWAIT);
514 if (m == NULL) {
515 priv->stats.dropped++;
516 NG_FREE_ITEM(item);
517 return (ENOMEM);
518 }
519 do_patch(priv, m);
520 m->m_pkthdr.csum_flags |= priv->config->csum_flags;
521 }
522
523 target = NULL;
524 if (hook == priv->in) {
525 /* return frames on 'in' hook if 'out' not connected */
526 if (priv->out != NULL)
527 target = priv->out;
528 else
529 target = priv->in;
530 }
531 if (hook == priv->out && priv->in != NULL)
532 target = priv->in;
533
534 if (target == NULL) {
535 priv->stats.dropped++;
536 NG_FREE_ITEM(item);
537 NG_FREE_M(m);
538 return (0);
539 }
540 NG_FWD_NEW_DATA(error, item, target, m);
541 return (error);
542 }
543
544 static int
545 ng_patch_shutdown(node_p node)
546 {
547 const priv_p privdata = NG_NODE_PRIVATE(node);
548
549 if (privdata->val != NULL)
550 free(privdata->val, M_NETGRAPH);
551 if (privdata->config != NULL)
552 free(privdata->config, M_NETGRAPH);
553 NG_NODE_SET_PRIVATE(node, NULL);
554 NG_NODE_UNREF(node);
555 free(privdata, M_NETGRAPH);
556 return (0);
557 }
558
559 static int
560 ng_patch_disconnect(hook_p hook)
561 {
562 priv_p priv;
563
564 priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
565 if (hook == priv->in) {
566 priv->in = NULL;
567 }
568 if (hook == priv->out) {
569 priv->out = NULL;
570 }
571 if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 &&
572 NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */
573 ng_rmnode_self(NG_HOOK_NODE(hook));
574 return (0);
575 }
576
Cache object: e1bc178cd795fbf94fe0f2afedb7140b
|