iproute2/ip/ipstats.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2#include <assert.h>
   3#include <errno.h>
   4#include <stdio.h>
   5#include <string.h>
   6#include <sys/param.h>
   7#include <sys/socket.h>
   8
   9#include "list.h"
  10#include "utils.h"
  11#include "ip_common.h"
  12
  13struct ipstats_stat_dump_filters {
  14        /* mask[0] filters outer attributes. Then individual nests have their
  15         * filtering mask at the index of the nested attribute.
  16         */
  17        __u32 mask[IFLA_STATS_MAX + 1];
  18};
  19
  20static void
  21ipstats_stat_desc_enable_bit(struct ipstats_stat_dump_filters *filters,
  22                             unsigned int group, unsigned int subgroup)
  23{
  24        filters->mask[0] |= IFLA_STATS_FILTER_BIT(group);
  25        if (subgroup)
  26                filters->mask[group] |= IFLA_STATS_FILTER_BIT(subgroup);
  27}
  28
  29struct ipstats_stat_show_attrs {
  30        struct if_stats_msg *ifsm;
  31        int len;
  32
  33        /* tbs[0] contains top-level attribute table. Then individual nests have
  34         * their attribute tables at the index of the nested attribute.
  35         */
  36        struct rtattr **tbs[IFLA_STATS_MAX + 1];
  37};
  38
  39static const char *const ipstats_levels[] = {
  40        "group",
  41        "subgroup",
  42        "suite",
  43};
  44
  45enum {
  46        IPSTATS_LEVELS_COUNT = ARRAY_SIZE(ipstats_levels),
  47};
  48
  49struct ipstats_sel {
  50        const char *sel[IPSTATS_LEVELS_COUNT];
  51};
  52
  53struct ipstats_stat_enabled_one {
  54        const struct ipstats_stat_desc *desc;
  55        struct ipstats_sel sel;
  56};
  57
  58struct ipstats_stat_enabled {
  59        struct ipstats_stat_enabled_one *enabled;
  60        size_t nenabled;
  61};
  62
  63static const unsigned int ipstats_stat_ifla_max[] = {
  64        [0] = IFLA_STATS_MAX,
  65        [IFLA_STATS_LINK_XSTATS] = LINK_XSTATS_TYPE_MAX,
  66        [IFLA_STATS_LINK_XSTATS_SLAVE] = LINK_XSTATS_TYPE_MAX,
  67        [IFLA_STATS_LINK_OFFLOAD_XSTATS] = IFLA_OFFLOAD_XSTATS_MAX,
  68        [IFLA_STATS_AF_SPEC] = AF_MAX - 1,
  69};
  70
  71static_assert(ARRAY_SIZE(ipstats_stat_ifla_max) == IFLA_STATS_MAX + 1,
  72              "An IFLA_STATS attribute is missing from the ifla_max table");
  73
  74static int
  75ipstats_stat_show_attrs_alloc_tb(struct ipstats_stat_show_attrs *attrs,
  76                                 unsigned int group)
  77{
  78        unsigned int ifla_max;
  79        int err;
  80
  81        assert(group < ARRAY_SIZE(ipstats_stat_ifla_max));
  82        assert(group < ARRAY_SIZE(attrs->tbs));
  83        ifla_max = ipstats_stat_ifla_max[group];
  84        assert(ifla_max != 0);
  85
  86        if (attrs->tbs[group])
  87                return 0;
  88
  89        attrs->tbs[group] = calloc(ifla_max + 1, sizeof(*attrs->tbs[group]));
  90        if (attrs->tbs[group] == NULL) {
  91                fprintf(stderr, "Error parsing netlink answer: %s\n",
  92                        strerror(errno));
  93                return -errno;
  94        }
  95
  96        if (group == 0)
  97                err = parse_rtattr(attrs->tbs[group], ifla_max,
  98                                   IFLA_STATS_RTA(attrs->ifsm), attrs->len);
  99        else
 100                err = parse_rtattr_nested(attrs->tbs[group], ifla_max,
 101                                          attrs->tbs[0][group]);
 102
 103        if (err != 0) {
 104                free(attrs->tbs[group]);
 105                attrs->tbs[group] = NULL;
 106        }
 107        return err;
 108}
 109
 110static const struct rtattr *
 111ipstats_stat_show_get_attr(struct ipstats_stat_show_attrs *attrs,
 112                           int group, int subgroup, int *err)
 113{
 114        int tmp_err;
 115
 116        if (err == NULL)
 117                err = &tmp_err;
 118
 119        *err = 0;
 120        if (subgroup == 0)
 121                return attrs->tbs[0][group];
 122
 123        if (attrs->tbs[0][group] == NULL)
 124                return NULL;
 125
 126        *err = ipstats_stat_show_attrs_alloc_tb(attrs, group);
 127        if (*err != 0)
 128                return NULL;
 129
 130        return attrs->tbs[group][subgroup];
 131}
 132
 133static void
 134ipstats_stat_show_attrs_free(struct ipstats_stat_show_attrs *attrs)
 135{
 136        size_t i;
 137
 138        for (i = 0; i < ARRAY_SIZE(attrs->tbs); i++)
 139                free(attrs->tbs[i]);
 140}
 141
 142#define IPSTATS_RTA_PAYLOAD(VAR, AT)                                    \
 143        do {                                                            \
 144                const struct rtattr *__at = (AT);                       \
 145                size_t __at_sz = __at->rta_len - RTA_LENGTH(0);         \
 146                size_t __var_sz = sizeof(VAR);                          \
 147                typeof(VAR) *__dest = &VAR;                             \
 148                                                                        \
 149                memset(__dest, 0, __var_sz);                            \
 150                memcpy(__dest, RTA_DATA(__at), MIN(__at_sz, __var_sz)); \
 151        } while (0)
 152
 153static int ipstats_show_64(struct ipstats_stat_show_attrs *attrs,
 154                           unsigned int group, unsigned int subgroup)
 155{
 156        struct rtnl_link_stats64 stats;
 157        const struct rtattr *at;
 158        int err;
 159
 160        at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
 161        if (at == NULL)
 162                return err;
 163
 164        IPSTATS_RTA_PAYLOAD(stats, at);
 165
 166        open_json_object("stats64");
 167        print_stats64(stdout, &stats, NULL, NULL);
 168        close_json_object();
 169        return 0;
 170}
 171
 172static void print_hw_stats64(FILE *fp, struct rtnl_hw_stats64 *s)
 173{
 174        unsigned int cols[] = {
 175                strlen("*X: bytes"),
 176                strlen("packets"),
 177                strlen("errors"),
 178                strlen("dropped"),
 179                strlen("overrun"),
 180        };
 181
 182        if (is_json_context()) {
 183                /* RX stats */
 184                open_json_object("rx");
 185                print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes);
 186                print_u64(PRINT_JSON, "packets", NULL, s->rx_packets);
 187                print_u64(PRINT_JSON, "errors", NULL, s->rx_errors);
 188                print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped);
 189                print_u64(PRINT_JSON, "multicast", NULL, s->multicast);
 190                close_json_object();
 191
 192                /* TX stats */
 193                open_json_object("tx");
 194                print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes);
 195                print_u64(PRINT_JSON, "packets", NULL, s->tx_packets);
 196                print_u64(PRINT_JSON, "errors", NULL, s->tx_errors);
 197                print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped);
 198                close_json_object();
 199        } else {
 200                size_columns(cols, ARRAY_SIZE(cols),
 201                             s->rx_bytes, s->rx_packets, s->rx_errors,
 202                             s->rx_dropped, s->multicast);
 203                size_columns(cols, ARRAY_SIZE(cols),
 204                             s->tx_bytes, s->tx_packets, s->tx_errors,
 205                             s->tx_dropped, 0);
 206
 207                /* RX stats */
 208                fprintf(fp, "    RX: %*s %*s %*s %*s %*s%s",
 209                        cols[0] - 4, "bytes", cols[1], "packets",
 210                        cols[2], "errors", cols[3], "dropped",
 211                        cols[4], "mcast", _SL_);
 212
 213                fprintf(fp, "    ");
 214                print_num(fp, cols[0], s->rx_bytes);
 215                print_num(fp, cols[1], s->rx_packets);
 216                print_num(fp, cols[2], s->rx_errors);
 217                print_num(fp, cols[3], s->rx_dropped);
 218                print_num(fp, cols[4], s->multicast);
 219                fprintf(fp, "%s", _SL_);
 220
 221                /* TX stats */
 222                fprintf(fp, "    TX: %*s %*s %*s %*s%s",
 223                        cols[0] - 4, "bytes", cols[1], "packets",
 224                        cols[2], "errors", cols[3], "dropped", _SL_);
 225
 226                fprintf(fp, "    ");
 227                print_num(fp, cols[0], s->tx_bytes);
 228                print_num(fp, cols[1], s->tx_packets);
 229                print_num(fp, cols[2], s->tx_errors);
 230                print_num(fp, cols[3], s->tx_dropped);
 231        }
 232}
 233
 234static int ipstats_show_hw64(const struct rtattr *at)
 235{
 236        struct rtnl_hw_stats64 stats;
 237
 238        IPSTATS_RTA_PAYLOAD(stats, at);
 239        print_hw_stats64(stdout, &stats);
 240        return 0;
 241}
 242
 243enum ipstats_maybe_on_off {
 244        IPSTATS_MOO_OFF = -1,
 245        IPSTATS_MOO_INVALID,
 246        IPSTATS_MOO_ON,
 247};
 248
 249static bool ipstats_moo_to_bool(enum ipstats_maybe_on_off moo)
 250{
 251        assert(moo != IPSTATS_MOO_INVALID);
 252        return moo + 1;
 253}
 254
 255static int ipstats_print_moo(enum output_type t, const char *key,
 256                             const char *fmt, enum ipstats_maybe_on_off moo)
 257{
 258        if (!moo)
 259                return 0;
 260        return print_on_off(t, key, fmt, ipstats_moo_to_bool(moo));
 261}
 262
 263struct ipstats_hw_s_info_one {
 264        enum ipstats_maybe_on_off request;
 265        enum ipstats_maybe_on_off used;
 266};
 267
 268enum ipstats_hw_s_info_idx {
 269        IPSTATS_HW_S_INFO_IDX_L3_STATS,
 270        IPSTATS_HW_S_INFO_IDX_COUNT
 271};
 272
 273static const char *const ipstats_hw_s_info_name[] = {
 274        "l3_stats",
 275};
 276
 277static_assert(ARRAY_SIZE(ipstats_hw_s_info_name) ==
 278              IPSTATS_HW_S_INFO_IDX_COUNT,
 279              "mismatch: enum ipstats_hw_s_info_idx x ipstats_hw_s_info_name");
 280
 281struct ipstats_hw_s_info {
 282        /* Indexed by enum ipstats_hw_s_info_idx. */
 283        struct ipstats_hw_s_info_one *infos[IPSTATS_HW_S_INFO_IDX_COUNT];
 284};
 285
 286static enum ipstats_maybe_on_off ipstats_dissect_01(int value, const char *what)
 287{
 288        switch (value) {
 289        case 0:
 290                return IPSTATS_MOO_OFF;
 291        case 1:
 292                return IPSTATS_MOO_ON;
 293        default:
 294                fprintf(stderr, "Invalid value for %s: expected 0 or 1, got %d.\n",
 295                        what, value);
 296                return IPSTATS_MOO_INVALID;
 297        }
 298}
 299
 300static int ipstats_dissect_hw_s_info_one(const struct rtattr *at,
 301                                         struct ipstats_hw_s_info_one *p_hwsio,
 302                                         const char *what)
 303{
 304        int attr_id_request = IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST;
 305        struct rtattr *tb[IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX + 1];
 306        int attr_id_used = IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED;
 307        struct ipstats_hw_s_info_one hwsio = {};
 308        int err;
 309        int v;
 310
 311        err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX, at);
 312        if (err)
 313                return err;
 314
 315        if (tb[attr_id_request]) {
 316                v = rta_getattr_u8(tb[attr_id_request]);
 317                hwsio.request = ipstats_dissect_01(v, "request");
 318
 319                /* This has to be present & valid. */
 320                if (!hwsio.request)
 321                        return -EINVAL;
 322        }
 323
 324        if (tb[attr_id_used]) {
 325                v = rta_getattr_u8(tb[attr_id_used]);
 326                hwsio.used = ipstats_dissect_01(v, "used");
 327        }
 328
 329        *p_hwsio = hwsio;
 330        return 0;
 331}
 332
 333static int ipstats_dissect_hw_s_info(const struct rtattr *at,
 334                                     struct ipstats_hw_s_info *hwsi)
 335{
 336        struct rtattr *tb[IFLA_OFFLOAD_XSTATS_MAX + 1];
 337        int attr_id_l3 = IFLA_OFFLOAD_XSTATS_L3_STATS;
 338        struct ipstats_hw_s_info_one *hwsio = NULL;
 339        int err;
 340
 341        err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_MAX, at);
 342        if (err)
 343                return err;
 344
 345        *hwsi = (struct ipstats_hw_s_info){};
 346
 347        if (tb[attr_id_l3]) {
 348                hwsio = malloc(sizeof(*hwsio));
 349                if (!hwsio) {
 350                        err = -ENOMEM;
 351                        goto out;
 352                }
 353
 354                err = ipstats_dissect_hw_s_info_one(tb[attr_id_l3], hwsio, "l3");
 355                if (err)
 356                        goto out;
 357
 358                hwsi->infos[IPSTATS_HW_S_INFO_IDX_L3_STATS] = hwsio;
 359                hwsio = NULL;
 360        }
 361
 362        return 0;
 363
 364out:
 365        free(hwsio);
 366        return err;
 367}
 368
 369static void ipstats_fini_hw_s_info(struct ipstats_hw_s_info *hwsi)
 370{
 371        int i;
 372
 373        for (i = 0; i < IPSTATS_HW_S_INFO_IDX_COUNT; i++)
 374                free(hwsi->infos[i]);
 375}
 376
 377static void
 378__ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info_one *hwsio)
 379{
 380        if (hwsio == NULL)
 381                return;
 382
 383        ipstats_print_moo(PRINT_ANY, "request", " %s", hwsio->request);
 384        ipstats_print_moo(PRINT_ANY, "used", " used %s", hwsio->used);
 385}
 386
 387static void
 388ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info *hwsi,
 389                           enum ipstats_hw_s_info_idx idx)
 390{
 391        const struct ipstats_hw_s_info_one *hwsio = hwsi->infos[idx];
 392        const char *name = ipstats_hw_s_info_name[idx];
 393
 394        if (hwsio == NULL)
 395                return;
 396
 397        print_string(PRINT_FP, NULL, "    %s", name);
 398        open_json_object(name);
 399        __ipstats_show_hw_s_info_one(hwsio);
 400        close_json_object();
 401}
 402
 403static int __ipstats_show_hw_s_info(const struct rtattr *at)
 404{
 405        struct ipstats_hw_s_info hwsi = {};
 406        int err;
 407
 408        err = ipstats_dissect_hw_s_info(at, &hwsi);
 409        if (err)
 410                return err;
 411
 412        open_json_object("info");
 413        ipstats_show_hw_s_info_one(&hwsi, IPSTATS_HW_S_INFO_IDX_L3_STATS);
 414        close_json_object();
 415
 416        ipstats_fini_hw_s_info(&hwsi);
 417        return 0;
 418}
 419
 420static int ipstats_show_hw_s_info(struct ipstats_stat_show_attrs *attrs,
 421                                  unsigned int group, unsigned int subgroup)
 422{
 423        const struct rtattr *at;
 424        int err;
 425
 426        at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
 427        if (at == NULL)
 428                return err;
 429
 430        print_nl();
 431        return __ipstats_show_hw_s_info(at);
 432}
 433
 434static int __ipstats_show_hw_stats(const struct rtattr *at_hwsi,
 435                                   const struct rtattr *at_stats,
 436                                   enum ipstats_hw_s_info_idx idx)
 437{
 438        int err = 0;
 439
 440        if (at_hwsi != NULL) {
 441                struct ipstats_hw_s_info hwsi = {};
 442
 443                err = ipstats_dissect_hw_s_info(at_hwsi, &hwsi);
 444                if (err)
 445                        return err;
 446
 447                open_json_object("info");
 448                __ipstats_show_hw_s_info_one(hwsi.infos[idx]);
 449                close_json_object();
 450
 451                ipstats_fini_hw_s_info(&hwsi);
 452        }
 453
 454        if (at_stats != NULL) {
 455                print_nl();
 456                open_json_object("stats64");
 457                err = ipstats_show_hw64(at_stats);
 458                close_json_object();
 459        }
 460
 461        return err;
 462}
 463
 464static int ipstats_show_hw_stats(struct ipstats_stat_show_attrs *attrs,
 465                                 unsigned int group,
 466                                 unsigned int hw_s_info,
 467                                 unsigned int hw_stats,
 468                                 enum ipstats_hw_s_info_idx idx)
 469{
 470        const struct rtattr *at_stats;
 471        const struct rtattr *at_hwsi;
 472        int err = 0;
 473
 474        at_hwsi = ipstats_stat_show_get_attr(attrs, group, hw_s_info, &err);
 475        if (at_hwsi == NULL)
 476                return err;
 477
 478        at_stats = ipstats_stat_show_get_attr(attrs, group, hw_stats, &err);
 479        if (at_stats == NULL && err != 0)
 480                return err;
 481
 482        return __ipstats_show_hw_stats(at_hwsi, at_stats, idx);
 483}
 484
 485static void
 486ipstats_stat_desc_pack_cpu_hit(struct ipstats_stat_dump_filters *filters,
 487                               const struct ipstats_stat_desc *desc)
 488{
 489        ipstats_stat_desc_enable_bit(filters,
 490                                     IFLA_STATS_LINK_OFFLOAD_XSTATS,
 491                                     IFLA_OFFLOAD_XSTATS_CPU_HIT);
 492}
 493
 494static int ipstats_stat_desc_show_cpu_hit(struct ipstats_stat_show_attrs *attrs,
 495                                          const struct ipstats_stat_desc *desc)
 496{
 497        print_nl();
 498        return ipstats_show_64(attrs,
 499                               IFLA_STATS_LINK_OFFLOAD_XSTATS,
 500                               IFLA_OFFLOAD_XSTATS_CPU_HIT);
 501}
 502
 503static const struct ipstats_stat_desc ipstats_stat_desc_offload_cpu_hit = {
 504        .name = "cpu_hit",
 505        .kind = IPSTATS_STAT_DESC_KIND_LEAF,
 506        .pack = &ipstats_stat_desc_pack_cpu_hit,
 507        .show = &ipstats_stat_desc_show_cpu_hit,
 508};
 509
 510static void
 511ipstats_stat_desc_pack_hw_stats_info(struct ipstats_stat_dump_filters *filters,
 512                                     const struct ipstats_stat_desc *desc)
 513{
 514        ipstats_stat_desc_enable_bit(filters,
 515                                     IFLA_STATS_LINK_OFFLOAD_XSTATS,
 516                                     IFLA_OFFLOAD_XSTATS_HW_S_INFO);
 517}
 518
 519static int
 520ipstats_stat_desc_show_hw_stats_info(struct ipstats_stat_show_attrs *attrs,
 521                                     const struct ipstats_stat_desc *desc)
 522{
 523        return ipstats_show_hw_s_info(attrs,
 524                                      IFLA_STATS_LINK_OFFLOAD_XSTATS,
 525                                      IFLA_OFFLOAD_XSTATS_HW_S_INFO);
 526}
 527
 528static const struct ipstats_stat_desc ipstats_stat_desc_offload_hw_s_info = {
 529        .name = "hw_stats_info",
 530        .kind = IPSTATS_STAT_DESC_KIND_LEAF,
 531        .pack = &ipstats_stat_desc_pack_hw_stats_info,
 532        .show = &ipstats_stat_desc_show_hw_stats_info,
 533};
 534
 535static void
 536ipstats_stat_desc_pack_l3_stats(struct ipstats_stat_dump_filters *filters,
 537                                const struct ipstats_stat_desc *desc)
 538{
 539        ipstats_stat_desc_enable_bit(filters,
 540                                     IFLA_STATS_LINK_OFFLOAD_XSTATS,
 541                                     IFLA_OFFLOAD_XSTATS_L3_STATS);
 542        ipstats_stat_desc_enable_bit(filters,
 543                                     IFLA_STATS_LINK_OFFLOAD_XSTATS,
 544                                     IFLA_OFFLOAD_XSTATS_HW_S_INFO);
 545}
 546
 547static int
 548ipstats_stat_desc_show_l3_stats(struct ipstats_stat_show_attrs *attrs,
 549                                const struct ipstats_stat_desc *desc)
 550{
 551        return ipstats_show_hw_stats(attrs,
 552                                     IFLA_STATS_LINK_OFFLOAD_XSTATS,
 553                                     IFLA_OFFLOAD_XSTATS_HW_S_INFO,
 554                                     IFLA_OFFLOAD_XSTATS_L3_STATS,
 555                                     IPSTATS_HW_S_INFO_IDX_L3_STATS);
 556}
 557
 558static const struct ipstats_stat_desc ipstats_stat_desc_offload_l3_stats = {
 559        .name = "l3_stats",
 560        .kind = IPSTATS_STAT_DESC_KIND_LEAF,
 561        .pack = &ipstats_stat_desc_pack_l3_stats,
 562        .show = &ipstats_stat_desc_show_l3_stats,
 563};
 564
 565static const struct ipstats_stat_desc *ipstats_stat_desc_offload_subs[] = {
 566        &ipstats_stat_desc_offload_cpu_hit,
 567        &ipstats_stat_desc_offload_hw_s_info,
 568        &ipstats_stat_desc_offload_l3_stats,
 569};
 570
 571static const struct ipstats_stat_desc ipstats_stat_desc_offload_group = {
 572        .name = "offload",
 573        .kind = IPSTATS_STAT_DESC_KIND_GROUP,
 574        .subs = ipstats_stat_desc_offload_subs,
 575        .nsubs = ARRAY_SIZE(ipstats_stat_desc_offload_subs),
 576};
 577
 578void ipstats_stat_desc_pack_xstats(struct ipstats_stat_dump_filters *filters,
 579                                   const struct ipstats_stat_desc *desc)
 580{
 581        struct ipstats_stat_desc_xstats *xdesc;
 582
 583        xdesc = container_of(desc, struct ipstats_stat_desc_xstats, desc);
 584        ipstats_stat_desc_enable_bit(filters, xdesc->xstats_at, 0);
 585}
 586
 587int ipstats_stat_desc_show_xstats(struct ipstats_stat_show_attrs *attrs,
 588                                  const struct ipstats_stat_desc *desc)
 589{
 590        struct ipstats_stat_desc_xstats *xdesc;
 591        const struct rtattr *at;
 592        const struct rtattr *i;
 593        int err;
 594
 595        xdesc = container_of(desc, struct ipstats_stat_desc_xstats, desc);
 596        at = ipstats_stat_show_get_attr(attrs,
 597                                        xdesc->xstats_at,
 598                                        xdesc->link_type_at, &err);
 599        if (at == NULL)
 600                return err;
 601
 602        rtattr_for_each_nested(i, at) {
 603                if (i->rta_type == xdesc->inner_at) {
 604                        print_nl();
 605                        xdesc->show_cb(i);
 606                }
 607        }
 608
 609        return 0;
 610}
 611
 612static const struct ipstats_stat_desc *ipstats_stat_desc_xstats_subs[] = {
 613        &ipstats_stat_desc_xstats_bridge_group,
 614        &ipstats_stat_desc_xstats_bond_group,
 615};
 616
 617static const struct ipstats_stat_desc ipstats_stat_desc_xstats_group = {
 618        .name = "xstats",
 619        .kind = IPSTATS_STAT_DESC_KIND_GROUP,
 620        .subs = ipstats_stat_desc_xstats_subs,
 621        .nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_subs),
 622};
 623
 624static const struct ipstats_stat_desc *ipstats_stat_desc_xstats_slave_subs[] = {
 625        &ipstats_stat_desc_xstats_slave_bridge_group,
 626        &ipstats_stat_desc_xstats_slave_bond_group,
 627};
 628
 629static const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_group = {
 630        .name = "xstats_slave",
 631        .kind = IPSTATS_STAT_DESC_KIND_GROUP,
 632        .subs = ipstats_stat_desc_xstats_slave_subs,
 633        .nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_slave_subs),
 634};
 635
 636static void
 637ipstats_stat_desc_pack_link(struct ipstats_stat_dump_filters *filters,
 638                            const struct ipstats_stat_desc *desc)
 639{
 640        ipstats_stat_desc_enable_bit(filters,
 641                                     IFLA_STATS_LINK_64, 0);
 642}
 643
 644static int
 645ipstats_stat_desc_show_link(struct ipstats_stat_show_attrs *attrs,
 646                            const struct ipstats_stat_desc *desc)
 647{
 648        print_nl();
 649        return ipstats_show_64(attrs, IFLA_STATS_LINK_64, 0);
 650}
 651
 652static const struct ipstats_stat_desc ipstats_stat_desc_toplev_link = {
 653        .name = "link",
 654        .kind = IPSTATS_STAT_DESC_KIND_LEAF,
 655        .pack = &ipstats_stat_desc_pack_link,
 656        .show = &ipstats_stat_desc_show_link,
 657};
 658
 659static const struct ipstats_stat_desc ipstats_stat_desc_afstats_group;
 660
 661static void
 662ipstats_stat_desc_pack_afstats(struct ipstats_stat_dump_filters *filters,
 663                               const struct ipstats_stat_desc *desc)
 664{
 665        ipstats_stat_desc_enable_bit(filters, IFLA_STATS_AF_SPEC, 0);
 666}
 667
 668static int
 669ipstats_stat_desc_show_afstats_mpls(struct ipstats_stat_show_attrs *attrs,
 670                                    const struct ipstats_stat_desc *desc)
 671{
 672        struct rtattr *mrtb[MPLS_STATS_MAX+1];
 673        struct mpls_link_stats stats;
 674        const struct rtattr *at;
 675        int err;
 676
 677        at = ipstats_stat_show_get_attr(attrs, IFLA_STATS_AF_SPEC,
 678                                        AF_MPLS, &err);
 679        if (at == NULL)
 680                return err;
 681
 682        parse_rtattr_nested(mrtb, MPLS_STATS_MAX, at);
 683        if (mrtb[MPLS_STATS_LINK] == NULL)
 684                return -ENOENT;
 685
 686        IPSTATS_RTA_PAYLOAD(stats, mrtb[MPLS_STATS_LINK]);
 687
 688        print_nl();
 689        open_json_object("mpls_stats");
 690        print_mpls_link_stats(stdout, &stats, "    ");
 691        close_json_object();
 692        return 0;
 693}
 694
 695static const struct ipstats_stat_desc ipstats_stat_desc_afstats_mpls = {
 696        .name = "mpls",
 697        .kind = IPSTATS_STAT_DESC_KIND_LEAF,
 698        .pack = &ipstats_stat_desc_pack_afstats,
 699        .show = &ipstats_stat_desc_show_afstats_mpls,
 700};
 701
 702static const struct ipstats_stat_desc *ipstats_stat_desc_afstats_subs[] = {
 703        &ipstats_stat_desc_afstats_mpls,
 704};
 705
 706static const struct ipstats_stat_desc ipstats_stat_desc_afstats_group = {
 707        .name = "afstats",
 708        .kind = IPSTATS_STAT_DESC_KIND_GROUP,
 709        .subs = ipstats_stat_desc_afstats_subs,
 710        .nsubs = ARRAY_SIZE(ipstats_stat_desc_afstats_subs),
 711};
 712static const struct ipstats_stat_desc *ipstats_stat_desc_toplev_subs[] = {
 713        &ipstats_stat_desc_toplev_link,
 714        &ipstats_stat_desc_xstats_group,
 715        &ipstats_stat_desc_xstats_slave_group,
 716        &ipstats_stat_desc_offload_group,
 717        &ipstats_stat_desc_afstats_group,
 718};
 719
 720static const struct ipstats_stat_desc ipstats_stat_desc_toplev_group = {
 721        .name = "top-level",
 722        .kind = IPSTATS_STAT_DESC_KIND_GROUP,
 723        .subs = ipstats_stat_desc_toplev_subs,
 724        .nsubs = ARRAY_SIZE(ipstats_stat_desc_toplev_subs),
 725};
 726
 727static void ipstats_show_group(const struct ipstats_sel *sel)
 728{
 729        int i;
 730
 731        for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
 732                if (sel->sel[i] == NULL)
 733                        break;
 734                print_string(PRINT_JSON, ipstats_levels[i], NULL, sel->sel[i]);
 735                print_string(PRINT_FP, NULL, " %s ", ipstats_levels[i]);
 736                print_string(PRINT_FP, NULL, "%s", sel->sel[i]);
 737        }
 738}
 739
 740static int
 741ipstats_process_ifsm(FILE *fp, struct nlmsghdr *answer,
 742                     struct ipstats_stat_enabled *enabled)
 743{
 744        struct ipstats_stat_show_attrs show_attrs = {};
 745        const char *dev;
 746        int err = 0;
 747        int i;
 748
 749        show_attrs.ifsm = NLMSG_DATA(answer);
 750        show_attrs.len = (answer->nlmsg_len -
 751                          NLMSG_LENGTH(sizeof(*show_attrs.ifsm)));
 752        if (show_attrs.len < 0) {
 753                fprintf(stderr, "BUG: wrong nlmsg len %d\n", show_attrs.len);
 754                return -EINVAL;
 755        }
 756
 757        err = ipstats_stat_show_attrs_alloc_tb(&show_attrs, 0);
 758        if (err)
 759                return err;
 760
 761        dev = ll_index_to_name(show_attrs.ifsm->ifindex);
 762
 763        print_headers(fp, "[STATS]");
 764
 765        for (i = 0; i < enabled->nenabled; i++) {
 766                const struct ipstats_stat_desc *desc = enabled->enabled[i].desc;
 767
 768                open_json_object(NULL);
 769                print_int(PRINT_ANY, "ifindex", "%d:",
 770                          show_attrs.ifsm->ifindex);
 771                print_color_string(PRINT_ANY, COLOR_IFNAME,
 772                                   "ifname", " %s:", dev);
 773                ipstats_show_group(&enabled->enabled[i].sel);
 774                err = desc->show(&show_attrs, desc);
 775                if (err != 0)
 776                        goto out;
 777                close_json_object();
 778                print_nl();
 779        }
 780
 781out:
 782        ipstats_stat_show_attrs_free(&show_attrs);
 783        return err;
 784}
 785
 786static bool
 787ipstats_req_should_filter_at(struct ipstats_stat_dump_filters *filters, int at)
 788{
 789        return filters->mask[at] != 0 &&
 790               filters->mask[at] != (1 << ipstats_stat_ifla_max[at]) - 1;
 791}
 792
 793static int
 794ipstats_req_add_filters(struct ipstats_req *req, void *data)
 795{
 796        struct ipstats_stat_dump_filters dump_filters = {};
 797        struct ipstats_stat_enabled *enabled = data;
 798        bool get_filters = false;
 799        int i;
 800
 801        for (i = 0; i < enabled->nenabled; i++)
 802                enabled->enabled[i].desc->pack(&dump_filters,
 803                                               enabled->enabled[i].desc);
 804
 805        for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
 806                if (ipstats_req_should_filter_at(&dump_filters, i)) {
 807                        get_filters = true;
 808                        break;
 809                }
 810        }
 811
 812        req->ifsm.filter_mask = dump_filters.mask[0];
 813        if (get_filters) {
 814                struct rtattr *nest;
 815
 816                nest = addattr_nest(&req->nlh, sizeof(*req),
 817                                    IFLA_STATS_GET_FILTERS | NLA_F_NESTED);
 818
 819                for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
 820                        if (ipstats_req_should_filter_at(&dump_filters, i))
 821                                addattr32(&req->nlh, sizeof(*req), i,
 822                                          dump_filters.mask[i]);
 823                }
 824
 825                addattr_nest_end(&req->nlh, nest);
 826        }
 827
 828        return 0;
 829}
 830
 831static int
 832ipstats_show_one(int ifindex, struct ipstats_stat_enabled *enabled)
 833{
 834        struct ipstats_req req = {
 835                .nlh.nlmsg_flags = NLM_F_REQUEST,
 836                .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
 837                .nlh.nlmsg_type = RTM_GETSTATS,
 838                .ifsm.family = PF_UNSPEC,
 839                .ifsm.ifindex = ifindex,
 840        };
 841        struct nlmsghdr *answer;
 842        int err = 0;
 843
 844        ipstats_req_add_filters(&req, enabled);
 845        if (rtnl_talk(&rth, &req.nlh, &answer) < 0)
 846                return -2;
 847        err = ipstats_process_ifsm(stdout, answer, enabled);
 848        free(answer);
 849
 850        return err;
 851}
 852
 853static int ipstats_dump_one(struct nlmsghdr *n, void *arg)
 854{
 855        struct ipstats_stat_enabled *enabled = arg;
 856        int rc;
 857
 858        rc = ipstats_process_ifsm(stdout, n, enabled);
 859        if (rc)
 860                return rc;
 861
 862        print_nl();
 863        return 0;
 864}
 865
 866static int ipstats_dump(struct ipstats_stat_enabled *enabled)
 867{
 868        int rc = 0;
 869
 870        if (rtnl_statsdump_req_filter(&rth, PF_UNSPEC, 0,
 871                                      ipstats_req_add_filters,
 872                                      enabled) < 0) {
 873                perror("Cannot send dump request");
 874                return -2;
 875        }
 876
 877        if (rtnl_dump_filter(&rth, ipstats_dump_one, enabled) < 0) {
 878                fprintf(stderr, "Dump terminated\n");
 879                rc = -2;
 880        }
 881
 882        fflush(stdout);
 883        return rc;
 884}
 885
 886static int
 887ipstats_show_do(int ifindex, struct ipstats_stat_enabled *enabled)
 888{
 889        int rc;
 890
 891        new_json_obj(json);
 892        if (ifindex)
 893                rc = ipstats_show_one(ifindex, enabled);
 894        else
 895                rc = ipstats_dump(enabled);
 896        delete_json_obj();
 897
 898        return rc;
 899}
 900
 901static int ipstats_add_enabled(struct ipstats_stat_enabled_one ens[],
 902                               size_t nens,
 903                               struct ipstats_stat_enabled *enabled)
 904{
 905        struct ipstats_stat_enabled_one *new_en;
 906
 907        new_en = realloc(enabled->enabled,
 908                         sizeof(*new_en) * (enabled->nenabled + nens));
 909        if (new_en == NULL)
 910                return -ENOMEM;
 911
 912        enabled->enabled = new_en;
 913        while (nens-- > 0)
 914                enabled->enabled[enabled->nenabled++] = *ens++;
 915        return 0;
 916}
 917
 918static void ipstats_select_push(struct ipstats_sel *sel, const char *name)
 919{
 920        int i;
 921
 922        for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
 923                if (sel->sel[i] == NULL) {
 924                        sel->sel[i] = name;
 925                        return;
 926                }
 927
 928        assert(false);
 929}
 930
 931static int
 932ipstats_enable_recursively(const struct ipstats_stat_desc *desc,
 933                           struct ipstats_stat_enabled *enabled,
 934                           const struct ipstats_sel *sel)
 935{
 936        bool found = false;
 937        size_t i;
 938        int err;
 939
 940        if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
 941                struct ipstats_stat_enabled_one en[] = {{
 942                        .desc = desc,
 943                        .sel = *sel,
 944                }};
 945
 946                return ipstats_add_enabled(en, ARRAY_SIZE(en), enabled);
 947        }
 948
 949        for (i = 0; i < desc->nsubs; i++) {
 950                struct ipstats_sel subsel = *sel;
 951
 952                ipstats_select_push(&subsel, desc->subs[i]->name);
 953                err = ipstats_enable_recursively(desc->subs[i], enabled,
 954                                                 &subsel);
 955                if (err == -ENOENT)
 956                        continue;
 957                if (err != 0)
 958                        return err;
 959                found = true;
 960        }
 961
 962        return found ? 0 : -ENOENT;
 963}
 964
 965static int ipstats_comp_enabled(const void *a, const void *b)
 966{
 967        const struct ipstats_stat_enabled_one *en_a = a;
 968        const struct ipstats_stat_enabled_one *en_b = b;
 969
 970        if (en_a->desc < en_b->desc)
 971                return -1;
 972        if (en_a->desc > en_b->desc)
 973                return 1;
 974
 975        return 0;
 976}
 977
 978static void ipstats_enabled_free(struct ipstats_stat_enabled *enabled)
 979{
 980        free(enabled->enabled);
 981}
 982
 983static const struct ipstats_stat_desc *
 984ipstats_stat_desc_find(const struct ipstats_stat_desc *desc,
 985                       const char *name)
 986{
 987        size_t i;
 988
 989        assert(desc->kind == IPSTATS_STAT_DESC_KIND_GROUP);
 990        for (i = 0; i < desc->nsubs; i++) {
 991                const struct ipstats_stat_desc *sub = desc->subs[i];
 992
 993                if (strcmp(sub->name, name) == 0)
 994                        return sub;
 995        }
 996
 997        return NULL;
 998}
 999
1000static const struct ipstats_stat_desc *
1001ipstats_enable_find_stat_desc(struct ipstats_sel *sel)
1002{
1003        const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
1004        const struct ipstats_stat_desc *desc = toplev;
1005        int i;
1006
1007        for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
1008                const struct ipstats_stat_desc *next_desc;
1009
1010                if (sel->sel[i] == NULL)
1011                        break;
1012                if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
1013                        fprintf(stderr, "Error: %s %s requested inside leaf %s %s\n",
1014                                ipstats_levels[i], sel->sel[i],
1015                                ipstats_levels[i - 1], desc->name);
1016                        return NULL;
1017                }
1018
1019                next_desc = ipstats_stat_desc_find(desc, sel->sel[i]);
1020                if (next_desc == NULL) {
1021                        fprintf(stderr, "Error: no %s named %s found inside %s\n",
1022                                ipstats_levels[i], sel->sel[i], desc->name);
1023                        return NULL;
1024                }
1025
1026                desc = next_desc;
1027        }
1028
1029        return desc;
1030}
1031
1032static int ipstats_enable(struct ipstats_sel *sel,
1033                          struct ipstats_stat_enabled *enabled)
1034{
1035        struct ipstats_stat_enabled new_enabled = {};
1036        const struct ipstats_stat_desc *desc;
1037        size_t i, j;
1038        int err = 0;
1039
1040        desc = ipstats_enable_find_stat_desc(sel);
1041        if (desc == NULL)
1042                return -EINVAL;
1043
1044        err = ipstats_enable_recursively(desc, &new_enabled, sel);
1045        if (err != 0)
1046                return err;
1047
1048        err = ipstats_add_enabled(new_enabled.enabled, new_enabled.nenabled,
1049                                  enabled);
1050        if (err != 0)
1051                goto out;
1052
1053        qsort(enabled->enabled, enabled->nenabled, sizeof(*enabled->enabled),
1054              ipstats_comp_enabled);
1055
1056        for (i = 1, j = 1; i < enabled->nenabled; i++) {
1057                if (enabled->enabled[i].desc != enabled->enabled[j - 1].desc)
1058                        enabled->enabled[j++] = enabled->enabled[i];
1059        }
1060        enabled->nenabled = j;
1061
1062out:
1063        ipstats_enabled_free(&new_enabled);
1064        return err;
1065}
1066
1067static int ipstats_enable_check(struct ipstats_sel *sel,
1068                                struct ipstats_stat_enabled *enabled)
1069{
1070        int err;
1071        int i;
1072
1073        err = ipstats_enable(sel, enabled);
1074        if (err == -ENOENT) {
1075                fprintf(stderr, "The request for");
1076                for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
1077                        if (sel->sel[i] != NULL)
1078                                fprintf(stderr, " %s %s",
1079                                        ipstats_levels[i], sel->sel[i]);
1080                        else
1081                                break;
1082                fprintf(stderr, " did not match any known stats.\n");
1083        }
1084
1085        return err;
1086}
1087
1088static int do_help(void)
1089{
1090        const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
1091        int i;
1092
1093        fprintf(stderr,
1094                "Usage: ip stats help\n"
1095                "       ip stats show [ dev DEV ] [ group GROUP [ subgroup SUBGROUP [ suite SUITE ] ... ] ... ] ...\n"
1096                "       ip stats set dev DEV l3_stats { on | off }\n"
1097                );
1098
1099        for (i = 0; i < toplev->nsubs; i++) {
1100                const struct ipstats_stat_desc *desc = toplev->subs[i];
1101
1102                if (i == 0)
1103                        fprintf(stderr, "GROUP := { %s", desc->name);
1104                else
1105                        fprintf(stderr, " | %s", desc->name);
1106        }
1107        if (i > 0)
1108                fprintf(stderr, " }\n");
1109
1110        for (i = 0; i < toplev->nsubs; i++) {
1111                const struct ipstats_stat_desc *desc = toplev->subs[i];
1112                bool opened = false;
1113                size_t j;
1114
1115                if (desc->kind != IPSTATS_STAT_DESC_KIND_GROUP)
1116                        continue;
1117
1118                for (j = 0; j < desc->nsubs; j++) {
1119                        size_t k;
1120
1121                        if (j == 0)
1122                                fprintf(stderr, "%s SUBGROUP := {", desc->name);
1123                        else
1124                                fprintf(stderr, " |");
1125                        fprintf(stderr, " %s", desc->subs[j]->name);
1126                        opened = true;
1127
1128                        if (desc->subs[j]->kind != IPSTATS_STAT_DESC_KIND_GROUP)
1129                                continue;
1130
1131                        for (k = 0; k < desc->subs[j]->nsubs; k++)
1132                                fprintf(stderr, " [ suite %s ]",
1133                                        desc->subs[j]->subs[k]->name);
1134                }
1135                if (opened)
1136                        fprintf(stderr, " }\n");
1137        }
1138
1139        return 0;
1140}
1141
1142static int ipstats_select(struct ipstats_sel *old_sel,
1143                          const char *new_sel, int level,
1144                          struct ipstats_stat_enabled *enabled)
1145{
1146        int err;
1147        int i;
1148
1149        for (i = 0; i < level; i++) {
1150                if (old_sel->sel[i] == NULL) {
1151                        fprintf(stderr, "Error: %s %s requested without selecting a %s first\n",
1152                                ipstats_levels[level], new_sel,
1153                                ipstats_levels[i]);
1154                        return -EINVAL;
1155                }
1156        }
1157
1158        for (i = level; i < IPSTATS_LEVELS_COUNT; i++) {
1159                if (old_sel->sel[i] != NULL) {
1160                        err = ipstats_enable_check(old_sel, enabled);
1161                        if (err)
1162                                return err;
1163                        break;
1164                }
1165        }
1166
1167        old_sel->sel[level] = new_sel;
1168        for (i = level + 1; i < IPSTATS_LEVELS_COUNT; i++)
1169                old_sel->sel[i] = NULL;
1170
1171        return 0;
1172}
1173
1174static int ipstats_show(int argc, char **argv)
1175{
1176        struct ipstats_stat_enabled enabled = {};
1177        struct ipstats_sel sel = {};
1178        const char *dev = NULL;
1179        int ifindex;
1180        int err;
1181        int i;
1182
1183        while (argc > 0) {
1184                if (strcmp(*argv, "dev") == 0) {
1185                        NEXT_ARG();
1186                        if (dev != NULL)
1187                                duparg2("dev", *argv);
1188                        if (check_ifname(*argv))
1189                                invarg("\"dev\" not a valid ifname", *argv);
1190                        dev = *argv;
1191                } else if (strcmp(*argv, "help") == 0) {
1192                        do_help();
1193                        return 0;
1194                } else {
1195                        bool found_level = false;
1196
1197                        for (i = 0; i < ARRAY_SIZE(ipstats_levels); i++) {
1198                                if (strcmp(*argv, ipstats_levels[i]) == 0) {
1199                                        NEXT_ARG();
1200                                        err = ipstats_select(&sel, *argv, i,
1201                                                             &enabled);
1202                                        if (err)
1203                                                goto err;
1204
1205                                        found_level = true;
1206                                }
1207                        }
1208
1209                        if (!found_level) {
1210                                fprintf(stderr, "What is \"%s\"?\n", *argv);
1211                                do_help();
1212                                err = -EINVAL;
1213                                goto err;
1214                        }
1215                }
1216
1217                NEXT_ARG_FWD();
1218        }
1219
1220        /* Push whatever was given. */
1221        err = ipstats_enable_check(&sel, &enabled);
1222        if (err)
1223                goto err;
1224
1225        if (dev) {
1226                ifindex = ll_name_to_index(dev);
1227                if (!ifindex) {
1228                        err = nodev(dev);
1229                        goto err;
1230                }
1231        } else {
1232                ifindex = 0;
1233        }
1234
1235
1236        err = ipstats_show_do(ifindex, &enabled);
1237
1238err:
1239        ipstats_enabled_free(&enabled);
1240        return err;
1241}
1242
1243static int ipstats_set_do(int ifindex, int at, bool enable)
1244{
1245        struct ipstats_req req = {
1246                .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
1247                .nlh.nlmsg_flags = NLM_F_REQUEST,
1248                .nlh.nlmsg_type = RTM_SETSTATS,
1249                .ifsm.family = PF_UNSPEC,
1250                .ifsm.ifindex = ifindex,
1251        };
1252
1253        addattr8(&req.nlh, sizeof(req), at, enable);
1254
1255        if (rtnl_talk(&rth, &req.nlh, NULL) < 0)
1256                return -2;
1257        return 0;
1258}
1259
1260static int ipstats_set(int argc, char **argv)
1261{
1262        const char *dev = NULL;
1263        bool enable = false;
1264        int ifindex;
1265        int at = 0;
1266
1267        while (argc > 0) {
1268                if (strcmp(*argv, "dev") == 0) {
1269                        NEXT_ARG();
1270                        if (dev)
1271                                duparg2("dev", *argv);
1272                        if (check_ifname(*argv))
1273                                invarg("\"dev\" not a valid ifname", *argv);
1274                        dev = *argv;
1275                } else if (strcmp(*argv, "l3_stats") == 0) {
1276                        int err;
1277
1278                        NEXT_ARG();
1279                        if (at) {
1280                                fprintf(stderr, "A statistics suite to toggle was already given.\n");
1281                                return -EINVAL;
1282                        }
1283                        at = IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS;
1284                        enable = parse_on_off("l3_stats", *argv, &err);
1285                        if (err)
1286                                return err;
1287                } else if (strcmp(*argv, "help") == 0) {
1288                        do_help();
1289                        return 0;
1290                } else {
1291                        fprintf(stderr, "What is \"%s\"?\n", *argv);
1292                        do_help();
1293                        return -EINVAL;
1294                }
1295
1296                NEXT_ARG_FWD();
1297        }
1298
1299        if (!dev) {
1300                fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
1301                exit(-1);
1302        }
1303
1304        if (!at) {
1305                fprintf(stderr, "Not enough information: stat type to toggle is required.\n");
1306                exit(-1);
1307        }
1308
1309        ifindex = ll_name_to_index(dev);
1310        if (!ifindex)
1311                return nodev(dev);
1312
1313        return ipstats_set_do(ifindex, at, enable);
1314}
1315
1316int do_ipstats(int argc, char **argv)
1317{
1318        int rc;
1319
1320        if (argc == 0) {
1321                rc = ipstats_show(0, NULL);
1322        } else if (strcmp(*argv, "help") == 0) {
1323                do_help();
1324                rc = 0;
1325        } else if (strcmp(*argv, "show") == 0) {
1326                /* Invoking "stats show" implies one -s. Passing -d adds one
1327                 * more -s.
1328                 */
1329                show_stats += show_details + 1;
1330                rc = ipstats_show(argc-1, argv+1);
1331        } else if (strcmp(*argv, "set") == 0) {
1332                rc = ipstats_set(argc-1, argv+1);
1333        } else {
1334                fprintf(stderr, "Command \"%s\" is unknown, try \"ip stats help\".\n",
1335                        *argv);
1336                rc = -1;
1337        }
1338
1339        return rc;
1340}
1341
1342int ipstats_print(struct nlmsghdr *n, void *arg)
1343{
1344        struct ipstats_stat_enabled_one one = {
1345                .desc = &ipstats_stat_desc_offload_hw_s_info,
1346        };
1347        struct ipstats_stat_enabled enabled = {
1348                .enabled = &one,
1349                .nenabled = 1,
1350        };
1351        FILE *fp = arg;
1352        int rc;
1353
1354        rc = ipstats_process_ifsm(fp, n, &enabled);
1355        if (rc)
1356                return rc;
1357
1358        fflush(fp);
1359        return 0;
1360}
1361