iproute2/tc/tc_util.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * tc_util.c            Misc TC utility functions.
   4 *
   5 * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   6 */
   7
   8#include <stdio.h>
   9#include <stdlib.h>
  10#include <unistd.h>
  11#include <fcntl.h>
  12#include <sys/socket.h>
  13#include <sys/param.h>
  14#include <netinet/in.h>
  15#include <arpa/inet.h>
  16#include <string.h>
  17#include <math.h>
  18#include <errno.h>
  19
  20#include "utils.h"
  21#include "names.h"
  22#include "tc_util.h"
  23#include "tc_common.h"
  24
  25#ifndef LIBDIR
  26#define LIBDIR "/usr/lib"
  27#endif
  28
  29static struct db_names *cls_names;
  30
  31#define NAMES_DB_USR CONF_USR_DIR "/tc_cls"
  32#define NAMES_DB_ETC CONF_ETC_DIR "/tc_cls"
  33
  34int cls_names_init(char *path)
  35{
  36        int ret;
  37
  38        cls_names = db_names_alloc();
  39        if (!cls_names)
  40                return -1;
  41
  42        if (path) {
  43                ret = db_names_load(cls_names, path);
  44                if (ret == -ENOENT) {
  45                        fprintf(stderr, "Can't open class names file: %s\n", path);
  46                        return -1;
  47                }
  48        }
  49
  50        ret = db_names_load(cls_names, NAMES_DB_ETC);
  51        if (ret == -ENOENT)
  52                ret = db_names_load(cls_names, NAMES_DB_USR);
  53
  54        if (ret) {
  55                db_names_free(cls_names);
  56                cls_names = NULL;
  57        }
  58
  59        return 0;
  60}
  61
  62void cls_names_uninit(void)
  63{
  64        db_names_free(cls_names);
  65}
  66
  67const char *get_tc_lib(void)
  68{
  69        const char *lib_dir;
  70
  71        lib_dir = getenv("TC_LIB_DIR");
  72        if (!lib_dir)
  73                lib_dir = LIBDIR "/tc/";
  74
  75        return lib_dir;
  76}
  77
  78int get_qdisc_handle(__u32 *h, const char *str)
  79{
  80        unsigned long maj;
  81        char *p;
  82
  83        maj = TC_H_UNSPEC;
  84        if (strcmp(str, "none") == 0)
  85                goto ok;
  86        maj = strtoul(str, &p, 16);
  87        if (p == str || maj >= (1 << 16))
  88                return -1;
  89        maj <<= 16;
  90        if (*p != ':' && *p != 0)
  91                return -1;
  92ok:
  93        *h = maj;
  94        return 0;
  95}
  96
  97int get_tc_classid(__u32 *h, const char *str)
  98{
  99        unsigned long maj, min;
 100        char *p;
 101
 102        maj = TC_H_ROOT;
 103        if (strcmp(str, "root") == 0)
 104                goto ok;
 105        maj = TC_H_UNSPEC;
 106        if (strcmp(str, "none") == 0)
 107                goto ok;
 108        maj = strtoul(str, &p, 16);
 109        if (p == str) {
 110                maj = 0;
 111                if (*p != ':')
 112                        return -1;
 113        }
 114        if (*p == ':') {
 115                if (maj >= (1<<16))
 116                        return -1;
 117                maj <<= 16;
 118                str = p+1;
 119                min = strtoul(str, &p, 16);
 120                if (*p != 0)
 121                        return -1;
 122                if (min >= (1<<16))
 123                        return -1;
 124                maj |= min;
 125        } else if (*p != 0)
 126                return -1;
 127
 128ok:
 129        *h = maj;
 130        return 0;
 131}
 132
 133int print_tc_classid(char *buf, int blen, __u32 h)
 134{
 135        SPRINT_BUF(handle) = {};
 136        int hlen = SPRINT_BSIZE - 1;
 137
 138        if (h == TC_H_ROOT)
 139                sprintf(handle, "root");
 140        else if (h == TC_H_UNSPEC)
 141                snprintf(handle, hlen, "none");
 142        else if (TC_H_MAJ(h) == 0)
 143                snprintf(handle, hlen, ":%x", TC_H_MIN(h));
 144        else if (TC_H_MIN(h) == 0)
 145                snprintf(handle, hlen, "%x:", TC_H_MAJ(h) >> 16);
 146        else
 147                snprintf(handle, hlen, "%x:%x", TC_H_MAJ(h) >> 16, TC_H_MIN(h));
 148
 149        if (use_names) {
 150                char clname[IDNAME_MAX] = {};
 151
 152                if (id_to_name(cls_names, h, clname))
 153                        snprintf(buf, blen, "%s#%s", clname, handle);
 154                else
 155                        snprintf(buf, blen, "%s", handle);
 156        } else {
 157                snprintf(buf, blen, "%s", handle);
 158        }
 159
 160        return 0;
 161}
 162
 163char *sprint_tc_classid(__u32 h, char *buf)
 164{
 165        if (print_tc_classid(buf, SPRINT_BSIZE-1, h))
 166                strcpy(buf, "???");
 167        return buf;
 168}
 169
 170/* Parse a percent e.g: '30%'
 171 * return: 0 = ok, -1 = error, 1 = out of range
 172 */
 173int parse_percent(double *val, const char *str)
 174{
 175        char *p;
 176
 177        *val = strtod(str, &p) / 100.;
 178        if (*val > 1.0 || *val < 0.0)
 179                return 1;
 180        if (*p && strcmp(p, "%"))
 181                return -1;
 182
 183        return 0;
 184}
 185
 186static int parse_percent_rate(char *rate, size_t len,
 187                              const char *str, const char *dev)
 188{
 189        long dev_mbit;
 190        int ret;
 191        double perc, rate_bit;
 192        char *str_perc = NULL;
 193
 194        if (!dev[0]) {
 195                fprintf(stderr, "No device specified; specify device to rate limit by percentage\n");
 196                return -1;
 197        }
 198
 199        if (read_prop(dev, "speed", &dev_mbit))
 200                return -1;
 201
 202        ret = sscanf(str, "%m[0-9.%]", &str_perc);
 203        if (ret != 1)
 204                goto malf;
 205
 206        ret = parse_percent(&perc, str_perc);
 207        if (ret == 1) {
 208                fprintf(stderr, "Invalid rate specified; should be between [0,100]%% but is %s\n", str);
 209                goto err;
 210        } else if (ret == -1) {
 211                goto malf;
 212        }
 213
 214        free(str_perc);
 215
 216        rate_bit = perc * dev_mbit * 1000 * 1000;
 217
 218        ret = snprintf(rate, len, "%lf", rate_bit);
 219        if (ret <= 0 || ret >= len) {
 220                fprintf(stderr, "Unable to parse calculated rate\n");
 221                return -1;
 222        }
 223
 224        return 0;
 225
 226malf:
 227        fprintf(stderr, "Specified rate value could not be read or is malformed\n");
 228err:
 229        free(str_perc);
 230        return -1;
 231}
 232
 233int get_percent_rate(unsigned int *rate, const char *str, const char *dev)
 234{
 235        char r_str[20];
 236
 237        if (parse_percent_rate(r_str, sizeof(r_str), str, dev))
 238                return -1;
 239
 240        return get_rate(rate, r_str);
 241}
 242
 243int get_percent_rate64(__u64 *rate, const char *str, const char *dev)
 244{
 245        char r_str[20];
 246
 247        if (parse_percent_rate(r_str, sizeof(r_str), str, dev))
 248                return -1;
 249
 250        return get_rate64(rate, r_str);
 251}
 252
 253void __attribute__((format(printf, 3, 0)))
 254tc_print_rate(enum output_type t, const char *key, const char *fmt,
 255                   unsigned long long rate)
 256{
 257        print_rate(use_iec, t, key, fmt, rate);
 258}
 259
 260int get_size_and_cell(unsigned int *size, int *cell_log, char *str)
 261{
 262        char *slash = strchr(str, '/');
 263
 264        if (slash)
 265                *slash = 0;
 266
 267        if (get_size(size, str))
 268                return -1;
 269
 270        if (slash) {
 271                int cell;
 272                int i;
 273
 274                if (get_integer(&cell, slash+1, 0))
 275                        return -1;
 276                *slash = '/';
 277
 278                for (i = 0; i < 32; i++) {
 279                        if ((1<<i) == cell) {
 280                                *cell_log = i;
 281                                return 0;
 282                        }
 283                }
 284                return -1;
 285        }
 286        return 0;
 287}
 288
 289void print_devname(enum output_type type, int ifindex)
 290{
 291        const char *ifname = ll_index_to_name(ifindex);
 292
 293        if (!is_json_context())
 294                printf("dev ");
 295
 296        print_color_string(type, COLOR_IFNAME,
 297                           "dev", "%s ", ifname);
 298}
 299
 300static const char *action_n2a(int action)
 301{
 302        static char buf[64];
 303
 304        if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN))
 305                return "goto";
 306        if (TC_ACT_EXT_CMP(action, TC_ACT_JUMP))
 307                return "jump";
 308        switch (action) {
 309        case TC_ACT_UNSPEC:
 310                return "continue";
 311        case TC_ACT_OK:
 312                return "pass";
 313        case TC_ACT_SHOT:
 314                return "drop";
 315        case TC_ACT_RECLASSIFY:
 316                return "reclassify";
 317        case TC_ACT_PIPE:
 318                return "pipe";
 319        case TC_ACT_STOLEN:
 320                return "stolen";
 321        case TC_ACT_TRAP:
 322                return "trap";
 323        default:
 324                snprintf(buf, 64, "%d", action);
 325                return buf;
 326        }
 327}
 328
 329/* Convert action branch name into numeric format.
 330 *
 331 * Parameters:
 332 * @arg - string to parse
 333 * @result - pointer to output variable
 334 * @allow_num - whether @arg may be in numeric format already
 335 *
 336 * In error case, returns -1 and does not touch @result. Otherwise returns 0.
 337 */
 338int action_a2n(char *arg, int *result, bool allow_num)
 339{
 340        int n;
 341        char dummy;
 342        struct {
 343                const char *a;
 344                int n;
 345        } a2n[] = {
 346                {"continue", TC_ACT_UNSPEC},
 347                {"drop", TC_ACT_SHOT},
 348                {"shot", TC_ACT_SHOT},
 349                {"pass", TC_ACT_OK},
 350                {"ok", TC_ACT_OK},
 351                {"reclassify", TC_ACT_RECLASSIFY},
 352                {"pipe", TC_ACT_PIPE},
 353                {"goto", TC_ACT_GOTO_CHAIN},
 354                {"jump", TC_ACT_JUMP},
 355                {"trap", TC_ACT_TRAP},
 356                { NULL },
 357        }, *iter;
 358
 359        for (iter = a2n; iter->a; iter++) {
 360                if (matches(arg, iter->a) != 0)
 361                        continue;
 362                n = iter->n;
 363                goto out_ok;
 364        }
 365        if (!allow_num || sscanf(arg, "%d%c", &n, &dummy) != 1)
 366                return -1;
 367
 368out_ok:
 369        if (result)
 370                *result = n;
 371        return 0;
 372}
 373
 374static int __parse_action_control(int *argc_p, char ***argv_p, int *result_p,
 375                                  bool allow_num, bool ignore_a2n_miss)
 376{
 377        int argc = *argc_p;
 378        char **argv = *argv_p;
 379        int result;
 380
 381        if (!argc)
 382                return -1;
 383        if (action_a2n(*argv, &result, allow_num) == -1) {
 384                if (!ignore_a2n_miss)
 385                        fprintf(stderr, "Bad action type %s\n", *argv);
 386                return -1;
 387        }
 388        if (result == TC_ACT_GOTO_CHAIN) {
 389                __u32 chain_index;
 390
 391                NEXT_ARG();
 392                if (matches(*argv, "chain") != 0) {
 393                        fprintf(stderr, "\"chain index\" expected\n");
 394                        return -1;
 395                }
 396                NEXT_ARG();
 397                if (get_u32(&chain_index, *argv, 10) ||
 398                    chain_index > TC_ACT_EXT_VAL_MASK) {
 399                        fprintf(stderr, "Illegal \"chain index\"\n");
 400                        return -1;
 401                }
 402                result |= chain_index;
 403        }
 404        if (result == TC_ACT_JUMP) {
 405                __u32 jump_cnt = 0;
 406
 407                NEXT_ARG();
 408                if (get_u32(&jump_cnt, *argv, 10) ||
 409                    jump_cnt > TC_ACT_EXT_VAL_MASK) {
 410                        fprintf(stderr, "Invalid \"jump count\" (%s)\n", *argv);
 411                        return -1;
 412                }
 413                result |= jump_cnt;
 414        }
 415        NEXT_ARG_FWD();
 416        *argc_p = argc;
 417        *argv_p = argv;
 418        *result_p = result;
 419        return 0;
 420}
 421
 422/* Parse action control including possible options.
 423 *
 424 * Parameters:
 425 * @argc_p - pointer to argc to parse
 426 * @argv_p - pointer to argv to parse
 427 * @result_p - pointer to output variable
 428 * @allow_num - whether action may be in numeric format already
 429 *
 430 * In error case, returns -1 and does not touch @result_1p. Otherwise returns 0.
 431 */
 432int parse_action_control(int *argc_p, char ***argv_p,
 433                         int *result_p, bool allow_num)
 434{
 435        return __parse_action_control(argc_p, argv_p, result_p,
 436                                      allow_num, false);
 437}
 438
 439/* Parse action control including possible options.
 440 *
 441 * Parameters:
 442 * @argc_p - pointer to argc to parse
 443 * @argv_p - pointer to argv to parse
 444 * @result_p - pointer to output variable
 445 * @allow_num - whether action may be in numeric format already
 446 * @default_result - set as a result in case of parsing error
 447 *
 448 * In case there is an error during parsing, the default result is used.
 449 */
 450void parse_action_control_dflt(int *argc_p, char ***argv_p,
 451                               int *result_p, bool allow_num,
 452                               int default_result)
 453{
 454        if (__parse_action_control(argc_p, argv_p, result_p, allow_num, true))
 455                *result_p = default_result;
 456}
 457
 458static int parse_action_control_slash_spaces(int *argc_p, char ***argv_p,
 459                                             int *result1_p, int *result2_p,
 460                                             bool allow_num)
 461{
 462        int argc = *argc_p;
 463        char **argv = *argv_p;
 464        int result1 = -1, result2 = -1;
 465        int *result_p = &result1;
 466        int ok = 0;
 467        int ret;
 468
 469        while (argc > 0) {
 470                switch (ok) {
 471                case 1:
 472                        if (strcmp(*argv, "/") != 0)
 473                                goto out;
 474                        result_p = &result2;
 475                        NEXT_ARG();
 476                        /* fall-through */
 477                case 0:
 478                        ret = parse_action_control(&argc, &argv,
 479                                                   result_p, allow_num);
 480                        if (ret)
 481                                return ret;
 482                        ok++;
 483                        break;
 484                default:
 485                        goto out;
 486                }
 487        }
 488out:
 489        *result1_p = result1;
 490        if (ok == 2)
 491                *result2_p = result2;
 492        *argc_p = argc;
 493        *argv_p = argv;
 494        return 0;
 495}
 496
 497/* Parse action control with slash including possible options.
 498 *
 499 * Parameters:
 500 * @argc_p - pointer to argc to parse
 501 * @argv_p - pointer to argv to parse
 502 * @result1_p - pointer to the first (before slash) output variable
 503 * @result2_p - pointer to the second (after slash) output variable
 504 * @allow_num - whether action may be in numeric format already
 505 *
 506 * In error case, returns -1 and does not touch @result*. Otherwise returns 0.
 507 */
 508int parse_action_control_slash(int *argc_p, char ***argv_p,
 509                               int *result1_p, int *result2_p, bool allow_num)
 510{
 511        int result1, result2, argc = *argc_p;
 512        char **argv = *argv_p;
 513        char *p = strchr(*argv, '/');
 514
 515        if (!p)
 516                return parse_action_control_slash_spaces(argc_p, argv_p,
 517                                                         result1_p, result2_p,
 518                                                         allow_num);
 519        *p = 0;
 520        if (action_a2n(*argv, &result1, allow_num)) {
 521                *p = '/';
 522                return -1;
 523        }
 524
 525        *p = '/';
 526        if (action_a2n(p + 1, &result2, allow_num))
 527                return -1;
 528
 529        *result1_p = result1;
 530        *result2_p = result2;
 531        NEXT_ARG_FWD();
 532        *argc_p = argc;
 533        *argv_p = argv;
 534        return 0;
 535}
 536
 537void print_action_control(const char *prefix, int action, const char *suffix)
 538{
 539        print_string(PRINT_FP, NULL, "%s", prefix);
 540        open_json_object("control_action");
 541        print_string(PRINT_ANY, "type", "%s", action_n2a(action));
 542        if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN))
 543                print_uint(PRINT_ANY, "chain", " chain %u",
 544                           action & TC_ACT_EXT_VAL_MASK);
 545        if (TC_ACT_EXT_CMP(action, TC_ACT_JUMP))
 546                print_uint(PRINT_ANY, "jump", " %u",
 547                           action & TC_ACT_EXT_VAL_MASK);
 548        close_json_object();
 549        print_string(PRINT_FP, NULL, "%s", suffix);
 550}
 551
 552int get_linklayer(unsigned int *val, const char *arg)
 553{
 554        int res;
 555
 556        if (matches(arg, "ethernet") == 0)
 557                res = LINKLAYER_ETHERNET;
 558        else if (matches(arg, "atm") == 0)
 559                res = LINKLAYER_ATM;
 560        else if (matches(arg, "adsl") == 0)
 561                res = LINKLAYER_ATM;
 562        else
 563                return -1; /* Indicate error */
 564
 565        *val = res;
 566        return 0;
 567}
 568
 569static void print_linklayer(char *buf, int len, unsigned int linklayer)
 570{
 571        switch (linklayer) {
 572        case LINKLAYER_UNSPEC:
 573                snprintf(buf, len, "%s", "unspec");
 574                return;
 575        case LINKLAYER_ETHERNET:
 576                snprintf(buf, len, "%s", "ethernet");
 577                return;
 578        case LINKLAYER_ATM:
 579                snprintf(buf, len, "%s", "atm");
 580                return;
 581        default:
 582                snprintf(buf, len, "%s", "unknown");
 583                return;
 584        }
 585}
 586
 587char *sprint_linklayer(unsigned int linklayer, char *buf)
 588{
 589        print_linklayer(buf, SPRINT_BSIZE-1, linklayer);
 590        return buf;
 591}
 592
 593/*
 594 * Limited list of clockid's
 595 * Since these are the ones the kernel qdisc can use
 596 * because they are available via ktim_get
 597 */
 598static const struct clockid_table {
 599        const char *name;
 600        clockid_t clockid;
 601} clockt_map[] = {
 602#ifdef CLOCK_BOOTTIME
 603        { "BOOTTIME", CLOCK_BOOTTIME },
 604#endif
 605#ifdef CLOCK_MONOTONIC
 606        { "MONOTONIC", CLOCK_MONOTONIC },
 607#endif
 608#ifdef CLOCK_REALTIME
 609        { "REALTIME", CLOCK_REALTIME },
 610#endif
 611#ifdef CLOCK_TAI
 612        { "TAI", CLOCK_TAI },
 613#endif
 614        { NULL }
 615};
 616
 617int get_clockid(__s32 *val, const char *arg)
 618{
 619        const struct clockid_table *c;
 620
 621        /* skip prefix if present */
 622        if (strcasestr(arg, "CLOCK_") != NULL)
 623                arg += sizeof("CLOCK_") - 1;
 624
 625        for (c = clockt_map; c->name; c++) {
 626                if (strcasecmp(c->name, arg) == 0) {
 627                        *val = c->clockid;
 628                        return 0;
 629                }
 630        }
 631
 632        return -1;
 633}
 634
 635const char *get_clock_name(clockid_t clockid)
 636{
 637        const struct clockid_table *c;
 638
 639        for (c = clockt_map; c->name; c++) {
 640                if (clockid == c->clockid)
 641                        return c->name;
 642        }
 643
 644        return "invalid";
 645}
 646
 647void print_tm(const struct tcf_t *tm)
 648{
 649        int hz = get_user_hz();
 650
 651        if (tm->install != 0)
 652                print_uint(PRINT_ANY, "installed", " installed %u sec",
 653                           tm->install / hz);
 654
 655        if (tm->lastuse != 0)
 656                print_uint(PRINT_ANY, "last_used", " used %u sec",
 657                           tm->lastuse / hz);
 658
 659        if (tm->firstuse != 0)
 660                print_uint(PRINT_ANY, "first_used", " firstused %u sec",
 661                           tm->firstuse / hz);
 662
 663        if (tm->expires != 0)
 664                print_uint(PRINT_ANY, "expires", " expires %u sec",
 665                           tm->expires / hz);
 666}
 667
 668static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix,
 669                                   __u64 packets64, __u64 packets64_hw)
 670{
 671        struct gnet_stats_basic bs_hw;
 672
 673        if (!tbs[TCA_STATS_BASIC_HW])
 674                return;
 675
 676        memcpy(&bs_hw, RTA_DATA(tbs[TCA_STATS_BASIC_HW]),
 677               MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC_HW]), sizeof(bs_hw)));
 678        packets64_hw = packets64_hw ? : bs_hw.packets;
 679
 680        if (bs_hw.bytes == 0 && packets64_hw == 0)
 681                return;
 682
 683        if (tbs[TCA_STATS_BASIC]) {
 684                struct gnet_stats_basic bs;
 685
 686                memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]),
 687                       MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]),
 688                           sizeof(bs)));
 689                packets64 = packets64 ? : bs.packets;
 690
 691                if (bs.bytes >= bs_hw.bytes && packets64 >= packets64_hw) {
 692                        print_nl();
 693                        print_string(PRINT_FP, NULL, "%s", prefix);
 694                        print_lluint(PRINT_ANY, "sw_bytes",
 695                                     "Sent software %llu bytes",
 696                                     bs.bytes - bs_hw.bytes);
 697                        print_lluint(PRINT_ANY, "sw_packets", " %llu pkt",
 698                                     packets64 - packets64_hw);
 699                }
 700        }
 701
 702        print_nl();
 703        print_string(PRINT_FP, NULL, "%s", prefix);
 704        print_lluint(PRINT_ANY, "hw_bytes", "Sent hardware %llu bytes",
 705                     bs_hw.bytes);
 706        print_lluint(PRINT_ANY, "hw_packets", " %llu pkt", packets64_hw);
 707}
 708
 709static void parse_packets64(const struct rtattr *nest, __u64 *p_packets64,
 710                            __u64 *p_packets64_hw)
 711{
 712        unsigned short prev_type = __TCA_STATS_MAX;
 713        const struct rtattr *pos;
 714
 715        /* 'TCA_STATS_PKT64' can appear twice in the 'TCA_ACT_STATS' nest.
 716         * Whether the attribute carries the combined or hardware only
 717         * statistics depends on the attribute that precedes it in the nest.
 718         */
 719        rtattr_for_each_nested(pos, nest) {
 720                if (pos->rta_type == TCA_STATS_PKT64 &&
 721                    prev_type == TCA_STATS_BASIC)
 722                        *p_packets64 = rta_getattr_u64(pos);
 723                else if (pos->rta_type == TCA_STATS_PKT64 &&
 724                         prev_type == TCA_STATS_BASIC_HW)
 725                        *p_packets64_hw = rta_getattr_u64(pos);
 726                prev_type = pos->rta_type;
 727        }
 728}
 729
 730void print_tcstats2_attr(struct rtattr *rta, const char *prefix, struct rtattr **xstats)
 731{
 732        struct rtattr *tbs[TCA_STATS_MAX + 1];
 733        __u64 packets64 = 0, packets64_hw = 0;
 734
 735        parse_rtattr_nested(tbs, TCA_STATS_MAX, rta);
 736        parse_packets64(rta, &packets64, &packets64_hw);
 737
 738        if (tbs[TCA_STATS_BASIC]) {
 739                struct gnet_stats_basic bs = {0};
 740
 741                memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]),
 742                       MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs)));
 743                print_string(PRINT_FP, NULL, "%s", prefix);
 744                print_lluint(PRINT_ANY, "bytes", "Sent %llu bytes", bs.bytes);
 745                if (packets64)
 746                        print_lluint(PRINT_ANY, "packets",
 747                                     " %llu pkt", packets64);
 748                else
 749                        print_uint(PRINT_ANY, "packets",
 750                                   " %u pkt", bs.packets);
 751        }
 752
 753        if (tbs[TCA_STATS_QUEUE]) {
 754                struct gnet_stats_queue q = {0};
 755
 756                memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]),
 757                       MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q)));
 758                print_uint(PRINT_ANY, "drops", " (dropped %u", q.drops);
 759                print_uint(PRINT_ANY, "overlimits", ", overlimits %u",
 760                           q.overlimits);
 761                print_uint(PRINT_ANY, "requeues", " requeues %u) ", q.requeues);
 762        }
 763
 764        if (tbs[TCA_STATS_BASIC_HW])
 765                print_tcstats_basic_hw(tbs, prefix, packets64, packets64_hw);
 766
 767        if (tbs[TCA_STATS_RATE_EST64]) {
 768                struct gnet_stats_rate_est64 re = {0};
 769
 770                memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST64]),
 771                       MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST64]),
 772                           sizeof(re)));
 773                print_string(PRINT_FP, NULL, "\n%s", prefix);
 774                print_lluint(PRINT_JSON, "rate", NULL, re.bps);
 775                tc_print_rate(PRINT_FP, NULL, "rate %s", re.bps);
 776                print_lluint(PRINT_ANY, "pps", " %llupps", re.pps);
 777        } else if (tbs[TCA_STATS_RATE_EST]) {
 778                struct gnet_stats_rate_est re = {0};
 779
 780                memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]),
 781                       MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re)));
 782                print_string(PRINT_FP, NULL, "\n%s", prefix);
 783                print_uint(PRINT_JSON, "rate", NULL, re.bps);
 784                tc_print_rate(PRINT_FP, NULL, "rate %s", re.bps);
 785                print_uint(PRINT_ANY, "pps", " %upps", re.pps);
 786        }
 787
 788        if (tbs[TCA_STATS_QUEUE]) {
 789                struct gnet_stats_queue q = {0};
 790
 791                memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]),
 792                       MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q)));
 793                if (!tbs[TCA_STATS_RATE_EST])
 794                        print_nl();
 795                print_string(PRINT_FP, NULL, "%s", prefix);
 796                print_size(PRINT_ANY, "backlog", "backlog %s", q.backlog);
 797                print_uint(PRINT_ANY, "qlen", " %up", q.qlen);
 798                print_uint(PRINT_FP, NULL, " requeues %u", q.requeues);
 799        }
 800
 801        if (xstats)
 802                *xstats = tbs[TCA_STATS_APP] ? : NULL;
 803}
 804
 805void print_tcstats_attr(FILE *fp, struct rtattr *tb[], const char *prefix,
 806                        struct rtattr **xstats)
 807{
 808        if (tb[TCA_STATS2]) {
 809                print_tcstats2_attr(tb[TCA_STATS2], prefix, xstats);
 810                if (xstats && !*xstats)
 811                        goto compat_xstats;
 812                return;
 813        }
 814        /* backward compatibility */
 815        if (tb[TCA_STATS]) {
 816                struct tc_stats st = {};
 817
 818                /* handle case where kernel returns more/less than we know about */
 819                memcpy(&st, RTA_DATA(tb[TCA_STATS]),
 820                       MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st)));
 821
 822                print_string(PRINT_FP, NULL, "%s", prefix);
 823                print_lluint(PRINT_ANY, "bytes", "Sent %llu bytes",
 824                             (unsigned long long)st.bytes);
 825                print_uint(PRINT_ANY, "packets", " %u pkts", st.packets);
 826                print_uint(PRINT_ANY, "dropped", " (dropped %u,", st.drops);
 827                print_uint(PRINT_ANY, "overlimits", " overlimits %u) ", st.overlimits);
 828
 829                if (st.bps || st.pps || st.qlen || st.backlog) {
 830                        print_nl();
 831                        print_string(PRINT_FP, NULL, "%s", prefix);
 832
 833                        if (st.bps || st.pps) {
 834                                print_string(PRINT_FP, NULL, "rate ", NULL);
 835                                if (st.bps)
 836                                        tc_print_rate(PRINT_ANY, "rate", "%s ", st.bps);
 837                                if (st.pps)
 838                                        print_uint(PRINT_ANY, "pps", "%upps ", st.pps);
 839                        }
 840                        if (st.qlen || st.backlog) {
 841                                print_string(PRINT_FP, NULL, "backlog ", NULL);
 842                                if (st.backlog)
 843                                        print_size(PRINT_ANY, "backlog", "%s ", st.backlog);
 844                                if (st.qlen)
 845                                        print_uint(PRINT_ANY, "qlen", "%up ", st.qlen);
 846                        }
 847                }
 848        }
 849
 850compat_xstats:
 851        if (tb[TCA_XSTATS] && xstats)
 852                *xstats = tb[TCA_XSTATS];
 853}
 854
 855static void print_masked_type(__u32 type_max,
 856                              __u32 (*rta_getattr_type)(const struct rtattr *),
 857                              const char *name, struct rtattr *attr,
 858                              struct rtattr *mask_attr, bool newline)
 859{
 860        __u32 value, mask;
 861
 862        if (!attr)
 863                return;
 864
 865        value = rta_getattr_type(attr);
 866        mask = mask_attr ? rta_getattr_type(mask_attr) : type_max;
 867
 868        if (newline)
 869                print_string(PRINT_FP, NULL, "%s  ", _SL_);
 870        else
 871                print_string(PRINT_FP, NULL, " ", _SL_);
 872
 873        print_uint_name_value(name, value);
 874
 875        if (mask != type_max) {
 876                char mask_name[SPRINT_BSIZE-6];
 877
 878                snprintf(mask_name, sizeof(mask_name), "%s_mask", name);
 879                print_hex(PRINT_ANY, mask_name, "/0x%x", mask);
 880        }
 881}
 882
 883void print_masked_u32(const char *name, struct rtattr *attr,
 884                      struct rtattr *mask_attr, bool newline)
 885{
 886        print_masked_type(UINT32_MAX, rta_getattr_u32, name, attr, mask_attr,
 887                          newline);
 888}
 889
 890static __u32 __rta_getattr_u16_u32(const struct rtattr *attr)
 891{
 892        return rta_getattr_u16(attr);
 893}
 894
 895void print_masked_u16(const char *name, struct rtattr *attr,
 896                      struct rtattr *mask_attr, bool newline)
 897{
 898        print_masked_type(UINT16_MAX, __rta_getattr_u16_u32, name, attr,
 899                          mask_attr, newline);
 900}
 901
 902static __u32 __rta_getattr_u8_u32(const struct rtattr *attr)
 903{
 904        return rta_getattr_u8(attr);
 905}
 906
 907void print_masked_u8(const char *name, struct rtattr *attr,
 908                     struct rtattr *mask_attr, bool newline)
 909{
 910        print_masked_type(UINT8_MAX,  __rta_getattr_u8_u32, name, attr,
 911                          mask_attr, newline);
 912}
 913
 914static __u32 __rta_getattr_be16_u32(const struct rtattr *attr)
 915{
 916        return rta_getattr_be16(attr);
 917}
 918
 919void print_masked_be16(const char *name, struct rtattr *attr,
 920                       struct rtattr *mask_attr, bool newline)
 921{
 922        print_masked_type(UINT16_MAX, __rta_getattr_be16_u32, name, attr,
 923                          mask_attr, newline);
 924}
 925
 926void print_ext_msg(struct rtattr **tb)
 927{
 928        if (!tb[TCA_EXT_WARN_MSG])
 929                return;
 930
 931        print_string(PRINT_ANY, "warn", "%s", rta_getattr_str(tb[TCA_EXT_WARN_MSG]));
 932        print_nl();
 933}
 934