]> git.proxmox.com Git - mirror_iproute2.git/blob - tc/m_action.c
tc: flower: support multiple MPLS LSE match
[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 <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
31 static struct action_util *action_list;
32 #ifdef CONFIG_GACT
33 static int gact_ld; /* f*ckin backward compatibility */
34 #endif
35 static int tab_flush;
36
37 static void act_usage(void)
38 {
39 /*XXX: In the near future add a action->print_help to improve
40 * usability
41 * This would mean new tc will not be backward compatible
42 * with any action .so from the old days. But if someone really
43 * does that, they would know how to fix this ..
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
65 static 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
73 static 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
89 static 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;
97 restart_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
120 reg:
121 a->next = action_list;
122 action_list = a;
123 return a;
124
125 noexist:
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
143 static bool
144 new_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
153 static 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 }, /* no bit set */
160 };
161
162 static 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
183 static 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
206 int 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; /* expect action parameters */
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 {
257 done0:
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
338 done:
339 *argc_p = argc;
340 *argv_p = argv;
341 return 0;
342 bad_val:
343 /* no need to undo things, returning from here should
344 * cause enough pain
345 */
346 fprintf(stderr, "parse_action: bad value (%d:%s)!\n", argc, *argv);
347 return -1;
348 }
349
350 static 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 (show_stats && tb[TCA_ACT_STATS]) {
378 print_string(PRINT_FP, NULL, "\tAction statistics:", NULL);
379 print_nl();
380 open_json_object("stats");
381 print_tcstats2_attr(f, tb[TCA_ACT_STATS], "\t", NULL);
382 close_json_object();
383 print_nl();
384 }
385 if (tb[TCA_ACT_COOKIE]) {
386 int strsz = RTA_PAYLOAD(tb[TCA_ACT_COOKIE]);
387 char b1[strsz * 2 + 1];
388
389 print_string(PRINT_ANY, "cookie", "\tcookie %s",
390 hexstring_n2a(RTA_DATA(tb[TCA_ACT_COOKIE]),
391 strsz, b1, sizeof(b1)));
392 print_nl();
393 }
394 if (tb[TCA_ACT_FLAGS]) {
395 struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_ACT_FLAGS]);
396
397 if (flags->selector & TCA_ACT_FLAGS_NO_PERCPU_STATS)
398 print_bool(PRINT_ANY, "no_percpu", "\tno_percpu",
399 flags->value &
400 TCA_ACT_FLAGS_NO_PERCPU_STATS);
401 print_nl();
402 }
403 if (tb[TCA_ACT_HW_STATS])
404 print_hw_stats(tb[TCA_ACT_HW_STATS], false);
405
406 if (tb[TCA_ACT_USED_HW_STATS])
407 print_hw_stats(tb[TCA_ACT_USED_HW_STATS], true);
408
409 return 0;
410 }
411
412 static int
413 tc_print_action_flush(FILE *f, const struct rtattr *arg)
414 {
415
416 struct rtattr *tb[TCA_MAX + 1];
417 int err = 0;
418 struct action_util *a = NULL;
419 __u32 *delete_count = 0;
420
421 parse_rtattr_nested(tb, TCA_MAX, arg);
422
423 if (tb[TCA_KIND] == NULL) {
424 fprintf(stderr, "NULL Action!\n");
425 return -1;
426 }
427
428 a = get_action_kind(RTA_DATA(tb[TCA_KIND]));
429 if (a == NULL)
430 return err;
431
432 delete_count = RTA_DATA(tb[TCA_FCNT]);
433 fprintf(f, " %s (%d entries)\n", a->id, *delete_count);
434 tab_flush = 0;
435 return 0;
436 }
437
438 int
439 tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
440 {
441
442 int i;
443
444 if (arg == NULL)
445 return 0;
446
447 if (!tot_acts)
448 tot_acts = TCA_ACT_MAX_PRIO;
449
450 struct rtattr *tb[tot_acts + 1];
451
452 parse_rtattr_nested(tb, tot_acts, arg);
453
454 if (tab_flush && tb[0] && !tb[1])
455 return tc_print_action_flush(f, tb[0]);
456
457 open_json_array(PRINT_JSON, "actions");
458 for (i = 0; i <= tot_acts; i++) {
459 if (tb[i]) {
460 open_json_object(NULL);
461 print_nl();
462 print_uint(PRINT_ANY, "order",
463 "\taction order %u: ", i);
464 if (tc_print_one_action(f, tb[i]) < 0) {
465 print_string(PRINT_FP, NULL,
466 "Error printing action\n", NULL);
467 }
468 close_json_object();
469 }
470
471 }
472 close_json_array(PRINT_JSON, NULL);
473
474 return 0;
475 }
476
477 int print_action(struct nlmsghdr *n, void *arg)
478 {
479 FILE *fp = (FILE *)arg;
480 struct tcamsg *t = NLMSG_DATA(n);
481 int len = n->nlmsg_len;
482 __u32 *tot_acts = NULL;
483 struct rtattr *tb[TCA_ROOT_MAX+1];
484
485 len -= NLMSG_LENGTH(sizeof(*t));
486
487 if (len < 0) {
488 fprintf(stderr, "Wrong len %d\n", len);
489 return -1;
490 }
491
492 parse_rtattr(tb, TCA_ROOT_MAX, TA_RTA(t), len);
493
494 if (tb[TCA_ROOT_COUNT])
495 tot_acts = RTA_DATA(tb[TCA_ROOT_COUNT]);
496
497 open_json_object(NULL);
498 print_uint(PRINT_ANY, "total acts", "total acts %u",
499 tot_acts ? *tot_acts : 0);
500 print_nl();
501 close_json_object();
502 if (tb[TCA_ACT_TAB] == NULL) {
503 if (n->nlmsg_type != RTM_GETACTION)
504 fprintf(stderr, "print_action: NULL kind\n");
505 return -1;
506 }
507
508 if (n->nlmsg_type == RTM_DELACTION) {
509 if (n->nlmsg_flags & NLM_F_ROOT) {
510 fprintf(fp, "Flushed table ");
511 tab_flush = 1;
512 } else {
513 fprintf(fp, "Deleted action ");
514 }
515 }
516
517 if (n->nlmsg_type == RTM_NEWACTION) {
518 if ((n->nlmsg_flags & NLM_F_CREATE) &&
519 !(n->nlmsg_flags & NLM_F_REPLACE)) {
520 fprintf(fp, "Added action ");
521 } else if (n->nlmsg_flags & NLM_F_REPLACE) {
522 fprintf(fp, "Replaced action ");
523 }
524 }
525
526 open_json_object(NULL);
527 tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0);
528 close_json_object();
529
530 return 0;
531 }
532
533 static int tc_action_gd(int cmd, unsigned int flags,
534 int *argc_p, char ***argv_p)
535 {
536 char k[FILTER_NAMESZ];
537 struct action_util *a = NULL;
538 int argc = *argc_p;
539 char **argv = *argv_p;
540 int prio = 0;
541 int ret = 0;
542 __u32 i = 0;
543 struct rtattr *tail;
544 struct rtattr *tail2;
545 struct nlmsghdr *ans = NULL;
546
547 struct {
548 struct nlmsghdr n;
549 struct tcamsg t;
550 char buf[MAX_MSG];
551 } req = {
552 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
553 .n.nlmsg_flags = NLM_F_REQUEST | flags,
554 .n.nlmsg_type = cmd,
555 .t.tca_family = AF_UNSPEC,
556 };
557
558 argc -= 1;
559 argv += 1;
560
561
562 tail = addattr_nest(&req.n, MAX_MSG, TCA_ACT_TAB);
563
564 while (argc > 0) {
565 if (strcmp(*argv, "action") == 0) {
566 argc--;
567 argv++;
568 continue;
569 } else if (strcmp(*argv, "help") == 0) {
570 return -1;
571 }
572
573 strncpy(k, *argv, sizeof(k) - 1);
574 a = get_action_kind(k);
575 if (a == NULL) {
576 fprintf(stderr, "Error: non existent action: %s\n", k);
577 ret = -1;
578 goto bad_val;
579 }
580 if (strcmp(a->id, k) != 0) {
581 fprintf(stderr, "Error: non existent action: %s\n", k);
582 ret = -1;
583 goto bad_val;
584 }
585
586 argc -= 1;
587 argv += 1;
588 if (argc <= 0) {
589 fprintf(stderr,
590 "Error: no index specified action: %s\n", k);
591 ret = -1;
592 goto bad_val;
593 }
594
595 if (matches(*argv, "index") == 0) {
596 NEXT_ARG();
597 if (get_u32(&i, *argv, 10)) {
598 fprintf(stderr, "Illegal \"index\"\n");
599 ret = -1;
600 goto bad_val;
601 }
602 argc -= 1;
603 argv += 1;
604 } else {
605 fprintf(stderr,
606 "Error: no index specified action: %s\n", k);
607 ret = -1;
608 goto bad_val;
609 }
610
611 tail2 = addattr_nest(&req.n, MAX_MSG, ++prio);
612 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
613 if (i > 0)
614 addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i);
615 addattr_nest_end(&req.n, tail2);
616
617 }
618
619 addattr_nest_end(&req.n, tail);
620
621 req.n.nlmsg_seq = rth.dump = ++rth.seq;
622
623 if (rtnl_talk(&rth, &req.n, cmd == RTM_DELACTION ? NULL : &ans) < 0) {
624 fprintf(stderr, "We have an error talking to the kernel\n");
625 return 1;
626 }
627
628 if (cmd == RTM_GETACTION) {
629 new_json_obj(json);
630 ret = print_action(ans, stdout);
631 if (ret < 0) {
632 fprintf(stderr, "Dump terminated\n");
633 free(ans);
634 delete_json_obj();
635 return 1;
636 }
637 delete_json_obj();
638 }
639 free(ans);
640
641 *argc_p = argc;
642 *argv_p = argv;
643 bad_val:
644 return ret;
645 }
646
647 static int tc_action_modify(int cmd, unsigned int flags,
648 int *argc_p, char ***argv_p)
649 {
650 int argc = *argc_p;
651 char **argv = *argv_p;
652 int ret = 0;
653 struct {
654 struct nlmsghdr n;
655 struct tcamsg t;
656 char buf[MAX_MSG];
657 } req = {
658 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
659 .n.nlmsg_flags = NLM_F_REQUEST | flags,
660 .n.nlmsg_type = cmd,
661 .t.tca_family = AF_UNSPEC,
662 };
663 struct rtattr *tail = NLMSG_TAIL(&req.n);
664
665 argc -= 1;
666 argv += 1;
667 if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
668 fprintf(stderr, "Illegal \"action\"\n");
669 return -1;
670 }
671 tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
672
673 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
674 fprintf(stderr, "We have an error talking to the kernel\n");
675 ret = -1;
676 }
677
678 *argc_p = argc;
679 *argv_p = argv;
680
681 return ret;
682 }
683
684 static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event)
685 {
686 struct rtattr *tail, *tail2, *tail3, *tail4;
687 int ret = 0, prio = 0, msg_size = 0;
688 struct action_util *a = NULL;
689 struct nla_bitfield32 flag_select = { 0 };
690 char **argv = *argv_p;
691 __u32 msec_since = 0;
692 int argc = *argc_p;
693 char k[FILTER_NAMESZ];
694 struct {
695 struct nlmsghdr n;
696 struct tcamsg t;
697 char buf[MAX_MSG];
698 } req = {
699 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
700 .t.tca_family = AF_UNSPEC,
701 };
702
703 tail = addattr_nest(&req.n, MAX_MSG, TCA_ACT_TAB);
704 tail2 = NLMSG_TAIL(&req.n);
705
706 strncpy(k, *argv, sizeof(k) - 1);
707 #ifdef CONFIG_GACT
708 if (!gact_ld)
709 get_action_kind("gact");
710
711 #endif
712 a = get_action_kind(k);
713 if (a == NULL) {
714 fprintf(stderr, "bad action %s\n", k);
715 goto bad_val;
716 }
717 if (strcmp(a->id, k) != 0) {
718 fprintf(stderr, "bad action %s\n", k);
719 goto bad_val;
720 }
721 strncpy(k, *argv, sizeof(k) - 1);
722
723 argc -= 1;
724 argv += 1;
725
726 if (argc && (strcmp(*argv, "since") == 0)) {
727 NEXT_ARG();
728 if (get_u32(&msec_since, *argv, 0))
729 invarg("dump time \"since\" is invalid", *argv);
730 }
731
732 addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0);
733 addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
734 tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2;
735 addattr_nest_end(&req.n, tail);
736
737 tail3 = NLMSG_TAIL(&req.n);
738 flag_select.value |= TCA_FLAG_LARGE_DUMP_ON;
739 flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON;
740 addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select,
741 sizeof(struct nla_bitfield32));
742 tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3;
743 if (msec_since) {
744 tail4 = NLMSG_TAIL(&req.n);
745 addattr32(&req.n, MAX_MSG, TCA_ROOT_TIME_DELTA, msec_since);
746 tail4->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail4;
747 }
748 msg_size = NLMSG_ALIGN(req.n.nlmsg_len)
749 - NLMSG_ALIGN(sizeof(struct nlmsghdr));
750
751 if (event == RTM_GETACTION) {
752 if (rtnl_dump_request(&rth, event,
753 (void *)&req.t, msg_size) < 0) {
754 perror("Cannot send dump request");
755 return 1;
756 }
757 new_json_obj(json);
758 ret = rtnl_dump_filter(&rth, print_action, stdout);
759 delete_json_obj();
760 }
761
762 if (event == RTM_DELACTION) {
763 req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len);
764 req.n.nlmsg_type = RTM_DELACTION;
765 req.n.nlmsg_flags |= NLM_F_ROOT;
766 req.n.nlmsg_flags |= NLM_F_REQUEST;
767 if (rtnl_talk(&rth, &req.n, NULL) < 0) {
768 fprintf(stderr, "We have an error flushing\n");
769 return 1;
770 }
771
772 }
773
774 bad_val:
775
776 *argc_p = argc;
777 *argv_p = argv;
778 return ret;
779 }
780
781 int do_action(int argc, char **argv)
782 {
783
784 int ret = 0;
785
786 while (argc > 0) {
787
788 if (matches(*argv, "add") == 0) {
789 ret = tc_action_modify(RTM_NEWACTION,
790 NLM_F_EXCL | NLM_F_CREATE,
791 &argc, &argv);
792 } else if (matches(*argv, "change") == 0 ||
793 matches(*argv, "replace") == 0) {
794 ret = tc_action_modify(RTM_NEWACTION,
795 NLM_F_CREATE | NLM_F_REPLACE,
796 &argc, &argv);
797 } else if (matches(*argv, "delete") == 0) {
798 argc -= 1;
799 argv += 1;
800 ret = tc_action_gd(RTM_DELACTION, 0, &argc, &argv);
801 } else if (matches(*argv, "get") == 0) {
802 argc -= 1;
803 argv += 1;
804 ret = tc_action_gd(RTM_GETACTION, 0, &argc, &argv);
805 } else if (matches(*argv, "list") == 0 ||
806 matches(*argv, "show") == 0 ||
807 matches(*argv, "lst") == 0) {
808 if (argc <= 2) {
809 act_usage();
810 return -1;
811 }
812
813 argc -= 2;
814 argv += 2;
815 return tc_act_list_or_flush(&argc, &argv,
816 RTM_GETACTION);
817 } else if (matches(*argv, "flush") == 0) {
818 if (argc <= 2) {
819 act_usage();
820 return -1;
821 }
822
823 argc -= 2;
824 argv += 2;
825 return tc_act_list_or_flush(&argc, &argv,
826 RTM_DELACTION);
827 } else if (matches(*argv, "help") == 0) {
828 act_usage();
829 return -1;
830 } else {
831 fprintf(stderr,
832 "Command \"%s\" is unknown, try \"tc actions help\".\n",
833 *argv);
834 return -1;
835 }
836
837 if (ret < 0)
838 return -1;
839 }
840
841 return 0;
842 }