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