iproute2/tc/q_netem.c
<<
>>
Prefs
   1/*
   2 * q_netem.c            NETEM.
   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:     Stephen Hemminger <shemminger@linux-foundation.org>
  10 *
  11 */
  12
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <math.h>
  16#include <ctype.h>
  17#include <unistd.h>
  18#include <fcntl.h>
  19#include <stdint.h>
  20#include <sys/socket.h>
  21#include <netinet/in.h>
  22#include <arpa/inet.h>
  23#include <string.h>
  24#include <errno.h>
  25
  26#include "utils.h"
  27#include "tc_util.h"
  28#include "tc_common.h"
  29
  30static void explain(void)
  31{
  32        fprintf(stderr,
  33                "Usage: ... netem       [ limit PACKETS ]\n" \
  34                "                       [ delay TIME [ JITTER [CORRELATION]]]\n" \
  35                "                       [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
  36                "                       [ corrupt PERCENT [CORRELATION]]\n" \
  37                "                       [ duplicate PERCENT [CORRELATION]]\n" \
  38                "                       [ loss random PERCENT [CORRELATION]]\n" \
  39                "                       [ loss state P13 [P31 [P32 [P23 P14]]]\n" \
  40                "                       [ loss gemodel PERCENT [R [1-H [1-K]]]\n" \
  41                "                       [ ecn ]\n" \
  42                "                       [ reorder PERCENT [CORRELATION] [ gap DISTANCE ]]\n" \
  43                "                       [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n" \
  44                "                       [ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS]" \
  45                " [bytes MAX_BYTES]]\n" \
  46                "               [ slot distribution" \
  47                " {uniform|normal|pareto|paretonormal|custom} DELAY JITTER" \
  48                " [packets MAX_PACKETS] [bytes MAX_BYTES]]\n");
  49}
  50
  51static void explain1(const char *arg)
  52{
  53        fprintf(stderr, "Illegal \"%s\"\n", arg);
  54}
  55
  56/* Upper bound on size of distribution
  57 *  really (TCA_BUF_MAX - other headers) / sizeof (__s16)
  58 */
  59#define MAX_DIST        (16*1024)
  60
  61/* Print values only if they are non-zero */
  62static void __print_int_opt(const char *label_json, const char *label_fp,
  63                            int val)
  64{
  65        print_int(PRINT_ANY, label_json, val ? label_fp : "", val);
  66}
  67#define PRINT_INT_OPT(label, val)                       \
  68        __print_int_opt(label, " " label " %d", (val))
  69
  70/* Time print prints normally with varying units, but for JSON prints
  71 * in seconds (1ms vs 0.001).
  72 */
  73static void __print_time64(const char *label_json, const char *label_fp,
  74                           __u64 val)
  75{
  76        SPRINT_BUF(b1);
  77
  78        print_string(PRINT_FP, NULL, label_fp, sprint_time64(val, b1));
  79        print_float(PRINT_JSON, label_json, NULL, val / 1000000000.);
  80}
  81#define __PRINT_TIME64(label_json, label_fp, val)       \
  82        __print_time64(label_json, label_fp " %s", (val))
  83#define PRINT_TIME64(label, val) __PRINT_TIME64(label, " " label, (val))
  84
  85/* Percent print prints normally in percentage points, but for JSON prints
  86 * an absolute value (1% vs 0.01).
  87 */
  88static void __print_percent(const char *label_json, const char *label_fp,
  89                            __u32 per)
  90{
  91        print_float(PRINT_FP, NULL, label_fp, (100. * per) / UINT32_MAX);
  92        print_float(PRINT_JSON, label_json, NULL, (1. * per) / UINT32_MAX);
  93}
  94#define __PRINT_PERCENT(label_json, label_fp, per)              \
  95        __print_percent(label_json, label_fp " %g%%", (per))
  96#define PRINT_PERCENT(label, per) __PRINT_PERCENT(label, " " label, (per))
  97
  98/* scaled value used to percent of maximum. */
  99static void set_percent(__u32 *percent, double per)
 100{
 101        *percent = rint(per * UINT32_MAX);
 102}
 103
 104static int get_percent(__u32 *percent, const char *str)
 105{
 106        double per;
 107
 108        if (parse_percent(&per, str))
 109                return -1;
 110
 111        set_percent(percent, per);
 112        return 0;
 113}
 114
 115static void print_corr(bool present, __u32 value)
 116{
 117        if (!is_json_context()) {
 118                if (present)
 119                        __PRINT_PERCENT("", "", value);
 120        } else {
 121                PRINT_PERCENT("correlation", value);
 122        }
 123}
 124
 125/*
 126 * Simplistic file parser for distrbution data.
 127 * Format is:
 128 *      # comment line(s)
 129 *      data0 data1 ...
 130 */
 131static int get_distribution(const char *type, __s16 *data, int maxdata)
 132{
 133        FILE *f;
 134        int n;
 135        long x;
 136        size_t len;
 137        char *line = NULL;
 138        char name[128];
 139
 140        snprintf(name, sizeof(name), "%s/%s.dist", get_tc_lib(), type);
 141        if ((f = fopen(name, "r")) == NULL) {
 142                fprintf(stderr, "No distribution data for %s (%s: %s)\n",
 143                        type, name, strerror(errno));
 144                return -1;
 145        }
 146
 147        n = 0;
 148        while (getline(&line, &len, f) != -1) {
 149                char *p, *endp;
 150
 151                if (*line == '\n' || *line == '#')
 152                        continue;
 153
 154                for (p = line; ; p = endp) {
 155                        x = strtol(p, &endp, 0);
 156                        if (endp == p)
 157                                break;
 158
 159                        if (n >= maxdata) {
 160                                fprintf(stderr, "%s: too much data\n",
 161                                        name);
 162                                n = -1;
 163                                goto error;
 164                        }
 165                        data[n++] = x;
 166                }
 167        }
 168 error:
 169        free(line);
 170        fclose(f);
 171        return n;
 172}
 173
 174#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
 175#define NEXT_IS_SIGNED_NUMBER() \
 176        (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
 177
 178/* Adjust for the fact that psched_ticks aren't always usecs
 179   (based on kernel PSCHED_CLOCK configuration */
 180static int get_ticks(__u32 *ticks, const char *str)
 181{
 182        unsigned int t;
 183
 184        if (get_time(&t, str))
 185                return -1;
 186
 187        if (tc_core_time2big(t)) {
 188                fprintf(stderr, "Illegal %u time (too large)\n", t);
 189                return -1;
 190        }
 191
 192        *ticks = tc_core_time2tick(t);
 193        return 0;
 194}
 195
 196static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
 197                           struct nlmsghdr *n, const char *dev)
 198{
 199        int dist_size = 0;
 200        int slot_dist_size = 0;
 201        struct rtattr *tail;
 202        struct tc_netem_qopt opt = { .limit = 1000 };
 203        struct tc_netem_corr cor = {};
 204        struct tc_netem_reorder reorder = {};
 205        struct tc_netem_corrupt corrupt = {};
 206        struct tc_netem_gimodel gimodel;
 207        struct tc_netem_gemodel gemodel;
 208        struct tc_netem_rate rate = {};
 209        struct tc_netem_slot slot = {};
 210        __s16 *dist_data = NULL;
 211        __s16 *slot_dist_data = NULL;
 212        __u16 loss_type = NETEM_LOSS_UNSPEC;
 213        int present[__TCA_NETEM_MAX] = {};
 214        __u64 rate64 = 0;
 215
 216        for ( ; argc > 0; --argc, ++argv) {
 217                if (matches(*argv, "limit") == 0) {
 218                        NEXT_ARG();
 219                        if (get_size(&opt.limit, *argv)) {
 220                                explain1("limit");
 221                                return -1;
 222                        }
 223                } else if (matches(*argv, "latency") == 0 ||
 224                           matches(*argv, "delay") == 0) {
 225                        NEXT_ARG();
 226                        if (get_ticks(&opt.latency, *argv)) {
 227                                explain1("latency");
 228                                return -1;
 229                        }
 230
 231                        if (NEXT_IS_NUMBER()) {
 232                                NEXT_ARG();
 233                                if (get_ticks(&opt.jitter, *argv)) {
 234                                        explain1("latency");
 235                                        return -1;
 236                                }
 237
 238                                if (NEXT_IS_NUMBER()) {
 239                                        NEXT_ARG();
 240                                        ++present[TCA_NETEM_CORR];
 241                                        if (get_percent(&cor.delay_corr, *argv)) {
 242                                                explain1("latency");
 243                                                return -1;
 244                                        }
 245                                }
 246                        }
 247                } else if (matches(*argv, "loss") == 0 ||
 248                           matches(*argv, "drop") == 0) {
 249                        if (opt.loss > 0 || loss_type != NETEM_LOSS_UNSPEC) {
 250                                explain1("duplicate loss argument\n");
 251                                return -1;
 252                        }
 253
 254                        NEXT_ARG();
 255                        /* Old (deprecated) random loss model syntax */
 256                        if (isdigit(argv[0][0]))
 257                                goto random_loss_model;
 258
 259                        if (!strcmp(*argv, "random")) {
 260                                NEXT_ARG();
 261                        random_loss_model:
 262                                if (get_percent(&opt.loss, *argv)) {
 263                                        explain1("loss percent");
 264                                        return -1;
 265                                }
 266                                if (NEXT_IS_NUMBER()) {
 267                                        NEXT_ARG();
 268                                        ++present[TCA_NETEM_CORR];
 269                                        if (get_percent(&cor.loss_corr, *argv)) {
 270                                                explain1("loss correlation");
 271                                                return -1;
 272                                        }
 273                                }
 274                        } else if (!strcmp(*argv, "state")) {
 275                                double p13;
 276
 277                                NEXT_ARG();
 278                                if (parse_percent(&p13, *argv)) {
 279                                        explain1("loss p13");
 280                                        return -1;
 281                                }
 282
 283                                /* set defaults */
 284                                set_percent(&gimodel.p13, p13);
 285                                set_percent(&gimodel.p31, 1. - p13);
 286                                set_percent(&gimodel.p32, 0);
 287                                set_percent(&gimodel.p23, 1.);
 288                                set_percent(&gimodel.p14, 0);
 289                                loss_type = NETEM_LOSS_GI;
 290
 291                                if (!NEXT_IS_NUMBER())
 292                                        continue;
 293                                NEXT_ARG();
 294                                if (get_percent(&gimodel.p31, *argv)) {
 295                                        explain1("loss p31");
 296                                        return -1;
 297                                }
 298
 299                                if (!NEXT_IS_NUMBER())
 300                                        continue;
 301                                NEXT_ARG();
 302                                if (get_percent(&gimodel.p32, *argv)) {
 303                                        explain1("loss p32");
 304                                        return -1;
 305                                }
 306
 307                                if (!NEXT_IS_NUMBER())
 308                                        continue;
 309                                NEXT_ARG();
 310                                if (get_percent(&gimodel.p23, *argv)) {
 311                                        explain1("loss p23");
 312                                        return -1;
 313                                }
 314                                if (!NEXT_IS_NUMBER())
 315                                        continue;
 316                                NEXT_ARG();
 317                                if (get_percent(&gimodel.p14, *argv)) {
 318                                        explain1("loss p14");
 319                                        return -1;
 320                                }
 321
 322                        } else if (!strcmp(*argv, "gemodel")) {
 323                                double p;
 324
 325                                NEXT_ARG();
 326                                if (parse_percent(&p, *argv)) {
 327                                        explain1("loss gemodel p");
 328                                        return -1;
 329                                }
 330                                set_percent(&gemodel.p, p);
 331
 332                                /* set defaults */
 333                                set_percent(&gemodel.r, 1. - p);
 334                                set_percent(&gemodel.h, 0);
 335                                set_percent(&gemodel.k1, 0);
 336                                loss_type = NETEM_LOSS_GE;
 337
 338                                if (!NEXT_IS_NUMBER())
 339                                        continue;
 340                                NEXT_ARG();
 341                                if (get_percent(&gemodel.r, *argv)) {
 342                                        explain1("loss gemodel r");
 343                                        return -1;
 344                                }
 345
 346                                if (!NEXT_IS_NUMBER())
 347                                        continue;
 348                                NEXT_ARG();
 349                                if (get_percent(&gemodel.h, *argv)) {
 350                                        explain1("loss gemodel h");
 351                                        return -1;
 352                                }
 353                                /* netem option is "1-h" but kernel
 354                                 * expects "h".
 355                                 */
 356                                gemodel.h = UINT32_MAX - gemodel.h;
 357
 358                                if (!NEXT_IS_NUMBER())
 359                                        continue;
 360                                NEXT_ARG();
 361                                if (get_percent(&gemodel.k1, *argv)) {
 362                                        explain1("loss gemodel k");
 363                                        return -1;
 364                                }
 365                        } else {
 366                                fprintf(stderr, "Unknown loss parameter: %s\n",
 367                                        *argv);
 368                                return -1;
 369                        }
 370                } else if (matches(*argv, "ecn") == 0) {
 371                        present[TCA_NETEM_ECN] = 1;
 372                } else if (matches(*argv, "reorder") == 0) {
 373                        NEXT_ARG();
 374                        present[TCA_NETEM_REORDER] = 1;
 375                        if (get_percent(&reorder.probability, *argv)) {
 376                                explain1("reorder");
 377                                return -1;
 378                        }
 379                        if (NEXT_IS_NUMBER()) {
 380                                NEXT_ARG();
 381                                ++present[TCA_NETEM_CORR];
 382                                if (get_percent(&reorder.correlation, *argv)) {
 383                                        explain1("reorder");
 384                                        return -1;
 385                                }
 386                        }
 387                } else if (matches(*argv, "corrupt") == 0) {
 388                        NEXT_ARG();
 389                        present[TCA_NETEM_CORRUPT] = 1;
 390                        if (get_percent(&corrupt.probability, *argv)) {
 391                                explain1("corrupt");
 392                                return -1;
 393                        }
 394                        if (NEXT_IS_NUMBER()) {
 395                                NEXT_ARG();
 396                                ++present[TCA_NETEM_CORR];
 397                                if (get_percent(&corrupt.correlation, *argv)) {
 398                                        explain1("corrupt");
 399                                        return -1;
 400                                }
 401                        }
 402                } else if (matches(*argv, "gap") == 0) {
 403                        NEXT_ARG();
 404                        if (get_u32(&opt.gap, *argv, 0)) {
 405                                explain1("gap");
 406                                return -1;
 407                        }
 408                } else if (matches(*argv, "duplicate") == 0) {
 409                        NEXT_ARG();
 410                        if (get_percent(&opt.duplicate, *argv)) {
 411                                explain1("duplicate");
 412                                return -1;
 413                        }
 414                        if (NEXT_IS_NUMBER()) {
 415                                NEXT_ARG();
 416                                if (get_percent(&cor.dup_corr, *argv)) {
 417                                        explain1("duplicate");
 418                                        return -1;
 419                                }
 420                        }
 421                } else if (matches(*argv, "distribution") == 0) {
 422                        NEXT_ARG();
 423                        dist_data = calloc(sizeof(dist_data[0]), MAX_DIST);
 424                        dist_size = get_distribution(*argv, dist_data, MAX_DIST);
 425                        if (dist_size <= 0) {
 426                                free(dist_data);
 427                                return -1;
 428                        }
 429                } else if (matches(*argv, "rate") == 0) {
 430                        ++present[TCA_NETEM_RATE];
 431                        NEXT_ARG();
 432                        if (strchr(*argv, '%')) {
 433                                if (get_percent_rate64(&rate64, *argv, dev)) {
 434                                        explain1("rate");
 435                                        return -1;
 436                                }
 437                        } else if (get_rate64(&rate64, *argv)) {
 438                                explain1("rate");
 439                                return -1;
 440                        }
 441                        if (NEXT_IS_SIGNED_NUMBER()) {
 442                                NEXT_ARG();
 443                                if (get_s32(&rate.packet_overhead, *argv, 0)) {
 444                                        explain1("rate");
 445                                        return -1;
 446                                }
 447                        }
 448                        if (NEXT_IS_NUMBER()) {
 449                                NEXT_ARG();
 450                                if (get_u32(&rate.cell_size, *argv, 0)) {
 451                                        explain1("rate");
 452                                        return -1;
 453                                }
 454                        }
 455                        if (NEXT_IS_SIGNED_NUMBER()) {
 456                                NEXT_ARG();
 457                                if (get_s32(&rate.cell_overhead, *argv, 0)) {
 458                                        explain1("rate");
 459                                        return -1;
 460                                }
 461                        }
 462                } else if (matches(*argv, "slot") == 0) {
 463                        if (NEXT_IS_NUMBER()) {
 464                                NEXT_ARG();
 465                                present[TCA_NETEM_SLOT] = 1;
 466                                if (get_time64(&slot.min_delay, *argv)) {
 467                                        explain1("slot min_delay");
 468                                        return -1;
 469                                }
 470                                if (NEXT_IS_NUMBER()) {
 471                                        NEXT_ARG();
 472                                        if (get_time64(&slot.max_delay, *argv) ||
 473                                            slot.max_delay < slot.min_delay) {
 474                                                explain1("slot max_delay");
 475                                                return -1;
 476                                        }
 477                                } else {
 478                                        slot.max_delay = slot.min_delay;
 479                                }
 480                        } else {
 481                                NEXT_ARG();
 482                                if (strcmp(*argv, "distribution") == 0) {
 483                                        present[TCA_NETEM_SLOT] = 1;
 484                                        NEXT_ARG();
 485                                        slot_dist_data = calloc(sizeof(slot_dist_data[0]), MAX_DIST);
 486                                        if (!slot_dist_data)
 487                                                return -1;
 488                                        slot_dist_size = get_distribution(*argv, slot_dist_data, MAX_DIST);
 489                                        if (slot_dist_size <= 0) {
 490                                                free(slot_dist_data);
 491                                                return -1;
 492                                        }
 493                                        NEXT_ARG();
 494                                        if (get_time64(&slot.dist_delay, *argv)) {
 495                                                explain1("slot delay");
 496                                                return -1;
 497                                        }
 498                                        NEXT_ARG();
 499                                        if (get_time64(&slot.dist_jitter, *argv)) {
 500                                                explain1("slot jitter");
 501                                                return -1;
 502                                        }
 503                                        if (slot.dist_jitter <= 0) {
 504                                                fprintf(stderr, "Non-positive jitter\n");
 505                                                return -1;
 506                                        }
 507                                } else {
 508                                        fprintf(stderr, "Unknown slot parameter: %s\n",
 509                                                *argv);
 510                                        return -1;
 511                                }
 512                        }
 513                        if (NEXT_ARG_OK() &&
 514                            matches(*(argv+1), "packets") == 0) {
 515                                NEXT_ARG();
 516                                if (!NEXT_ARG_OK() ||
 517                                    get_s32(&slot.max_packets, *(argv+1), 0)) {
 518                                        explain1("slot packets");
 519                                        return -1;
 520                                }
 521                                NEXT_ARG();
 522                        }
 523                        if (NEXT_ARG_OK() &&
 524                            matches(*(argv+1), "bytes") == 0) {
 525                                unsigned int max_bytes;
 526                                NEXT_ARG();
 527                                if (!NEXT_ARG_OK() ||
 528                                    get_size(&max_bytes, *(argv+1))) {
 529                                        explain1("slot bytes");
 530                                        return -1;
 531                                }
 532                                slot.max_bytes = (int) max_bytes;
 533                                NEXT_ARG();
 534                        }
 535                } else if (strcmp(*argv, "help") == 0) {
 536                        explain();
 537                        return -1;
 538                } else {
 539                        fprintf(stderr, "What is \"%s\"?\n", *argv);
 540                        explain();
 541                        return -1;
 542                }
 543        }
 544
 545        tail = NLMSG_TAIL(n);
 546
 547        if (reorder.probability) {
 548                if (opt.latency == 0) {
 549                        fprintf(stderr, "reordering not possible without specifying some delay\n");
 550                        explain();
 551                        return -1;
 552                }
 553                if (opt.gap == 0)
 554                        opt.gap = 1;
 555        } else if (opt.gap > 0) {
 556                fprintf(stderr, "gap specified without reorder probability\n");
 557                explain();
 558                return -1;
 559        }
 560
 561        if (present[TCA_NETEM_ECN]) {
 562                if (opt.loss <= 0 && loss_type == NETEM_LOSS_UNSPEC) {
 563                        fprintf(stderr, "ecn requested without loss model\n");
 564                        explain();
 565                        return -1;
 566                }
 567        }
 568
 569        if (dist_data && (opt.latency == 0 || opt.jitter == 0)) {
 570                fprintf(stderr, "distribution specified but no latency and jitter values\n");
 571                explain();
 572                return -1;
 573        }
 574
 575        if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
 576                return -1;
 577
 578        if (present[TCA_NETEM_CORR] &&
 579            addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
 580                return -1;
 581
 582        if (present[TCA_NETEM_REORDER] &&
 583            addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
 584                return -1;
 585
 586        if (present[TCA_NETEM_ECN] &&
 587            addattr_l(n, 1024, TCA_NETEM_ECN, &present[TCA_NETEM_ECN],
 588                      sizeof(present[TCA_NETEM_ECN])) < 0)
 589                return -1;
 590
 591        if (present[TCA_NETEM_CORRUPT] &&
 592            addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
 593                return -1;
 594
 595        if (present[TCA_NETEM_SLOT] &&
 596            addattr_l(n, 1024, TCA_NETEM_SLOT, &slot, sizeof(slot)) < 0)
 597                return -1;
 598
 599        if (loss_type != NETEM_LOSS_UNSPEC) {
 600                struct rtattr *start;
 601
 602                start = addattr_nest(n, 1024, TCA_NETEM_LOSS | NLA_F_NESTED);
 603                if (loss_type == NETEM_LOSS_GI) {
 604                        if (addattr_l(n, 1024, NETEM_LOSS_GI,
 605                                      &gimodel, sizeof(gimodel)) < 0)
 606                                return -1;
 607                } else if (loss_type == NETEM_LOSS_GE) {
 608                        if (addattr_l(n, 1024, NETEM_LOSS_GE,
 609                                      &gemodel, sizeof(gemodel)) < 0)
 610                                return -1;
 611                } else {
 612                        fprintf(stderr, "loss in the weeds!\n");
 613                        return -1;
 614                }
 615
 616                addattr_nest_end(n, start);
 617        }
 618
 619        if (present[TCA_NETEM_RATE]) {
 620                if (rate64 >= (1ULL << 32)) {
 621                        if (addattr_l(n, 1024,
 622                                      TCA_NETEM_RATE64, &rate64, sizeof(rate64)) < 0)
 623                                return -1;
 624                        rate.rate = ~0U;
 625                } else {
 626                        rate.rate = rate64;
 627                }
 628                if (addattr_l(n, 1024, TCA_NETEM_RATE, &rate, sizeof(rate)) < 0)
 629                        return -1;
 630        }
 631
 632        if (dist_data) {
 633                if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
 634                              TCA_NETEM_DELAY_DIST,
 635                              dist_data, dist_size * sizeof(dist_data[0])) < 0)
 636                        return -1;
 637                free(dist_data);
 638        }
 639
 640        if (slot_dist_data) {
 641                if (addattr_l(n, MAX_DIST * sizeof(slot_dist_data[0]),
 642                              TCA_NETEM_SLOT_DIST,
 643                              slot_dist_data, slot_dist_size * sizeof(slot_dist_data[0])) < 0)
 644                        return -1;
 645                free(slot_dist_data);
 646        }
 647        tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
 648        return 0;
 649}
 650
 651static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 652{
 653        const struct tc_netem_corr *cor = NULL;
 654        const struct tc_netem_reorder *reorder = NULL;
 655        const struct tc_netem_corrupt *corrupt = NULL;
 656        const struct tc_netem_gimodel *gimodel = NULL;
 657        const struct tc_netem_gemodel *gemodel = NULL;
 658        int *ecn = NULL;
 659        struct tc_netem_qopt qopt;
 660        const struct tc_netem_rate *rate = NULL;
 661        const struct tc_netem_slot *slot = NULL;
 662        int len;
 663        __u64 rate64 = 0;
 664
 665        SPRINT_BUF(b1);
 666
 667        if (opt == NULL)
 668                return 0;
 669
 670        len = RTA_PAYLOAD(opt) - sizeof(qopt);
 671        if (len < 0) {
 672                fprintf(stderr, "options size error\n");
 673                return -1;
 674        }
 675        memcpy(&qopt, RTA_DATA(opt), sizeof(qopt));
 676
 677        if (len > 0) {
 678                struct rtattr *tb[TCA_NETEM_MAX+1];
 679
 680                parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt),
 681                             len);
 682
 683                if (tb[TCA_NETEM_CORR]) {
 684                        if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor))
 685                                return -1;
 686                        cor = RTA_DATA(tb[TCA_NETEM_CORR]);
 687                }
 688                if (tb[TCA_NETEM_REORDER]) {
 689                        if (RTA_PAYLOAD(tb[TCA_NETEM_REORDER]) < sizeof(*reorder))
 690                                return -1;
 691                        reorder = RTA_DATA(tb[TCA_NETEM_REORDER]);
 692                }
 693                if (tb[TCA_NETEM_CORRUPT]) {
 694                        if (RTA_PAYLOAD(tb[TCA_NETEM_CORRUPT]) < sizeof(*corrupt))
 695                                return -1;
 696                        corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
 697                }
 698                if (tb[TCA_NETEM_LOSS]) {
 699                        struct rtattr *lb[NETEM_LOSS_MAX + 1];
 700
 701                        parse_rtattr_nested(lb, NETEM_LOSS_MAX, tb[TCA_NETEM_LOSS]);
 702                        if (lb[NETEM_LOSS_GI])
 703                                gimodel = RTA_DATA(lb[NETEM_LOSS_GI]);
 704                        if (lb[NETEM_LOSS_GE])
 705                                gemodel = RTA_DATA(lb[NETEM_LOSS_GE]);
 706                }
 707                if (tb[TCA_NETEM_RATE]) {
 708                        if (RTA_PAYLOAD(tb[TCA_NETEM_RATE]) < sizeof(*rate))
 709                                return -1;
 710                        rate = RTA_DATA(tb[TCA_NETEM_RATE]);
 711                }
 712                if (tb[TCA_NETEM_ECN]) {
 713                        if (RTA_PAYLOAD(tb[TCA_NETEM_ECN]) < sizeof(*ecn))
 714                                return -1;
 715                        ecn = RTA_DATA(tb[TCA_NETEM_ECN]);
 716                }
 717                if (tb[TCA_NETEM_RATE64]) {
 718                        if (RTA_PAYLOAD(tb[TCA_NETEM_RATE64]) < sizeof(rate64))
 719                                return -1;
 720                        rate64 = rta_getattr_u64(tb[TCA_NETEM_RATE64]);
 721                }
 722                if (tb[TCA_NETEM_SLOT]) {
 723                        if (RTA_PAYLOAD(tb[TCA_NETEM_SLOT]) < sizeof(*slot))
 724                                return -1;
 725                        slot = RTA_DATA(tb[TCA_NETEM_SLOT]);
 726                }
 727        }
 728
 729        print_uint(PRINT_ANY, "limit", "limit %d", qopt.limit);
 730
 731        if (qopt.latency) {
 732                open_json_object("delay");
 733                if (!is_json_context()) {
 734                        print_string(PRINT_FP, NULL, " delay %s",
 735                                     sprint_ticks(qopt.latency, b1));
 736
 737                        if (qopt.jitter)
 738                                print_string(PRINT_FP, NULL, "  %s",
 739                                             sprint_ticks(qopt.jitter, b1));
 740                } else {
 741                        print_float(PRINT_JSON, "delay", NULL,
 742                                    tc_core_tick2time(qopt.latency) /
 743                                    1000000.);
 744                        print_float(PRINT_JSON, "jitter", NULL,
 745                                    tc_core_tick2time(qopt.jitter) /
 746                                    1000000.);
 747                }
 748                print_corr(qopt.jitter && cor && cor->delay_corr,
 749                           cor ? cor->delay_corr : 0);
 750                close_json_object();
 751        }
 752
 753        if (qopt.loss) {
 754                open_json_object("loss-random");
 755                PRINT_PERCENT("loss", qopt.loss);
 756                print_corr(cor && cor->loss_corr, cor ? cor->loss_corr : 0);
 757                close_json_object();
 758        }
 759
 760        if (gimodel) {
 761                open_json_object("loss-state");
 762                __PRINT_PERCENT("p13", " loss state p13", gimodel->p13);
 763                PRINT_PERCENT("p31", gimodel->p31);
 764                PRINT_PERCENT("p32", gimodel->p32);
 765                PRINT_PERCENT("p23", gimodel->p23);
 766                PRINT_PERCENT("p14", gimodel->p14);
 767                close_json_object();
 768        }
 769
 770        if (gemodel) {
 771                open_json_object("loss-gemodel");
 772                __PRINT_PERCENT("p", " loss gemodel p", gemodel->p);
 773                PRINT_PERCENT("r", gemodel->r);
 774                PRINT_PERCENT("1-h", UINT32_MAX - gemodel->h);
 775                PRINT_PERCENT("1-k", gemodel->k1);
 776                close_json_object();
 777        }
 778
 779        if (qopt.duplicate) {
 780                open_json_object("duplicate");
 781                PRINT_PERCENT("duplicate", qopt.duplicate);
 782                print_corr(cor && cor->dup_corr, cor ? cor->dup_corr : 0);
 783                close_json_object();
 784        }
 785
 786        if (reorder && reorder->probability) {
 787                open_json_object("reorder");
 788                PRINT_PERCENT("reorder", reorder->probability);
 789                print_corr(reorder->correlation, reorder->correlation);
 790                close_json_object();
 791        }
 792
 793        if (corrupt && corrupt->probability) {
 794                open_json_object("corrupt");
 795                PRINT_PERCENT("corrupt", corrupt->probability);
 796                print_corr(corrupt->correlation, corrupt->correlation);
 797                close_json_object();
 798        }
 799
 800        if (rate && rate->rate) {
 801                open_json_object("rate");
 802                rate64 = rate64 ? : rate->rate;
 803                tc_print_rate(PRINT_ANY, "rate", " rate %s", rate64);
 804                PRINT_INT_OPT("packetoverhead", rate->packet_overhead);
 805                print_uint(PRINT_ANY, "cellsize",
 806                           rate->cell_size ? " cellsize %u" : "",
 807                           rate->cell_size);
 808                PRINT_INT_OPT("celloverhead", rate->cell_overhead);
 809                close_json_object();
 810        }
 811
 812        if (slot) {
 813                open_json_object("slot");
 814                if (slot->dist_jitter > 0) {
 815                        __PRINT_TIME64("distribution", " slot distribution",
 816                                       slot->dist_delay);
 817                        __PRINT_TIME64("jitter", "", slot->dist_jitter);
 818                } else {
 819                        __PRINT_TIME64("min-delay", " slot", slot->min_delay);
 820                        __PRINT_TIME64("max-delay", "", slot->max_delay);
 821                }
 822                PRINT_INT_OPT("packets", slot->max_packets);
 823                PRINT_INT_OPT("bytes", slot->max_bytes);
 824                close_json_object();
 825        }
 826
 827        print_bool(PRINT_ANY, "ecn", ecn ? " ecn " : "", ecn);
 828        print_luint(PRINT_ANY, "gap", qopt.gap ? " gap %lu" : "",
 829                    (unsigned long)qopt.gap);
 830
 831        return 0;
 832}
 833
 834struct qdisc_util netem_qdisc_util = {
 835        .id             = "netem",
 836        .parse_qopt     = netem_parse_opt,
 837        .print_qopt     = netem_print_opt,
 838};
 839