iproute2/tc/tc_qdisc.c
<<
>>
Prefs
   1/*
   2 * tc_qdisc.c           "tc qdisc".
   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 *              J Hadi Salim: Extension to ingress
  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 <math.h>
  22#include <malloc.h>
  23
  24#include "utils.h"
  25#include "tc_util.h"
  26#include "tc_common.h"
  27
  28static int usage(void)
  29{
  30        fprintf(stderr,
  31                "Usage: tc qdisc [ add | del | replace | change | show ] dev STRING\n"
  32                "       [ handle QHANDLE ] [ root | ingress | clsact | parent CLASSID ]\n"
  33                "       [ estimator INTERVAL TIME_CONSTANT ]\n"
  34                "       [ stab [ help | STAB_OPTIONS] ]\n"
  35                "       [ ingress_block BLOCK_INDEX ] [ egress_block BLOCK_INDEX ]\n"
  36                "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
  37                "\n"
  38                "       tc qdisc { show | list } [ dev STRING ] [ QDISC_ID ] [ invisible ]\n"
  39                "Where:\n"
  40                "QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n"
  41                "OPTIONS := ... try tc qdisc add <desired QDISC_KIND> help\n"
  42                "STAB_OPTIONS := ... try tc qdisc add stab help\n"
  43                "QDISC_ID := { root | ingress | handle QHANDLE | parent CLASSID }\n");
  44        return -1;
  45}
  46
  47static int tc_qdisc_modify(int cmd, unsigned int flags, int argc, char **argv)
  48{
  49        struct qdisc_util *q = NULL;
  50        struct tc_estimator est = {};
  51        struct {
  52                struct tc_sizespec      szopts;
  53                __u16                   *data;
  54        } stab = {};
  55        char  d[IFNAMSIZ] = {};
  56        char  k[FILTER_NAMESZ] = {};
  57        struct {
  58                struct nlmsghdr n;
  59                struct tcmsg            t;
  60                char                    buf[TCA_BUF_MAX];
  61        } req = {
  62                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
  63                .n.nlmsg_flags = NLM_F_REQUEST | flags,
  64                .n.nlmsg_type = cmd,
  65                .t.tcm_family = AF_UNSPEC,
  66        };
  67        __u32 ingress_block = 0;
  68        __u32 egress_block = 0;
  69
  70        while (argc > 0) {
  71                if (strcmp(*argv, "dev") == 0) {
  72                        NEXT_ARG();
  73                        if (d[0])
  74                                duparg("dev", *argv);
  75                        strncpy(d, *argv, sizeof(d)-1);
  76                } else if (strcmp(*argv, "handle") == 0) {
  77                        __u32 handle;
  78
  79                        if (req.t.tcm_handle)
  80                                duparg("handle", *argv);
  81                        NEXT_ARG();
  82                        if (get_qdisc_handle(&handle, *argv))
  83                                invarg("invalid qdisc ID", *argv);
  84                        req.t.tcm_handle = handle;
  85                } else if (strcmp(*argv, "root") == 0) {
  86                        if (req.t.tcm_parent) {
  87                                fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
  88                                return -1;
  89                        }
  90                        req.t.tcm_parent = TC_H_ROOT;
  91                } else if (strcmp(*argv, "clsact") == 0) {
  92                        if (req.t.tcm_parent) {
  93                                fprintf(stderr, "Error: \"clsact\" is a duplicate parent ID\n");
  94                                return -1;
  95                        }
  96                        req.t.tcm_parent = TC_H_CLSACT;
  97                        strncpy(k, "clsact", sizeof(k) - 1);
  98                        q = get_qdisc_kind(k);
  99                        req.t.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
 100                        NEXT_ARG_FWD();
 101                        break;
 102                } else if (strcmp(*argv, "ingress") == 0) {
 103                        if (req.t.tcm_parent) {
 104                                fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n");
 105                                return -1;
 106                        }
 107                        req.t.tcm_parent = TC_H_INGRESS;
 108                        strncpy(k, "ingress", sizeof(k) - 1);
 109                        q = get_qdisc_kind(k);
 110                        req.t.tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
 111                        NEXT_ARG_FWD();
 112                        break;
 113                } else if (strcmp(*argv, "parent") == 0) {
 114                        __u32 handle;
 115
 116                        NEXT_ARG();
 117                        if (req.t.tcm_parent)
 118                                duparg("parent", *argv);
 119                        if (get_tc_classid(&handle, *argv))
 120                                invarg("invalid parent ID", *argv);
 121                        req.t.tcm_parent = handle;
 122                } else if (matches(*argv, "estimator") == 0) {
 123                        if (parse_estimator(&argc, &argv, &est))
 124                                return -1;
 125                } else if (matches(*argv, "stab") == 0) {
 126                        if (parse_size_table(&argc, &argv, &stab.szopts) < 0)
 127                                return -1;
 128                        continue;
 129                } else if (matches(*argv, "ingress_block") == 0) {
 130                        NEXT_ARG();
 131                        if (get_u32(&ingress_block, *argv, 0) || !ingress_block)
 132                                invarg("invalid ingress block index value", *argv);
 133                } else if (matches(*argv, "egress_block") == 0) {
 134                        NEXT_ARG();
 135                        if (get_u32(&egress_block, *argv, 0) || !egress_block)
 136                                invarg("invalid egress block index value", *argv);
 137                } else if (matches(*argv, "help") == 0) {
 138                        usage();
 139                } else {
 140                        strncpy(k, *argv, sizeof(k)-1);
 141
 142                        q = get_qdisc_kind(k);
 143                        argc--; argv++;
 144                        break;
 145                }
 146                argc--; argv++;
 147        }
 148
 149        if (k[0])
 150                addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
 151        if (est.ewma_log)
 152                addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
 153
 154        if (ingress_block)
 155                addattr32(&req.n, sizeof(req),
 156                          TCA_INGRESS_BLOCK, ingress_block);
 157        if (egress_block)
 158                addattr32(&req.n, sizeof(req),
 159                          TCA_EGRESS_BLOCK, egress_block);
 160
 161        if (q) {
 162                if (q->parse_qopt) {
 163                        if (q->parse_qopt(q, argc, argv, &req.n, d))
 164                                return 1;
 165                } else if (argc) {
 166                        fprintf(stderr, "qdisc '%s' does not support option parsing\n", k);
 167                        return -1;
 168                }
 169        } else {
 170                if (argc) {
 171                        if (matches(*argv, "help") == 0)
 172                                usage();
 173
 174                        fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc qdisc help\".\n", *argv);
 175                        return -1;
 176                }
 177        }
 178
 179        if (check_size_table_opts(&stab.szopts)) {
 180                struct rtattr *tail;
 181
 182                if (tc_calc_size_table(&stab.szopts, &stab.data) < 0) {
 183                        fprintf(stderr, "failed to calculate size table.\n");
 184                        return -1;
 185                }
 186
 187                tail = addattr_nest(&req.n, sizeof(req), TCA_STAB);
 188                addattr_l(&req.n, sizeof(req), TCA_STAB_BASE, &stab.szopts,
 189                          sizeof(stab.szopts));
 190                if (stab.data)
 191                        addattr_l(&req.n, sizeof(req), TCA_STAB_DATA, stab.data,
 192                                  stab.szopts.tsize * sizeof(__u16));
 193                addattr_nest_end(&req.n, tail);
 194                if (stab.data)
 195                        free(stab.data);
 196        }
 197
 198        if (d[0])  {
 199                int idx;
 200
 201                ll_init_map(&rth);
 202
 203                idx = ll_name_to_index(d);
 204                if (!idx)
 205                        return -nodev(d);
 206                req.t.tcm_ifindex = idx;
 207        }
 208
 209        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 210                return 2;
 211
 212        return 0;
 213}
 214
 215static int filter_ifindex;
 216static __u32 filter_parent;
 217static __u32 filter_handle;
 218
 219int print_qdisc(struct nlmsghdr *n, void *arg)
 220{
 221        FILE *fp = (FILE *)arg;
 222        struct tcmsg *t = NLMSG_DATA(n);
 223        int len = n->nlmsg_len;
 224        struct rtattr *tb[TCA_MAX+1];
 225        struct qdisc_util *q;
 226        char abuf[256];
 227
 228        if (n->nlmsg_type != RTM_NEWQDISC && n->nlmsg_type != RTM_DELQDISC) {
 229                fprintf(stderr, "Not a qdisc\n");
 230                return 0;
 231        }
 232        len -= NLMSG_LENGTH(sizeof(*t));
 233        if (len < 0) {
 234                fprintf(stderr, "Wrong len %d\n", len);
 235                return -1;
 236        }
 237
 238        if (filter_ifindex && filter_ifindex != t->tcm_ifindex)
 239                return 0;
 240
 241        if (filter_handle && filter_handle != t->tcm_handle)
 242                return 0;
 243
 244        if (filter_parent && filter_parent != t->tcm_parent)
 245                return 0;
 246
 247        parse_rtattr_flags(tb, TCA_MAX, TCA_RTA(t), len, NLA_F_NESTED);
 248
 249        if (tb[TCA_KIND] == NULL) {
 250                fprintf(stderr, "print_qdisc: NULL kind\n");
 251                return -1;
 252        }
 253
 254        open_json_object(NULL);
 255
 256        if (n->nlmsg_type == RTM_DELQDISC)
 257                print_bool(PRINT_ANY, "deleted", "deleted ", true);
 258
 259        if (n->nlmsg_type == RTM_NEWQDISC &&
 260                        (n->nlmsg_flags & NLM_F_CREATE) &&
 261                        (n->nlmsg_flags & NLM_F_REPLACE))
 262                print_bool(PRINT_ANY, "replaced", "replaced ", true);
 263
 264        if (n->nlmsg_type == RTM_NEWQDISC &&
 265                        (n->nlmsg_flags & NLM_F_CREATE) &&
 266                        (n->nlmsg_flags & NLM_F_EXCL))
 267                print_bool(PRINT_ANY, "added", "added ", true);
 268
 269        print_string(PRINT_ANY, "kind", "qdisc %s",
 270                     rta_getattr_str(tb[TCA_KIND]));
 271        sprintf(abuf, "%x:", t->tcm_handle >> 16);
 272        print_string(PRINT_ANY, "handle", " %s", abuf);
 273        if (show_raw) {
 274                sprintf(abuf, "[%08x]", t->tcm_handle);
 275                print_string(PRINT_FP, NULL, "%s", abuf);
 276        }
 277        print_string(PRINT_FP, NULL, " ", NULL);
 278
 279        if (filter_ifindex == 0)
 280                print_devname(PRINT_ANY, t->tcm_ifindex);
 281
 282        if (t->tcm_parent == TC_H_ROOT)
 283                print_bool(PRINT_ANY, "root", "root ", true);
 284        else if (t->tcm_parent) {
 285                print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
 286                print_string(PRINT_ANY, "parent", "parent %s ", abuf);
 287        }
 288
 289        if (t->tcm_info != 1)
 290                print_uint(PRINT_ANY, "refcnt", "refcnt %u ", t->tcm_info);
 291
 292        if (tb[TCA_HW_OFFLOAD] &&
 293            (rta_getattr_u8(tb[TCA_HW_OFFLOAD])))
 294                print_bool(PRINT_ANY, "offloaded", "offloaded ", true);
 295
 296        if (tb[TCA_INGRESS_BLOCK] &&
 297            RTA_PAYLOAD(tb[TCA_INGRESS_BLOCK]) >= sizeof(__u32)) {
 298                __u32 block = rta_getattr_u32(tb[TCA_INGRESS_BLOCK]);
 299
 300                if (block)
 301                        print_uint(PRINT_ANY, "ingress_block",
 302                                   "ingress_block %u ", block);
 303        }
 304
 305        if (tb[TCA_EGRESS_BLOCK] &&
 306            RTA_PAYLOAD(tb[TCA_EGRESS_BLOCK]) >= sizeof(__u32)) {
 307                __u32 block = rta_getattr_u32(tb[TCA_EGRESS_BLOCK]);
 308
 309                if (block)
 310                        print_uint(PRINT_ANY, "egress_block",
 311                                   "egress_block %u ", block);
 312        }
 313
 314        /* pfifo_fast is generic enough to warrant the hardcoding --JHS */
 315        if (strcmp("pfifo_fast", RTA_DATA(tb[TCA_KIND])) == 0)
 316                q = get_qdisc_kind("prio");
 317        else
 318                q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND]));
 319
 320        open_json_object("options");
 321        if (tb[TCA_OPTIONS]) {
 322                if (q)
 323                        q->print_qopt(q, fp, tb[TCA_OPTIONS]);
 324                else
 325                        fprintf(stderr, "Cannot parse qdisc parameters\n");
 326        }
 327        close_json_object();
 328
 329        print_nl();
 330
 331        if (show_details && tb[TCA_STAB]) {
 332                print_size_table(fp, " ", tb[TCA_STAB]);
 333                print_nl();
 334        }
 335
 336        if (show_stats) {
 337                struct rtattr *xstats = NULL;
 338
 339                if (tb[TCA_STATS] || tb[TCA_STATS2] || tb[TCA_XSTATS]) {
 340                        print_tcstats_attr(fp, tb, " ", &xstats);
 341                        print_nl();
 342                }
 343
 344                if (q && xstats && q->print_xstats) {
 345                        q->print_xstats(q, fp, xstats);
 346                        print_nl();
 347                }
 348        }
 349        close_json_object();
 350        fflush(fp);
 351        return 0;
 352}
 353
 354static int tc_qdisc_list(int argc, char **argv)
 355{
 356        struct {
 357                struct nlmsghdr n;
 358                struct tcmsg t;
 359                char buf[256];
 360        } req = {
 361                .n.nlmsg_type = RTM_GETQDISC,
 362                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
 363                .t.tcm_family = AF_UNSPEC,
 364        };
 365
 366        char d[IFNAMSIZ] = {};
 367        bool dump_invisible = false;
 368        __u32 handle;
 369
 370        while (argc > 0) {
 371                if (strcmp(*argv, "dev") == 0) {
 372                        NEXT_ARG();
 373                        strncpy(d, *argv, sizeof(d)-1);
 374                } else if (strcmp(*argv, "root") == 0) {
 375                        if (filter_parent)
 376                                invarg("parent is already specified", *argv);
 377                        else if (filter_handle)
 378                                invarg("handle is already specified", *argv);
 379                        filter_parent = TC_H_ROOT;
 380                } else if (strcmp(*argv, "ingress") == 0 ||
 381                                strcmp(*argv, "clsact") == 0) {
 382                        if (filter_parent)
 383                                invarg("parent is already specified", *argv);
 384                        else if (filter_handle)
 385                                invarg("handle is already specified", *argv);
 386                        filter_parent = TC_H_INGRESS;
 387                } else if (matches(*argv, "parent") == 0) {
 388                        if (filter_parent)
 389                                invarg("parent is already specified", *argv);
 390                        else if (filter_handle)
 391                                invarg("handle is already specified", *argv);
 392                        NEXT_ARG();
 393                        if (get_tc_classid(&handle, *argv))
 394                                invarg("invalid parent ID", *argv);
 395                        filter_parent = handle;
 396                } else if (matches(*argv, "handle") == 0) {
 397                        if (filter_parent)
 398                                invarg("parent is already specified", *argv);
 399                        else if (filter_handle)
 400                                invarg("handle is already specified", *argv);
 401                        NEXT_ARG();
 402                        if (get_qdisc_handle(&handle, *argv))
 403                                invarg("invalid handle ID", *argv);
 404                        filter_handle = handle;
 405                } else if (matches(*argv, "help") == 0) {
 406                        usage();
 407                } else if (strcmp(*argv, "invisible") == 0) {
 408                        dump_invisible = true;
 409                } else {
 410                        fprintf(stderr, "What is \"%s\"? Try \"tc qdisc help\".\n", *argv);
 411                        return -1;
 412                }
 413
 414                argc--; argv++;
 415        }
 416
 417        ll_init_map(&rth);
 418
 419        if (d[0]) {
 420                req.t.tcm_ifindex = ll_name_to_index(d);
 421                if (!req.t.tcm_ifindex)
 422                        return -nodev(d);
 423                filter_ifindex = req.t.tcm_ifindex;
 424        }
 425
 426        if (dump_invisible) {
 427                addattr(&req.n, 256, TCA_DUMP_INVISIBLE);
 428        }
 429
 430        if (rtnl_dump_request_n(&rth, &req.n) < 0) {
 431                perror("Cannot send request");
 432                return 1;
 433        }
 434
 435        new_json_obj(json);
 436        if (rtnl_dump_filter(&rth, print_qdisc, stdout) < 0) {
 437                fprintf(stderr, "Dump terminated\n");
 438                return 1;
 439        }
 440        delete_json_obj();
 441
 442        return 0;
 443}
 444
 445int do_qdisc(int argc, char **argv)
 446{
 447        if (argc < 1)
 448                return tc_qdisc_list(0, NULL);
 449        if (matches(*argv, "add") == 0)
 450                return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1);
 451        if (matches(*argv, "change") == 0)
 452                return tc_qdisc_modify(RTM_NEWQDISC, 0, argc-1, argv+1);
 453        if (matches(*argv, "replace") == 0)
 454                return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
 455        if (matches(*argv, "link") == 0)
 456                return tc_qdisc_modify(RTM_NEWQDISC, NLM_F_REPLACE, argc-1, argv+1);
 457        if (matches(*argv, "delete") == 0)
 458                return tc_qdisc_modify(RTM_DELQDISC, 0,  argc-1, argv+1);
 459        if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
 460            || matches(*argv, "lst") == 0)
 461                return tc_qdisc_list(argc-1, argv+1);
 462        if (matches(*argv, "help") == 0) {
 463                usage();
 464                return 0;
 465        }
 466        fprintf(stderr, "Command \"%s\" is unknown, try \"tc qdisc help\".\n", *argv);
 467        return -1;
 468}
 469
 470struct tc_qdisc_block_exists_ctx {
 471        __u32 block_index;
 472        bool found;
 473};
 474
 475static int tc_qdisc_block_exists_cb(struct nlmsghdr *n, void *arg)
 476{
 477        struct tc_qdisc_block_exists_ctx *ctx = arg;
 478        struct tcmsg *t = NLMSG_DATA(n);
 479        struct rtattr *tb[TCA_MAX+1];
 480        int len = n->nlmsg_len;
 481        struct qdisc_util *q;
 482        const char *kind;
 483        int err;
 484
 485        if (n->nlmsg_type != RTM_NEWQDISC)
 486                return 0;
 487
 488        len -= NLMSG_LENGTH(sizeof(*t));
 489        if (len < 0)
 490                return -1;
 491
 492        parse_rtattr_flags(tb, TCA_MAX, TCA_RTA(t), len, NLA_F_NESTED);
 493
 494        if (tb[TCA_KIND] == NULL)
 495                return -1;
 496
 497        if (tb[TCA_INGRESS_BLOCK] &&
 498            RTA_PAYLOAD(tb[TCA_INGRESS_BLOCK]) >= sizeof(__u32)) {
 499                __u32 block = rta_getattr_u32(tb[TCA_INGRESS_BLOCK]);
 500
 501                if (block == ctx->block_index)
 502                        ctx->found = true;
 503        }
 504
 505        if (tb[TCA_EGRESS_BLOCK] &&
 506            RTA_PAYLOAD(tb[TCA_EGRESS_BLOCK]) >= sizeof(__u32)) {
 507                __u32 block = rta_getattr_u32(tb[TCA_EGRESS_BLOCK]);
 508
 509                if (block == ctx->block_index)
 510                        ctx->found = true;
 511        }
 512
 513        kind = rta_getattr_str(tb[TCA_KIND]);
 514        q = get_qdisc_kind(kind);
 515        if (!q)
 516                return -1;
 517        if (q->has_block) {
 518                bool found = false;
 519
 520                err = q->has_block(q, tb[TCA_OPTIONS], ctx->block_index, &found);
 521                if (err)
 522                        return err;
 523                if (found)
 524                        ctx->found = true;
 525        }
 526
 527        return 0;
 528}
 529
 530bool tc_qdisc_block_exists(__u32 block_index)
 531{
 532        struct tc_qdisc_block_exists_ctx ctx = { .block_index = block_index };
 533        struct tcmsg t = { .tcm_family = AF_UNSPEC };
 534
 535        if (rtnl_dump_request(&rth, RTM_GETQDISC, &t, sizeof(t)) < 0) {
 536                perror("Cannot send dump request");
 537                return false;
 538        }
 539
 540        if (rtnl_dump_filter(&rth, tc_qdisc_block_exists_cb, &ctx) < 0) {
 541                perror("Dump terminated\n");
 542                return false;
 543        }
 544
 545        return ctx.found;
 546}
 547