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