iproute2/tc/m_action.c
<<
>>
Prefs
   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
  31static struct action_util *action_list;
  32#ifdef CONFIG_GACT
  33static int gact_ld; /* f*ckin backward compatibility */
  34#endif
  35static int tab_flush;
  36
  37static 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
  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 }, /* no bit set */
 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; /* 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 {
 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        /* 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
 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