]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/m_action.c
0dce97f08ba694dcf82d9312107856bdbf441c43
[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[16];
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
305 fprintf(f, "\tAction statistics:\n");
306 print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
307 if (tb[TCA_ACT_COOKIE]) {
308 int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]);
309 char b1[strsz * 2 + 1];
310
311 fprintf(f, "\n\tcookie len %d %s ", strsz,
312 hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]),
313 strsz, b1, sizeof(b1)));
314 }
315 fprintf(f, "\n");
316 }
317
318 return 0;
319 }
320
321 static int
322 tc_print_action_flush(FILE *f, const struct rtattr *arg)
323 {
324
325 struct rtattr *tb[TCA_MAX + 1];
326 int err = 0;
327 struct action_util *a = NULL;
328 __u32 *delete_count = 0;
329
330 parse_rtattr_nested(tb, TCA_MAX, arg);
331
332 if (tb[TCA_KIND] == NULL) {
333 fprintf(stderr, "NULL Action!\n");
334 return -1;
335 }
336
337 a = get_action_kind(RTA_DATA(tb[TCA_KIND]));
338 if (a == NULL)
339 return err;
340
341 delete_count = RTA_DATA(tb[TCA_FCNT]);
342 fprintf(f, " %s (%d entries)\n", a->id, *delete_count);
343 tab_flush = 0;
344 return 0;
345 }
346
347 int
348 tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
349 {
350
351 int i;
352
353 if (arg == NULL)
354 return 0;
355
356 if (!tot_acts)
357 tot_acts = TCA_ACT_MAX_PRIO;
358
359 struct rtattr *tb[tot_acts + 1];
360
361 parse_rtattr_nested(tb, tot_acts, arg);
362
363 if (tab_flush && NULL != tb[0] && NULL == tb[1])
364 return tc_print_action_flush(f, tb[0]);
365
366 for (i = 0; i < tot_acts; i++) {
367 if (tb[i]) {
368 fprintf(f, "\n\taction order %d: ", i);
369 if (tc_print_one_action(f, tb[i]) < 0) {
370 fprintf(f, "Error printing action\n");
371 }
372 }
373
374 }
375
376 return 0;
377 }
378
379 int print_action(const struct sockaddr_nl *who,
380 struct nlmsghdr *n,
381 void *arg)
382 {
383 FILE *fp = (FILE *)arg;
384 struct tcamsg *t = NLMSG_DATA(n);
385 int len = n->nlmsg_len;
386 __u32 *tot_acts = NULL;
387 struct rtattr *tb[TCA_ROOT_MAX+1];
388
389 len -= NLMSG_LENGTH(sizeof(*t));
390
391 if (len < 0) {
392 fprintf(stderr, "Wrong len %d\n", len);
393 return -1;
394 }
395
396 parse_rtattr(tb, TCA_ROOT_MAX, TA_RTA(t), len);
397
398 if (tb[TCA_ROOT_COUNT])
399 tot_acts = RTA_DATA(tb[TCA_ROOT_COUNT]);
400
401 fprintf(fp, "total acts %d\n", tot_acts ? *tot_acts:0);
402 if (tb[TCA_ACT_TAB] == NULL) {
403 if (n->nlmsg_type != RTM_GETACTION)
404 fprintf(stderr, "print_action: NULL kind\n");
405 return -1;
406 }
407
408 if (n->nlmsg_type == RTM_DELACTION) {
409 if (n->nlmsg_flags & NLM_F_ROOT) {
410 fprintf(fp, "Flushed table ");
411 tab_flush = 1;
412 } else {
413 fprintf(fp, "Deleted action ");
414 }
415 }
416
417 if (n->nlmsg_type == RTM_NEWACTION) {
418 if ((n->nlmsg_flags & NLM_F_CREATE) &&
419 !(n->nlmsg_flags & NLM_F_REPLACE)) {
420 fprintf(fp, "Added action ");
421 } else if (n->nlmsg_flags & NLM_F_REPLACE) {
422 fprintf(fp, "Replaced action ");
423 }
424 }
425
426
427 tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0);
428
429 return 0;
430 }
431
432 static int tc_action_gd(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
433 {
434 char k[16];
435 struct action_util *a = NULL;
436 int argc = *argc_p;
437 char **argv = *argv_p;
438 int prio = 0;
439 int ret = 0;
440 __u32 i = 0;
441 struct rtattr *tail;
442 struct rtattr *tail2;
443 struct nlmsghdr *ans = NULL;
444
445 struct {
446 struct nlmsghdr n;
447 struct tcamsg t;
448 char buf[MAX_MSG];
449 } req = {
450 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
451 .n.nlmsg_flags = NLM_F_REQUEST | flags,
452 .n.nlmsg_type = cmd,
453 .t.tca_family = AF_UNSPEC,
454 };
455
456 argc -= 1;
457 argv += 1;
458
459
460 tail = NLMSG_TAIL(&req.n);
461 addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
462
463 while (argc > 0) {
464 if (strcmp(*argv, "action") == 0) {
465 argc--;
466 argv++;
467 continue;
468 } else if (strcmp(*argv, "help") == 0) {
469 return -1;
470 }
471
472 strncpy(k, *argv, sizeof(k) - 1);
473 a = get_action_kind(k);
474 if (a == NULL) {
475 fprintf(stderr, "Error: non existent action: %s\n", k);
476 ret = -1;
477 goto bad_val;
478 }
479 if (strcmp(a->id, k) != 0) {
480 fprintf(stderr, "Error: non existent action: %s\n", k);
481 ret = -1;
482 goto bad_val;
483 }
484
485 argc -= 1;
486 argv += 1;
487 if (argc <= 0) {
488 fprintf(stderr, "Error: no index specified action: %s\n", k);
489 ret = -1;
490 goto bad_val;
491 }
492
493 if (matches(*argv, "index") == 0) {
494 NEXT_ARG();
495 if (get_u32(&i, *argv, 10)) {
496 fprintf(stderr, "Illegal \"index\"\n");
497 ret = -1;
498 goto bad_val;
499 }
500 argc -= 1;
501 argv += 1;
502 } else {
503 fprintf(stderr, "Error: no index specified action: %s\n", k);
504 ret = -1;
505 goto bad_val;
506 }
507
508 tail2 = NLMSG_TAIL(&req.n);
509 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
510 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
511 if (i > 0)
512 addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
513 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
514
515 }
516
517 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
518
519 req.n.nlmsg_seq = rth.dump = ++rth.seq;
520
521 if (rtnl_talk(&rth, &req.n, &ans) < 0) {
522 fprintf(stderr, "We have an error talking to the kernel\n");
523 return 1;
524 }
525
526 if (cmd == RTM_GETACTION && print_action(NULL, ans, stdout) < 0) {
527 fprintf(stderr, "Dump terminated\n");
528 free(ans);
529 return 1;
530 }
531 free(ans);
532
533 *argc_p = argc;
534 *argv_p = argv;
535 bad_val:
536 return ret;
537 }
538
539 static int tc_action_modify(int cmd, unsigned int flags, int *argc_p, char ***argv_p)
540 {
541 int argc = *argc_p;
542 char **argv = *argv_p;
543 int ret = 0;
544 struct {
545 struct nlmsghdr n;
546 struct tcamsg t;
547 char buf[MAX_MSG];
548 } req = {
549 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
550 .n.nlmsg_flags = NLM_F_REQUEST | flags,
551 .n.nlmsg_type = cmd,
552 .t.tca_family = AF_UNSPEC,
553 };
554 struct rtattr *tail = NLMSG_TAIL(&req.n);
555
556 argc -= 1;
557 argv += 1;
558 if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
559 fprintf(stderr, "Illegal \"action\"\n");
560 return -1;
561 }
562 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
563
564 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
565 fprintf(stderr, "We have an error talking to the kernel\n");
566 ret = -1;
567 }
568
569 *argc_p = argc;
570 *argv_p = argv;
571
572 return ret;
573 }
574
575 static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event)
576 {
577 struct rtattr *tail, *tail2, *tail3, *tail4;
578 int ret = 0, prio = 0, msg_size = 0;
579 struct action_util *a = NULL;
580 struct nla_bitfield32 flag_select = { 0 };
581 char **argv = *argv_p;
582 __u32 msec_since = 0;
583 int argc = *argc_p;
584 char k[16];
585 struct {
586 struct nlmsghdr n;
587 struct tcamsg t;
588 char buf[MAX_MSG];
589 } req = {
590 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
591 .t.tca_family = AF_UNSPEC,
592 };
593
594 tail = NLMSG_TAIL(&req.n);
595 addattr_l(&req.n, MAX_MSG, TCA_ACT_TAB, NULL, 0);
596 tail2 = NLMSG_TAIL(&req.n);
597
598 strncpy(k, *argv, sizeof(k) - 1);
599 #ifdef CONFIG_GACT
600 if (!gact_ld) {
601 get_action_kind("gact");
602 }
603 #endif
604 a = get_action_kind(k);
605 if (a == NULL) {
606 fprintf(stderr, "bad action %s\n", k);
607 goto bad_val;
608 }
609 if (strcmp(a->id, k) != 0) {
610 fprintf(stderr, "bad action %s\n", k);
611 goto bad_val;
612 }
613 strncpy(k, *argv, sizeof(k) - 1);
614
615 argc -= 1;
616 argv += 1;
617
618 if (argc && (strcmp(*argv, "since") == 0)) {
619 NEXT_ARG();
620 if (get_u32(&msec_since, *argv, 0))
621 invarg("dump time \"since\" is invalid", *argv);
622 }
623
624 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
625 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
626 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
627 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
628
629 tail3 = NLMSG_TAIL(&req.n);
630 flag_select.value |= TCA_FLAG_LARGE_DUMP_ON;
631 flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON;
632 addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select,
633 sizeof(struct nla_bitfield32));
634 tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3;
635 if (msec_since) {
636 tail4 = NLMSG_TAIL(&req.n);
637 addattr32(&req.n, MAX_MSG, TCA_ROOT_TIME_DELTA, msec_since);
638 tail4->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail4;
639 }
640 msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr));
641
642 if (event == RTM_GETACTION) {
643 if (rtnl_dump_request(&rth, event, (void *)&req.t, msg_size) < 0) {
644 perror("Cannot send dump request");
645 return 1;
646 }
647 ret = rtnl_dump_filter(&rth, print_action, stdout);
648 }
649
650 if (event == RTM_DELACTION) {
651 req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
652 req.n.nlmsg_type = RTM_DELACTION;
653 req.n.nlmsg_flags |= NLM_F_ROOT;
654 req.n.nlmsg_flags |= NLM_F_REQUEST;
655 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
656 fprintf(stderr, "We have an error flushing\n");
657 return 1;
658 }
659
660 }
661
662 bad_val:
663
664 *argc_p = argc;
665 *argv_p = argv;
666 return ret;
667 }
668
669 int do_action(int argc, char **argv)
670 {
671
672 int ret = 0;
673
674 while (argc > 0) {
675
676 if (matches(*argv, "add") == 0) {
677 ret = tc_action_modify(RTM_NEWACTION, NLM_F_EXCL|NLM_F_CREATE, &argc, &argv);
678 } else if (matches(*argv, "change") == 0 ||
679 matches(*argv, "replace") == 0) {
680 ret = tc_action_modify(RTM_NEWACTION, NLM_F_CREATE|NLM_F_REPLACE, &argc, &argv);
681 } else if (matches(*argv, "delete") == 0) {
682 argc -= 1;
683 argv += 1;
684 ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv);
685 } else if (matches(*argv, "get") == 0) {
686 argc -= 1;
687 argv += 1;
688 ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv);
689 } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
690 || matches(*argv, "lst") == 0) {
691 if (argc <= 2) {
692 act_usage();
693 return -1;
694 }
695
696 argc -= 2;
697 argv += 2;
698 return tc_act_list_or_flush(&argc, &argv,
699 RTM_GETACTION);
700 } else if (matches(*argv, "flush") == 0) {
701 if (argc <= 2) {
702 act_usage();
703 return -1;
704 }
705
706 argc -= 2;
707 argv += 2;
708 return tc_act_list_or_flush(&argc, &argv,
709 RTM_DELACTION);
710 } else if (matches(*argv, "help") == 0) {
711 act_usage();
712 return -1;
713 } else {
714 fprintf(stderr, "Command \"%s\" is unknown, try \"tc actions help\".\n", *argv);
715 return -1;
716 }
717
718 if (ret < 0)
719 return -1;
720 }
721
722 return 0;
723 }