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