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