iproute2/tc/tc_filter.c
<<
>>
Prefs
   1/*
   2 * tc_filter.c          "tc filter".
   3 *
   4 *              This program is free software; you can redistribute 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:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  10 *
  11 */
  12
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <unistd.h>
  16#include <fcntl.h>
  17#include <sys/socket.h>
  18#include <netinet/in.h>
  19#include <arpa/inet.h>
  20#include <string.h>
  21#include <linux/if_ether.h>
  22
  23#include "rt_names.h"
  24#include "utils.h"
  25#include "tc_util.h"
  26#include "tc_common.h"
  27
  28static void usage(void)
  29{
  30        fprintf(stderr,
  31                "Usage: tc filter [ add | del | change | replace | show ] [ dev STRING ]\n"
  32                "       tc filter [ add | del | change | replace | show ] [ block BLOCK_INDEX ]\n"
  33                "       tc filter get dev STRING parent CLASSID protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n"
  34                "       tc filter get block BLOCK_INDEX protocol PROTO handle FILTERID pref PRIO FILTER_TYPE\n"
  35                "       [ pref PRIO ] protocol PROTO [ chain CHAIN_INDEX ]\n"
  36                "       [ estimator INTERVAL TIME_CONSTANT ]\n"
  37                "       [ root | ingress | egress | parent CLASSID ]\n"
  38                "       [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"
  39                "\n"
  40                "       tc filter show [ dev STRING ] [ root | ingress | egress | parent CLASSID ]\n"
  41                "       tc filter show [ block BLOCK_INDEX ]\n"
  42                "Where:\n"
  43                "FILTER_TYPE := { rsvp | u32 | bpf | fw | route | etc. }\n"
  44                "FILTERID := ... format depends on classifier, see there\n"
  45                "OPTIONS := ... try tc filter add <desired FILTER_KIND> help\n");
  46}
  47
  48static void chain_usage(void)
  49{
  50        fprintf(stderr,
  51                "Usage: tc chain [ add | del | get | show ] [ dev STRING ]\n"
  52                "       tc chain [ add | del | get | show ] [ block BLOCK_INDEX ] ]\n");
  53}
  54
  55struct tc_filter_req {
  56        struct nlmsghdr         n;
  57        struct tcmsg            t;
  58        char                    buf[MAX_MSG];
  59};
  60
  61static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
  62{
  63        struct {
  64                struct nlmsghdr n;
  65                struct tcmsg            t;
  66                char                    buf[MAX_MSG];
  67        } req = {
  68                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
  69                .n.nlmsg_flags = NLM_F_REQUEST | flags,
  70                .n.nlmsg_type = cmd,
  71                .t.tcm_family = AF_UNSPEC,
  72        };
  73        struct filter_util *q = NULL;
  74        __u32 prio = 0;
  75        __u32 protocol = 0;
  76        int protocol_set = 0;
  77        __u32 block_index = 0;
  78        __u32 chain_index;
  79        int chain_index_set = 0;
  80        char *fhandle = NULL;
  81        char  d[IFNAMSIZ] = {};
  82        char  k[FILTER_NAMESZ] = {};
  83        struct tc_estimator est = {};
  84
  85        if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)
  86                protocol = htons(ETH_P_ALL);
  87
  88        while (argc > 0) {
  89                if (strcmp(*argv, "dev") == 0) {
  90                        NEXT_ARG();
  91                        if (d[0])
  92                                duparg("dev", *argv);
  93                        if (block_index) {
  94                                fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n");
  95                                return -1;
  96                        }
  97                        strncpy(d, *argv, sizeof(d)-1);
  98                } else if (matches(*argv, "block") == 0) {
  99                        NEXT_ARG();
 100                        if (block_index)
 101                                duparg("block", *argv);
 102                        if (d[0]) {
 103                                fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n");
 104                                return -1;
 105                        }
 106                        if (get_u32(&block_index, *argv, 0) || !block_index)
 107                                invarg("invalid block index value", *argv);
 108                } else if (strcmp(*argv, "root") == 0) {
 109                        if (req.t.tcm_parent) {
 110                                fprintf(stderr,
 111                                        "Error: \"root\" is duplicate parent ID\n");
 112                                return -1;
 113                        }
 114                        req.t.tcm_parent = TC_H_ROOT;
 115                } else if (strcmp(*argv, "ingress") == 0) {
 116                        if (req.t.tcm_parent) {
 117                                fprintf(stderr,
 118                                        "Error: \"ingress\" is duplicate parent ID\n");
 119                                return -1;
 120                        }
 121                        req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
 122                                                     TC_H_MIN_INGRESS);
 123                } else if (strcmp(*argv, "egress") == 0) {
 124                        if (req.t.tcm_parent) {
 125                                fprintf(stderr,
 126                                        "Error: \"egress\" is duplicate parent ID\n");
 127                                return -1;
 128                        }
 129                        req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
 130                                                     TC_H_MIN_EGRESS);
 131                } else if (strcmp(*argv, "parent") == 0) {
 132                        __u32 handle;
 133
 134                        NEXT_ARG();
 135                        if (req.t.tcm_parent)
 136                                duparg("parent", *argv);
 137                        if (get_tc_classid(&handle, *argv))
 138                                invarg("Invalid parent ID", *argv);
 139                        req.t.tcm_parent = handle;
 140                } else if (strcmp(*argv, "handle") == 0) {
 141                        NEXT_ARG();
 142                        if (fhandle)
 143                                duparg("handle", *argv);
 144                        fhandle = *argv;
 145                } else if (matches(*argv, "preference") == 0 ||
 146                           matches(*argv, "priority") == 0) {
 147                        NEXT_ARG();
 148                        if (prio)
 149                                duparg("priority", *argv);
 150                        if (get_u32(&prio, *argv, 0) || prio > 0xFFFF)
 151                                invarg("invalid priority value", *argv);
 152                } else if (matches(*argv, "protocol") == 0) {
 153                        __u16 id;
 154
 155                        NEXT_ARG();
 156                        if (protocol_set)
 157                                duparg("protocol", *argv);
 158                        if (ll_proto_a2n(&id, *argv))
 159                                invarg("invalid protocol", *argv);
 160                        protocol = id;
 161                        protocol_set = 1;
 162                } else if (matches(*argv, "chain") == 0) {
 163                        NEXT_ARG();
 164                        if (chain_index_set)
 165                                duparg("chain", *argv);
 166                        if (get_u32(&chain_index, *argv, 0))
 167                                invarg("invalid chain index value", *argv);
 168                        chain_index_set = 1;
 169                } else if (matches(*argv, "estimator") == 0) {
 170                        if (parse_estimator(&argc, &argv, &est) < 0)
 171                                return -1;
 172                } else if (matches(*argv, "help") == 0) {
 173                        usage();
 174                        return 0;
 175                } else {
 176                        strncpy(k, *argv, sizeof(k)-1);
 177
 178                        q = get_filter_kind(k);
 179                        argc--; argv++;
 180                        break;
 181                }
 182
 183                argc--; argv++;
 184        }
 185
 186        req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
 187
 188        if (chain_index_set)
 189                addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
 190
 191        if (k[0])
 192                addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
 193
 194        if (d[0])  {
 195                ll_init_map(&rth);
 196
 197                req.t.tcm_ifindex = ll_name_to_index(d);
 198                if (req.t.tcm_ifindex == 0) {
 199                        fprintf(stderr, "Cannot find device \"%s\"\n", d);
 200                        return 1;
 201                }
 202        } else if (block_index) {
 203                req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
 204                req.t.tcm_block_index = block_index;
 205        }
 206
 207        if (q) {
 208                if (q->parse_fopt(q, fhandle, argc, argv, &req.n))
 209                        return 1;
 210        } else {
 211                if (fhandle) {
 212                        fprintf(stderr,
 213                                "Must specify filter type when using \"handle\"\n");
 214                        return -1;
 215                }
 216                if (argc) {
 217                        if (matches(*argv, "help") == 0)
 218                                usage();
 219                        fprintf(stderr,
 220                                "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n",
 221                                *argv);
 222                        return -1;
 223                }
 224        }
 225
 226        if (est.ewma_log)
 227                addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
 228
 229        if (rtnl_talk(&rth, &req.n, NULL) < 0) {
 230                fprintf(stderr, "We have an error talking to the kernel\n");
 231                return 2;
 232        }
 233
 234        return 0;
 235}
 236
 237static __u32 filter_parent;
 238static int filter_ifindex;
 239static __u32 filter_prio;
 240static __u32 filter_protocol;
 241static __u32 filter_chain_index;
 242static int filter_chain_index_set;
 243static __u32 filter_block_index;
 244__u16 f_proto;
 245
 246int print_filter(struct nlmsghdr *n, void *arg)
 247{
 248        FILE *fp = (FILE *)arg;
 249        struct tcmsg *t = NLMSG_DATA(n);
 250        int len = n->nlmsg_len;
 251        struct rtattr *tb[TCA_MAX+1];
 252        struct filter_util *q;
 253        char abuf[256];
 254
 255        if (n->nlmsg_type != RTM_NEWTFILTER &&
 256            n->nlmsg_type != RTM_GETTFILTER &&
 257            n->nlmsg_type != RTM_DELTFILTER &&
 258            n->nlmsg_type != RTM_NEWCHAIN &&
 259            n->nlmsg_type != RTM_GETCHAIN &&
 260            n->nlmsg_type != RTM_DELCHAIN) {
 261                fprintf(stderr, "Not a filter(cmd %d)\n", n->nlmsg_type);
 262                return 0;
 263        }
 264        len -= NLMSG_LENGTH(sizeof(*t));
 265        if (len < 0) {
 266                fprintf(stderr, "Wrong len %d\n", len);
 267                return -1;
 268        }
 269
 270        parse_rtattr_flags(tb, TCA_MAX, TCA_RTA(t), len, NLA_F_NESTED);
 271
 272        if (tb[TCA_KIND] == NULL && (n->nlmsg_type == RTM_NEWTFILTER ||
 273                                     n->nlmsg_type == RTM_GETTFILTER ||
 274                                     n->nlmsg_type == RTM_DELTFILTER)) {
 275                fprintf(stderr, "print_filter: NULL kind\n");
 276                return -1;
 277        }
 278
 279        open_json_object(NULL);
 280
 281        if (n->nlmsg_type == RTM_DELTFILTER || n->nlmsg_type == RTM_DELCHAIN)
 282                print_bool(PRINT_ANY, "deleted", "deleted ", true);
 283
 284        if ((n->nlmsg_type == RTM_NEWTFILTER ||
 285             n->nlmsg_type == RTM_NEWCHAIN) &&
 286                        (n->nlmsg_flags & NLM_F_CREATE) &&
 287                        !(n->nlmsg_flags & NLM_F_EXCL))
 288                print_bool(PRINT_ANY, "replaced", "replaced ", true);
 289
 290        if ((n->nlmsg_type == RTM_NEWTFILTER ||
 291             n->nlmsg_type == RTM_NEWCHAIN) &&
 292                        (n->nlmsg_flags & NLM_F_CREATE) &&
 293                        (n->nlmsg_flags & NLM_F_EXCL))
 294                print_bool(PRINT_ANY, "added", "added ", true);
 295
 296        if (n->nlmsg_type == RTM_NEWTFILTER ||
 297            n->nlmsg_type == RTM_GETTFILTER ||
 298            n->nlmsg_type == RTM_DELTFILTER)
 299                print_string(PRINT_FP, NULL, "filter ", NULL);
 300        else
 301                print_string(PRINT_FP, NULL, "chain ", NULL);
 302        if (t->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
 303                if (!filter_block_index ||
 304                    filter_block_index != t->tcm_block_index)
 305                        print_uint(PRINT_ANY, "block", "block %u ",
 306                                   t->tcm_block_index);
 307        } else {
 308                if (!filter_ifindex || filter_ifindex != t->tcm_ifindex)
 309                        print_devname(PRINT_ANY, t->tcm_ifindex);
 310
 311                if (!filter_parent || filter_parent != t->tcm_parent) {
 312                        if (t->tcm_parent == TC_H_ROOT)
 313                                print_bool(PRINT_ANY, "root", "root ", true);
 314                        else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS))
 315                                print_bool(PRINT_ANY, "ingress", "ingress ", true);
 316                        else if (t->tcm_parent == TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS))
 317                                print_bool(PRINT_ANY, "egress", "egress ", true);
 318                        else {
 319                                print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
 320                                print_string(PRINT_ANY, "parent", "parent %s ", abuf);
 321                        }
 322                }
 323        }
 324
 325        if (t->tcm_info && (n->nlmsg_type == RTM_NEWTFILTER ||
 326                            n->nlmsg_type == RTM_DELTFILTER ||
 327                            n->nlmsg_type == RTM_GETTFILTER)) {
 328                f_proto = TC_H_MIN(t->tcm_info);
 329                __u32 prio = TC_H_MAJ(t->tcm_info)>>16;
 330
 331                if (!filter_protocol || filter_protocol != f_proto) {
 332                        if (f_proto) {
 333                                SPRINT_BUF(b1);
 334                                print_string(PRINT_ANY, "protocol",
 335                                             "protocol %s ",
 336                                             ll_proto_n2a(f_proto, b1, sizeof(b1)));
 337                        }
 338                }
 339                if (!filter_prio || filter_prio != prio) {
 340                        if (prio)
 341                                print_uint(PRINT_ANY, "pref", "pref %u ", prio);
 342                }
 343        }
 344        if (tb[TCA_KIND])
 345                print_string(PRINT_ANY, "kind", "%s ", rta_getattr_str(tb[TCA_KIND]));
 346
 347        if (tb[TCA_CHAIN]) {
 348                __u32 chain_index = rta_getattr_u32(tb[TCA_CHAIN]);
 349
 350                if (!filter_chain_index_set ||
 351                    filter_chain_index != chain_index)
 352                        print_uint(PRINT_ANY, "chain", "chain %u ",
 353                                   chain_index);
 354        }
 355
 356        if (tb[TCA_KIND]) {
 357                q = get_filter_kind(RTA_DATA(tb[TCA_KIND]));
 358                if (tb[TCA_OPTIONS]) {
 359                        open_json_object("options");
 360                        if (q)
 361                                q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle);
 362                        else
 363                                fprintf(stderr, "cannot parse option parameters\n");
 364                        close_json_object();
 365                }
 366        }
 367        print_nl();
 368
 369        if (show_stats && (tb[TCA_STATS] || tb[TCA_STATS2])) {
 370                print_tcstats_attr(fp, tb, " ", NULL);
 371                print_nl();
 372        }
 373
 374        close_json_object();
 375        fflush(fp);
 376        return 0;
 377}
 378
 379static int tc_filter_get(int cmd, unsigned int flags, int argc, char **argv)
 380{
 381        struct {
 382                struct nlmsghdr n;
 383                struct tcmsg            t;
 384                char                    buf[MAX_MSG];
 385        } req = {
 386                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 387                /* NLM_F_ECHO is for backward compatibility. old kernels never
 388                 * respond without it and newer kernels will ignore it.
 389                 * In old kernels there is a side effect:
 390                 * In addition to a response to the GET you will receive an
 391                 * event (if you do tc mon).
 392                 */
 393                .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ECHO | flags,
 394                .n.nlmsg_type = cmd,
 395                .t.tcm_parent = TC_H_UNSPEC,
 396                .t.tcm_family = AF_UNSPEC,
 397        };
 398        struct nlmsghdr *answer;
 399        struct filter_util *q = NULL;
 400        __u32 prio = 0;
 401        __u32 protocol = 0;
 402        int protocol_set = 0;
 403        __u32 chain_index;
 404        int chain_index_set = 0;
 405        __u32 block_index = 0;
 406        __u32 parent_handle = 0;
 407        char *fhandle = NULL;
 408        char  d[IFNAMSIZ] = {};
 409        char  k[FILTER_NAMESZ] = {};
 410
 411        while (argc > 0) {
 412                if (strcmp(*argv, "dev") == 0) {
 413                        NEXT_ARG();
 414                        if (d[0])
 415                                duparg("dev", *argv);
 416                        if (block_index) {
 417                                fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n");
 418                                return -1;
 419                        }
 420                        strncpy(d, *argv, sizeof(d)-1);
 421                } else if (matches(*argv, "block") == 0) {
 422                        NEXT_ARG();
 423                        if (block_index)
 424                                duparg("block", *argv);
 425                        if (d[0]) {
 426                                fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exclusive\n");
 427                                return -1;
 428                        }
 429                        if (get_u32(&block_index, *argv, 0) || !block_index)
 430                                invarg("invalid block index value", *argv);
 431                } else if (strcmp(*argv, "root") == 0) {
 432                        if (req.t.tcm_parent) {
 433                                fprintf(stderr,
 434                                        "Error: \"root\" is duplicate parent ID\n");
 435                                return -1;
 436                        }
 437                        req.t.tcm_parent = TC_H_ROOT;
 438                } else if (strcmp(*argv, "ingress") == 0) {
 439                        if (req.t.tcm_parent) {
 440                                fprintf(stderr,
 441                                        "Error: \"ingress\" is duplicate parent ID\n");
 442                                return -1;
 443                        }
 444                        req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
 445                                                     TC_H_MIN_INGRESS);
 446                } else if (strcmp(*argv, "egress") == 0) {
 447                        if (req.t.tcm_parent) {
 448                                fprintf(stderr,
 449                                        "Error: \"egress\" is duplicate parent ID\n");
 450                                return -1;
 451                        }
 452                        req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
 453                                                     TC_H_MIN_EGRESS);
 454                } else if (strcmp(*argv, "parent") == 0) {
 455
 456                        NEXT_ARG();
 457                        if (req.t.tcm_parent)
 458                                duparg("parent", *argv);
 459                        if (get_tc_classid(&parent_handle, *argv))
 460                                invarg("Invalid parent ID", *argv);
 461                        req.t.tcm_parent = parent_handle;
 462                } else if (strcmp(*argv, "handle") == 0) {
 463                        NEXT_ARG();
 464                        if (fhandle)
 465                                duparg("handle", *argv);
 466                        fhandle = *argv;
 467                } else if (matches(*argv, "preference") == 0 ||
 468                           matches(*argv, "priority") == 0) {
 469                        NEXT_ARG();
 470                        if (prio)
 471                                duparg("priority", *argv);
 472                        if (get_u32(&prio, *argv, 0) || prio > 0xFFFF)
 473                                invarg("invalid priority value", *argv);
 474                } else if (matches(*argv, "protocol") == 0) {
 475                        __u16 id;
 476
 477                        NEXT_ARG();
 478                        if (protocol_set)
 479                                duparg("protocol", *argv);
 480                        if (ll_proto_a2n(&id, *argv))
 481                                invarg("invalid protocol", *argv);
 482                        protocol = id;
 483                        protocol_set = 1;
 484                } else if (matches(*argv, "chain") == 0) {
 485                        NEXT_ARG();
 486                        if (chain_index_set)
 487                                duparg("chain", *argv);
 488                        if (get_u32(&chain_index, *argv, 0))
 489                                invarg("invalid chain index value", *argv);
 490                        chain_index_set = 1;
 491                } else if (matches(*argv, "help") == 0) {
 492                        usage();
 493                        return 0;
 494                } else {
 495                        if (!**argv)
 496                                invarg("invalid filter name", *argv);
 497
 498                        strncpy(k, *argv, sizeof(k)-1);
 499
 500                        q = get_filter_kind(k);
 501                        argc--; argv++;
 502                        break;
 503                }
 504
 505                argc--; argv++;
 506        }
 507
 508        if (cmd == RTM_GETTFILTER) {
 509                if (!protocol_set) {
 510                        fprintf(stderr, "Must specify filter protocol\n");
 511                        return -1;
 512                }
 513
 514                if (!prio) {
 515                        fprintf(stderr, "Must specify filter priority\n");
 516                        return -1;
 517                }
 518
 519                req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
 520        }
 521
 522        if (chain_index_set)
 523                addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
 524
 525        if (req.t.tcm_parent == TC_H_UNSPEC) {
 526                fprintf(stderr, "Must specify filter parent\n");
 527                return -1;
 528        }
 529
 530        if (cmd == RTM_GETTFILTER) {
 531                if (k[0])
 532                        addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
 533                else {
 534                        fprintf(stderr, "Must specify filter type\n");
 535                        return -1;
 536                }
 537        }
 538
 539        if (d[0])  {
 540                ll_init_map(&rth);
 541
 542                req.t.tcm_ifindex = ll_name_to_index(d);
 543                if (!req.t.tcm_ifindex)
 544                        return -nodev(d);
 545                filter_ifindex = req.t.tcm_ifindex;
 546        } else if (block_index) {
 547                req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
 548                req.t.tcm_block_index = block_index;
 549                filter_block_index = block_index;
 550        } else {
 551                fprintf(stderr, "Must specify netdevice \"dev\" or block index \"block\"\n");
 552                return -1;
 553        }
 554
 555        if (cmd == RTM_GETTFILTER &&
 556            q->parse_fopt(q, fhandle, argc, argv, &req.n))
 557                return 1;
 558
 559        if (!fhandle && cmd == RTM_GETTFILTER) {
 560                fprintf(stderr, "Must specify filter \"handle\"\n");
 561                return -1;
 562        }
 563
 564        if (argc) {
 565                if (matches(*argv, "help") == 0)
 566                        usage();
 567                fprintf(stderr,
 568                        "Garbage instead of arguments \"%s ...\". Try \"tc filter help\".\n",
 569                        *argv);
 570                return -1;
 571        }
 572
 573        if (rtnl_talk(&rth, &req.n, &answer) < 0) {
 574                fprintf(stderr, "We have an error talking to the kernel\n");
 575                return 2;
 576        }
 577
 578        new_json_obj(json);
 579        print_filter(answer, (void *)stdout);
 580        delete_json_obj();
 581
 582        free(answer);
 583        return 0;
 584}
 585
 586static int tc_filter_list(int cmd, int argc, char **argv)
 587{
 588        struct {
 589                struct nlmsghdr n;
 590                struct tcmsg t;
 591                char buf[MAX_MSG];
 592        } req = {
 593                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 594                .n.nlmsg_type = cmd,
 595                .t.tcm_parent = TC_H_UNSPEC,
 596                .t.tcm_family = AF_UNSPEC,
 597        };
 598        char d[IFNAMSIZ] = {};
 599        __u32 prio = 0;
 600        __u32 protocol = 0;
 601        __u32 chain_index;
 602        __u32 block_index = 0;
 603        char *fhandle = NULL;
 604
 605        while (argc > 0) {
 606                if (strcmp(*argv, "dev") == 0) {
 607                        NEXT_ARG();
 608                        if (d[0])
 609                                duparg("dev", *argv);
 610                        if (block_index) {
 611                                fprintf(stderr, "Error: \"dev\" cannot be used in the same time as \"block\"\n");
 612                                return -1;
 613                        }
 614                        strncpy(d, *argv, sizeof(d)-1);
 615                } else if (matches(*argv, "block") == 0) {
 616                        NEXT_ARG();
 617                        if (block_index)
 618                                duparg("block", *argv);
 619                        if (d[0]) {
 620                                fprintf(stderr, "Error: \"block\" cannot be used in the same time as \"dev\"\n");
 621                                return -1;
 622                        }
 623                        if (get_u32(&block_index, *argv, 0) || !block_index)
 624                                invarg("invalid block index value", *argv);
 625                } else if (strcmp(*argv, "root") == 0) {
 626                        if (req.t.tcm_parent) {
 627                                fprintf(stderr,
 628                                        "Error: \"root\" is duplicate parent ID\n");
 629                                return -1;
 630                        }
 631                        filter_parent = req.t.tcm_parent = TC_H_ROOT;
 632                } else if (strcmp(*argv, "ingress") == 0) {
 633                        if (req.t.tcm_parent) {
 634                                fprintf(stderr,
 635                                        "Error: \"ingress\" is duplicate parent ID\n");
 636                                return -1;
 637                        }
 638                        filter_parent = TC_H_MAKE(TC_H_CLSACT,
 639                                                  TC_H_MIN_INGRESS);
 640                        req.t.tcm_parent = filter_parent;
 641                } else if (strcmp(*argv, "egress") == 0) {
 642                        if (req.t.tcm_parent) {
 643                                fprintf(stderr,
 644                                        "Error: \"egress\" is duplicate parent ID\n");
 645                                return -1;
 646                        }
 647                        filter_parent = TC_H_MAKE(TC_H_CLSACT,
 648                                                  TC_H_MIN_EGRESS);
 649                        req.t.tcm_parent = filter_parent;
 650                } else if (strcmp(*argv, "parent") == 0) {
 651                        __u32 handle;
 652
 653                        NEXT_ARG();
 654                        if (req.t.tcm_parent)
 655                                duparg("parent", *argv);
 656                        if (get_tc_classid(&handle, *argv))
 657                                invarg("invalid parent ID", *argv);
 658                        filter_parent = req.t.tcm_parent = handle;
 659                } else if (strcmp(*argv, "handle") == 0) {
 660                        NEXT_ARG();
 661                        if (fhandle)
 662                                duparg("handle", *argv);
 663                        fhandle = *argv;
 664                } else if (matches(*argv, "preference") == 0 ||
 665                           matches(*argv, "priority") == 0) {
 666                        NEXT_ARG();
 667                        if (prio)
 668                                duparg("priority", *argv);
 669                        if (get_u32(&prio, *argv, 0))
 670                                invarg("invalid preference", *argv);
 671                        filter_prio = prio;
 672                } else if (matches(*argv, "protocol") == 0) {
 673                        __u16 res;
 674
 675                        NEXT_ARG();
 676                        if (protocol)
 677                                duparg("protocol", *argv);
 678                        if (ll_proto_a2n(&res, *argv))
 679                                invarg("invalid protocol", *argv);
 680                        protocol = res;
 681                        filter_protocol = protocol;
 682                } else if (matches(*argv, "chain") == 0) {
 683                        NEXT_ARG();
 684                        if (filter_chain_index_set)
 685                                duparg("chain", *argv);
 686                        if (get_u32(&chain_index, *argv, 0))
 687                                invarg("invalid chain index value", *argv);
 688                        filter_chain_index_set = 1;
 689                        filter_chain_index = chain_index;
 690                } else if (matches(*argv, "help") == 0) {
 691                        usage();
 692                } else {
 693                        fprintf(stderr,
 694                                " What is \"%s\"? Try \"tc filter help\"\n",
 695                                *argv);
 696                        return -1;
 697                }
 698
 699                argc--; argv++;
 700        }
 701
 702        req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
 703
 704        ll_init_map(&rth);
 705
 706        if (d[0]) {
 707                req.t.tcm_ifindex = ll_name_to_index(d);
 708                if (!req.t.tcm_ifindex)
 709                        return -nodev(d);
 710                filter_ifindex = req.t.tcm_ifindex;
 711        } else if (block_index) {
 712                if (!tc_qdisc_block_exists(block_index)) {
 713                        fprintf(stderr, "Cannot find block \"%u\"\n", block_index);
 714                        return 1;
 715                }
 716                req.t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
 717                req.t.tcm_block_index = block_index;
 718                filter_block_index = block_index;
 719        }
 720
 721        if (filter_chain_index_set)
 722                addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
 723
 724        if (brief) {
 725                struct nla_bitfield32 flags = {
 726                        .value = TCA_DUMP_FLAGS_TERSE,
 727                        .selector = TCA_DUMP_FLAGS_TERSE
 728                };
 729
 730                addattr_l(&req.n, MAX_MSG, TCA_DUMP_FLAGS, &flags, sizeof(flags));
 731        }
 732
 733        if (rtnl_dump_request_n(&rth, &req.n) < 0) {
 734                perror("Cannot send dump request");
 735                return 1;
 736        }
 737
 738        new_json_obj(json);
 739        if (rtnl_dump_filter(&rth, print_filter, stdout) < 0) {
 740                fprintf(stderr, "Dump terminated\n");
 741                return 1;
 742        }
 743        delete_json_obj();
 744
 745        return 0;
 746}
 747
 748int do_filter(int argc, char **argv)
 749{
 750        if (argc < 1)
 751                return tc_filter_list(RTM_GETTFILTER, 0, NULL);
 752        if (matches(*argv, "add") == 0)
 753                return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE,
 754                                        argc-1, argv+1);
 755        if (matches(*argv, "change") == 0)
 756                return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1);
 757        if (matches(*argv, "replace") == 0)
 758                return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1,
 759                                        argv+1);
 760        if (matches(*argv, "delete") == 0)
 761                return tc_filter_modify(RTM_DELTFILTER, 0,  argc-1, argv+1);
 762        if (matches(*argv, "get") == 0)
 763                return tc_filter_get(RTM_GETTFILTER, 0,  argc-1, argv+1);
 764        if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
 765            || matches(*argv, "lst") == 0)
 766                return tc_filter_list(RTM_GETTFILTER, argc-1, argv+1);
 767        if (matches(*argv, "help") == 0) {
 768                usage();
 769                return 0;
 770        }
 771        fprintf(stderr, "Command \"%s\" is unknown, try \"tc filter help\".\n",
 772                *argv);
 773        return -1;
 774}
 775
 776int do_chain(int argc, char **argv)
 777{
 778        if (argc < 1)
 779                return tc_filter_list(RTM_GETCHAIN, 0, NULL);
 780        if (matches(*argv, "add") == 0) {
 781                return tc_filter_modify(RTM_NEWCHAIN, NLM_F_EXCL | NLM_F_CREATE,
 782                                        argc - 1, argv + 1);
 783        } else if (matches(*argv, "delete") == 0) {
 784                return tc_filter_modify(RTM_DELCHAIN, 0,
 785                                        argc - 1, argv + 1);
 786        } else if (matches(*argv, "get") == 0) {
 787                return tc_filter_get(RTM_GETCHAIN, 0,
 788                                     argc - 1, argv + 1);
 789        } else if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 ||
 790                   matches(*argv, "lst") == 0) {
 791                return tc_filter_list(RTM_GETCHAIN, argc - 1, argv + 1);
 792        } else if (matches(*argv, "help") == 0) {
 793                chain_usage();
 794                return 0;
 795        }
 796        fprintf(stderr, "Command \"%s\" is unknown, try \"tc chain help\".\n",
 797                *argv);
 798        return -1;
 799}
 800