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")) {
326 if (parse_percent(&p
, *argv
)) {
327 explain1("loss gemodel p");
330 set_percent(&gemodel
.p
, p
);
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
;
338 if (!NEXT_IS_NUMBER())
341 if (get_percent(&gemodel
.r
, *argv
)) {
342 explain1("loss gemodel r");
346 if (!NEXT_IS_NUMBER())
349 if (get_percent(&gemodel
.h
, *argv
)) {
350 explain1("loss gemodel h");
353 /* netem option is "1-h" but kernel
356 gemodel
.h
= UINT32_MAX
- gemodel
.h
;
358 if (!NEXT_IS_NUMBER())
361 if (get_percent(&gemodel
.k1
, *argv
)) {
362 explain1("loss gemodel k");
366 fprintf(stderr
, "Unknown loss parameter: %s\n",
370 } else if (matches(*argv
, "ecn") == 0) {
371 present
[TCA_NETEM_ECN
] = 1;
372 } else if (matches(*argv
, "reorder") == 0) {
374 present
[TCA_NETEM_REORDER
] = 1;
375 if (get_percent(&reorder
.probability
, *argv
)) {
379 if (NEXT_IS_NUMBER()) {
381 ++present
[TCA_NETEM_CORR
];
382 if (get_percent(&reorder
.correlation
, *argv
)) {
387 } else if (matches(*argv
, "corrupt") == 0) {
389 present
[TCA_NETEM_CORRUPT
] = 1;
390 if (get_percent(&corrupt
.probability
, *argv
)) {
394 if (NEXT_IS_NUMBER()) {
396 ++present
[TCA_NETEM_CORR
];
397 if (get_percent(&corrupt
.correlation
, *argv
)) {
402 } else if (matches(*argv
, "gap") == 0) {
404 if (get_u32(&opt
.gap
, *argv
, 0)) {
408 } else if (matches(*argv
, "duplicate") == 0) {
410 if (get_percent(&opt
.duplicate
, *argv
)) {
411 explain1("duplicate");
414 if (NEXT_IS_NUMBER()) {
416 if (get_percent(&cor
.dup_corr
, *argv
)) {
417 explain1("duplicate");
421 } else if (matches(*argv
, "distribution") == 0) {
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) {
429 } else if (matches(*argv
, "rate") == 0) {
430 ++present
[TCA_NETEM_RATE
];
432 if (strchr(*argv
, '%')) {
433 if (get_percent_rate64(&rate64
, *argv
, dev
)) {
437 } else if (get_rate64(&rate64
, *argv
)) {
441 if (NEXT_IS_SIGNED_NUMBER()) {
443 if (get_s32(&rate
.packet_overhead
, *argv
, 0)) {
448 if (NEXT_IS_NUMBER()) {
450 if (get_u32(&rate
.cell_size
, *argv
, 0)) {
455 if (NEXT_IS_SIGNED_NUMBER()) {
457 if (get_s32(&rate
.cell_overhead
, *argv
, 0)) {
462 } else if (matches(*argv
, "slot") == 0) {
463 if (NEXT_IS_NUMBER()) {
465 present
[TCA_NETEM_SLOT
] = 1;
466 if (get_time64(&slot
.min_delay
, *argv
)) {
467 explain1("slot min_delay");
470 if (NEXT_IS_NUMBER()) {
472 if (get_time64(&slot
.max_delay
, *argv
) ||
473 slot
.max_delay
< slot
.min_delay
) {
474 explain1("slot max_delay");
478 slot
.max_delay
= slot
.min_delay
;
482 if (strcmp(*argv
, "distribution") == 0) {
483 present
[TCA_NETEM_SLOT
] = 1;
485 slot_dist_data
= calloc(sizeof(slot_dist_data
[0]), MAX_DIST
);
488 slot_dist_size
= get_distribution(*argv
, slot_dist_data
, MAX_DIST
);
489 if (slot_dist_size
<= 0) {
490 free(slot_dist_data
);
494 if (get_time64(&slot
.dist_delay
, *argv
)) {
495 explain1("slot delay");
499 if (get_time64(&slot
.dist_jitter
, *argv
)) {
500 explain1("slot jitter");
503 if (slot
.dist_jitter
<= 0) {
504 fprintf(stderr
, "Non-positive jitter\n");
508 fprintf(stderr
, "Unknown slot parameter: %s\n",
514 matches(*(argv
+1), "packets") == 0) {
516 if (!NEXT_ARG_OK() ||
517 get_s32(&slot
.max_packets
, *(argv
+1), 0)) {
518 explain1("slot packets");
524 matches(*(argv
+1), "bytes") == 0) {
525 unsigned int max_bytes
;
527 if (!NEXT_ARG_OK() ||
528 get_size(&max_bytes
, *(argv
+1))) {
529 explain1("slot bytes");
532 slot
.max_bytes
= (int) max_bytes
;
535 } else if (strcmp(*argv
, "help") == 0) {
539 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
545 tail
= NLMSG_TAIL(n
);
547 if (reorder
.probability
) {
548 if (opt
.latency
== 0) {
549 fprintf(stderr
, "reordering not possible without specifying some delay\n");
555 } else if (opt
.gap
> 0) {
556 fprintf(stderr
, "gap specified without reorder probability\n");
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");
569 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
570 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
575 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
578 if (present
[TCA_NETEM_CORR
] &&
579 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
582 if (present
[TCA_NETEM_REORDER
] &&
583 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
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)
591 if (present
[TCA_NETEM_CORRUPT
] &&
592 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
595 if (present
[TCA_NETEM_SLOT
] &&
596 addattr_l(n
, 1024, TCA_NETEM_SLOT
, &slot
, sizeof(slot
)) < 0)
599 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
600 struct rtattr
*start
;
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)
607 } else if (loss_type
== NETEM_LOSS_GE
) {
608 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
609 &gemodel
, sizeof(gemodel
)) < 0)
612 fprintf(stderr
, "loss in the weeds!\n");
616 addattr_nest_end(n
, start
);
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)
628 if (addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
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)
640 if (slot_dist_data
) {
641 if (addattr_l(n
, MAX_DIST
* sizeof(slot_dist_data
[0]),
643 slot_dist_data
, slot_dist_size
* sizeof(slot_dist_data
[0])) < 0)
645 free(slot_dist_data
);
647 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
651 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
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
;
659 struct tc_netem_qopt qopt
;
660 const struct tc_netem_rate
*rate
= NULL
;
661 const struct tc_netem_slot
*slot
= NULL
;
670 len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
672 fprintf(stderr
, "options size error\n");
675 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
678 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
680 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
683 if (tb
[TCA_NETEM_CORR
]) {
684 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
686 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
688 if (tb
[TCA_NETEM_REORDER
]) {
689 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
691 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
693 if (tb
[TCA_NETEM_CORRUPT
]) {
694 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
696 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
698 if (tb
[TCA_NETEM_LOSS
]) {
699 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
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
]);
707 if (tb
[TCA_NETEM_RATE
]) {
708 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
710 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
712 if (tb
[TCA_NETEM_ECN
]) {
713 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
715 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
717 if (tb
[TCA_NETEM_RATE64
]) {
718 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE64
]) < sizeof(rate64
))
720 rate64
= rta_getattr_u64(tb
[TCA_NETEM_RATE64
]);
722 if (tb
[TCA_NETEM_SLOT
]) {
723 if (RTA_PAYLOAD(tb
[TCA_NETEM_SLOT
]) < sizeof(*slot
))
725 slot
= RTA_DATA(tb
[TCA_NETEM_SLOT
]);
729 print_uint(PRINT_ANY
, "limit", "limit %d", qopt
.limit
);
732 open_json_object("delay");
733 if (!is_json_context()) {
734 print_string(PRINT_FP
, NULL
, " delay %s",
735 sprint_ticks(qopt
.latency
, b1
));
738 print_string(PRINT_FP
, NULL
, " %s",
739 sprint_ticks(qopt
.jitter
, b1
));
741 print_float(PRINT_JSON
, "delay", NULL
,
742 tc_core_tick2time(qopt
.latency
) /
744 print_float(PRINT_JSON
, "jitter", NULL
,
745 tc_core_tick2time(qopt
.jitter
) /
748 print_corr(qopt
.jitter
&& cor
&& cor
->delay_corr
,
749 cor
? cor
->delay_corr
: 0);
754 open_json_object("loss-random");
755 PRINT_PERCENT("loss", qopt
.loss
);
756 print_corr(cor
&& cor
->loss_corr
, cor
? cor
->loss_corr
: 0);
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
);
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
);
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);
786 if (reorder
&& reorder
->probability
) {
787 open_json_object("reorder");
788 PRINT_PERCENT("reorder", reorder
->probability
);
789 print_corr(reorder
->correlation
, reorder
->correlation
);
793 if (corrupt
&& corrupt
->probability
) {
794 open_json_object("corrupt");
795 PRINT_PERCENT("corrupt", corrupt
->probability
);
796 print_corr(corrupt
->correlation
, corrupt
->correlation
);
800 if (rate
&& rate
->rate
) {
801 open_json_object("rate");
802 rate64
= rate64
? : rate
->rate
;
803 print_string(PRINT_FP
, NULL
, " rate %s",
804 sprint_rate(rate64
, b1
));
805 print_lluint(PRINT_JSON
, "rate", NULL
, rate64
);
806 PRINT_INT_OPT("packetoverhead", rate
->packet_overhead
);
807 print_uint(PRINT_ANY
, "cellsize",
808 rate
->cell_size
? " cellsize %u" : "",
810 PRINT_INT_OPT("celloverhead", rate
->cell_overhead
);
815 open_json_object("slot");
816 if (slot
->dist_jitter
> 0) {
817 __PRINT_TIME64("distribution", " slot distribution",
819 __PRINT_TIME64("jitter", "", slot
->dist_jitter
);
821 __PRINT_TIME64("min-delay", " slot", slot
->min_delay
);
822 __PRINT_TIME64("max-delay", "", slot
->max_delay
);
824 PRINT_INT_OPT("packets", slot
->max_packets
);
825 PRINT_INT_OPT("bytes", slot
->max_bytes
);
829 print_bool(PRINT_ANY
, "ecn", ecn
? " ecn " : "", ecn
);
830 print_luint(PRINT_ANY
, "gap", qopt
.gap
? " gap %lu" : "",
831 (unsigned long)qopt
.gap
);
836 struct qdisc_util netem_qdisc_util
= {
838 .parse_qopt
= netem_parse_opt
,
839 .print_qopt
= netem_print_opt
,