]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/m_pedit.c
tc/pedit: Introduce 'add' operation
[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 <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
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 "\tNOTE: if 'ex' is set, extended functionality will be supported (kernel >= 4.11)\n"
49 "For Example usage look at the examples directory\n");
50
51 }
52
53 static void usage(void)
54 {
55 explain();
56 exit(-1);
57 }
58
59 static int pedit_parse_nopopt(int *argc_p, char ***argv_p,
60 struct m_pedit_sel *sel,
61 struct m_pedit_key *tkey)
62 {
63 int argc = *argc_p;
64 char **argv = *argv_p;
65
66 if (argc) {
67 fprintf(stderr,
68 "Unknown action hence option \"%s\" is unparsable\n",
69 *argv);
70 return -1;
71 }
72
73 return 0;
74
75 }
76
77 static struct m_pedit_util *get_pedit_kind(const char *str)
78 {
79 static void *pBODY;
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 = calloc(1, sizeof(*p));
112 if (p) {
113 strncpy(p->id, str, sizeof(p->id) - 1);
114 p->parse_peopt = pedit_parse_nopopt;
115 goto reg;
116 }
117 return p;
118 }
119
120 int pack_key(struct m_pedit_sel *_sel, struct m_pedit_key *tkey)
121 {
122 struct tc_pedit_sel *sel = &_sel->sel;
123 struct m_pedit_key_ex *keys_ex = _sel->keys_ex;
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
141 if (_sel->extended) {
142 keys_ex[hwm].htype = tkey->htype;
143 keys_ex[hwm].cmd = tkey->cmd;
144 } else {
145 if (tkey->htype != TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK ||
146 tkey->cmd != TCA_PEDIT_KEY_EX_CMD_SET) {
147 fprintf(stderr, "Munge parameters not supported. Use 'munge ex'.\n");
148 return -1;
149 }
150 }
151
152 sel->nkeys++;
153 return 0;
154 }
155
156 int pack_key32(__u32 retain, struct m_pedit_sel *sel,
157 struct m_pedit_key *tkey)
158 {
159 if (tkey->off > (tkey->off & ~3)) {
160 fprintf(stderr,
161 "pack_key32: 32 bit offsets must begin in 32bit boundaries\n");
162 return -1;
163 }
164
165 tkey->val = htonl(tkey->val & retain);
166 tkey->mask = htonl(tkey->mask | ~retain);
167 return pack_key(sel, tkey);
168 }
169
170 int pack_key16(__u32 retain, struct m_pedit_sel *sel,
171 struct m_pedit_key *tkey)
172 {
173 int ind, stride;
174 __u32 m[4] = { 0x0000FFFF, 0xFF0000FF, 0xFFFF0000 };
175
176 if (tkey->val > 0xFFFF || tkey->mask > 0xFFFF) {
177 fprintf(stderr, "pack_key16 bad value\n");
178 return -1;
179 }
180
181 ind = tkey->off & 3;
182
183 if (ind == 3) {
184 fprintf(stderr, "pack_key16 bad index value %d\n", ind);
185 return -1;
186 }
187
188 stride = 8 * (2 - ind);
189 tkey->val = htonl((tkey->val & retain) << stride);
190 tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]);
191
192 tkey->off &= ~3;
193
194 if (pedit_debug)
195 printf("pack_key16: Final val %08x mask %08x\n",
196 tkey->val, tkey->mask);
197 return pack_key(sel, tkey);
198
199 }
200
201 int pack_key8(__u32 retain, struct m_pedit_sel *sel, struct m_pedit_key *tkey)
202 {
203 int ind, stride;
204 __u32 m[4] = { 0x00FFFFFF, 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00 };
205
206 if (tkey->val > 0xFF || tkey->mask > 0xFF) {
207 fprintf(stderr, "pack_key8 bad value (val %x mask %x\n",
208 tkey->val, tkey->mask);
209 return -1;
210 }
211
212 ind = tkey->off & 3;
213
214 stride = 8 * (3 - ind);
215 tkey->val = htonl((tkey->val & retain) << stride);
216 tkey->mask = htonl(((tkey->mask | ~retain) << stride) | m[ind]);
217
218 tkey->off &= ~3;
219
220 if (pedit_debug)
221 printf("pack_key8: Final word off %d val %08x mask %08x\n",
222 tkey->off, tkey->val, tkey->mask);
223 return pack_key(sel, tkey);
224 }
225
226 int parse_val(int *argc_p, char ***argv_p, __u32 *val, int type)
227 {
228 int argc = *argc_p;
229 char **argv = *argv_p;
230
231 if (argc <= 0)
232 return -1;
233
234 if (type == TINT)
235 return get_integer((int *)val, *argv, 0);
236
237 if (type == TU32)
238 return get_u32(val, *argv, 0);
239
240 if (type == TIPV4) {
241 inet_prefix addr;
242
243 if (get_prefix_1(&addr, *argv, AF_INET))
244 return -1;
245
246 *val = addr.data[0];
247 return 0;
248 }
249
250 if (type == TIPV6)
251 return -1; /* not implemented yet */
252
253 return -1;
254 }
255
256 int parse_cmd(int *argc_p, char ***argv_p, __u32 len, int type, __u32 retain,
257 struct m_pedit_sel *sel, struct m_pedit_key *tkey)
258 {
259 __u32 mask = 0, val = 0;
260 __u32 o = 0xFF;
261 int res = -1;
262 int argc = *argc_p;
263 char **argv = *argv_p;
264
265 if (argc <= 0)
266 return -1;
267
268 if (pedit_debug)
269 printf("parse_cmd argc %d %s offset %d length %d\n",
270 argc, *argv, tkey->off, len);
271
272 if (len == 2)
273 o = 0xFFFF;
274 if (len == 4)
275 o = 0xFFFFFFFF;
276
277 if (matches(*argv, "invert") == 0) {
278 val = mask = o;
279 } else if (matches(*argv, "set") == 0 ||
280 matches(*argv, "add") == 0) {
281 if (matches(*argv, "add") == 0)
282 tkey->cmd = TCA_PEDIT_KEY_EX_CMD_ADD;
283
284 if (!sel->extended && tkey->cmd) {
285 fprintf(stderr, "Non extended mode. only 'set' command is supported\n");
286 return -1;
287 }
288
289 NEXT_ARG();
290 if (parse_val(&argc, &argv, &val, type))
291 return -1;
292 } else if (matches(*argv, "preserve") == 0) {
293 retain = 0;
294 } else {
295 if (matches(*argv, "clear") != 0)
296 return -1;
297 }
298
299 argc--;
300 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--;
307 argv++;
308 }
309
310 tkey->val = val;
311 tkey->mask = mask;
312
313 if (type == TIPV4)
314 tkey->val = ntohl(tkey->val);
315
316 if (len == 1) {
317 res = pack_key8(retain, sel, tkey);
318 goto done;
319 }
320 if (len == 2) {
321 res = pack_key16(retain, sel, tkey);
322 goto done;
323 }
324 if (len == 4) {
325 res = pack_key32(retain, sel, tkey);
326 goto done;
327 }
328
329 return -1;
330 done:
331 if (pedit_debug)
332 printf("parse_cmd done argc %d %s offset %d length %d\n",
333 argc, *argv, tkey->off, len);
334 *argc_p = argc;
335 *argv_p = argv;
336 return res;
337
338 }
339
340 int parse_offset(int *argc_p, char ***argv_p, struct m_pedit_sel *sel,
341 struct m_pedit_key *tkey)
342 {
343 int off;
344 __u32 len, retain;
345 int argc = *argc_p;
346 char **argv = *argv_p;
347 int res = -1;
348
349 if (argc <= 0)
350 return -1;
351
352 if (get_integer(&off, *argv, 0))
353 return -1;
354 tkey->off = off;
355
356 argc--;
357 argv++;
358
359 if (argc <= 0)
360 return -1;
361
362 if (matches(*argv, "u32") == 0) {
363 len = 4;
364 retain = 0xFFFFFFFF;
365 goto done;
366 }
367 if (matches(*argv, "u16") == 0) {
368 len = 2;
369 retain = 0xffff;
370 goto done;
371 }
372 if (matches(*argv, "u8") == 0) {
373 len = 1;
374 retain = 0xff;
375 goto done;
376 }
377
378 return -1;
379
380 done:
381
382 NEXT_ARG();
383
384 /* [at <someval> offmask <maskval> shift <shiftval>] */
385 if (matches(*argv, "at") == 0) {
386
387 __u32 atv = 0, offmask = 0x0, shift = 0;
388
389 NEXT_ARG();
390 if (get_u32(&atv, *argv, 0))
391 return -1;
392 tkey->at = atv;
393
394 NEXT_ARG();
395
396 if (get_u32(&offmask, *argv, 16))
397 return -1;
398 tkey->offmask = offmask;
399
400 NEXT_ARG();
401
402 if (get_u32(&shift, *argv, 0))
403 return -1;
404 tkey->shift = shift;
405
406 NEXT_ARG();
407 }
408
409 res = parse_cmd(&argc, &argv, len, TU32, retain, sel, tkey);
410
411 *argc_p = argc;
412 *argv_p = argv;
413 return res;
414 }
415
416 static int parse_munge(int *argc_p, char ***argv_p, struct m_pedit_sel *sel)
417 {
418 struct m_pedit_key tkey = {};
419 int argc = *argc_p;
420 char **argv = *argv_p;
421 int res = -1;
422
423 if (argc <= 0)
424 return -1;
425
426 if (matches(*argv, "offset") == 0) {
427 NEXT_ARG();
428 res = parse_offset(&argc, &argv, sel, &tkey);
429 goto done;
430 } else {
431 char k[16];
432 struct m_pedit_util *p = NULL;
433
434 strncpy(k, *argv, sizeof(k) - 1);
435
436 if (argc > 0) {
437 p = get_pedit_kind(k);
438 if (p == NULL)
439 goto bad_val;
440 NEXT_ARG();
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 static int pedit_keys_ex_getattr(struct rtattr *attr,
461 struct m_pedit_key_ex *keys_ex, int n)
462 {
463 struct rtattr *i;
464 int rem = RTA_PAYLOAD(attr);
465 struct rtattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
466 struct m_pedit_key_ex *k = keys_ex;
467
468 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
469 if (!n)
470 return -1;
471
472 if (i->rta_type != TCA_PEDIT_KEY_EX)
473 return -1;
474
475 parse_rtattr_nested(tb, TCA_PEDIT_KEY_EX_MAX, i);
476
477 k->htype = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
478 k->cmd = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
479
480 k++;
481 n--;
482 }
483
484 return !!n;
485 }
486
487 static int pedit_keys_ex_addattr(struct m_pedit_sel *sel, struct nlmsghdr *n)
488 {
489 struct m_pedit_key_ex *k = sel->keys_ex;
490 struct rtattr *keys_start;
491 int i;
492
493 if (!sel->extended)
494 return 0;
495
496 keys_start = addattr_nest(n, MAX_MSG, TCA_PEDIT_KEYS_EX | NLA_F_NESTED);
497
498 for (i = 0; i < sel->sel.nkeys; i++) {
499 struct rtattr *key_start;
500
501 key_start = addattr_nest(n, MAX_MSG,
502 TCA_PEDIT_KEY_EX | NLA_F_NESTED);
503
504 if (addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_HTYPE, k->htype) ||
505 addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_CMD, k->cmd)) {
506 return -1;
507 }
508
509 addattr_nest_end(n, key_start);
510
511 k++;
512 }
513
514 addattr_nest_end(n, keys_start);
515
516 return 0;
517 }
518
519 int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
520 struct nlmsghdr *n)
521 {
522 struct m_pedit_sel sel = {};
523
524 int argc = *argc_p;
525 char **argv = *argv_p;
526 int ok = 0, iok = 0;
527 struct rtattr *tail;
528
529 while (argc > 0) {
530 if (pedit_debug > 1)
531 fprintf(stderr, "while pedit (%d:%s)\n", argc, *argv);
532 if (matches(*argv, "pedit") == 0) {
533 NEXT_ARG();
534 ok++;
535
536 if (matches(*argv, "ex") == 0) {
537 if (ok > 1) {
538 fprintf(stderr, "'ex' must be before first 'munge'\n");
539 explain();
540 return -1;
541 }
542 sel.extended = true;
543 NEXT_ARG();
544 }
545
546 continue;
547 } else if (matches(*argv, "help") == 0) {
548 usage();
549 } else if (matches(*argv, "munge") == 0) {
550 if (!ok) {
551 fprintf(stderr, "Bad pedit construct (%s)\n",
552 *argv);
553 explain();
554 return -1;
555 }
556 NEXT_ARG();
557
558 if (parse_munge(&argc, &argv, &sel)) {
559 fprintf(stderr, "Bad pedit construct (%s)\n",
560 *argv);
561 explain();
562 return -1;
563 }
564 ok++;
565 } else {
566 break;
567 }
568
569 }
570
571 if (!ok) {
572 explain();
573 return -1;
574 }
575
576 if (argc && !action_a2n(*argv, &sel.sel.action, false))
577 NEXT_ARG();
578
579 if (argc) {
580 if (matches(*argv, "index") == 0) {
581 NEXT_ARG();
582 if (get_u32(&sel.sel.index, *argv, 10)) {
583 fprintf(stderr, "Pedit: Illegal \"index\"\n");
584 return -1;
585 }
586 argc--;
587 argv++;
588 iok++;
589 }
590 }
591
592 tail = NLMSG_TAIL(n);
593 addattr_l(n, MAX_MSG, tca_id, NULL, 0);
594 if (!sel.extended) {
595 addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS, &sel,
596 sizeof(sel.sel) +
597 sel.sel.nkeys * sizeof(struct tc_pedit_key));
598 } else {
599 addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS_EX, &sel,
600 sizeof(sel.sel) +
601 sel.sel.nkeys * sizeof(struct tc_pedit_key));
602
603 pedit_keys_ex_addattr(&sel, n);
604 }
605
606 tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
607
608 *argc_p = argc;
609 *argv_p = argv;
610 return 0;
611 }
612
613 const char *pedit_htype_str[] = {
614 [TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK] = "",
615 [TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = "eth",
616 [TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = "ipv4",
617 [TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = "ipv6",
618 [TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = "tcp",
619 [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = "udp",
620 };
621
622 static void print_pedit_location(FILE *f,
623 enum pedit_header_type htype, __u32 off)
624 {
625 if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
626 fprintf(f, "%d", (unsigned int)off);
627 return;
628 }
629
630 if (htype < ARRAY_SIZE(pedit_htype_str))
631 fprintf(f, "%s", pedit_htype_str[htype]);
632 else
633 fprintf(f, "unknown(%d)", htype);
634
635 fprintf(f, "%c%d", (int)off >= 0 ? '+' : '-', abs((int)off));
636 }
637
638 int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
639 {
640 struct tc_pedit_sel *sel;
641 struct rtattr *tb[TCA_PEDIT_MAX + 1];
642 struct m_pedit_key_ex *keys_ex = NULL;
643
644 if (arg == NULL)
645 return -1;
646
647 parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
648
649 if (!tb[TCA_PEDIT_PARMS] && !tb[TCA_PEDIT_PARMS_EX]) {
650 fprintf(f, "[NULL pedit parameters]");
651 return -1;
652 }
653
654 if (tb[TCA_PEDIT_PARMS]) {
655 sel = RTA_DATA(tb[TCA_PEDIT_PARMS]);
656 } else {
657 int err;
658
659 sel = RTA_DATA(tb[TCA_PEDIT_PARMS_EX]);
660
661 if (!tb[TCA_PEDIT_KEYS_EX]) {
662 fprintf(f, "Netlink error\n");
663 return -1;
664 }
665
666 keys_ex = calloc(sel->nkeys, sizeof(*keys_ex));
667 if (!keys_ex) {
668 fprintf(f, "Out of memory\n");
669 return -1;
670 }
671
672 err = pedit_keys_ex_getattr(tb[TCA_PEDIT_KEYS_EX], keys_ex,
673 sel->nkeys);
674 if (err) {
675 fprintf(f, "Netlink error\n");
676
677 free(keys_ex);
678 return -1;
679 }
680 }
681
682 fprintf(f, " pedit action %s keys %d\n ",
683 action_n2a(sel->action), sel->nkeys);
684 fprintf(f, "\t index %u ref %d bind %d", sel->index, sel->refcnt,
685 sel->bindcnt);
686
687 if (show_stats) {
688 if (tb[TCA_PEDIT_TM]) {
689 struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]);
690
691 print_tm(f, tm);
692 }
693 }
694 if (sel->nkeys) {
695 int i;
696 struct tc_pedit_key *key = sel->keys;
697 struct m_pedit_key_ex *key_ex = keys_ex;
698
699 for (i = 0; i < sel->nkeys; i++, key++) {
700 enum pedit_header_type htype =
701 TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
702 enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
703
704 if (keys_ex) {
705 htype = key_ex->htype;
706 cmd = key_ex->cmd;
707
708 key_ex++;
709 }
710
711 fprintf(f, "\n\t key #%d", i);
712
713 fprintf(f, " at ");
714
715 print_pedit_location(f, htype, key->off);
716
717 fprintf(f, ": %s %08x mask %08x",
718 cmd ? "add" : "val",
719 (unsigned int)ntohl(key->val),
720 (unsigned int)ntohl(key->mask));
721 }
722 } else {
723 fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,
724 sel->nkeys);
725 }
726
727 fprintf(f, "\n ");
728
729 free(keys_ex);
730 return 0;
731 }
732
733 int pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats)
734 {
735 return 0;
736 }
737
738 struct action_util pedit_action_util = {
739 .id = "pedit",
740 .parse_aopt = parse_pedit,
741 .print_aopt = print_pedit,
742 };