]>
Commit | Line | Data |
---|---|---|
7e7c7372 | 1 | /* |
ae665a52 | 2 | * m_pedit.c generic packet editor actions module |
7e7c7372 | 3 | * |
4 | * This program is free software; you can distribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
ae665a52 SH |
9 | * Authors: J Hadi Salim (hadi@cyberus.ca) |
10 | * | |
11 | * TODO: | |
7e7c7372 | 12 | * 1) Big endian broken in some spots |
13 | * 2) A lot of this stuff was added on the fly; get a big double-double | |
14 | * and clean it up at some point. | |
ae665a52 | 15 | * |
7e7c7372 | 16 | */ |
17 | ||
18 | #include <stdio.h> | |
19 | #include <stdlib.h> | |
20 | #include <unistd.h> | |
21 | #include <syslog.h> | |
22 | #include <fcntl.h> | |
23 | #include <sys/socket.h> | |
24 | #include <netinet/in.h> | |
25 | #include <arpa/inet.h> | |
26 | #include <string.h> | |
27 | #include <dlfcn.h> | |
28 | #include "utils.h" | |
29 | #include "tc_util.h" | |
30 | #include "m_pedit.h" | |
31 | ||
7e7c7372 | 32 | static struct m_pedit_util *pedit_list; |
33 | int pedit_debug = 1; | |
34 | ||
35 | static void | |
ebf32083 | 36 | explain(void) |
7e7c7372 | 37 | { |
ebf32083 | 38 | fprintf(stderr, "Usage: ... pedit munge <MUNGE>\n"); |
7e7c7372 | 39 | fprintf(stderr, |
ae665a52 | 40 | "Where: MUNGE := <RAW>|<LAYERED>\n" |
ebf32083 JHS |
41 | "\t<RAW>:= <OFFSETC>[ATC]<CMD>\n " |
42 | "\t\tOFFSETC:= offset <offval> <u8|u16|u32>\n " | |
43 | "\t\tATC:= at <atval> offmask <maskval> shift <shiftval>\n " | |
44 | "\t\tNOTE: offval is byte offset, must be multiple of 4\n " | |
45 | "\t\tNOTE: maskval is a 32 bit hex number\n " | |
46 | "\t\tNOTE: shiftval is a is a shift value\n " | |
47 | "\t\tCMD:= clear | invert | set <setval>| retain\n " | |
48 | "\t<LAYERED>:= ip <ipdata> | ip6 <ip6data> \n " | |
49 | " \t\t| udp <udpdata> | tcp <tcpdata> | icmp <icmpdata> \n" | |
50 | "For Example usage look at the examples directory\n"); | |
7e7c7372 | 51 | |
52 | } | |
53 | ||
ebf32083 JHS |
54 | static void |
55 | usage(void) | |
56 | { | |
57 | explain(); | |
58 | exit(-1); | |
59 | } | |
7e7c7372 | 60 | |
ae665a52 SH |
61 | static int |
62 | pedit_parse_nopopt (int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) | |
7e7c7372 | 63 | { |
64 | int argc = *argc_p; | |
65 | char **argv = *argv_p; | |
66 | ||
67 | if (argc) { | |
68 | fprintf(stderr, "Unknown action hence option \"%s\" is unparsable\n", *argv); | |
69 | return -1; | |
70 | } | |
71 | ||
72 | return 0; | |
73 | ||
74 | } | |
75 | ||
ae665a52 | 76 | struct m_pedit_util |
7e7c7372 | 77 | *get_pedit_kind(char *str) |
78 | { | |
fe102f61 | 79 | static void *pBODY; |
7e7c7372 | 80 | void *dlh; |
81 | char buf[256]; | |
82 | struct m_pedit_util *p; | |
83 | ||
84 | for (p = pedit_list; p; p = p->next) { | |
85 | if (strcmp(p->id, str) == 0) | |
86 | return p; | |
87 | } | |
88 | ||
89 | snprintf(buf, sizeof(buf), "p_%s.so", str); | |
90 | dlh = dlopen(buf, RTLD_LAZY); | |
91 | if (dlh == NULL) { | |
92 | dlh = pBODY; | |
93 | if (dlh == NULL) { | |
94 | dlh = pBODY = dlopen(NULL, RTLD_LAZY); | |
95 | if (dlh == NULL) | |
96 | goto noexist; | |
97 | } | |
98 | } | |
99 | ||
100 | snprintf(buf, sizeof(buf), "p_pedit_%s", str); | |
101 | p = dlsym(dlh, buf); | |
102 | if (p == NULL) | |
103 | goto noexist; | |
104 | ||
105 | reg: | |
106 | p->next = pedit_list; | |
107 | pedit_list = p; | |
108 | return p; | |
109 | ||
110 | noexist: | |
111 | p = malloc(sizeof(*p)); | |
112 | if (p) { | |
113 | memset(p, 0, sizeof(*p)); | |
6107d297 | 114 | strncpy(p->id, str, sizeof(p->id)-1); |
7e7c7372 | 115 | p->parse_peopt = pedit_parse_nopopt; |
116 | goto reg; | |
117 | } | |
118 | return p; | |
119 | } | |
120 | ||
121 | int | |
122 | pack_key(struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) | |
123 | { | |
124 | int hwm = sel->nkeys; | |
125 | ||
126 | if (hwm >= MAX_OFFS) | |
127 | return -1; | |
128 | ||
129 | if (tkey->off % 4) { | |
130 | fprintf(stderr, "offsets MUST be in 32 bit boundaries\n"); | |
131 | return -1; | |
132 | } | |
133 | ||
134 | sel->keys[hwm].val = tkey->val; | |
135 | sel->keys[hwm].mask = tkey->mask; | |
136 | sel->keys[hwm].off = tkey->off; | |
137 | sel->keys[hwm].at = tkey->at; | |
138 | sel->keys[hwm].offmask = tkey->offmask; | |
139 | sel->keys[hwm].shift = tkey->shift; | |
140 | sel->nkeys++; | |
141 | return 0; | |
142 | } | |
143 | ||
144 | ||
145 | int | |
146 | pack_key32(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) | |
147 | { | |
148 | if (tkey->off > (tkey->off & ~3)) { | |
149 | fprintf(stderr, | |
150 | "pack_key32: 32 bit offsets must begin in 32bit boundaries\n"); | |
151 | return -1; | |
152 | } | |
153 | ||
154 | tkey->val = htonl(tkey->val & retain); | |
155 | tkey->mask = htonl(tkey->mask | ~retain); | |
156 | /* jamal remove this - it is not necessary given the if check above */ | |
157 | tkey->off &= ~3; | |
158 | return pack_key(sel,tkey); | |
159 | } | |
160 | ||
161 | int | |
162 | pack_key16(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) | |
163 | { | |
164 | int ind = 0, stride = 0; | |
165 | __u32 m[4] = {0xFFFF0000,0xFF0000FF,0x0000FFFF}; | |
166 | ||
167 | if (0 > tkey->off) { | |
168 | ind = tkey->off + 1; | |
169 | if (0 > ind) | |
170 | ind = -1*ind; | |
171 | } else { | |
172 | ind = tkey->off; | |
173 | } | |
174 | ||
175 | if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) { | |
176 | fprintf(stderr, "pack_key16 bad value\n"); | |
177 | return -1; | |
178 | } | |
179 | ||
180 | ind = tkey->off & 3; | |
181 | ||
182 | if (0 > ind || 2 < ind) { | |
183 | fprintf(stderr, "pack_key16 bad index value %d\n",ind); | |
184 | return -1; | |
185 | } | |
186 | ||
187 | stride = 8 * ind; | |
188 | tkey->val = htons(tkey->val); | |
189 | if (stride > 0) { | |
190 | tkey->val <<= stride; | |
191 | tkey->mask <<= stride; | |
192 | retain <<= stride; | |
193 | } | |
194 | tkey->mask = retain|m[ind]; | |
195 | ||
196 | tkey->off &= ~3; | |
197 | ||
198 | if (pedit_debug) | |
199 | printf("pack_key16: Final val %08x mask %08x \n",tkey->val,tkey->mask); | |
200 | return pack_key(sel,tkey); | |
201 | ||
202 | } | |
203 | ||
204 | int | |
205 | pack_key8(__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) | |
206 | { | |
207 | int ind = 0, stride = 0; | |
208 | __u32 m[4] = {0xFFFFFF00,0xFFFF00FF,0xFF00FFFF,0x00FFFFFF}; | |
209 | ||
210 | if (0 > tkey->off) { | |
211 | ind = tkey->off + 1; | |
212 | if (0 > ind) | |
213 | ind = -1*ind; | |
214 | } else { | |
215 | ind = tkey->off; | |
216 | } | |
217 | ||
218 | if (tkey->val > 0xFF || tkey->mask > 0xFF) { | |
219 | fprintf(stderr, "pack_key8 bad value (val %x mask %x\n", tkey->val, tkey->mask); | |
220 | return -1; | |
221 | } | |
222 | ||
223 | ind = tkey->off & 3; | |
224 | stride = 8 * ind; | |
225 | tkey->val <<= stride; | |
226 | tkey->mask <<= stride; | |
227 | retain <<= stride; | |
228 | tkey->mask = retain|m[ind]; | |
229 | tkey->off &= ~3; | |
ae665a52 | 230 | |
7e7c7372 | 231 | if (pedit_debug) |
232 | printf("pack_key8: Final word off %d val %08x mask %08x \n",tkey->off , tkey->val,tkey->mask); | |
233 | return pack_key(sel,tkey); | |
234 | } | |
235 | ||
236 | int | |
237 | parse_val(int *argc_p, char ***argv_p, __u32 * val, int type) | |
238 | { | |
239 | int argc = *argc_p; | |
240 | char **argv = *argv_p; | |
241 | ||
242 | if (argc <= 0) | |
243 | return -1; | |
244 | ||
245 | if (TINT == type) | |
f332d169 SH |
246 | return get_integer((int *) val, *argv, 0); |
247 | ||
7e7c7372 | 248 | if (TU32 == type) |
249 | return get_u32(val, *argv, 0); | |
f332d169 | 250 | |
7e7c7372 | 251 | if (TIPV4 == type) { |
252 | inet_prefix addr; | |
253 | if (get_prefix_1(&addr, *argv, AF_INET)) { | |
254 | return -1; | |
255 | } | |
256 | *val=addr.data[0]; | |
257 | return 0; | |
258 | } | |
259 | if (TIPV6 == type) { | |
260 | /* not implemented yet */ | |
261 | return -1; | |
262 | } | |
263 | ||
264 | return -1; | |
265 | } | |
266 | ||
267 | int | |
268 | parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type,__u32 retain,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) | |
269 | { | |
270 | __u32 mask = 0, val = 0; | |
271 | __u32 o = 0xFF; | |
272 | int res = -1; | |
273 | int argc = *argc_p; | |
274 | char **argv = *argv_p; | |
275 | ||
276 | if (argc <= 0) | |
277 | return -1; | |
278 | ||
279 | if (pedit_debug) | |
280 | printf("parse_cmd argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); | |
281 | ||
282 | if (len == 2) | |
283 | o = 0xFFFF; | |
284 | if (len == 4) | |
285 | o = 0xFFFFFFFF; | |
286 | ||
287 | if (matches(*argv, "invert") == 0) { | |
288 | retain = val = mask = o; | |
289 | } else if (matches(*argv, "set") == 0) { | |
290 | NEXT_ARG(); | |
291 | if (parse_val(&argc, &argv, &val, type)) | |
292 | return -1; | |
293 | } else if (matches(*argv, "preserve") == 0) { | |
294 | retain = mask = o; | |
295 | } else { | |
ae665a52 | 296 | if (matches(*argv, "clear") != 0) |
7e7c7372 | 297 | return -1; |
298 | } | |
299 | ||
300 | argc--; argv++; | |
301 | ||
302 | if (argc && matches(*argv, "retain") == 0) { | |
303 | NEXT_ARG(); | |
304 | if (parse_val(&argc, &argv, &retain, TU32)) | |
305 | return -1; | |
306 | argc--; argv++; | |
307 | } | |
308 | ||
309 | tkey->val = val; | |
310 | ||
311 | if (len == 1) { | |
312 | tkey->mask = 0xFF; | |
313 | res = pack_key8(retain,sel,tkey); | |
314 | goto done; | |
315 | } | |
316 | if (len == 2) { | |
317 | tkey->mask = mask; | |
318 | res = pack_key16(retain,sel,tkey); | |
319 | goto done; | |
320 | } | |
321 | if (len == 4) { | |
322 | tkey->mask = mask; | |
323 | res = pack_key32(retain,sel,tkey); | |
324 | goto done; | |
325 | } | |
326 | ||
327 | return -1; | |
328 | done: | |
329 | if (pedit_debug) | |
330 | printf("parse_cmd done argc %d %s offset %d length %d\n",argc,*argv,tkey->off,len); | |
331 | *argc_p = argc; | |
332 | *argv_p = argv; | |
333 | return res; | |
334 | ||
335 | } | |
336 | ||
337 | int | |
338 | parse_offset(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel,struct tc_pedit_key *tkey) | |
339 | { | |
340 | int off; | |
341 | __u32 len, retain; | |
342 | int argc = *argc_p; | |
343 | char **argv = *argv_p; | |
344 | int res = -1; | |
345 | ||
346 | if (argc <= 0) | |
347 | return -1; | |
348 | ||
349 | if (get_integer(&off, *argv, 0)) | |
350 | return -1; | |
351 | tkey->off = off; | |
352 | ||
353 | argc--; | |
354 | argv++; | |
355 | ||
356 | if (argc <= 0) | |
357 | return -1; | |
358 | ||
359 | ||
360 | if (matches(*argv, "u32") == 0) { | |
361 | len = 4; | |
362 | retain = 0xFFFFFFFF; | |
363 | goto done; | |
364 | } | |
365 | if (matches(*argv, "u16") == 0) { | |
366 | len = 2; | |
367 | retain = 0x0; | |
368 | goto done; | |
369 | } | |
370 | if (matches(*argv, "u8") == 0) { | |
371 | len = 1; | |
372 | retain = 0x0; | |
373 | goto done; | |
374 | } | |
375 | ||
376 | return -1; | |
377 | ||
378 | done: | |
379 | ||
380 | NEXT_ARG(); | |
381 | ||
382 | /* [at <someval> offmask <maskval> shift <shiftval>] */ | |
383 | if (matches(*argv, "at") == 0) { | |
384 | ||
385 | __u32 atv=0,offmask=0x0,shift=0; | |
386 | ||
387 | NEXT_ARG(); | |
388 | if (get_u32(&atv, *argv, 0)) | |
389 | return -1; | |
390 | tkey->at = atv; | |
391 | ||
392 | NEXT_ARG(); | |
ae665a52 | 393 | |
7e7c7372 | 394 | if (get_u32(&offmask, *argv, 16)) |
395 | return -1; | |
396 | tkey->offmask = offmask; | |
397 | ||
398 | NEXT_ARG(); | |
399 | ||
400 | if (get_u32(&shift, *argv, 0)) | |
401 | return -1; | |
402 | tkey->shift = shift; | |
403 | ||
404 | NEXT_ARG(); | |
405 | } | |
406 | ||
407 | res = parse_cmd(&argc, &argv, len, TU32,retain,sel,tkey); | |
408 | ||
409 | *argc_p = argc; | |
410 | *argv_p = argv; | |
411 | return res; | |
412 | } | |
413 | ||
414 | int | |
415 | parse_munge(int *argc_p, char ***argv_p,struct tc_pedit_sel *sel) | |
416 | { | |
417 | struct tc_pedit_key tkey; | |
418 | int argc = *argc_p; | |
419 | char **argv = *argv_p; | |
420 | int res = -1; | |
421 | ||
422 | if (argc <= 0) | |
423 | return -1; | |
424 | ||
425 | memset(&tkey, 0, sizeof(tkey)); | |
426 | ||
427 | if (matches(*argv, "offset") == 0) { | |
428 | NEXT_ARG(); | |
429 | res = parse_offset(&argc, &argv,sel,&tkey); | |
430 | goto done; | |
7e7c7372 | 431 | } else { |
432 | char k[16]; | |
433 | struct m_pedit_util *p = NULL; | |
434 | ||
435 | strncpy(k, *argv, sizeof (k) - 1); | |
436 | ||
437 | if (argc > 0 ) { | |
438 | p = get_pedit_kind(k); | |
439 | if (NULL == p) | |
440 | goto bad_val; | |
441 | res = p->parse_peopt(&argc, &argv, sel,&tkey); | |
442 | if (res < 0) { | |
443 | fprintf(stderr,"bad pedit parsing\n"); | |
444 | goto bad_val; | |
445 | } | |
446 | goto done; | |
447 | } | |
448 | } | |
449 | ||
450 | bad_val: | |
451 | return -1; | |
452 | ||
453 | done: | |
454 | ||
455 | *argc_p = argc; | |
456 | *argv_p = argv; | |
457 | return res; | |
458 | } | |
459 | ||
460 | int | |
461 | parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n) | |
462 | { | |
463 | struct { | |
464 | struct tc_pedit_sel sel; | |
465 | struct tc_pedit_key keys[MAX_OFFS]; | |
466 | } sel; | |
467 | ||
468 | int argc = *argc_p; | |
469 | char **argv = *argv_p; | |
470 | int ok = 0, iok = 0; | |
471 | struct rtattr *tail; | |
472 | ||
473 | memset(&sel, 0, sizeof(sel)); | |
474 | ||
475 | while (argc > 0) { | |
476 | if (pedit_debug > 1) | |
477 | fprintf(stderr, "while pedit (%d:%s)\n",argc, *argv); | |
478 | if (matches(*argv, "pedit") == 0) { | |
479 | NEXT_ARG(); | |
480 | ok++; | |
481 | continue; | |
ebf32083 JHS |
482 | } else if (matches(*argv, "help") == 0) { |
483 | usage(); | |
7e7c7372 | 484 | } else if (matches(*argv, "munge") == 0) { |
485 | if (!ok) { | |
486 | fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); | |
ebf32083 | 487 | explain(); |
7e7c7372 | 488 | return -1; |
489 | } | |
490 | NEXT_ARG(); | |
491 | if (parse_munge(&argc, &argv,&sel.sel)) { | |
492 | fprintf(stderr, "Illegal pedit construct (%s) \n", *argv); | |
ebf32083 | 493 | explain(); |
7e7c7372 | 494 | return -1; |
495 | } | |
496 | ok++; | |
497 | } else { | |
498 | break; | |
499 | } | |
500 | ||
501 | } | |
502 | ||
503 | if (!ok) { | |
ebf32083 | 504 | explain(); |
7e7c7372 | 505 | return -1; |
506 | } | |
507 | ||
508 | if (argc) { | |
509 | if (matches(*argv, "reclassify") == 0) { | |
510 | sel.sel.action = TC_ACT_RECLASSIFY; | |
511 | NEXT_ARG(); | |
512 | } else if (matches(*argv, "pipe") == 0) { | |
513 | sel.sel.action = TC_ACT_PIPE; | |
514 | NEXT_ARG(); | |
515 | } else if (matches(*argv, "drop") == 0 || | |
516 | matches(*argv, "shot") == 0) { | |
517 | sel.sel.action = TC_ACT_SHOT; | |
518 | NEXT_ARG(); | |
519 | } else if (matches(*argv, "continue") == 0) { | |
520 | sel.sel.action = TC_ACT_UNSPEC; | |
521 | NEXT_ARG(); | |
522 | } else if (matches(*argv, "pass") == 0) { | |
523 | sel.sel.action = TC_ACT_OK; | |
524 | NEXT_ARG(); | |
525 | } | |
526 | } | |
527 | ||
528 | if (argc) { | |
529 | if (matches(*argv, "index") == 0) { | |
530 | NEXT_ARG(); | |
531 | if (get_u32(&sel.sel.index, *argv, 10)) { | |
532 | fprintf(stderr, "Pedit: Illegal \"index\"\n"); | |
533 | return -1; | |
534 | } | |
535 | argc--; | |
536 | argv++; | |
537 | iok++; | |
538 | } | |
539 | } | |
540 | ||
fc78a8e9 | 541 | tail = NLMSG_TAIL(n); |
7e7c7372 | 542 | addattr_l(n, MAX_MSG, tca_id, NULL, 0); |
543 | addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS,&sel, sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_pedit_key)); | |
fc78a8e9 | 544 | tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; |
7e7c7372 | 545 | |
546 | *argc_p = argc; | |
547 | *argv_p = argv; | |
548 | return 0; | |
549 | } | |
550 | ||
551 | int | |
552 | print_pedit(struct action_util *au,FILE * f, struct rtattr *arg) | |
553 | { | |
554 | struct tc_pedit_sel *sel; | |
555 | struct rtattr *tb[TCA_PEDIT_MAX + 1]; | |
556 | SPRINT_BUF(b1); | |
557 | ||
558 | if (arg == NULL) | |
559 | return -1; | |
560 | ||
78934000 | 561 | parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg); |
7e7c7372 | 562 | |
563 | if (tb[TCA_PEDIT_PARMS] == NULL) { | |
564 | fprintf(f, "[NULL pedit parameters]"); | |
565 | return -1; | |
566 | } | |
567 | sel = RTA_DATA(tb[TCA_PEDIT_PARMS]); | |
568 | ||
569 | fprintf(f, " pedit action %s keys %d\n ", action_n2a(sel->action, b1, sizeof (b1)),sel->nkeys); | |
570 | fprintf(f, "\t index %d ref %d bind %d", sel->index,sel->refcnt, sel->bindcnt); | |
571 | ||
572 | if (show_stats) { | |
573 | if (tb[TCA_PEDIT_TM]) { | |
574 | struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]); | |
575 | print_tm(f,tm); | |
576 | } | |
577 | } | |
578 | if (sel->nkeys) { | |
579 | int i; | |
580 | struct tc_pedit_key *key = sel->keys; | |
581 | ||
582 | for (i=0; i<sel->nkeys; i++, key++) { | |
583 | fprintf(f, "\n\t key #%d",i); | |
584 | fprintf(f, " at %d: val %08x mask %08x", | |
585 | (unsigned int)key->off, | |
586 | (unsigned int)ntohl(key->val), | |
587 | (unsigned int)ntohl(key->mask)); | |
588 | } | |
589 | } else { | |
590 | fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,sel->nkeys); | |
591 | } | |
592 | ||
593 | ||
594 | fprintf(f, "\n "); | |
595 | return 0; | |
596 | } | |
597 | ||
ae665a52 | 598 | int |
7e7c7372 | 599 | pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats) |
600 | { | |
601 | return 0; | |
602 | } | |
603 | ||
604 | struct action_util pedit_action_util = { | |
605 | .id = "pedit", | |
606 | .parse_aopt = parse_pedit, | |
607 | .print_aopt = print_pedit, | |
608 | }; |