1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <stdbool.h>
19#include <unistd.h>
20#include <fcntl.h>
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <string.h>
25#include <dlfcn.h>
26
27#include "utils.h"
28#include "tc_common.h"
29#include "tc_util.h"
30
31static struct action_util *action_list;
32#ifdef CONFIG_GACT
33static int gact_ld;
34#endif
35static int tab_flush;
36
37static void act_usage(void)
38{
39
40
41
42
43
44
45
46 fprintf(stderr,
47 "usage: tc actions <ACTSPECOP>*\n"
48 "Where: ACTSPECOP := ACR | GD | FL\n"
49 " ACR := add | change | replace <ACTSPEC>*\n"
50 " GD := get | delete | <ACTISPEC>*\n"
51 " FL := ls | list | flush | <ACTNAMESPEC>\n"
52 " ACTNAMESPEC := action <ACTNAME>\n"
53 " ACTISPEC := <ACTNAMESPEC> <INDEXSPEC>\n"
54 " ACTSPEC := action <ACTDETAIL> [INDEXSPEC] [HWSTATSSPEC]\n"
55 " INDEXSPEC := index <32 bit indexvalue>\n"
56 " HWSTATSSPEC := hw_stats [ immediate | delayed | disabled ]\n"
57 " ACTDETAIL := <ACTNAME> <ACTPARAMS>\n"
58 " Example ACTNAME is gact, mirred, bpf, etc\n"
59 " Each action has its own parameters (ACTPARAMS)\n"
60 "\n");
61
62 exit(-1);
63}
64
65static int print_noaopt(struct action_util *au, FILE *f, struct rtattr *opt)
66{
67 if (opt && RTA_PAYLOAD(opt))
68 fprintf(f, "[Unknown action, optlen=%u] ",
69 (unsigned int) RTA_PAYLOAD(opt));
70 return 0;
71}
72
73static int parse_noaopt(struct action_util *au, int *argc_p,
74 char ***argv_p, int code, struct nlmsghdr *n)
75{
76 int argc = *argc_p;
77 char **argv = *argv_p;
78
79 if (argc)
80 fprintf(stderr,
81 "Unknown action \"%s\", hence option \"%s\" is unparsable\n",
82 au->id, *argv);
83 else
84 fprintf(stderr, "Unknown action \"%s\"\n", au->id);
85
86 return -1;
87}
88
89static struct action_util *get_action_kind(char *str)
90{
91 static void *aBODY;
92 void *dlh;
93 char buf[256];
94 struct action_util *a;
95#ifdef CONFIG_GACT
96 int looked4gact = 0;
97restart_s:
98#endif
99 for (a = action_list; a; a = a->next) {
100 if (strcmp(a->id, str) == 0)
101 return a;
102 }
103
104 snprintf(buf, sizeof(buf), "%s/m_%s.so", get_tc_lib(), str);
105 dlh = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
106 if (dlh == NULL) {
107 dlh = aBODY;
108 if (dlh == NULL) {
109 dlh = aBODY = dlopen(NULL, RTLD_LAZY);
110 if (dlh == NULL)
111 goto noexist;
112 }
113 }
114
115 snprintf(buf, sizeof(buf), "%s_action_util", str);
116 a = dlsym(dlh, buf);
117 if (a == NULL)
118 goto noexist;
119
120reg:
121 a->next = action_list;
122 action_list = a;
123 return a;
124
125noexist:
126#ifdef CONFIG_GACT
127 if (!looked4gact) {
128 looked4gact = 1;
129 strcpy(str, "gact");
130 goto restart_s;
131 }
132#endif
133 a = calloc(1, sizeof(*a));
134 if (a) {
135 strncpy(a->id, "noact", 15);
136 a->parse_aopt = parse_noaopt;
137 a->print_aopt = print_noaopt;
138 goto reg;
139 }
140 return a;
141}
142
143static bool
144new_cmd(char **argv)
145{
146 return (matches(*argv, "change") == 0) ||
147 (matches(*argv, "replace") == 0) ||
148 (matches(*argv, "delete") == 0) ||
149 (matches(*argv, "get") == 0) ||
150 (matches(*argv, "add") == 0);
151}
152
153static const struct hw_stats_item {
154 const char *str;
155 __u8 type;
156} hw_stats_items[] = {
157 { "immediate", TCA_ACT_HW_STATS_IMMEDIATE },
158 { "delayed", TCA_ACT_HW_STATS_DELAYED },
159 { "disabled", 0 },
160};
161
162static void print_hw_stats(const struct rtattr *arg, bool print_used)
163{
164 struct nla_bitfield32 *hw_stats_bf = RTA_DATA(arg);
165 __u8 hw_stats;
166 int i;
167
168 hw_stats = hw_stats_bf->value & hw_stats_bf->selector;
169 print_string(PRINT_FP, NULL, "\t", NULL);
170 open_json_array(PRINT_ANY, print_used ? "used_hw_stats" : "hw_stats");
171
172 for (i = 0; i < ARRAY_SIZE(hw_stats_items); i++) {
173 const struct hw_stats_item *item;
174
175 item = &hw_stats_items[i];
176 if ((!hw_stats && !item->type) || hw_stats & item->type)
177 print_string(PRINT_ANY, NULL, " %s", item->str);
178 }
179 close_json_array(PRINT_JSON, NULL);
180 print_nl();
181}
182
183static int parse_hw_stats(const char *str, struct nlmsghdr *n)
184{
185 int i;
186
187 for (i = 0; i < ARRAY_SIZE(hw_stats_items); i++) {
188 const struct hw_stats_item *item;
189
190 item = &hw_stats_items[i];
191 if (matches(str, item->str) == 0) {
192 struct nla_bitfield32 hw_stats_bf = {
193 .value = item->type,
194 .selector = item->type
195 };
196
197 addattr_l(n, MAX_MSG, TCA_ACT_HW_STATS,
198 &hw_stats_bf, sizeof(hw_stats_bf));
199 return 0;
200 }
201
202 }
203 return -1;
204}
205
206int parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
207{
208 int argc = *argc_p;
209 char **argv = *argv_p;
210 struct rtattr *tail, *tail2;
211 char k[FILTER_NAMESZ];
212 int act_ck_len = 0;
213 int ok = 0;
214 int eap = 0;
215
216 int ret = 0;
217 int prio = 0;
218 unsigned char act_ck[TC_COOKIE_MAX_SIZE];
219
220 if (argc <= 0)
221 return -1;
222
223 tail2 = addattr_nest(n, MAX_MSG, tca_id);
224
225 while (argc > 0) {
226
227 memset(k, 0, sizeof(k));
228
229 if (strcmp(*argv, "action") == 0) {
230 argc--;
231 argv++;
232 eap = 1;
233#ifdef CONFIG_GACT
234 if (!gact_ld)
235 get_action_kind("gact");
236#endif
237 continue;
238 } else if (strcmp(*argv, "flowid") == 0) {
239 break;
240 } else if (strcmp(*argv, "classid") == 0) {
241 break;
242 } else if (strcmp(*argv, "help") == 0) {
243 return -1;
244 } else if (new_cmd(argv)) {
245 goto done0;
246 } else {
247 struct action_util *a = NULL;
248
249 if (!action_a2n(*argv, NULL, false))
250 strncpy(k, "gact", sizeof(k) - 1);
251 else
252 strncpy(k, *argv, sizeof(k) - 1);
253 eap = 0;
254 if (argc > 0) {
255 a = get_action_kind(k);
256 } else {
257done0:
258 if (ok)
259 break;
260 else
261 goto done;
262 }
263
264 if (a == NULL)
265 goto bad_val;
266
267
268 tail = addattr_nest(n, MAX_MSG, ++prio);
269 addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
270
271 ret = a->parse_aopt(a, &argc, &argv,
272 TCA_ACT_OPTIONS | NLA_F_NESTED,
273 n);
274
275 if (ret < 0) {
276 fprintf(stderr, "bad action parsing\n");
277 goto bad_val;
278 }
279
280 if (*argv && strcmp(*argv, "cookie") == 0) {
281 size_t slen;
282
283 NEXT_ARG();
284 slen = strlen(*argv);
285 if (slen > TC_COOKIE_MAX_SIZE * 2) {
286 char cookie_err_m[128];
287
288 snprintf(cookie_err_m, 128,
289 "%zd Max allowed size %d",
290 slen, TC_COOKIE_MAX_SIZE*2);
291 invarg(cookie_err_m, *argv);
292 }
293
294 if (slen % 2 ||
295 hex2mem(*argv, act_ck, slen / 2) < 0)
296 invarg("cookie must be a hex string\n",
297 *argv);
298
299 act_ck_len = slen / 2;
300 argc--;
301 argv++;
302 }
303
304 if (act_ck_len)
305 addattr_l(n, MAX_MSG, TCA_ACT_COOKIE,
306 &act_ck, act_ck_len);
307
308 if (*argv && matches(*argv, "hw_stats") == 0) {
309 NEXT_ARG();
310 ret = parse_hw_stats(*argv, n);
311 if (ret < 0)
312 invarg("value is invalid\n", *argv);
313 NEXT_ARG_FWD();
314 }
315
316 if (*argv && strcmp(*argv, "no_percpu") == 0) {
317 struct nla_bitfield32 flags =
318 { TCA_ACT_FLAGS_NO_PERCPU_STATS,
319 TCA_ACT_FLAGS_NO_PERCPU_STATS };
320
321 addattr_l(n, MAX_MSG, TCA_ACT_FLAGS, &flags,
322 sizeof(struct nla_bitfield32));
323 NEXT_ARG_FWD();
324 }
325
326 addattr_nest_end(n, tail);
327 ok++;
328 }
329 }
330
331 if (eap > 0) {
332 fprintf(stderr, "bad action empty %d\n", eap);
333 goto bad_val;
334 }
335
336 addattr_nest_end(n, tail2);
337
338done:
339 *argc_p = argc;
340 *argv_p = argv;
341 return 0;
342bad_val:
343
344
345
346 fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv);
347 return -1;
348}
349
350static int tc_print_one_action(FILE *f, struct rtattr *arg)
351{
352
353 struct rtattr *tb[TCA_ACT_MAX + 1];
354 int err = 0;
355 struct action_util *a = NULL;
356
357 if (arg == NULL)
358 return -1;
359
360 parse_rtattr_nested(tb, TCA_ACT_MAX, arg);
361
362 if (tb[TCA_ACT_KIND] == NULL) {
363 fprintf(stderr, "NULL Action!\n");
364 return -1;
365 }
366
367
368 a = get_action_kind(RTA_DATA(tb[TCA_ACT_KIND]));
369 if (a == NULL)
370 return err;
371
372 err = a->print_aopt(a, f, tb[TCA_ACT_OPTIONS]);
373
374 if (err < 0)
375 return err;
376
377 if (brief && tb[TCA_ACT_INDEX]) {
378 print_uint(PRINT_ANY, "index", "\t index %u",
379 rta_getattr_u32(tb[TCA_ACT_INDEX]));
380 print_nl();
381 }
382 if (show_stats && tb[TCA_ACT_STATS]) {
383 print_string(PRINT_FP, NULL, "\tAction statistics:", NULL);
384 print_nl();
385 open_json_object("stats");
386 print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
387 close_json_object();
388 print_nl();
389 }
390 if (tb[TCA_ACT_COOKIE]) {
391 int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]);
392 char b1[strsz * 2 + 1];
393
394 print_string(PRINT_ANY, "cookie", "\tcookie %s",
395 hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]),
396 strsz, b1, sizeof(b1)));
397 print_nl();
398 }
399 if (tb[TCA_ACT_FLAGS]) {
400 struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_ACT_FLAGS]);
401
402 if (flags->selector & TCA_ACT_FLAGS_NO_PERCPU_STATS)
403 print_bool(PRINT_ANY, "no_percpu", "\tno_percpu",
404 flags->value &
405 TCA_ACT_FLAGS_NO_PERCPU_STATS);
406 print_nl();
407 }
408 if (tb[TCA_ACT_HW_STATS])
409 print_hw_stats(tb[TCA_ACT_HW_STATS], false);
410
411 if (tb[TCA_ACT_USED_HW_STATS])
412 print_hw_stats(tb[TCA_ACT_USED_HW_STATS], true);
413
414 return 0;
415}
416
417static int
418tc_print_action_flush(FILE *f, const struct rtattr *arg)
419{
420
421 struct rtattr *tb[TCA_MAX + 1];
422 int err = 0;
423 struct action_util *a = NULL;
424 __u32 *delete_count = 0;
425
426 parse_rtattr_nested(tb, TCA_MAX, arg);
427
428 if (tb[TCA_KIND] == NULL) {
429 fprintf(stderr, "NULL Action!\n");
430 return -1;
431 }
432
433 a = get_action_kind(RTA_DATA(tb[TCA_KIND]));
434 if (a == NULL)
435 return err;
436
437 delete_count = RTA_DATA(tb[TCA_FCNT]);
438 fprintf(f, " %s (%d entries)\n", a->id, *delete_count);
439 tab_flush = 0;
440 return 0;
441}
442
443int
444tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
445{
446
447 int i;
448
449 if (arg == NULL)
450 return 0;
451
452 if (!tot_acts)
453 tot_acts = TCA_ACT_MAX_PRIO;
454
455 struct rtattr *tb[tot_acts + 1];
456
457 parse_rtattr_nested(tb, tot_acts, arg);
458
459 if (tab_flush && tb[0] && !tb[1])
460 return tc_print_action_flush(f, tb[0]);
461
462 open_json_array(PRINT_JSON, "actions");
463 for (i = 0; i <= tot_acts; i++) {
464 if (tb[i]) {
465 open_json_object(NULL);
466 print_nl();
467 print_uint(PRINT_ANY, "order",
468 "\taction order %u: ", i);
469 if (tc_print_one_action(f, tb[i]) < 0) {
470 print_string(PRINT_FP, NULL,
471 "Error printing action\n", NULL);
472 }
473 close_json_object();
474 }
475
476 }
477 close_json_array(PRINT_JSON, NULL);
478
479 return 0;
480}
481
482int print_action(struct nlmsghdr *n, void *arg)
483{
484 FILE *fp = (FILE *)arg;
485 struct tcamsg *t = NLMSG_DATA(n);
486 int len = n->nlmsg_len;
487 __u32 *tot_acts = NULL;
488 struct rtattr *tb[TCA_ROOT_MAX+1];
489
490 len -= NLMSG_LENGTH(sizeof(*t));
491
492 if (len < 0) {
493 fprintf(stderr, "Wrong len %d\n", len);
494 return -1;
495 }
496
497 parse_rtattr(tb, TCA_ROOT_MAX, TA_RTA(t), len);
498
499 if (tb[TCA_ROOT_COUNT])
500 tot_acts = RTA_DATA(tb[TCA_ROOT_COUNT]);
501
502 open_json_object(NULL);
503 print_uint(PRINT_ANY, "total acts", "total acts %u",
504 tot_acts ? *tot_acts : 0);
505 print_nl();
506 close_json_object();
507 if (tb[TCA_ACT_TAB] == NULL) {
508 if (n->nlmsg_type != RTM_GETACTION)
509 fprintf(stderr, "print_action: NULL kind\n");
510 return -1;
511 }
512
513 if (n->nlmsg_type == RTM_DELACTION) {
514 if (n->nlmsg_flags & NLM_F_ROOT) {
515 fprintf(fp, "Flushed table ");
516 tab_flush = 1;
517 } else {
518 fprintf(fp, "Deleted action ");
519 }
520 }
521
522 if (n->nlmsg_type == RTM_NEWACTION) {
523 if ((n->nlmsg_flags & NLM_F_CREATE) &&
524 !(n->nlmsg_flags & NLM_F_REPLACE)) {
525 fprintf(fp, "Added action ");
526 } else if (n->nlmsg_flags & NLM_F_REPLACE) {
527 fprintf(fp, "Replaced action ");
528 }
529 }
530
531 open_json_object(NULL);
532 tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0);
533 close_json_object();
534
535 return 0;
536}
537
538static int tc_action_gd(int cmd, unsigned int flags,
539 int *argc_p, char ***argv_p)
540{
541 char k[FILTER_NAMESZ];
542 struct action_util *a = NULL;
543 int argc = *argc_p;
544 char **argv = *argv_p;
545 int prio = 0;
546 int ret = 0;
547 __u32 i = 0;
548 struct rtattr *tail;
549 struct rtattr *tail2;
550 struct nlmsghdr *ans = NULL;
551
552 struct {
553 struct nlmsghdr n;
554 struct tcamsg t;
555 char buf[MAX_MSG];
556 } req = {
557 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
558 .n.nlmsg_flags = NLM_F_REQUEST | flags,
559 .n.nlmsg_type = cmd,
560 .t.tca_family = AF_UNSPEC,
561 };
562
563 argc -= 1;
564 argv += 1;
565
566
567 tail = addattr_nest(&req.n, MAX_MSG, TCA_ACT_TAB);
568
569 while (argc > 0) {
570 if (strcmp(*argv, "action") == 0) {
571 argc--;
572 argv++;
573 continue;
574 } else if (strcmp(*argv, "help") == 0) {
575 return -1;
576 }
577
578 strncpy(k, *argv, sizeof(k) - 1);
579 a = get_action_kind(k);
580 if (a == NULL) {
581 fprintf(stderr, "Error: non existent action: %s\n", k);
582 ret = -1;
583 goto bad_val;
584 }
585 if (strcmp(a->id, k) != 0) {
586 fprintf(stderr, "Error: non existent action: %s\n", k);
587 ret = -1;
588 goto bad_val;
589 }
590
591 argc -= 1;
592 argv += 1;
593 if (argc <= 0) {
594 fprintf(stderr,
595 "Error: no index specified action: %s\n", k);
596 ret = -1;
597 goto bad_val;
598 }
599
600 if (matches(*argv, "index") == 0) {
601 NEXT_ARG();
602 if (get_u32(&i, *argv, 10)) {
603 fprintf(stderr, "Illegal \"index\"\n");
604 ret = -1;
605 goto bad_val;
606 }
607 argc -= 1;
608 argv += 1;
609 } else {
610 fprintf(stderr,
611 "Error: no index specified action: %s\n", k);
612 ret = -1;
613 goto bad_val;
614 }
615
616 tail2 = addattr_nest(&req.n, MAX_MSG, ++prio);
617 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
618 if (i > 0)
619 addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
620 addattr_nest_end(&req.n, tail2);
621
622 }
623
624 addattr_nest_end(&req.n, tail);
625
626 req.n.nlmsg_seq = rth.dump = ++rth.seq;
627
628 if (rtnl_talk(&rth, &req.n, cmd == RTM_DELACTION ? NULL : &ans) < 0) {
629 fprintf(stderr, "We have an error talking to the kernel\n");
630 return 1;
631 }
632
633 if (cmd == RTM_GETACTION) {
634 new_json_obj(json);
635 ret = print_action(ans, stdout);
636 if (ret < 0) {
637 fprintf(stderr, "Dump terminated\n");
638 free(ans);
639 delete_json_obj();
640 return 1;
641 }
642 delete_json_obj();
643 }
644 free(ans);
645
646 *argc_p = argc;
647 *argv_p = argv;
648bad_val:
649 return ret;
650}
651
652static int tc_action_modify(int cmd, unsigned int flags,
653 int *argc_p, char ***argv_p)
654{
655 int argc = *argc_p;
656 char **argv = *argv_p;
657 int ret = 0;
658 struct {
659 struct nlmsghdr n;
660 struct tcamsg t;
661 char buf[MAX_MSG];
662 } req = {
663 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
664 .n.nlmsg_flags = NLM_F_REQUEST | flags,
665 .n.nlmsg_type = cmd,
666 .t.tca_family = AF_UNSPEC,
667 };
668 struct rtattr *tail = NLMSG_TAIL(&req.n);
669
670 argc -= 1;
671 argv += 1;
672 if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
673 fprintf(stderr, "Illegal \"action\"\n");
674 return -1;
675 }
676 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
677
678 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
679 fprintf(stderr, "We have an error talking to the kernel\n");
680 ret = -1;
681 }
682
683 *argc_p = argc;
684 *argv_p = argv;
685
686 return ret;
687}
688
689static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event)
690{
691 struct rtattr *tail, *tail2, *tail3, *tail4;
692 int ret = 0, prio = 0, msg_size = 0;
693 struct action_util *a = NULL;
694 struct nla_bitfield32 flag_select = { 0 };
695 char **argv = *argv_p;
696 __u32 msec_since = 0;
697 int argc = *argc_p;
698 char k[FILTER_NAMESZ];
699 struct {
700 struct nlmsghdr n;
701 struct tcamsg t;
702 char buf[MAX_MSG];
703 } req = {
704 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
705 .t.tca_family = AF_UNSPEC,
706 };
707
708 tail = addattr_nest(&req.n, MAX_MSG, TCA_ACT_TAB);
709 tail2 = NLMSG_TAIL(&req.n);
710
711 strncpy(k, *argv, sizeof(k) - 1);
712#ifdef CONFIG_GACT
713 if (!gact_ld)
714 get_action_kind("gact");
715
716#endif
717 a = get_action_kind(k);
718 if (a == NULL) {
719 fprintf(stderr, "bad action %s\n", k);
720 goto bad_val;
721 }
722 if (strcmp(a->id, k) != 0) {
723 fprintf(stderr, "bad action %s\n", k);
724 goto bad_val;
725 }
726 strncpy(k, *argv, sizeof(k) - 1);
727
728 argc -= 1;
729 argv += 1;
730
731 if (argc && (strcmp(*argv, "since") == 0)) {
732 NEXT_ARG();
733 if (get_u32(&msec_since, *argv, 0))
734 invarg("dump time \"since\" is invalid", *argv);
735 }
736
737 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
738 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
739 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
740 addattr_nest_end(&req.n, tail);
741
742 tail3 = NLMSG_TAIL(&req.n);
743 flag_select.value |= TCA_ACT_FLAG_LARGE_DUMP_ON;
744 flag_select.selector |= TCA_ACT_FLAG_LARGE_DUMP_ON;
745 if (brief) {
746 flag_select.value |= TCA_ACT_FLAG_TERSE_DUMP;
747 flag_select.selector |= TCA_ACT_FLAG_TERSE_DUMP;
748 }
749 addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select,
750 sizeof(struct nla_bitfield32));
751 tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3;
752 if (msec_since) {
753 tail4 = NLMSG_TAIL(&req.n);
754 addattr32(&req.n, MAX_MSG, TCA_ROOT_TIME_DELTA, msec_since);
755 tail4->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail4;
756 }
757 msg_size = NLMSG_ALIGN(req.n.nlmsg_len)
758 - NLMSG_ALIGN(sizeof(struct nlmsghdr));
759
760 if (event == RTM_GETACTION) {
761 if (rtnl_dump_request(&rth, event,
762 (void *)&req.t, msg_size) < 0) {
763 perror("Cannot send dump request");
764 return 1;
765 }
766 new_json_obj(json);
767 ret = rtnl_dump_filter(&rth, print_action, stdout);
768 delete_json_obj();
769 }
770
771 if (event == RTM_DELACTION) {
772 req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
773 req.n.nlmsg_type = RTM_DELACTION;
774 req.n.nlmsg_flags |= NLM_F_ROOT;
775 req.n.nlmsg_flags |= NLM_F_REQUEST;
776 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
777 fprintf(stderr, "We have an error flushing\n");
778 return 1;
779 }
780
781 }
782
783bad_val:
784
785 *argc_p = argc;
786 *argv_p = argv;
787 return ret;
788}
789
790int do_action(int argc, char **argv)
791{
792
793 int ret = 0;
794
795 while (argc > 0) {
796
797 if (matches(*argv, "add") == 0) {
798 ret = tc_action_modify(RTM_NEWACTION,
799 NLM_F_EXCL | NLM_F_CREATE,
800 &argc, &argv);
801 } else if (matches(*argv, "change") == 0 ||
802 matches(*argv, "replace") == 0) {
803 ret = tc_action_modify(RTM_NEWACTION,
804 NLM_F_CREATE | NLM_F_REPLACE,
805 &argc, &argv);
806 } else if (matches(*argv, "delete") == 0) {
807 argc -= 1;
808 argv += 1;
809 ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv);
810 } else if (matches(*argv, "get") == 0) {
811 argc -= 1;
812 argv += 1;
813 ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv);
814 } else if (matches(*argv, "list") == 0 ||
815 matches(*argv, "show") == 0 ||
816 matches(*argv, "lst") == 0) {
817 if (argc <= 2) {
818 act_usage();
819 return -1;
820 }
821
822 argc -= 2;
823 argv += 2;
824 return tc_act_list_or_flush(&argc, &argv,
825 RTM_GETACTION);
826 } else if (matches(*argv, "flush") == 0) {
827 if (argc <= 2) {
828 act_usage();
829 return -1;
830 }
831
832 argc -= 2;
833 argv += 2;
834 return tc_act_list_or_flush(&argc, &argv,
835 RTM_DELACTION);
836 } else if (matches(*argv, "help") == 0) {
837 act_usage();
838 return -1;
839 } else {
840 fprintf(stderr,
841 "Command \"%s\" is unknown, try \"tc actions help\".\n",
842 *argv);
843 return -1;
844 }
845
846 if (ret < 0)
847 return -1;
848 }
849
850 return 0;
851}
852