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