iproute2/tc/tc_class.c
<<
>>
Prefs
   1/*
   2 * tc_class.c           "tc class".
   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 <math.h>
  22
  23#include "utils.h"
  24#include "tc_util.h"
  25#include "tc_common.h"
  26#include "list.h"
  27
  28struct graph_node {
  29        struct hlist_node hlist;
  30        __u32 id;
  31        __u32 parent_id;
  32        struct graph_node *parent_node;
  33        struct graph_node *right_node;
  34        void *data;
  35        int data_len;
  36        int nodes_count;
  37};
  38
  39static struct hlist_head cls_list = {};
  40static struct hlist_head root_cls_list = {};
  41
  42static void usage(void);
  43
  44static void usage(void)
  45{
  46        fprintf(stderr,
  47                "Usage: tc class [ add | del | change | replace | show ] dev STRING\n"
  48                "       [ classid CLASSID ] [ root | parent CLASSID ]\n"
  49                "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
  50                "\n"
  51                "       tc class show [ dev STRING ] [ root | parent CLASSID ]\n"
  52                "Where:\n"
  53                "QDISC_KIND := { prio | cbq | etc. }\n"
  54                "OPTIONS := ... try tc class add <desired QDISC_KIND> help\n");
  55}
  56
  57static int tc_class_modify(int cmd, unsigned int flags, int argc, char **argv)
  58{
  59        struct {
  60                struct nlmsghdr n;
  61                struct tcmsg            t;
  62                char                    buf[4096];
  63        } req = {
  64                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
  65                .n.nlmsg_flags = NLM_F_REQUEST | flags,
  66                .n.nlmsg_type = cmd,
  67                .t.tcm_family = AF_UNSPEC,
  68        };
  69        struct qdisc_util *q = NULL;
  70        struct tc_estimator est = {};
  71        char  d[IFNAMSIZ] = {};
  72        char  k[FILTER_NAMESZ] = {};
  73
  74        while (argc > 0) {
  75                if (strcmp(*argv, "dev") == 0) {
  76                        NEXT_ARG();
  77                        if (d[0])
  78                                duparg("dev", *argv);
  79                        strncpy(d, *argv, sizeof(d)-1);
  80                } else if (strcmp(*argv, "classid") == 0) {
  81                        __u32 handle;
  82
  83                        NEXT_ARG();
  84                        if (req.t.tcm_handle)
  85                                duparg("classid", *argv);
  86                        if (get_tc_classid(&handle, *argv))
  87                                invarg("invalid class ID", *argv);
  88                        req.t.tcm_handle = handle;
  89                } else if (strcmp(*argv, "handle") == 0) {
  90                        fprintf(stderr, "Error: try \"classid\" instead of \"handle\"\n");
  91                        return -1;
  92                } else if (strcmp(*argv, "root") == 0) {
  93                        if (req.t.tcm_parent) {
  94                                fprintf(stderr, "Error: \"root\" is duplicate parent ID.\n");
  95                                return -1;
  96                        }
  97                        req.t.tcm_parent = TC_H_ROOT;
  98                } else if (strcmp(*argv, "parent") == 0) {
  99                        __u32 handle;
 100
 101                        NEXT_ARG();
 102                        if (req.t.tcm_parent)
 103                                duparg("parent", *argv);
 104                        if (get_tc_classid(&handle, *argv))
 105                                invarg("invalid parent ID", *argv);
 106                        req.t.tcm_parent = handle;
 107                } else if (matches(*argv, "estimator") == 0) {
 108                        if (parse_estimator(&argc, &argv, &est))
 109                                return -1;
 110                } else if (matches(*argv, "help") == 0) {
 111                        usage();
 112                } else {
 113                        strncpy(k, *argv, sizeof(k)-1);
 114
 115                        q = get_qdisc_kind(k);
 116                        argc--; argv++;
 117                        break;
 118                }
 119                argc--; argv++;
 120        }
 121
 122        if (k[0])
 123                addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
 124        if (est.ewma_log)
 125                addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
 126
 127        if (q) {
 128                if (q->parse_copt == NULL) {
 129                        fprintf(stderr, "Error: Qdisc \"%s\" is classless.\n", k);
 130                        return 1;
 131                }
 132                if (q->parse_copt(q, argc, argv, &req.n, d))
 133                        return 1;
 134        } else {
 135                if (argc) {
 136                        if (matches(*argv, "help") == 0)
 137                                usage();
 138                        fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc class help\".", *argv);
 139                        return -1;
 140                }
 141        }
 142
 143        if (d[0])  {
 144                ll_init_map(&rth);
 145
 146                req.t.tcm_ifindex = ll_name_to_index(d);
 147                if (!req.t.tcm_ifindex)
 148                        return -nodev(d);
 149        }
 150
 151        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 152                return 2;
 153
 154        return 0;
 155}
 156
 157static int filter_ifindex;
 158static __u32 filter_qdisc;
 159static __u32 filter_classid;
 160
 161static void graph_node_add(__u32 parent_id, __u32 id, void *data,
 162                int len)
 163{
 164        struct graph_node *node = calloc(1, sizeof(struct graph_node));
 165
 166        node->id         = id;
 167        node->parent_id  = parent_id;
 168
 169        if (data && len) {
 170                node->data       = malloc(len);
 171                node->data_len   = len;
 172                memcpy(node->data, data, len);
 173        }
 174
 175        if (parent_id == TC_H_ROOT)
 176                hlist_add_head(&node->hlist, &root_cls_list);
 177        else
 178                hlist_add_head(&node->hlist, &cls_list);
 179}
 180
 181static void graph_indent(char *buf, struct graph_node *node, int is_newline,
 182                int add_spaces)
 183{
 184        char spaces[100] = {0};
 185
 186        while (node && node->parent_node) {
 187                node->parent_node->right_node = node;
 188                node = node->parent_node;
 189        }
 190        while (node && node->right_node) {
 191                if (node->hlist.next)
 192                        strcat(buf, "|    ");
 193                else
 194                        strcat(buf, "     ");
 195
 196                node = node->right_node;
 197        }
 198
 199        if (is_newline) {
 200                if (node->hlist.next && node->nodes_count)
 201                        strcat(buf, "|    |");
 202                else if (node->hlist.next)
 203                        strcat(buf, "|     ");
 204                else if (node->nodes_count)
 205                        strcat(buf, "     |");
 206                else if (!node->hlist.next)
 207                        strcat(buf, "      ");
 208        }
 209        if (add_spaces > 0) {
 210                sprintf(spaces, "%-*s", add_spaces, "");
 211                strcat(buf, spaces);
 212        }
 213}
 214
 215static void graph_cls_show(FILE *fp, char *buf, struct hlist_head *root_list,
 216                int level)
 217{
 218        struct hlist_node *n, *tmp_cls;
 219        char cls_id_str[256] = {};
 220        struct rtattr *tb[TCA_MAX + 1];
 221        struct qdisc_util *q;
 222        char str[300] = {};
 223
 224        hlist_for_each_safe(n, tmp_cls, root_list) {
 225                struct hlist_node *c, *tmp_chld;
 226                struct hlist_head children = {};
 227                struct graph_node *cls = container_of(n, struct graph_node,
 228                                hlist);
 229
 230                hlist_for_each_safe(c, tmp_chld, &cls_list) {
 231                        struct graph_node *child = container_of(c,
 232                                        struct graph_node, hlist);
 233
 234                        if (cls->id == child->parent_id) {
 235                                hlist_del(c);
 236                                hlist_add_head(c, &children);
 237                                cls->nodes_count++;
 238                                child->parent_node = cls;
 239                        }
 240                }
 241
 242                graph_indent(buf, cls, 0, 0);
 243
 244                print_tc_classid(cls_id_str, sizeof(cls_id_str), cls->id);
 245                snprintf(str, sizeof(str),
 246                         "+---(%s)", cls_id_str);
 247                strcat(buf, str);
 248
 249                parse_rtattr_flags(tb, TCA_MAX, (struct rtattr *)cls->data,
 250                                   cls->data_len, NLA_F_NESTED);
 251
 252                if (tb[TCA_KIND] == NULL) {
 253                        strcat(buf, " [unknown qdisc kind] ");
 254                } else {
 255                        const char *kind = rta_getattr_str(tb[TCA_KIND]);
 256
 257                        sprintf(str, " %s ", kind);
 258                        strcat(buf, str);
 259                        fprintf(fp, "%s", buf);
 260                        buf[0] = '\0';
 261
 262                        q = get_qdisc_kind(kind);
 263                        if (q && q->print_copt) {
 264                                q->print_copt(q, fp, tb[TCA_OPTIONS]);
 265                        }
 266                        if (q && show_stats) {
 267                                int cls_indent = strlen(q->id) - 2 +
 268                                        strlen(cls_id_str);
 269                                struct rtattr *stats = NULL;
 270
 271                                graph_indent(buf, cls, 1, cls_indent);
 272
 273                                if (tb[TCA_STATS] || tb[TCA_STATS2]) {
 274                                        fprintf(fp, "\n");
 275                                        print_tcstats_attr(fp, tb, buf, &stats);
 276                                        buf[0] = '\0';
 277                                }
 278                                if (cls->hlist.next || cls->nodes_count) {
 279                                        strcat(buf, "\n");
 280                                        graph_indent(buf, cls, 1, 0);
 281                                }
 282                        }
 283                }
 284                free(cls->data);
 285                fprintf(fp, "%s\n", buf);
 286                buf[0] = '\0';
 287
 288                graph_cls_show(fp, buf, &children, level + 1);
 289                if (!cls->hlist.next) {
 290                        graph_indent(buf, cls, 0, 0);
 291                        strcat(buf, "\n");
 292                }
 293
 294                fprintf(fp, "%s", buf);
 295                buf[0] = '\0';
 296                free(cls);
 297        }
 298}
 299
 300int print_class(struct nlmsghdr *n, void *arg)
 301{
 302        FILE *fp = (FILE *)arg;
 303        struct tcmsg *t = NLMSG_DATA(n);
 304        int len = n->nlmsg_len;
 305        struct rtattr *tb[TCA_MAX + 1];
 306        struct qdisc_util *q;
 307        char abuf[256];
 308
 309        if (n->nlmsg_type != RTM_NEWTCLASS && n->nlmsg_type != RTM_DELTCLASS) {
 310                fprintf(stderr, "Not a class\n");
 311                return 0;
 312        }
 313        len -= NLMSG_LENGTH(sizeof(*t));
 314        if (len < 0) {
 315                fprintf(stderr, "Wrong len %d\n", len);
 316                return -1;
 317        }
 318
 319        if (show_graph) {
 320                graph_node_add(t->tcm_parent, t->tcm_handle, TCA_RTA(t), len);
 321                return 0;
 322        }
 323
 324        if (filter_qdisc && TC_H_MAJ(t->tcm_handle^filter_qdisc))
 325                return 0;
 326
 327        if (filter_classid && t->tcm_handle != filter_classid)
 328                return 0;
 329
 330        parse_rtattr_flags(tb, TCA_MAX, TCA_RTA(t), len, NLA_F_NESTED);
 331
 332        if (tb[TCA_KIND] == NULL) {
 333                fprintf(stderr, "print_class: NULL kind\n");
 334                return -1;
 335        }
 336
 337        if (n->nlmsg_type == RTM_DELTCLASS)
 338                fprintf(fp, "deleted ");
 339
 340        abuf[0] = 0;
 341        if (t->tcm_handle) {
 342                if (filter_qdisc)
 343                        print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_handle));
 344                else
 345                        print_tc_classid(abuf, sizeof(abuf), t->tcm_handle);
 346        }
 347        fprintf(fp, "class %s %s ", rta_getattr_str(tb[TCA_KIND]), abuf);
 348
 349        if (filter_ifindex == 0)
 350                fprintf(fp, "dev %s ", ll_index_to_name(t->tcm_ifindex));
 351
 352        if (t->tcm_parent == TC_H_ROOT)
 353                fprintf(fp, "root ");
 354        else {
 355                if (filter_qdisc)
 356                        print_tc_classid(abuf, sizeof(abuf), TC_H_MIN(t->tcm_parent));
 357                else
 358                        print_tc_classid(abuf, sizeof(abuf), t->tcm_parent);
 359                fprintf(fp, "parent %s ", abuf);
 360        }
 361        if (t->tcm_info)
 362                fprintf(fp, "leaf %x: ", t->tcm_info>>16);
 363        q = get_qdisc_kind(RTA_DATA(tb[TCA_KIND]));
 364        if (tb[TCA_OPTIONS]) {
 365                if (q && q->print_copt)
 366                        q->print_copt(q, fp, tb[TCA_OPTIONS]);
 367                else
 368                        fprintf(fp, "[cannot parse class parameters]");
 369        }
 370        fprintf(fp, "\n");
 371        if (show_stats) {
 372                struct rtattr *xstats = NULL;
 373
 374                if (tb[TCA_STATS] || tb[TCA_STATS2]) {
 375                        print_tcstats_attr(fp, tb, " ", &xstats);
 376                        fprintf(fp, "\n");
 377                }
 378                if (q && (xstats || tb[TCA_XSTATS]) && q->print_xstats) {
 379                        q->print_xstats(q, fp, xstats ? : tb[TCA_XSTATS]);
 380                        fprintf(fp, "\n");
 381                }
 382        }
 383        fflush(fp);
 384        return 0;
 385}
 386
 387
 388static int tc_class_list(int argc, char **argv)
 389{
 390        struct tcmsg t = { .tcm_family = AF_UNSPEC };
 391        char d[IFNAMSIZ] = {};
 392        char buf[1024] = {0};
 393
 394        filter_qdisc = 0;
 395        filter_classid = 0;
 396
 397        while (argc > 0) {
 398                if (strcmp(*argv, "dev") == 0) {
 399                        NEXT_ARG();
 400                        if (d[0])
 401                                duparg("dev", *argv);
 402                        strncpy(d, *argv, sizeof(d)-1);
 403                } else if (strcmp(*argv, "qdisc") == 0) {
 404                        NEXT_ARG();
 405                        if (filter_qdisc)
 406                                duparg("qdisc", *argv);
 407                        if (get_qdisc_handle(&filter_qdisc, *argv))
 408                                invarg("invalid qdisc ID", *argv);
 409                } else if (strcmp(*argv, "classid") == 0) {
 410                        NEXT_ARG();
 411                        if (filter_classid)
 412                                duparg("classid", *argv);
 413                        if (get_tc_classid(&filter_classid, *argv))
 414                                invarg("invalid class ID", *argv);
 415                } else if (strcmp(*argv, "root") == 0) {
 416                        if (t.tcm_parent) {
 417                                fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
 418                                return -1;
 419                        }
 420                        t.tcm_parent = TC_H_ROOT;
 421                } else if (strcmp(*argv, "parent") == 0) {
 422                        __u32 handle;
 423
 424                        if (t.tcm_parent)
 425                                duparg("parent", *argv);
 426                        NEXT_ARG();
 427                        if (get_tc_classid(&handle, *argv))
 428                                invarg("invalid parent ID", *argv);
 429                        t.tcm_parent = handle;
 430                } else if (matches(*argv, "help") == 0) {
 431                        usage();
 432                } else {
 433                        fprintf(stderr, "What is \"%s\"? Try \"tc class help\".\n", *argv);
 434                        return -1;
 435                }
 436
 437                argc--; argv++;
 438        }
 439
 440        ll_init_map(&rth);
 441
 442        if (d[0]) {
 443                t.tcm_ifindex = ll_name_to_index(d);
 444                if (!t.tcm_ifindex)
 445                        return -nodev(d);
 446                filter_ifindex = t.tcm_ifindex;
 447        }
 448
 449        if (rtnl_dump_request(&rth, RTM_GETTCLASS, &t, sizeof(t)) < 0) {
 450                perror("Cannot send dump request");
 451                return 1;
 452        }
 453
 454        if (rtnl_dump_filter(&rth, print_class, stdout) < 0) {
 455                fprintf(stderr, "Dump terminated\n");
 456                return 1;
 457        }
 458
 459        if (show_graph)
 460                graph_cls_show(stdout, &buf[0], &root_cls_list, 0);
 461
 462        return 0;
 463}
 464
 465int do_class(int argc, char **argv)
 466{
 467        if (argc < 1)
 468                return tc_class_list(0, NULL);
 469        if (matches(*argv, "add") == 0)
 470                return tc_class_modify(RTM_NEWTCLASS, NLM_F_EXCL|NLM_F_CREATE, argc-1, argv+1);
 471        if (matches(*argv, "change") == 0)
 472                return tc_class_modify(RTM_NEWTCLASS, 0, argc-1, argv+1);
 473        if (matches(*argv, "replace") == 0)
 474                return tc_class_modify(RTM_NEWTCLASS, NLM_F_CREATE, argc-1, argv+1);
 475        if (matches(*argv, "delete") == 0)
 476                return tc_class_modify(RTM_DELTCLASS, 0,  argc-1, argv+1);
 477#if 0
 478        if (matches(*argv, "get") == 0)
 479                return tc_class_get(RTM_GETTCLASS, 0,  argc-1, argv+1);
 480#endif
 481        if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
 482            || matches(*argv, "lst") == 0)
 483                return tc_class_list(argc-1, argv+1);
 484        if (matches(*argv, "help") == 0) {
 485                usage();
 486                return 0;
 487        }
 488        fprintf(stderr, "Command \"%s\" is unknown, try \"tc class help\".\n", *argv);
 489        return -1;
 490}
 491