]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/m_pedit.c
2aeb56d9615f1e9ee4029aa5280c7170fe95e730
[mirror_iproute2.git] / tc / m_pedit.c
1 /*
2 * m_pedit.c generic packet editor actions module
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 *
9 * Authors: J Hadi Salim (hadi@cyberus.ca)
10 *
11 * TODO:
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.
15 *
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <string.h>
26 #include <dlfcn.h>
27 #include "utils.h"
28 #include "tc_util.h"
29 #include "m_pedit.h"
30 #include "rt_names.h"
31
32 static struct m_pedit_util *pedit_list;
33 static int pedit_debug;
34
35 static void explain(void)
36 {
37 fprintf(stderr, "Usage: ... pedit munge [ex] <MUNGE> [CONTROL]\n");
38 fprintf(stderr,
39 "Where: MUNGE := <RAW>|<LAYERED>\n"
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 shift value\n"
44 "\t\tCMD:= clear | invert | set <setval>| add <addval> | retain\n"
45 "\t<LAYERED>:= ip <ipdata> | ip6 <ip6data>\n"
46 " \t\t| udp <udpdata> | tcp <tcpdata> | icmp <icmpdata>\n"
47 "\tCONTROL:= reclassify | pipe | drop | continue | pass |\n"
48 "\t goto chain <CHAIN_INDEX>\n"
49 "\tNOTE: if 'ex' is set, extended functionality will be supported (kernel >= 4.11)\n"
50 "For Example usage look at the examples directory\n");
51
52 }
53
54 static void usage(void)
55 {
56 explain();
57 exit(-1);
58 }
59
60 static int pedit_parse_nopopt(int *argc_p, char ***argv_p,
61 struct m_pedit_sel *sel,
62 struct m_pedit_key *tkey)
63 {
64 int argc = *argc_p;
65 char **argv = *argv_p;
66
67 if (argc) {
68 fprintf(stderr,
69 "Unknown action hence option \"%s\" is unparsable\n",
70 *argv);
71 return -1;
72 }
73
74 return 0;
75
76 }
77
78 static struct m_pedit_util *get_pedit_kind(const char *str)
79 {
80 static void *pBODY;
81 void *dlh;
82 char buf[256];
83 struct m_pedit_util *p;
84
85 for (p = pedit_list; p; p = p->next) {
86 if (strcmp(p->id, str) == 0)
87 return p;
88 }
89
90 snprintf(buf, sizeof(buf), "p_%s.so", str);
91 dlh = dlopen(buf, RTLD_LAZY);
92 if (dlh == NULL) {
93 dlh = pBODY;
94 if (dlh == NULL) {
95 dlh = pBODY = dlopen(NULL, RTLD_LAZY);
96 if (dlh == NULL)
97 goto noexist;
98 }
99 }
100
101 snprintf(buf, sizeof(buf), "p_pedit_%s", str);
102 p = dlsym(dlh, buf);
103 if (p == NULL)
104 goto noexist;
105
106 reg:
107 p->next = pedit_list;
108 pedit_list = p;
109 return p;
110
111 noexist:
112 p = calloc(1, sizeof(*p));
113 if (p) {
114 strlcpy(p->id, str, sizeof(p->id));
115 p->parse_peopt = pedit_parse_nopopt;
116 goto reg;
117 }
118 return p;
119 }
120
121 int pack_key(struct m_pedit_sel *_sel, struct m_pedit_key *tkey)
122 {
123 struct tc_pedit_sel *sel = &_sel->sel;
124 struct m_pedit_key_ex *keys_ex = _sel->keys_ex;
125 int hwm = sel->nkeys;
126
127 if (hwm >= MAX_OFFS)
128 return -1;
129
130 if (tkey->off % 4) {
131 fprintf(stderr, "offsets MUST be in 32 bit boundaries\n");
132 return -1;
133 }
134
135 sel->keys[hwm].val = tkey->val;
136 sel->keys[hwm].mask = tkey->mask;
137 sel->keys[hwm].off = tkey->off;
138 sel->keys[hwm].at = tkey->at;
139 sel->keys[hwm].offmask = tkey->offmask;
140 sel->keys[hwm].shift = tkey->shift;
141
142 if (_sel->extended) {
143 keys_ex[hwm].htype = tkey->htype;
144 keys_ex[hwm].cmd = tkey->cmd;
145 } else {
146 if (tkey->htype != TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK ||
147 tkey->cmd != TCA_PEDIT_KEY_EX_CMD_SET) {
148 fprintf(stderr,
149 "Munge parameters not supported. Use 'pedit ex munge ...'.\n");
150 return -1;
151 }
152 }
153
154 sel->nkeys++;
155 return 0;
156 }
157
158 int pack_key32(__u32 retain, struct m_pedit_sel *sel,
159 struct m_pedit_key *tkey)
160 {
161 if (tkey->off > (tkey->off & ~3)) {
162 fprintf(stderr,
163 "pack_key32: 32 bit offsets must begin in 32bit boundaries\n");
164 return -1;
165 }
166
167 tkey->val = htonl(tkey->val & retain);
168 tkey->mask = htonl(tkey->mask | ~retain);
169 return pack_key(sel, tkey);
170 }
171
172 int pack_key16(__u32 retain, struct m_pedit_sel *sel,
173 struct m_pedit_key *tkey)
174 {
175 int ind, stride;
176 __u32 m[4] = { 0x0000FFFF, 0xFF0000FF, 0xFFFF0000 };
177
178 if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) {
179 fprintf(stderr, "pack_key16 bad value\n");
180 return -1;
181 }
182
183 ind = tkey->off & 3;
184
185 if (ind == 3) {
186 fprintf(stderr, "pack_key16 bad index value %d\n", ind);
187 return -1;
188 }
189
190 stride = 8 * (2 - ind);
191 tkey->val = htonl((tkey->val & retain) << stride);
192 tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]);
193
194 tkey->off &= ~3;
195
196 if (pedit_debug)
197 printf("pack_key16: Final val %08x mask %08x\n",
198 tkey->val, tkey->mask);
199 return pack_key(sel, tkey);
200
201 }
202
203 int pack_key8(__u32 retain, struct m_pedit_sel *sel, struct m_pedit_key *tkey)
204 {
205 int ind, stride;
206 __u32 m[4] = { 0x00FFFFFF, 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00 };
207
208 if (tkey->val > 0xFF || tkey->mask > 0xFF) {
209 fprintf(stderr, "pack_key8 bad value (val %x mask %x\n",
210 tkey->val, tkey->mask);
211 return -1;
212 }
213
214 ind = tkey->off & 3;
215
216 stride = 8 * (3 - ind);
217 tkey->val = htonl((tkey->val & retain) << stride);
218 tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]);
219
220 tkey->off &= ~3;
221
222 if (pedit_debug)
223 printf("pack_key8: Final word off %d val %08x mask %08x\n",
224 tkey->off, tkey->val, tkey->mask);
225 return pack_key(sel, tkey);
226 }
227
228 static int pack_mac(struct m_pedit_sel *sel, struct m_pedit_key *tkey,
229 __u8 *mac)
230 {
231 int ret = 0;
232
233 if (!(tkey->off & 0x3)) {
234 tkey->mask = 0;
235 tkey->val = ntohl(*((__u32 *)mac));
236 ret |= pack_key32(~0, sel, tkey);
237
238 tkey->off += 4;
239 tkey->mask = 0;
240 tkey->val = ntohs(*((__u16 *)&mac[4]));
241 ret |= pack_key16(~0, sel, tkey);
242 } else if (!(tkey->off & 0x1)) {
243 tkey->mask = 0;
244 tkey->val = ntohs(*((__u16 *)mac));
245 ret |= pack_key16(~0, sel, tkey);
246
247 tkey->off += 4;
248 tkey->mask = 0;
249 tkey->val = ntohl(*((__u32 *)(mac + 2)));
250 ret |= pack_key32(~0, sel, tkey);
251 } else {
252 fprintf(stderr,
253 "pack_mac: mac offsets must begin in 32bit or 16bit boundaries\n");
254 return -1;
255 }
256
257 return ret;
258 }
259
260 static int pack_ipv6(struct m_pedit_sel *sel, struct m_pedit_key *tkey,
261 __u32 *ipv6)
262 {
263 int ret = 0;
264 int i;
265
266 if (tkey->off & 0x3) {
267 fprintf(stderr,
268 "pack_ipv6: IPv6 offsets must begin in 32bit boundaries\n");
269 return -1;
270 }
271
272 for (i = 0; i < 4; i++) {
273 tkey->mask = 0;
274 tkey->val = ntohl(ipv6[i]);
275
276 ret = pack_key32(~0, sel, tkey);
277 if (ret)
278 return ret;
279
280 tkey->off += 4;
281 }
282
283 return 0;
284 }
285
286 int parse_val(int *argc_p, char ***argv_p, __u32 *val, int type)
287 {
288 int argc = *argc_p;
289 char **argv = *argv_p;
290
291 if (argc <= 0)
292 return -1;
293
294 if (type == TINT)
295 return get_integer((int *)val, *argv, 0);
296
297 if (type == TU32)
298 return get_u32(val, *argv, 0);
299
300 if (type == TIPV4) {
301 inet_prefix addr;
302
303 if (get_prefix_1(&addr, *argv, AF_INET))
304 return -1;
305
306 *val = addr.data[0];
307 return 0;
308 }
309
310 if (type == TIPV6) {
311 inet_prefix addr;
312
313 if (get_prefix_1(&addr, *argv, AF_INET6))
314 return -1;
315
316 memcpy(val, addr.data, addr.bytelen);
317
318 return 0;
319 }
320
321 if (type == TMAC) {
322 #define MAC_ALEN 6
323 int ret = ll_addr_a2n((char *)val, MAC_ALEN, *argv);
324
325 if (ret == MAC_ALEN)
326 return 0;
327 }
328
329 return -1;
330 }
331
332 int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type, __u32 retain,
333 struct m_pedit_sel *sel, struct m_pedit_key *tkey)
334 {
335 __u32 mask[4] = { 0 };
336 __u32 val[4] = { 0 };
337 __u32 *m = &mask[0];
338 __u32 *v = &val[0];
339 __u32 o = 0xFF;
340 int res = -1;
341 int argc = *argc_p;
342 char **argv = *argv_p;
343
344 if (argc <= 0)
345 return -1;
346
347 if (pedit_debug)
348 printf("parse_cmd argc %d %s offset %d length %d\n",
349 argc, *argv, tkey->off, len);
350
351 if (len == 2)
352 o = 0xFFFF;
353 if (len == 4)
354 o = 0xFFFFFFFF;
355
356 if (matches(*argv, "invert") == 0) {
357 *v = *m = o;
358 } else if (matches(*argv, "set") == 0 ||
359 matches(*argv, "add") == 0) {
360 if (matches(*argv, "add") == 0)
361 tkey->cmd = TCA_PEDIT_KEY_EX_CMD_ADD;
362
363 if (!sel->extended && tkey->cmd) {
364 fprintf(stderr,
365 "Non extended mode. only 'set' command is supported\n");
366 return -1;
367 }
368
369 NEXT_ARG();
370 if (parse_val(&argc, &argv, val, type))
371 return -1;
372 } else if (matches(*argv, "preserve") == 0) {
373 retain = 0;
374 } else {
375 if (matches(*argv, "clear") != 0)
376 return -1;
377 }
378
379 argc--;
380 argv++;
381
382 if (argc && matches(*argv, "retain") == 0) {
383 NEXT_ARG();
384 if (parse_val(&argc, &argv, &retain, TU32))
385 return -1;
386 argc--;
387 argv++;
388 }
389
390 if (len > 4 && retain != ~0) {
391 fprintf(stderr,
392 "retain is not supported for fields longer the 32 bits\n");
393 return -1;
394 }
395
396 if (type == TMAC) {
397 res = pack_mac(sel, tkey, (__u8 *)val);
398 goto done;
399 }
400
401 if (type == TIPV6) {
402 res = pack_ipv6(sel, tkey, val);
403 goto done;
404 }
405
406 tkey->val = *v;
407 tkey->mask = *m;
408
409 if (type == TIPV4)
410 tkey->val = ntohl(tkey->val);
411
412 if (len == 1) {
413 res = pack_key8(retain, sel, tkey);
414 goto done;
415 }
416 if (len == 2) {
417 res = pack_key16(retain, sel, tkey);
418 goto done;
419 }
420 if (len == 4) {
421 res = pack_key32(retain, sel, tkey);
422 goto done;
423 }
424
425 return -1;
426 done:
427 if (pedit_debug)
428 printf("parse_cmd done argc %d %s offset %d length %d\n",
429 argc, *argv, tkey->off, len);
430 *argc_p = argc;
431 *argv_p = argv;
432 return res;
433
434 }
435
436 int parse_offset(int *argc_p, char ***argv_p, struct m_pedit_sel *sel,
437 struct m_pedit_key *tkey)
438 {
439 int off;
440 __u32 len, retain;
441 int argc = *argc_p;
442 char **argv = *argv_p;
443 int res = -1;
444
445 if (argc <= 0)
446 return -1;
447
448 if (get_integer(&off, *argv, 0))
449 return -1;
450 tkey->off = off;
451
452 argc--;
453 argv++;
454
455 if (argc <= 0)
456 return -1;
457
458 if (matches(*argv, "u32") == 0) {
459 len = 4;
460 retain = 0xFFFFFFFF;
461 goto done;
462 }
463 if (matches(*argv, "u16") == 0) {
464 len = 2;
465 retain = 0xffff;
466 goto done;
467 }
468 if (matches(*argv, "u8") == 0) {
469 len = 1;
470 retain = 0xff;
471 goto done;
472 }
473
474 return -1;
475
476 done:
477
478 NEXT_ARG();
479
480 /* [at <someval> offmask <maskval> shift <shiftval>] */
481 if (matches(*argv, "at") == 0) {
482
483 __u32 atv = 0, offmask = 0x0, shift = 0;
484
485 NEXT_ARG();
486 if (get_u32(&atv, *argv, 0))
487 return -1;
488 tkey->at = atv;
489
490 NEXT_ARG();
491
492 if (get_u32(&offmask, *argv, 16))
493 return -1;
494 tkey->offmask = offmask;
495
496 NEXT_ARG();
497
498 if (get_u32(&shift, *argv, 0))
499 return -1;
500 tkey->shift = shift;
501
502 NEXT_ARG();
503 }
504
505 res = parse_cmd(&argc, &argv, len, TU32, retain, sel, tkey);
506
507 *argc_p = argc;
508 *argv_p = argv;
509 return res;
510 }
511
512 static int parse_munge(int *argc_p, char ***argv_p, struct m_pedit_sel *sel)
513 {
514 struct m_pedit_key tkey = {};
515 int argc = *argc_p;
516 char **argv = *argv_p;
517 int res = -1;
518
519 if (argc <= 0)
520 return -1;
521
522 if (matches(*argv, "offset") == 0) {
523 NEXT_ARG();
524 res = parse_offset(&argc, &argv, sel, &tkey);
525 goto done;
526 } else {
527 char k[FILTER_NAMESZ];
528 struct m_pedit_util *p = NULL;
529
530 strncpy(k, *argv, sizeof(k) - 1);
531
532 if (argc > 0) {
533 p = get_pedit_kind(k);
534 if (p == NULL)
535 goto bad_val;
536 NEXT_ARG();
537 res = p->parse_peopt(&argc, &argv, sel, &tkey);
538 if (res < 0) {
539 fprintf(stderr, "bad pedit parsing\n");
540 goto bad_val;
541 }
542 goto done;
543 }
544 }
545
546 bad_val:
547 return -1;
548
549 done:
550
551 *argc_p = argc;
552 *argv_p = argv;
553 return res;
554 }
555
556 static int pedit_keys_ex_getattr(struct rtattr *attr,
557 struct m_pedit_key_ex *keys_ex, int n)
558 {
559 struct rtattr *i;
560 int rem = RTA_PAYLOAD(attr);
561 struct rtattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
562 struct m_pedit_key_ex *k = keys_ex;
563
564 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
565 if (!n)
566 return -1;
567
568 if (i->rta_type != TCA_PEDIT_KEY_EX)
569 return -1;
570
571 parse_rtattr_nested(tb, TCA_PEDIT_KEY_EX_MAX, i);
572
573 k->htype = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
574 k->cmd = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
575
576 k++;
577 n--;
578 }
579
580 return !!n;
581 }
582
583 static int pedit_keys_ex_addattr(struct m_pedit_sel *sel, struct nlmsghdr *n)
584 {
585 struct m_pedit_key_ex *k = sel->keys_ex;
586 struct rtattr *keys_start;
587 int i;
588
589 if (!sel->extended)
590 return 0;
591
592 keys_start = addattr_nest(n, MAX_MSG, TCA_PEDIT_KEYS_EX | NLA_F_NESTED);
593
594 for (i = 0; i < sel->sel.nkeys; i++) {
595 struct rtattr *key_start;
596
597 key_start = addattr_nest(n, MAX_MSG,
598 TCA_PEDIT_KEY_EX | NLA_F_NESTED);
599
600 if (addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_HTYPE, k->htype) ||
601 addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_CMD, k->cmd)) {
602 return -1;
603 }
604
605 addattr_nest_end(n, key_start);
606
607 k++;
608 }
609
610 addattr_nest_end(n, keys_start);
611
612 return 0;
613 }
614
615 int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
616 struct nlmsghdr *n)
617 {
618 struct m_pedit_sel sel = {};
619
620 int argc = *argc_p;
621 char **argv = *argv_p;
622 int ok = 0, iok = 0;
623 struct rtattr *tail;
624
625 while (argc > 0) {
626 if (pedit_debug > 1)
627 fprintf(stderr, "while pedit (%d:%s)\n", argc, *argv);
628 if (matches(*argv, "pedit") == 0) {
629 NEXT_ARG();
630 ok++;
631
632 if (matches(*argv, "ex") == 0) {
633 if (ok > 1) {
634 fprintf(stderr,
635 "'ex' must be before first 'munge'\n");
636 explain();
637 return -1;
638 }
639 sel.extended = true;
640 NEXT_ARG();
641 }
642
643 continue;
644 } else if (matches(*argv, "help") == 0) {
645 usage();
646 } else if (matches(*argv, "munge") == 0) {
647 if (!ok) {
648 fprintf(stderr, "Bad pedit construct (%s)\n",
649 *argv);
650 explain();
651 return -1;
652 }
653 NEXT_ARG();
654
655 if (parse_munge(&argc, &argv, &sel)) {
656 fprintf(stderr, "Bad pedit construct (%s)\n",
657 *argv);
658 explain();
659 return -1;
660 }
661 ok++;
662 } else {
663 break;
664 }
665
666 }
667
668 if (!ok) {
669 explain();
670 return -1;
671 }
672
673 parse_action_control_dflt(&argc, &argv, &sel.sel.action, false, TC_ACT_OK);
674
675 if (argc) {
676 if (matches(*argv, "index") == 0) {
677 NEXT_ARG();
678 if (get_u32(&sel.sel.index, *argv, 10)) {
679 fprintf(stderr, "Pedit: Illegal \"index\"\n");
680 return -1;
681 }
682 argc--;
683 argv++;
684 iok++;
685 }
686 }
687
688 tail = addattr_nest(n, MAX_MSG, tca_id);
689 if (!sel.extended) {
690 addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS, &sel,
691 sizeof(sel.sel) +
692 sel.sel.nkeys * sizeof(struct tc_pedit_key));
693 } else {
694 addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS_EX, &sel,
695 sizeof(sel.sel) +
696 sel.sel.nkeys * sizeof(struct tc_pedit_key));
697
698 pedit_keys_ex_addattr(&sel, n);
699 }
700
701 addattr_nest_end(n, tail);
702
703 *argc_p = argc;
704 *argv_p = argv;
705 return 0;
706 }
707
708 const char *pedit_htype_str[] = {
709 [TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK] = "",
710 [TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = "eth",
711 [TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = "ipv4",
712 [TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = "ipv6",
713 [TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = "tcp",
714 [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = "udp",
715 };
716
717 static void print_pedit_location(FILE *f,
718 enum pedit_header_type htype, __u32 off)
719 {
720 if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
721 fprintf(f, "%d", (unsigned int)off);
722 return;
723 }
724
725 if (htype < ARRAY_SIZE(pedit_htype_str))
726 fprintf(f, "%s", pedit_htype_str[htype]);
727 else
728 fprintf(f, "unknown(%d)", htype);
729
730 fprintf(f, "%c%d", (int)off >= 0 ? '+' : '-', abs((int)off));
731 }
732
733 int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
734 {
735 struct tc_pedit_sel *sel;
736 struct rtattr *tb[TCA_PEDIT_MAX + 1];
737 struct m_pedit_key_ex *keys_ex = NULL;
738
739 if (arg == NULL)
740 return -1;
741
742 parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
743
744 if (!tb[TCA_PEDIT_PARMS] && !tb[TCA_PEDIT_PARMS_EX]) {
745 fprintf(f, "[NULL pedit parameters]");
746 return -1;
747 }
748
749 if (tb[TCA_PEDIT_PARMS]) {
750 sel = RTA_DATA(tb[TCA_PEDIT_PARMS]);
751 } else {
752 int err;
753
754 sel = RTA_DATA(tb[TCA_PEDIT_PARMS_EX]);
755
756 if (!tb[TCA_PEDIT_KEYS_EX]) {
757 fprintf(f, "Netlink error\n");
758 return -1;
759 }
760
761 keys_ex = calloc(sel->nkeys, sizeof(*keys_ex));
762 if (!keys_ex) {
763 fprintf(f, "Out of memory\n");
764 return -1;
765 }
766
767 err = pedit_keys_ex_getattr(tb[TCA_PEDIT_KEYS_EX], keys_ex,
768 sel->nkeys);
769 if (err) {
770 fprintf(f, "Netlink error\n");
771
772 free(keys_ex);
773 return -1;
774 }
775 }
776
777 fprintf(f, " pedit ");
778 print_action_control(f, "action ", sel->action, " ");
779 fprintf(f,"keys %d\n ", sel->nkeys);
780 fprintf(f, "\t index %u ref %d bind %d", sel->index, sel->refcnt,
781 sel->bindcnt);
782
783 if (show_stats) {
784 if (tb[TCA_PEDIT_TM]) {
785 struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]);
786
787 print_tm(f, tm);
788 }
789 }
790 if (sel->nkeys) {
791 int i;
792 struct tc_pedit_key *key = sel->keys;
793 struct m_pedit_key_ex *key_ex = keys_ex;
794
795 for (i = 0; i < sel->nkeys; i++, key++) {
796 enum pedit_header_type htype =
797 TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
798 enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
799
800 if (keys_ex) {
801 htype = key_ex->htype;
802 cmd = key_ex->cmd;
803
804 key_ex++;
805 }
806
807 fprintf(f, "\n\t key #%d", i);
808
809 fprintf(f, " at ");
810
811 print_pedit_location(f, htype, key->off);
812
813 fprintf(f, ": %s %08x mask %08x",
814 cmd ? "add" : "val",
815 (unsigned int)ntohl(key->val),
816 (unsigned int)ntohl(key->mask));
817 }
818 } else {
819 fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,
820 sel->nkeys);
821 }
822
823 fprintf(f, "\n ");
824
825 free(keys_ex);
826 return 0;
827 }
828
829 int pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats)
830 {
831 return 0;
832 }
833
834 struct action_util pedit_action_util = {
835 .id = "pedit",
836 .parse_aopt = parse_pedit,
837 .print_aopt = print_pedit,
838 };