1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
32static struct m_pedit_util *pedit_list;
33static int pedit_debug;
34
35static void explain(void)
36{
37 fprintf(stderr,
38 "Usage: ... pedit munge [ex] <MUNGE> [CONTROL]\n"
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
54static void usage(void)
55{
56 explain();
57 exit(-1);
58}
59
60static 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
78static 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
106reg:
107 p->next = pedit_list;
108 pedit_list = p;
109 return p;
110
111noexist:
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
121static 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
158static 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
172static 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
202static int pack_key8(__u32 retain, struct m_pedit_sel *sel,
203 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
228static 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
260static 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
286static 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
332int 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;
426done:
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
436static 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
476done:
477
478 NEXT_ARG();
479
480
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
512static 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
546bad_val:
547 return -1;
548
549done:
550
551 *argc_p = argc;
552 *argv_p = argv;
553 return res;
554}
555
556static 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
583static 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
615static int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p,
616 int tca_id, 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
708static const char * const 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
717static int print_pedit_location(FILE *f,
718 enum pedit_header_type htype, __u32 off)
719{
720 char *buf = NULL;
721 int rc;
722
723 if (htype != TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
724 if (htype < ARRAY_SIZE(pedit_htype_str))
725 rc = asprintf(&buf, "%s", pedit_htype_str[htype]);
726 else
727 rc = asprintf(&buf, "unknown(%d)", htype);
728 if (rc < 0)
729 return rc;
730 print_string(PRINT_ANY, "htype", "%s", buf);
731 print_int(PRINT_ANY, "offset", "%+d", off);
732 } else {
733 print_string(PRINT_JSON, "htype", NULL, "network");
734 print_int(PRINT_ANY, "offset", "%d", off);
735 }
736
737 free(buf);
738 return 0;
739}
740
741static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
742{
743 struct tc_pedit_sel *sel;
744 struct rtattr *tb[TCA_PEDIT_MAX + 1];
745 struct m_pedit_key_ex *keys_ex = NULL;
746 int err;
747
748 if (arg == NULL)
749 return -1;
750
751 parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
752
753 if (!tb[TCA_PEDIT_PARMS] && !tb[TCA_PEDIT_PARMS_EX]) {
754 fprintf(stderr, "Missing pedit parameters\n");
755 return -1;
756 }
757
758 if (tb[TCA_PEDIT_PARMS]) {
759 sel = RTA_DATA(tb[TCA_PEDIT_PARMS]);
760 } else {
761 int err;
762
763 sel = RTA_DATA(tb[TCA_PEDIT_PARMS_EX]);
764
765 if (!tb[TCA_PEDIT_KEYS_EX]) {
766 fprintf(f, "Netlink error\n");
767 return -1;
768 }
769
770 keys_ex = calloc(sel->nkeys, sizeof(*keys_ex));
771 if (!keys_ex) {
772 fprintf(f, "Out of memory\n");
773 return -1;
774 }
775
776 err = pedit_keys_ex_getattr(tb[TCA_PEDIT_KEYS_EX], keys_ex,
777 sel->nkeys);
778 if (err) {
779 fprintf(f, "Netlink error\n");
780
781 free(keys_ex);
782 return -1;
783 }
784 }
785
786 print_string(PRINT_ANY, "kind", " %s ", "pedit");
787 print_action_control(f, "action ", sel->action, " ");
788 print_uint(PRINT_ANY, "nkeys", "keys %d\n", sel->nkeys);
789 print_uint(PRINT_ANY, "index", " \t index %u", sel->index);
790 print_int(PRINT_ANY, "ref", " ref %d", sel->refcnt);
791 print_int(PRINT_ANY, "bind", " bind %d", sel->bindcnt);
792
793 if (show_stats) {
794 if (tb[TCA_PEDIT_TM]) {
795 struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]);
796
797 print_tm(f, tm);
798 }
799 }
800 open_json_array(PRINT_JSON, "keys");
801 if (sel->nkeys) {
802 int i;
803 struct tc_pedit_key *key = sel->keys;
804 struct m_pedit_key_ex *key_ex = keys_ex;
805
806 for (i = 0; i < sel->nkeys; i++, key++) {
807 enum pedit_header_type htype =
808 TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
809 enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
810
811 if (keys_ex) {
812 htype = key_ex->htype;
813 cmd = key_ex->cmd;
814
815 key_ex++;
816 }
817
818 open_json_object(NULL);
819 print_uint(PRINT_FP, NULL, "\n\t key #%d at ", i);
820
821 err = print_pedit_location(f, htype, key->off);
822 if (err) {
823 free(keys_ex);
824 return err;
825 }
826
827
828
829
830 print_string(PRINT_FP, NULL, ": %s",
831 cmd ? "add" : "val");
832 print_string(PRINT_JSON, "cmd", NULL,
833 cmd ? "add" : "set");
834 print_hex(PRINT_ANY, "val", " %08x",
835 (unsigned int)ntohl(key->val));
836 print_hex(PRINT_ANY, "mask", " mask %08x",
837 (unsigned int)ntohl(key->mask));
838 close_json_object();
839 }
840 } else {
841 fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,
842 sel->nkeys);
843 }
844 close_json_array(PRINT_JSON, " ");
845
846 print_nl();
847
848 free(keys_ex);
849 return 0;
850}
851
852struct action_util pedit_action_util = {
853 .id = "pedit",
854 .parse_aopt = parse_pedit,
855 .print_aopt = print_pedit,
856};
857