]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/m_action.c
tc: remove action cookie len from printout
[mirror_iproute2.git] / tc / m_action.c
1 /*
2 * m_action.c Action Management
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 * - parse to be passed a filedescriptor for logging purposes
13 *
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <string.h>
24 #include <dlfcn.h>
25
26 #include "utils.h"
27 #include "tc_common.h"
28 #include "tc_util.h"
29
30 static struct action_util *action_list;
31 #ifdef CONFIG_GACT
32 int gact_ld; /* f*ckin backward compatibility */
33 #endif
34 int tab_flush;
35
36 static void act_usage(void)
37 {
38 /*XXX: In the near future add a action->print_help to improve
39 * usability
40 * This would mean new tc will not be backward compatible
41 * with any action .so from the old days. But if someone really
42 * does that, they would know how to fix this ..
43 *
44 */
45 fprintf(stderr, "usage: tc actions <ACTSPECOP>*\n");
46 fprintf(stderr,
47 "Where: \tACTSPECOP := ACR | GD | FL\n"
48 "\tACR := add | change | replace <ACTSPEC>*\n"
49 "\tGD := get | delete | <ACTISPEC>*\n"
50 "\tFL := ls | list | flush | <ACTNAMESPEC>\n"
51 "\tACTNAMESPEC := action <ACTNAME>\n"
52 "\tACTISPEC := <ACTNAMESPEC> <INDEXSPEC>\n"
53 "\tACTSPEC := action <ACTDETAIL> [INDEXSPEC]\n"
54 "\tINDEXSPEC := index <32 bit indexvalue>\n"
55 "\tACTDETAIL := <ACTNAME> <ACTPARAMS>\n"
56 "\t\tExample ACTNAME is gact, mirred, bpf, etc\n"
57 "\t\tEach action has its own parameters (ACTPARAMS)\n"
58 "\n");
59
60 exit(-1);
61 }
62
63 static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt)
64 {
65 if (opt && RTA_PAYLOAD(opt))
66 fprintf(f, "[Unknown action, optlen=%u] ",
67 (unsigned int) RTA_PAYLOAD(opt));
68 return 0;
69 }
70
71 static int parse_noaopt(struct action_util *au, int *argc_p, char ***argv_p, int code, struct nlmsghdr *n)
72 {
73 int argc = *argc_p;
74 char **argv = *argv_p;
75
76 if (argc) {
77 fprintf(stderr, "Unknown action \"%s\", hence option \"%s\" is unparsable\n", au->id, *argv);
78 } else {
79 fprintf(stderr, "Unknown action \"%s\"\n", au->id);
80 }
81 return -1;
82 }
83
84 static struct action_util *get_action_kind(char *str)
85 {
86 static void *aBODY;
87 void *dlh;
88 char buf[256];
89 struct action_util *a;
90 #ifdef CONFIG_GACT
91 int looked4gact = 0;
92 restart_s:
93 #endif
94 for (a = action_list; a; a = a->next) {
95 if (strcmp(a->id, str) == 0)
96 return a;
97 }
98
99 snprintf(buf, sizeof(buf), "%s/m_%s.so", get_tc_lib(), str);
100 dlh = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
101 if (dlh == NULL) {
102 dlh = aBODY;
103 if (dlh == NULL) {
104 dlh = aBODY = dlopen(NULL, RTLD_LAZY);
105 if (dlh == NULL)
106 goto noexist;
107 }
108 }
109
110 snprintf(buf, sizeof(buf), "%s_action_util", str);
111 a = dlsym(dlh, buf);
112 if (a == NULL)
113 goto noexist;
114
115 reg:
116 a->next = action_list;
117 action_list = a;
118 return a;
119
120 noexist:
121 #ifdef CONFIG_GACT
122 if (!looked4gact) {
123 looked4gact = 1;
124 strcpy(str, "gact");
125 goto restart_s;
126 }
127 #endif
128 a = calloc(1, sizeof(*a));
129 if (a) {
130 strncpy(a->id, "noact", 15);
131 a->parse_aopt = parse_noaopt;
132 a->print_aopt = print_noaopt;
133 goto reg;
134 }
135 return a;
136 }
137
138 static int
139 new_cmd(char **argv)
140 {
141 if ((matches(*argv, "change") == 0) ||
142 (matches(*argv, "replace") == 0) ||
143 (matches(*argv, "delete") == 0) ||
144 (matches(*argv, "get") == 0) ||
145 (matches(*argv, "add") == 0))
146 return 1;
147
148 return 0;
149
150 }
151
152 int parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
153 {
154 int argc = *argc_p;
155 char **argv = *argv_p;
156 struct rtattr *tail, *tail2;
157 char k[FILTER_NAMESZ];
158 int act_ck_len = 0;
159 int ok = 0;
160 int eap = 0; /* expect action parameters */
161
162 int ret = 0;
163 int prio = 0;
164 unsigned char act_ck[TC_COOKIE_MAX_SIZE];
165
166 if (argc <= 0)
167 return -1;
168
169 tail = tail2 = NLMSG_TAIL(n);
170
171 addattr_l(n, MAX_MSG, tca_id, NULL, 0);
172
173 while (argc > 0) {
174
175 memset(k, 0, sizeof(k));
176
177 if (strcmp(*argv, "action") == 0) {
178 argc--;
179 argv++;
180 eap = 1;
181 #ifdef CONFIG_GACT
182 if (!gact_ld) {
183 get_action_kind("gact");
184 }
185 #endif
186 continue;
187 } else if (strcmp(*argv, "flowid") == 0) {
188 break;
189 } else if (strcmp(*argv, "classid") == 0) {
190 break;
191 } else if (strcmp(*argv, "help") == 0) {
192 return -1;
193 } else if (new_cmd(argv)) {
194 goto done0;
195 } else {
196 struct action_util *a = NULL;
197
198 strncpy(k, *argv, sizeof(k) - 1);
199 eap = 0;
200 if (argc > 0) {
201 a = get_action_kind(k);
202 } else {
203 done0:
204 if (ok)
205 break;
206 else
207 goto done;
208 }
209
210 if (a == NULL) {
211 goto bad_val;
212 }
213
214 tail = NLMSG_TAIL(n);
215 addattr_l(n, MAX_MSG, ++prio, NULL, 0);
216 addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
217
218 ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS,
219 n);
220
221 if (ret < 0) {
222 fprintf(stderr, "bad action parsing\n");
223 goto bad_val;
224 }
225
226 if (*argv && strcmp(*argv, "cookie") == 0) {
227 size_t slen;
228
229 NEXT_ARG();
230 slen = strlen(*argv);
231 if (slen > TC_COOKIE_MAX_SIZE * 2) {
232 char cookie_err_m[128];
233
234 snprintf(cookie_err_m, 128,
235 "%zd Max allowed size %d",
236 slen, TC_COOKIE_MAX_SIZE*2);
237 invarg(cookie_err_m, *argv);
238 }
239
240 if (hex2mem(*argv, act_ck, slen / 2) < 0)
241 invarg("cookie must be a hex string\n",
242 *argv);
243
244 act_ck_len = slen / 2;
245 argc--;
246 argv++;
247 }
248
249 if (act_ck_len)
250 addattr_l(n, MAX_MSG, TCA_ACT_COOKIE,
251 &act_ck, act_ck_len);
252
253 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
254 ok++;
255 }
256 }
257
258 if (eap > 0) {
259 fprintf(stderr, "bad action empty %d\n", eap);
260 goto bad_val;
261 }
262
263 tail2->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail2;
264
265 done:
266 *argc_p = argc;
267 *argv_p = argv;
268 return 0;
269 bad_val:
270 /* no need to undo things, returning from here should
271 * cause enough pain */
272 fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv);
273 return -1;
274 }
275
276 static int tc_print_one_action(FILE *f, struct rtattr *arg)
277 {
278
279 struct rtattr *tb[TCA_ACT_MAX + 1];
280 int err = 0;
281 struct action_util *a = NULL;
282
283 if (arg == NULL)
284 return -1;
285
286 parse_rtattr_nested(tb, TCA_ACT_MAX, arg);
287
288 if (tb[TCA_ACT_KIND] == NULL) {
289 fprintf(stderr, "NULL Action!\n");
290 return -1;
291 }
292
293
294 a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
295 if (a == NULL)
296 return err;
297
298 err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]);
299
300 if (err < 0)
301 return err;
302
303 if (show_stats && tb[TCA_ACT_STATS]) {
304 fprintf(f, "\tAction statistics:\n");
305 print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
306 fprintf(f, "\n");
307 }
308 if (tb[TCA_ACT_COOKIE]) {
309 int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]);
310 char b1[strsz * 2 + 1];
311
312 fprintf(f, "\tcookie %s\n",
313 hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]),
314 strsz, b1, sizeof(b1)));
315 }
316
317 return 0;
318 }
319
320 static int
321 tc_print_action_flush(FILE *f, const struct rtattr *arg)
322 {
323
324 struct rtattr *tb[TCA_MAX + 1];
325 int err = 0;
326 struct action_util *a = NULL;
327 __u32 *delete_count = 0;
328
329 parse_rtattr_nested(tb, TCA_MAX, arg);
330
331 if (tb[TCA_KIND] == NULL) {
332 fprintf(stderr, "NULL Action!\n");
333 return -1;
334 }
335
336 a = get_action_kind(RTA_DATA(tb[TCA_KIND]));
337 if (a == NULL)
338 return err;
339
340 delete_count = RTA_DATA(tb[TCA_FCNT]);
341 fprintf(f, " %s (%d entries)\n", a->id, *delete_count);
342 tab_flush = 0;
343 return 0;
344 }
345
346 int
347 tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
348 {
349
350 int i;
351
352 if (arg == NULL)
353 return 0;
354
355 if (!tot_acts)
356 tot_acts = TCA_ACT_MAX_PRIO;
357
358 struct rtattr *tb[tot_acts + 1];
359
360 parse_rtattr_nested(tb, tot_acts, arg);
361
362 if (tab_flush && NULL != tb[0] && NULL == tb[1])
363 return tc_print_action_flush(f, tb[0]);
364
365 for (i = 0; i < tot_acts; i++) {
366 if (tb[i]) {
367 fprintf(f, "\n\taction order %d: ", i);
368 if (tc_print_one_action(f, tb[i]) < 0) {
369 fprintf(f, "Error printing action\n");
370 }
371 }
372
373 }
374
375 return 0;
376 }
377
378 int print_action(const struct sockaddr_nl *who,
379 struct nlmsghdr *n,
380 void *arg)
381 {
382 FILE *fp = (FILE *)arg;
383 struct tcamsg *t = NLMSG_DATA(n);
384 int len = n->nlmsg_len;
385 __u32 *tot_acts = NULL;
386 struct rtattr *tb[TCA_ROOT_MAX+1];
387
388 len -= NLMSG_LENGTH(sizeof(*t));
389
390 if (len < 0) {
391 fprintf(stderr, "Wrong len %d\n", len);
392 return -1;
393 }
394
395 parse_rtattr(tb, TCA_ROOT_MAX, TA_RTA(t), len);
396
397 if (tb[TCA_ROOT_COUNT])
398 tot_acts = RTA_DATA(tb[TCA_ROOT_COUNT]);
399
400 fprintf(fp, "total acts %d\n", tot_acts ? *tot_acts:0);
401 if (tb[TCA_ACT_TAB] == NULL) {
402 if (n->nlmsg_type != RTM_GETACTION)
403 fprintf(stderr, "print_action: NULL kind\n");
404 return -1;
405 }
406
407 if (n->nlmsg_type == RTM_DELACTION) {
408 if (n->nlmsg_flags & NLM_F_ROOT) {
409 fprintf(fp, "Flushed table ");
410 tab_flush = 1;
411 } else {
412 fprintf(fp, "Deleted action ");
413 }
414 }
415
416 if (n->nlmsg_type == RTM_NEWACTION) {
417 if ((n->nlmsg_flags & NLM_F_CREATE) &&
418 !(n->nlmsg_flags & NLM_F_REPLACE)) {
419 fprintf(fp, "Added action ");
420 } else if (n->nlmsg_flags & NLM_F_REPLACE) {
421 fprintf(fp, "Replaced action ");
422 }
423 }
424
425
426 tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0);
427
428 return 0;
429 }
430
431 static int tc_action_gd(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
432 {
433 char k[FILTER_NAMESZ];
434 struct action_util *a = NULL;
435 int argc = *argc_p;
436 char **argv = *argv_p;
437 int prio = 0;
438 int ret = 0;
439 __u32 i = 0;
440 struct rtattr *tail;
441 struct rtattr *tail2;
442 struct nlmsghdr *ans = NULL;
443
444 struct {
445 struct nlmsghdr n;
446 struct tcamsg t;
447 char buf[MAX_MSG];
448 } req = {
449 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
450 .n.nlmsg_flags = NLM_F_REQUEST | flags,
451 .n.nlmsg_type = cmd,
452 .t.tca_family = AF_UNSPEC,
453 };
454
455 argc -= 1;
456 argv += 1;
457
458
459 tail = NLMSG_TAIL(&req.n);
460 addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
461
462 while (argc > 0) {
463 if (strcmp(*argv, "action") == 0) {
464 argc--;
465 argv++;
466 continue;
467 } else if (strcmp(*argv, "help") == 0) {
468 return -1;
469 }
470
471 strncpy(k, *argv, sizeof(k) - 1);
472 a = get_action_kind(k);
473 if (a == NULL) {
474 fprintf(stderr, "Error: non existent action: %s\n", k);
475 ret = -1;
476 goto bad_val;
477 }
478 if (strcmp(a->id, k) != 0) {
479 fprintf(stderr, "Error: non existent action: %s\n", k);
480 ret = -1;
481 goto bad_val;
482 }
483
484 argc -= 1;
485 argv += 1;
486 if (argc <= 0) {
487 fprintf(stderr, "Error: no index specified action: %s\n", k);
488 ret = -1;
489 goto bad_val;
490 }
491
492 if (matches(*argv, "index") == 0) {
493 NEXT_ARG();
494 if (get_u32(&i, *argv, 10)) {
495 fprintf(stderr, "Illegal \"index\"\n");
496 ret = -1;
497 goto bad_val;
498 }
499 argc -= 1;
500 argv += 1;
501 } else {
502 fprintf(stderr, "Error: no index specified action: %s\n", k);
503 ret = -1;
504 goto bad_val;
505 }
506
507 tail2 = NLMSG_TAIL(&req.n);
508 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
509 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
510 if (i > 0)
511 addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
512 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
513
514 }
515
516 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
517
518 req.n.nlmsg_seq = rth.dump = ++rth.seq;
519
520 if (rtnl_talk(&rth, &req.n, &ans) < 0) {
521 fprintf(stderr, "We have an error talking to the kernel\n");
522 return 1;
523 }
524
525 if (cmd == RTM_GETACTION && print_action(NULL, ans, stdout) < 0) {
526 fprintf(stderr, "Dump terminated\n");
527 free(ans);
528 return 1;
529 }
530 free(ans);
531
532 *argc_p = argc;
533 *argv_p = argv;
534 bad_val:
535 return ret;
536 }
537
538 static int tc_action_modify(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
539 {
540 int argc = *argc_p;
541 char **argv = *argv_p;
542 int ret = 0;
543 struct {
544 struct nlmsghdr n;
545 struct tcamsg t;
546 char buf[MAX_MSG];
547 } req = {
548 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
549 .n.nlmsg_flags = NLM_F_REQUEST | flags,
550 .n.nlmsg_type = cmd,
551 .t.tca_family = AF_UNSPEC,
552 };
553 struct rtattr *tail = NLMSG_TAIL(&req.n);
554
555 argc -= 1;
556 argv += 1;
557 if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
558 fprintf(stderr, "Illegal \"action\"\n");
559 return -1;
560 }
561 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
562
563 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
564 fprintf(stderr, "We have an error talking to the kernel\n");
565 ret = -1;
566 }
567
568 *argc_p = argc;
569 *argv_p = argv;
570
571 return ret;
572 }
573
574 static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event)
575 {
576 struct rtattr *tail, *tail2, *tail3, *tail4;
577 int ret = 0, prio = 0, msg_size = 0;
578 struct action_util *a = NULL;
579 struct nla_bitfield32 flag_select = { 0 };
580 char **argv = *argv_p;
581 __u32 msec_since = 0;
582 int argc = *argc_p;
583 char k[FILTER_NAMESZ];
584 struct {
585 struct nlmsghdr n;
586 struct tcamsg t;
587 char buf[MAX_MSG];
588 } req = {
589 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
590 .t.tca_family = AF_UNSPEC,
591 };
592
593 tail = NLMSG_TAIL(&req.n);
594 addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
595 tail2 = NLMSG_TAIL(&req.n);
596
597 strncpy(k, *argv, sizeof(k) - 1);
598 #ifdef CONFIG_GACT
599 if (!gact_ld) {
600 get_action_kind("gact");
601 }
602 #endif
603 a = get_action_kind(k);
604 if (a == NULL) {
605 fprintf(stderr, "bad action %s\n", k);
606 goto bad_val;
607 }
608 if (strcmp(a->id, k) != 0) {
609 fprintf(stderr, "bad action %s\n", k);
610 goto bad_val;
611 }
612 strncpy(k, *argv, sizeof(k) - 1);
613
614 argc -= 1;
615 argv += 1;
616
617 if (argc && (strcmp(*argv, "since") == 0)) {
618 NEXT_ARG();
619 if (get_u32(&msec_since, *argv, 0))
620 invarg("dump time \"since\" is invalid", *argv);
621 }
622
623 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
624 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
625 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
626 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
627
628 tail3 = NLMSG_TAIL(&req.n);
629 flag_select.value |= TCA_FLAG_LARGE_DUMP_ON;
630 flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON;
631 addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select,
632 sizeof(struct nla_bitfield32));
633 tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3;
634 if (msec_since) {
635 tail4 = NLMSG_TAIL(&req.n);
636 addattr32(&req.n, MAX_MSG, TCA_ROOT_TIME_DELTA, msec_since);
637 tail4->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail4;
638 }
639 msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
640
641 if (event == RTM_GETACTION) {
642 if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
643 perror("Cannot send dump request");
644 return 1;
645 }
646 ret = rtnl_dump_filter(&rth, print_action, stdout);
647 }
648
649 if (event == RTM_DELACTION) {
650 req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
651 req.n.nlmsg_type = RTM_DELACTION;
652 req.n.nlmsg_flags |= NLM_F_ROOT;
653 req.n.nlmsg_flags |= NLM_F_REQUEST;
654 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
655 fprintf(stderr, "We have an error flushing\n");
656 return 1;
657 }
658
659 }
660
661 bad_val:
662
663 *argc_p = argc;
664 *argv_p = argv;
665 return ret;
666 }
667
668 int do_action(int argc, char **argv)
669 {
670
671 int ret = 0;
672
673 while (argc > 0) {
674
675 if (matches(*argv, "add") == 0) {
676 ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv);
677 } else if (matches(*argv, "change") == 0 ||
678 matches(*argv, "replace") == 0) {
679 ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv);
680 } else if (matches(*argv, "delete") == 0) {
681 argc -= 1;
682 argv += 1;
683 ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv);
684 } else if (matches(*argv, "get") == 0) {
685 argc -= 1;
686 argv += 1;
687 ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv);
688 } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
689 || matches(*argv, "lst") == 0) {
690 if (argc <= 2) {
691 act_usage();
692 return -1;
693 }
694
695 argc -= 2;
696 argv += 2;
697 return tc_act_list_or_flush(&argc, &argv,
698 RTM_GETACTION);
699 } else if (matches(*argv, "flush") == 0) {
700 if (argc <= 2) {
701 act_usage();
702 return -1;
703 }
704
705 argc -= 2;
706 argv += 2;
707 return tc_act_list_or_flush(&argc, &argv,
708 RTM_DELACTION);
709 } else if (matches(*argv, "help") == 0) {
710 act_usage();
711 return -1;
712 } else {
713 fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv);
714 return -1;
715 }
716
717 if (ret < 0)
718 return -1;
719 }
720
721 return 0;
722 }