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.
9 * Authors: Stephen Hemminger <shemminger@linux-foundation.org>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
28 #include "tc_common.h"
30 static void explain(void)
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" \
42 " [ reorder PRECENT [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");
51 static void explain1(const char *arg
)
53 fprintf(stderr
, "Illegal \"%s\"\n", arg
);
56 /* Upper bound on size of distribution
57 * really (TCA_BUF_MAX - other headers) / sizeof (__s16)
59 #define MAX_DIST (16*1024)
61 /* Print values only if they are non-zero */
62 static void __print_int_opt(const char *label_json
, const char *label_fp
,
65 print_int(PRINT_ANY
, label_json
, val
? label_fp
: "", val
);
67 #define PRINT_INT_OPT(label, val) \
68 __print_int_opt(label, " " label " %d", (val))
70 /* Time print prints normally with varying units, but for JSON prints
71 * in seconds (1ms vs 0.001).
73 static void __print_time64(const char *label_json
, const char *label_fp
,
78 print_string(PRINT_FP
, NULL
, label_fp
, sprint_time64(val
, b1
));
79 print_float(PRINT_JSON
, label_json
, NULL
, val
/ 1000000000.);
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))
85 /* Percent print prints normally in percentage points, but for JSON prints
86 * an absolute value (1% vs 0.01).
88 static void __print_percent(const char *label_json
, const char *label_fp
,
91 print_float(PRINT_FP
, NULL
, label_fp
, (100. * per
) / UINT32_MAX
);
92 print_float(PRINT_JSON
, label_json
, NULL
, (1. * per
) / UINT32_MAX
);
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))
98 /* scaled value used to percent of maximum. */
99 static void set_percent(__u32
*percent
, double per
)
101 *percent
= rint(per
* UINT32_MAX
);
104 static int get_percent(__u32
*percent
, const char *str
)
108 if (parse_percent(&per
, str
))
111 set_percent(percent
, per
);
115 static void print_corr(bool present
, __u32 value
)
117 if (!is_json_context()) {
119 __PRINT_PERCENT("", "", value
);
121 PRINT_PERCENT("correlation", value
);
126 * Simplistic file parser for distrbution data.
131 static int get_distribution(const char *type
, __s16
*data
, int maxdata
)
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
));
148 while (getline(&line
, &len
, f
) != -1) {
151 if (*line
== '\n' || *line
== '#')
154 for (p
= line
; ; p
= endp
) {
155 x
= strtol(p
, &endp
, 0);
160 fprintf(stderr
, "%s: too much data\n",
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] == '-'))
178 /* Adjust for the fact that psched_ticks aren't always usecs
179 (based on kernel PSCHED_CLOCK configuration */
180 static int get_ticks(__u32
*ticks
, const char *str
)
184 if (get_time(&t
, str
))
187 if (tc_core_time2big(t
)) {
188 fprintf(stderr
, "Illegal %u time (too large)\n", t
);
192 *ticks
= tc_core_time2tick(t
);
196 static int netem_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
,
197 struct nlmsghdr
*n
, const char *dev
)
200 int slot_dist_size
= 0;
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
] = {};
216 for ( ; argc
> 0; --argc
, ++argv
) {
217 if (matches(*argv
, "limit") == 0) {
219 if (get_size(&opt
.limit
, *argv
)) {
223 } else if (matches(*argv
, "latency") == 0 ||
224 matches(*argv
, "delay") == 0) {
226 if (get_ticks(&opt
.latency
, *argv
)) {
231 if (NEXT_IS_NUMBER()) {
233 if (get_ticks(&opt
.jitter
, *argv
)) {
238 if (NEXT_IS_NUMBER()) {
240 ++present
[TCA_NETEM_CORR
];
241 if (get_percent(&cor
.delay_corr
, *argv
)) {
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");
255 /* Old (deprecated) random loss model syntax */
256 if (isdigit(argv
[0][0]))
257 goto random_loss_model
;
259 if (!strcmp(*argv
, "random")) {
262 if (get_percent(&opt
.loss
, *argv
)) {
263 explain1("loss percent");
266 if (NEXT_IS_NUMBER()) {
268 ++present
[TCA_NETEM_CORR
];
269 if (get_percent(&cor
.loss_corr
, *argv
)) {
270 explain1("loss correllation");
274 } else if (!strcmp(*argv
, "state")) {
278 if (parse_percent(&p13
, *argv
)) {
279 explain1("loss p13");
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
;
291 if (!NEXT_IS_NUMBER())
294 if (get_percent(&gimodel
.p31
, *argv
)) {
295 explain1("loss p31");
299 if (!NEXT_IS_NUMBER())
302 if (get_percent(&gimodel
.p32
, *argv
)) {
303 explain1("loss p32");
307 if (!NEXT_IS_NUMBER())
310 if (get_percent(&gimodel
.p23
, *argv
)) {
311 explain1("loss p23");
314 if (!NEXT_IS_NUMBER())
317 if (get_percent(&gimodel
.p14
, *argv
)) {
318 explain1("loss p14");
322 } else if (!strcmp(*argv
, "gemodel")) {
324 if (get_percent(&gemodel
.p
, *argv
)) {
325 explain1("loss gemodel p");
330 set_percent(&gemodel
.r
, 1.);
331 set_percent(&gemodel
.h
, 0);
332 set_percent(&gemodel
.k1
, 0);
333 loss_type
= NETEM_LOSS_GE
;
335 if (!NEXT_IS_NUMBER())
338 if (get_percent(&gemodel
.r
, *argv
)) {
339 explain1("loss gemodel r");
343 if (!NEXT_IS_NUMBER())
346 if (get_percent(&gemodel
.h
, *argv
)) {
347 explain1("loss gemodel h");
350 /* netem option is "1-h" but kernel
353 gemodel
.h
= UINT32_MAX
- gemodel
.h
;
355 if (!NEXT_IS_NUMBER())
358 if (get_percent(&gemodel
.k1
, *argv
)) {
359 explain1("loss gemodel k");
363 fprintf(stderr
, "Unknown loss parameter: %s\n",
367 } else if (matches(*argv
, "ecn") == 0) {
368 present
[TCA_NETEM_ECN
] = 1;
369 } else if (matches(*argv
, "reorder") == 0) {
371 present
[TCA_NETEM_REORDER
] = 1;
372 if (get_percent(&reorder
.probability
, *argv
)) {
376 if (NEXT_IS_NUMBER()) {
378 ++present
[TCA_NETEM_CORR
];
379 if (get_percent(&reorder
.correlation
, *argv
)) {
384 } else if (matches(*argv
, "corrupt") == 0) {
386 present
[TCA_NETEM_CORRUPT
] = 1;
387 if (get_percent(&corrupt
.probability
, *argv
)) {
391 if (NEXT_IS_NUMBER()) {
393 ++present
[TCA_NETEM_CORR
];
394 if (get_percent(&corrupt
.correlation
, *argv
)) {
399 } else if (matches(*argv
, "gap") == 0) {
401 if (get_u32(&opt
.gap
, *argv
, 0)) {
405 } else if (matches(*argv
, "duplicate") == 0) {
407 if (get_percent(&opt
.duplicate
, *argv
)) {
408 explain1("duplicate");
411 if (NEXT_IS_NUMBER()) {
413 if (get_percent(&cor
.dup_corr
, *argv
)) {
414 explain1("duplicate");
418 } else if (matches(*argv
, "distribution") == 0) {
420 dist_data
= calloc(sizeof(dist_data
[0]), MAX_DIST
);
421 dist_size
= get_distribution(*argv
, dist_data
, MAX_DIST
);
422 if (dist_size
<= 0) {
426 } else if (matches(*argv
, "rate") == 0) {
427 ++present
[TCA_NETEM_RATE
];
429 if (strchr(*argv
, '%')) {
430 if (get_percent_rate64(&rate64
, *argv
, dev
)) {
434 } else if (get_rate64(&rate64
, *argv
)) {
438 if (NEXT_IS_SIGNED_NUMBER()) {
440 if (get_s32(&rate
.packet_overhead
, *argv
, 0)) {
445 if (NEXT_IS_NUMBER()) {
447 if (get_u32(&rate
.cell_size
, *argv
, 0)) {
452 if (NEXT_IS_SIGNED_NUMBER()) {
454 if (get_s32(&rate
.cell_overhead
, *argv
, 0)) {
459 } else if (matches(*argv
, "slot") == 0) {
460 if (NEXT_IS_NUMBER()) {
462 present
[TCA_NETEM_SLOT
] = 1;
463 if (get_time64(&slot
.min_delay
, *argv
)) {
464 explain1("slot min_delay");
467 if (NEXT_IS_NUMBER()) {
469 if (get_time64(&slot
.max_delay
, *argv
) ||
470 slot
.max_delay
< slot
.min_delay
) {
471 explain1("slot max_delay");
475 slot
.max_delay
= slot
.min_delay
;
479 if (strcmp(*argv
, "distribution") == 0) {
480 present
[TCA_NETEM_SLOT
] = 1;
482 slot_dist_data
= calloc(sizeof(slot_dist_data
[0]), MAX_DIST
);
485 slot_dist_size
= get_distribution(*argv
, slot_dist_data
, MAX_DIST
);
486 if (slot_dist_size
<= 0) {
487 free(slot_dist_data
);
491 if (get_time64(&slot
.dist_delay
, *argv
)) {
492 explain1("slot delay");
496 if (get_time64(&slot
.dist_jitter
, *argv
)) {
497 explain1("slot jitter");
500 if (slot
.dist_jitter
<= 0) {
501 fprintf(stderr
, "Non-positive jitter\n");
505 fprintf(stderr
, "Unknown slot parameter: %s\n",
511 matches(*(argv
+1), "packets") == 0) {
513 if (!NEXT_ARG_OK() ||
514 get_s32(&slot
.max_packets
, *(argv
+1), 0)) {
515 explain1("slot packets");
521 matches(*(argv
+1), "bytes") == 0) {
522 unsigned int max_bytes
;
524 if (!NEXT_ARG_OK() ||
525 get_size(&max_bytes
, *(argv
+1))) {
526 explain1("slot bytes");
529 slot
.max_bytes
= (int) max_bytes
;
532 } else if (strcmp(*argv
, "help") == 0) {
536 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
542 tail
= NLMSG_TAIL(n
);
544 if (reorder
.probability
) {
545 if (opt
.latency
== 0) {
546 fprintf(stderr
, "reordering not possible without specifying some delay\n");
552 } else if (opt
.gap
> 0) {
553 fprintf(stderr
, "gap specified without reorder probability\n");
558 if (present
[TCA_NETEM_ECN
]) {
559 if (opt
.loss
<= 0 && loss_type
== NETEM_LOSS_UNSPEC
) {
560 fprintf(stderr
, "ecn requested without loss model\n");
566 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
567 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
572 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
575 if (present
[TCA_NETEM_CORR
] &&
576 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
579 if (present
[TCA_NETEM_REORDER
] &&
580 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
583 if (present
[TCA_NETEM_ECN
] &&
584 addattr_l(n
, 1024, TCA_NETEM_ECN
, &present
[TCA_NETEM_ECN
],
585 sizeof(present
[TCA_NETEM_ECN
])) < 0)
588 if (present
[TCA_NETEM_CORRUPT
] &&
589 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
592 if (present
[TCA_NETEM_SLOT
] &&
593 addattr_l(n
, 1024, TCA_NETEM_SLOT
, &slot
, sizeof(slot
)) < 0)
596 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
597 struct rtattr
*start
;
599 start
= addattr_nest(n
, 1024, TCA_NETEM_LOSS
| NLA_F_NESTED
);
600 if (loss_type
== NETEM_LOSS_GI
) {
601 if (addattr_l(n
, 1024, NETEM_LOSS_GI
,
602 &gimodel
, sizeof(gimodel
)) < 0)
604 } else if (loss_type
== NETEM_LOSS_GE
) {
605 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
606 &gemodel
, sizeof(gemodel
)) < 0)
609 fprintf(stderr
, "loss in the weeds!\n");
613 addattr_nest_end(n
, start
);
616 if (present
[TCA_NETEM_RATE
]) {
617 if (rate64
>= (1ULL << 32)) {
618 if (addattr_l(n
, 1024,
619 TCA_NETEM_RATE64
, &rate64
, sizeof(rate64
)) < 0)
625 if (addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
630 if (addattr_l(n
, MAX_DIST
* sizeof(dist_data
[0]),
631 TCA_NETEM_DELAY_DIST
,
632 dist_data
, dist_size
* sizeof(dist_data
[0])) < 0)
637 if (slot_dist_data
) {
638 if (addattr_l(n
, MAX_DIST
* sizeof(slot_dist_data
[0]),
640 slot_dist_data
, slot_dist_size
* sizeof(slot_dist_data
[0])) < 0)
642 free(slot_dist_data
);
644 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
648 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
650 const struct tc_netem_corr
*cor
= NULL
;
651 const struct tc_netem_reorder
*reorder
= NULL
;
652 const struct tc_netem_corrupt
*corrupt
= NULL
;
653 const struct tc_netem_gimodel
*gimodel
= NULL
;
654 const struct tc_netem_gemodel
*gemodel
= NULL
;
656 struct tc_netem_qopt qopt
;
657 const struct tc_netem_rate
*rate
= NULL
;
658 const struct tc_netem_slot
*slot
= NULL
;
667 len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
669 fprintf(stderr
, "options size error\n");
672 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
675 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
677 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
680 if (tb
[TCA_NETEM_CORR
]) {
681 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
683 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
685 if (tb
[TCA_NETEM_REORDER
]) {
686 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
688 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
690 if (tb
[TCA_NETEM_CORRUPT
]) {
691 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
693 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
695 if (tb
[TCA_NETEM_LOSS
]) {
696 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
698 parse_rtattr_nested(lb
, NETEM_LOSS_MAX
, tb
[TCA_NETEM_LOSS
]);
699 if (lb
[NETEM_LOSS_GI
])
700 gimodel
= RTA_DATA(lb
[NETEM_LOSS_GI
]);
701 if (lb
[NETEM_LOSS_GE
])
702 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GE
]);
704 if (tb
[TCA_NETEM_RATE
]) {
705 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
707 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
709 if (tb
[TCA_NETEM_ECN
]) {
710 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
712 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
714 if (tb
[TCA_NETEM_RATE64
]) {
715 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE64
]) < sizeof(rate64
))
717 rate64
= rta_getattr_u64(tb
[TCA_NETEM_RATE64
]);
719 if (tb
[TCA_NETEM_SLOT
]) {
720 if (RTA_PAYLOAD(tb
[TCA_NETEM_SLOT
]) < sizeof(*slot
))
722 slot
= RTA_DATA(tb
[TCA_NETEM_SLOT
]);
726 print_uint(PRINT_ANY
, "limit", "limit %d", qopt
.limit
);
729 open_json_object("delay");
730 if (!is_json_context()) {
731 print_string(PRINT_FP
, NULL
, " delay %s",
732 sprint_ticks(qopt
.latency
, b1
));
735 print_string(PRINT_FP
, NULL
, " %s",
736 sprint_ticks(qopt
.jitter
, b1
));
738 print_float(PRINT_JSON
, "delay", NULL
,
739 tc_core_tick2time(qopt
.latency
) /
741 print_float(PRINT_JSON
, "jitter", NULL
,
742 tc_core_tick2time(qopt
.jitter
) /
745 print_corr(qopt
.jitter
&& cor
&& cor
->delay_corr
,
746 cor
? cor
->delay_corr
: 0);
751 open_json_object("loss-random");
752 PRINT_PERCENT("loss", qopt
.loss
);
753 print_corr(cor
&& cor
->loss_corr
, cor
? cor
->loss_corr
: 0);
758 open_json_object("loss-state");
759 __PRINT_PERCENT("p13", " loss state p13", gimodel
->p13
);
760 PRINT_PERCENT("p31", gimodel
->p31
);
761 PRINT_PERCENT("p32", gimodel
->p32
);
762 PRINT_PERCENT("p23", gimodel
->p23
);
763 PRINT_PERCENT("p14", gimodel
->p14
);
768 open_json_object("loss-gemodel");
769 __PRINT_PERCENT("p", " loss gemodel p", gemodel
->p
);
770 PRINT_PERCENT("r", gemodel
->r
);
771 PRINT_PERCENT("1-h", UINT32_MAX
- gemodel
->h
);
772 PRINT_PERCENT("1-k", gemodel
->k1
);
776 if (qopt
.duplicate
) {
777 open_json_object("duplicate");
778 PRINT_PERCENT("duplicate", qopt
.duplicate
);
779 print_corr(cor
&& cor
->dup_corr
, cor
? cor
->dup_corr
: 0);
783 if (reorder
&& reorder
->probability
) {
784 open_json_object("reorder");
785 PRINT_PERCENT("reorder", reorder
->probability
);
786 print_corr(reorder
->correlation
, reorder
->correlation
);
790 if (corrupt
&& corrupt
->probability
) {
791 open_json_object("corrupt");
792 PRINT_PERCENT("corrupt", corrupt
->probability
);
793 print_corr(corrupt
->correlation
, corrupt
->correlation
);
797 if (rate
&& rate
->rate
) {
798 open_json_object("rate");
799 rate64
= rate64
? : rate
->rate
;
800 print_string(PRINT_FP
, NULL
, " rate %s",
801 sprint_rate(rate64
, b1
));
802 print_lluint(PRINT_JSON
, "rate", NULL
, rate64
);
803 PRINT_INT_OPT("packetoverhead", rate
->packet_overhead
);
804 print_uint(PRINT_ANY
, "cellsize",
805 rate
->cell_size
? " cellsize %u" : "",
807 PRINT_INT_OPT("celloverhead", rate
->cell_overhead
);
812 open_json_object("slot");
813 if (slot
->dist_jitter
> 0) {
814 __PRINT_TIME64("distribution", " slot distribution",
816 __PRINT_TIME64("jitter", "", slot
->dist_jitter
);
818 __PRINT_TIME64("min-delay", " slot", slot
->min_delay
);
819 __PRINT_TIME64("max-delay", "", slot
->max_delay
);
821 PRINT_INT_OPT("packets", slot
->max_packets
);
822 PRINT_INT_OPT("bytes", slot
->max_bytes
);
826 print_bool(PRINT_ANY
, "ecn", ecn
? " ecn " : "", ecn
);
827 print_luint(PRINT_ANY
, "gap", qopt
.gap
? " gap %lu" : "",
828 (unsigned long)qopt
.gap
);
833 struct qdisc_util netem_qdisc_util
= {
835 .parse_qopt
= netem_parse_opt
,
836 .print_qopt
= netem_print_opt
,