]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/m_pedit.c
tc/pedit: Support fields bigger than 32 bits
[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[4] = { 0 };
260 __u32 val[4] = { 0 };
261 __u32 *m = &mask[0];
262 __u32 *v = &val[0];
263 __u32 o = 0xFF;
264 int res = -1;
265 int argc = *argc_p;
266 char **argv = *argv_p;
267
268 if (argc <= 0)
269 return -1;
270
271 if (pedit_debug)
272 printf("parse_cmd argc %d %s offset %d length %d\n",
273 argc, *argv, tkey->off, len);
274
275 if (len == 2)
276 o = 0xFFFF;
277 if (len == 4)
278 o = 0xFFFFFFFF;
279
280 if (matches(*argv, "invert") == 0) {
281 *v = *m = o;
282 } else if (matches(*argv, "set") == 0 ||
283 matches(*argv, "add") == 0) {
284 if (matches(*argv, "add") == 0)
285 tkey->cmd = TCA_PEDIT_KEY_EX_CMD_ADD;
286
287 if (!sel->extended && tkey->cmd) {
288 fprintf(stderr, "Non extended mode. only 'set' command is supported\n");
289 return -1;
290 }
291
292 NEXT_ARG();
293 if (parse_val(&argc, &argv, val, type))
294 return -1;
295 } else if (matches(*argv, "preserve") == 0) {
296 retain = 0;
297 } else {
298 if (matches(*argv, "clear") != 0)
299 return -1;
300 }
301
302 argc--;
303 argv++;
304
305 if (argc && matches(*argv, "retain") == 0) {
306 NEXT_ARG();
307 if (parse_val(&argc, &argv, &retain, TU32))
308 return -1;
309 argc--;
310 argv++;
311 }
312
313 tkey->val = *v;
314 tkey->mask = *m;
315
316 if (type == TIPV4)
317 tkey->val = ntohl(tkey->val);
318
319 if (len == 1) {
320 res = pack_key8(retain, sel, tkey);
321 goto done;
322 }
323 if (len == 2) {
324 res = pack_key16(retain, sel, tkey);
325 goto done;
326 }
327 if (len == 4) {
328 res = pack_key32(retain, sel, tkey);
329 goto done;
330 }
331
332 return -1;
333 done:
334 if (pedit_debug)
335 printf("parse_cmd done argc %d %s offset %d length %d\n",
336 argc, *argv, tkey->off, len);
337 *argc_p = argc;
338 *argv_p = argv;
339 return res;
340
341 }
342
343 int parse_offset(int *argc_p, char ***argv_p, struct m_pedit_sel *sel,
344 struct m_pedit_key *tkey)
345 {
346 int off;
347 __u32 len, retain;
348 int argc = *argc_p;
349 char **argv = *argv_p;
350 int res = -1;
351
352 if (argc <= 0)
353 return -1;
354
355 if (get_integer(&off, *argv, 0))
356 return -1;
357 tkey->off = off;
358
359 argc--;
360 argv++;
361
362 if (argc <= 0)
363 return -1;
364
365 if (matches(*argv, "u32") == 0) {
366 len = 4;
367 retain = 0xFFFFFFFF;
368 goto done;
369 }
370 if (matches(*argv, "u16") == 0) {
371 len = 2;
372 retain = 0xffff;
373 goto done;
374 }
375 if (matches(*argv, "u8") == 0) {
376 len = 1;
377 retain = 0xff;
378 goto done;
379 }
380
381 return -1;
382
383 done:
384
385 NEXT_ARG();
386
387 /* [at <someval> offmask <maskval> shift <shiftval>] */
388 if (matches(*argv, "at") == 0) {
389
390 __u32 atv = 0, offmask = 0x0, shift = 0;
391
392 NEXT_ARG();
393 if (get_u32(&atv, *argv, 0))
394 return -1;
395 tkey->at = atv;
396
397 NEXT_ARG();
398
399 if (get_u32(&offmask, *argv, 16))
400 return -1;
401 tkey->offmask = offmask;
402
403 NEXT_ARG();
404
405 if (get_u32(&shift, *argv, 0))
406 return -1;
407 tkey->shift = shift;
408
409 NEXT_ARG();
410 }
411
412 res = parse_cmd(&argc, &argv, len, TU32, retain, sel, tkey);
413
414 *argc_p = argc;
415 *argv_p = argv;
416 return res;
417 }
418
419 static int parse_munge(int *argc_p, char ***argv_p, struct m_pedit_sel *sel)
420 {
421 struct m_pedit_key tkey = {};
422 int argc = *argc_p;
423 char **argv = *argv_p;
424 int res = -1;
425
426 if (argc <= 0)
427 return -1;
428
429 if (matches(*argv, "offset") == 0) {
430 NEXT_ARG();
431 res = parse_offset(&argc, &argv, sel, &tkey);
432 goto done;
433 } else {
434 char k[16];
435 struct m_pedit_util *p = NULL;
436
437 strncpy(k, *argv, sizeof(k) - 1);
438
439 if (argc > 0) {
440 p = get_pedit_kind(k);
441 if (p == NULL)
442 goto bad_val;
443 NEXT_ARG();
444 res = p->parse_peopt(&argc, &argv, sel, &tkey);
445 if (res < 0) {
446 fprintf(stderr, "bad pedit parsing\n");
447 goto bad_val;
448 }
449 goto done;
450 }
451 }
452
453 bad_val:
454 return -1;
455
456 done:
457
458 *argc_p = argc;
459 *argv_p = argv;
460 return res;
461 }
462
463 static int pedit_keys_ex_getattr(struct rtattr *attr,
464 struct m_pedit_key_ex *keys_ex, int n)
465 {
466 struct rtattr *i;
467 int rem = RTA_PAYLOAD(attr);
468 struct rtattr *tb[TCA_PEDIT_KEY_EX_MAX + 1];
469 struct m_pedit_key_ex *k = keys_ex;
470
471 for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
472 if (!n)
473 return -1;
474
475 if (i->rta_type != TCA_PEDIT_KEY_EX)
476 return -1;
477
478 parse_rtattr_nested(tb, TCA_PEDIT_KEY_EX_MAX, i);
479
480 k->htype = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_HTYPE]);
481 k->cmd = rta_getattr_u16(tb[TCA_PEDIT_KEY_EX_CMD]);
482
483 k++;
484 n--;
485 }
486
487 return !!n;
488 }
489
490 static int pedit_keys_ex_addattr(struct m_pedit_sel *sel, struct nlmsghdr *n)
491 {
492 struct m_pedit_key_ex *k = sel->keys_ex;
493 struct rtattr *keys_start;
494 int i;
495
496 if (!sel->extended)
497 return 0;
498
499 keys_start = addattr_nest(n, MAX_MSG, TCA_PEDIT_KEYS_EX | NLA_F_NESTED);
500
501 for (i = 0; i < sel->sel.nkeys; i++) {
502 struct rtattr *key_start;
503
504 key_start = addattr_nest(n, MAX_MSG,
505 TCA_PEDIT_KEY_EX | NLA_F_NESTED);
506
507 if (addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_HTYPE, k->htype) ||
508 addattr16(n, MAX_MSG, TCA_PEDIT_KEY_EX_CMD, k->cmd)) {
509 return -1;
510 }
511
512 addattr_nest_end(n, key_start);
513
514 k++;
515 }
516
517 addattr_nest_end(n, keys_start);
518
519 return 0;
520 }
521
522 int parse_pedit(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
523 struct nlmsghdr *n)
524 {
525 struct m_pedit_sel sel = {};
526
527 int argc = *argc_p;
528 char **argv = *argv_p;
529 int ok = 0, iok = 0;
530 struct rtattr *tail;
531
532 while (argc > 0) {
533 if (pedit_debug > 1)
534 fprintf(stderr, "while pedit (%d:%s)\n", argc, *argv);
535 if (matches(*argv, "pedit") == 0) {
536 NEXT_ARG();
537 ok++;
538
539 if (matches(*argv, "ex") == 0) {
540 if (ok > 1) {
541 fprintf(stderr, "'ex' must be before first 'munge'\n");
542 explain();
543 return -1;
544 }
545 sel.extended = true;
546 NEXT_ARG();
547 }
548
549 continue;
550 } else if (matches(*argv, "help") == 0) {
551 usage();
552 } else if (matches(*argv, "munge") == 0) {
553 if (!ok) {
554 fprintf(stderr, "Bad pedit construct (%s)\n",
555 *argv);
556 explain();
557 return -1;
558 }
559 NEXT_ARG();
560
561 if (parse_munge(&argc, &argv, &sel)) {
562 fprintf(stderr, "Bad pedit construct (%s)\n",
563 *argv);
564 explain();
565 return -1;
566 }
567 ok++;
568 } else {
569 break;
570 }
571
572 }
573
574 if (!ok) {
575 explain();
576 return -1;
577 }
578
579 if (argc && !action_a2n(*argv, &sel.sel.action, false))
580 NEXT_ARG();
581
582 if (argc) {
583 if (matches(*argv, "index") == 0) {
584 NEXT_ARG();
585 if (get_u32(&sel.sel.index, *argv, 10)) {
586 fprintf(stderr, "Pedit: Illegal \"index\"\n");
587 return -1;
588 }
589 argc--;
590 argv++;
591 iok++;
592 }
593 }
594
595 tail = NLMSG_TAIL(n);
596 addattr_l(n, MAX_MSG, tca_id, NULL, 0);
597 if (!sel.extended) {
598 addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS, &sel,
599 sizeof(sel.sel) +
600 sel.sel.nkeys * sizeof(struct tc_pedit_key));
601 } else {
602 addattr_l(n, MAX_MSG, TCA_PEDIT_PARMS_EX, &sel,
603 sizeof(sel.sel) +
604 sel.sel.nkeys * sizeof(struct tc_pedit_key));
605
606 pedit_keys_ex_addattr(&sel, n);
607 }
608
609 tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
610
611 *argc_p = argc;
612 *argv_p = argv;
613 return 0;
614 }
615
616 const char *pedit_htype_str[] = {
617 [TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK] = "",
618 [TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = "eth",
619 [TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = "ipv4",
620 [TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = "ipv6",
621 [TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = "tcp",
622 [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = "udp",
623 };
624
625 static void print_pedit_location(FILE *f,
626 enum pedit_header_type htype, __u32 off)
627 {
628 if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
629 fprintf(f, "%d", (unsigned int)off);
630 return;
631 }
632
633 if (htype < ARRAY_SIZE(pedit_htype_str))
634 fprintf(f, "%s", pedit_htype_str[htype]);
635 else
636 fprintf(f, "unknown(%d)", htype);
637
638 fprintf(f, "%c%d", (int)off >= 0 ? '+' : '-', abs((int)off));
639 }
640
641 int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
642 {
643 struct tc_pedit_sel *sel;
644 struct rtattr *tb[TCA_PEDIT_MAX + 1];
645 struct m_pedit_key_ex *keys_ex = NULL;
646
647 if (arg == NULL)
648 return -1;
649
650 parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
651
652 if (!tb[TCA_PEDIT_PARMS] && !tb[TCA_PEDIT_PARMS_EX]) {
653 fprintf(f, "[NULL pedit parameters]");
654 return -1;
655 }
656
657 if (tb[TCA_PEDIT_PARMS]) {
658 sel = RTA_DATA(tb[TCA_PEDIT_PARMS]);
659 } else {
660 int err;
661
662 sel = RTA_DATA(tb[TCA_PEDIT_PARMS_EX]);
663
664 if (!tb[TCA_PEDIT_KEYS_EX]) {
665 fprintf(f, "Netlink error\n");
666 return -1;
667 }
668
669 keys_ex = calloc(sel->nkeys, sizeof(*keys_ex));
670 if (!keys_ex) {
671 fprintf(f, "Out of memory\n");
672 return -1;
673 }
674
675 err = pedit_keys_ex_getattr(tb[TCA_PEDIT_KEYS_EX], keys_ex,
676 sel->nkeys);
677 if (err) {
678 fprintf(f, "Netlink error\n");
679
680 free(keys_ex);
681 return -1;
682 }
683 }
684
685 fprintf(f, " pedit action %s keys %d\n ",
686 action_n2a(sel->action), sel->nkeys);
687 fprintf(f, "\t index %u ref %d bind %d", sel->index, sel->refcnt,
688 sel->bindcnt);
689
690 if (show_stats) {
691 if (tb[TCA_PEDIT_TM]) {
692 struct tcf_t *tm = RTA_DATA(tb[TCA_PEDIT_TM]);
693
694 print_tm(f, tm);
695 }
696 }
697 if (sel->nkeys) {
698 int i;
699 struct tc_pedit_key *key = sel->keys;
700 struct m_pedit_key_ex *key_ex = keys_ex;
701
702 for (i = 0; i < sel->nkeys; i++, key++) {
703 enum pedit_header_type htype =
704 TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK;
705 enum pedit_cmd cmd = TCA_PEDIT_KEY_EX_CMD_SET;
706
707 if (keys_ex) {
708 htype = key_ex->htype;
709 cmd = key_ex->cmd;
710
711 key_ex++;
712 }
713
714 fprintf(f, "\n\t key #%d", i);
715
716 fprintf(f, " at ");
717
718 print_pedit_location(f, htype, key->off);
719
720 fprintf(f, ": %s %08x mask %08x",
721 cmd ? "add" : "val",
722 (unsigned int)ntohl(key->val),
723 (unsigned int)ntohl(key->mask));
724 }
725 } else {
726 fprintf(f, "\npedit %x keys %d is not LEGIT", sel->index,
727 sel->nkeys);
728 }
729
730 fprintf(f, "\n ");
731
732 free(keys_ex);
733 return 0;
734 }
735
736 int pedit_print_xstats(struct action_util *au, FILE *f, struct rtattr *xstats)
737 {
738 return 0;
739 }
740
741 struct action_util pedit_action_util = {
742 .id = "pedit",
743 .parse_aopt = parse_pedit,
744 .print_aopt = print_pedit,
745 };