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 /* scaled value used to percent of maximum. */
62 static void set_percent(__u32
*percent
, double per
)
64 *percent
= rint(per
* UINT32_MAX
);
67 static int get_percent(__u32
*percent
, const char *str
)
71 if (parse_percent(&per
, str
))
74 set_percent(percent
, per
);
78 static void print_percent(char *buf
, int len
, __u32 per
)
80 snprintf(buf
, len
, "%g%%", (100. * per
) / UINT32_MAX
);
83 static char *sprint_percent(__u32 per
, char *buf
)
85 print_percent(buf
, SPRINT_BSIZE
-1, per
);
90 * Simplistic file parser for distrbution data.
95 static int get_distribution(const char *type
, __s16
*data
, int maxdata
)
104 snprintf(name
, sizeof(name
), "%s/%s.dist", get_tc_lib(), type
);
105 if ((f
= fopen(name
, "r")) == NULL
) {
106 fprintf(stderr
, "No distribution data for %s (%s: %s)\n",
107 type
, name
, strerror(errno
));
112 while (getline(&line
, &len
, f
) != -1) {
115 if (*line
== '\n' || *line
== '#')
118 for (p
= line
; ; p
= endp
) {
119 x
= strtol(p
, &endp
, 0);
124 fprintf(stderr
, "%s: too much data\n",
138 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
139 #define NEXT_IS_SIGNED_NUMBER() \
140 (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
142 /* Adjust for the fact that psched_ticks aren't always usecs
143 (based on kernel PSCHED_CLOCK configuration */
144 static int get_ticks(__u32
*ticks
, const char *str
)
148 if (get_time(&t
, str
))
151 if (tc_core_time2big(t
)) {
152 fprintf(stderr
, "Illegal %u time (too large)\n", t
);
156 *ticks
= tc_core_time2tick(t
);
160 static int netem_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
,
161 struct nlmsghdr
*n
, const char *dev
)
164 int slot_dist_size
= 0;
166 struct tc_netem_qopt opt
= { .limit
= 1000 };
167 struct tc_netem_corr cor
= {};
168 struct tc_netem_reorder reorder
= {};
169 struct tc_netem_corrupt corrupt
= {};
170 struct tc_netem_gimodel gimodel
;
171 struct tc_netem_gemodel gemodel
;
172 struct tc_netem_rate rate
= {};
173 struct tc_netem_slot slot
= {};
174 __s16
*dist_data
= NULL
;
175 __s16
*slot_dist_data
= NULL
;
176 __u16 loss_type
= NETEM_LOSS_UNSPEC
;
177 int present
[__TCA_NETEM_MAX
] = {};
180 for ( ; argc
> 0; --argc
, ++argv
) {
181 if (matches(*argv
, "limit") == 0) {
183 if (get_size(&opt
.limit
, *argv
)) {
187 } else if (matches(*argv
, "latency") == 0 ||
188 matches(*argv
, "delay") == 0) {
190 if (get_ticks(&opt
.latency
, *argv
)) {
195 if (NEXT_IS_NUMBER()) {
197 if (get_ticks(&opt
.jitter
, *argv
)) {
202 if (NEXT_IS_NUMBER()) {
204 ++present
[TCA_NETEM_CORR
];
205 if (get_percent(&cor
.delay_corr
, *argv
)) {
211 } else if (matches(*argv
, "loss") == 0 ||
212 matches(*argv
, "drop") == 0) {
213 if (opt
.loss
> 0 || loss_type
!= NETEM_LOSS_UNSPEC
) {
214 explain1("duplicate loss argument\n");
219 /* Old (deprecated) random loss model syntax */
220 if (isdigit(argv
[0][0]))
221 goto random_loss_model
;
223 if (!strcmp(*argv
, "random")) {
226 if (get_percent(&opt
.loss
, *argv
)) {
227 explain1("loss percent");
230 if (NEXT_IS_NUMBER()) {
232 ++present
[TCA_NETEM_CORR
];
233 if (get_percent(&cor
.loss_corr
, *argv
)) {
234 explain1("loss correllation");
238 } else if (!strcmp(*argv
, "state")) {
242 if (parse_percent(&p13
, *argv
)) {
243 explain1("loss p13");
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.);
252 set_percent(&gimodel
.p14
, 0);
253 loss_type
= NETEM_LOSS_GI
;
255 if (!NEXT_IS_NUMBER())
258 if (get_percent(&gimodel
.p31
, *argv
)) {
259 explain1("loss p31");
263 if (!NEXT_IS_NUMBER())
266 if (get_percent(&gimodel
.p32
, *argv
)) {
267 explain1("loss p32");
271 if (!NEXT_IS_NUMBER())
274 if (get_percent(&gimodel
.p23
, *argv
)) {
275 explain1("loss p23");
278 if (!NEXT_IS_NUMBER())
281 if (get_percent(&gimodel
.p14
, *argv
)) {
282 explain1("loss p14");
286 } else if (!strcmp(*argv
, "gemodel")) {
288 if (get_percent(&gemodel
.p
, *argv
)) {
289 explain1("loss gemodel p");
294 set_percent(&gemodel
.r
, 1.);
295 set_percent(&gemodel
.h
, 0);
296 set_percent(&gemodel
.k1
, 0);
297 loss_type
= NETEM_LOSS_GE
;
299 if (!NEXT_IS_NUMBER())
302 if (get_percent(&gemodel
.r
, *argv
)) {
303 explain1("loss gemodel r");
307 if (!NEXT_IS_NUMBER())
310 if (get_percent(&gemodel
.h
, *argv
)) {
311 explain1("loss gemodel h");
314 /* netem option is "1-h" but kernel
317 gemodel
.h
= UINT32_MAX
- gemodel
.h
;
319 if (!NEXT_IS_NUMBER())
322 if (get_percent(&gemodel
.k1
, *argv
)) {
323 explain1("loss gemodel k");
327 fprintf(stderr
, "Unknown loss parameter: %s\n",
331 } else if (matches(*argv
, "ecn") == 0) {
332 present
[TCA_NETEM_ECN
] = 1;
333 } else if (matches(*argv
, "reorder") == 0) {
335 present
[TCA_NETEM_REORDER
] = 1;
336 if (get_percent(&reorder
.probability
, *argv
)) {
340 if (NEXT_IS_NUMBER()) {
342 ++present
[TCA_NETEM_CORR
];
343 if (get_percent(&reorder
.correlation
, *argv
)) {
348 } else if (matches(*argv
, "corrupt") == 0) {
350 present
[TCA_NETEM_CORRUPT
] = 1;
351 if (get_percent(&corrupt
.probability
, *argv
)) {
355 if (NEXT_IS_NUMBER()) {
357 ++present
[TCA_NETEM_CORR
];
358 if (get_percent(&corrupt
.correlation
, *argv
)) {
363 } else if (matches(*argv
, "gap") == 0) {
365 if (get_u32(&opt
.gap
, *argv
, 0)) {
369 } else if (matches(*argv
, "duplicate") == 0) {
371 if (get_percent(&opt
.duplicate
, *argv
)) {
372 explain1("duplicate");
375 if (NEXT_IS_NUMBER()) {
377 if (get_percent(&cor
.dup_corr
, *argv
)) {
378 explain1("duplicate");
382 } else if (matches(*argv
, "distribution") == 0) {
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) {
390 } else if (matches(*argv
, "rate") == 0) {
391 ++present
[TCA_NETEM_RATE
];
393 if (strchr(*argv
, '%')) {
394 if (get_percent_rate64(&rate64
, *argv
, dev
)) {
398 } else if (get_rate64(&rate64
, *argv
)) {
402 if (NEXT_IS_SIGNED_NUMBER()) {
404 if (get_s32(&rate
.packet_overhead
, *argv
, 0)) {
409 if (NEXT_IS_NUMBER()) {
411 if (get_u32(&rate
.cell_size
, *argv
, 0)) {
416 if (NEXT_IS_SIGNED_NUMBER()) {
418 if (get_s32(&rate
.cell_overhead
, *argv
, 0)) {
423 } else if (matches(*argv
, "slot") == 0) {
424 if (NEXT_IS_NUMBER()) {
426 present
[TCA_NETEM_SLOT
] = 1;
427 if (get_time64(&slot
.min_delay
, *argv
)) {
428 explain1("slot min_delay");
431 if (NEXT_IS_NUMBER()) {
433 if (get_time64(&slot
.max_delay
, *argv
) ||
434 slot
.max_delay
< slot
.min_delay
) {
435 explain1("slot max_delay");
439 slot
.max_delay
= slot
.min_delay
;
443 if (strcmp(*argv
, "distribution") == 0) {
444 present
[TCA_NETEM_SLOT
] = 1;
446 slot_dist_data
= calloc(sizeof(slot_dist_data
[0]), MAX_DIST
);
449 slot_dist_size
= get_distribution(*argv
, slot_dist_data
, MAX_DIST
);
450 if (slot_dist_size
<= 0) {
451 free(slot_dist_data
);
455 if (get_time64(&slot
.dist_delay
, *argv
)) {
456 explain1("slot delay");
460 if (get_time64(&slot
.dist_jitter
, *argv
)) {
461 explain1("slot jitter");
464 if (slot
.dist_jitter
<= 0) {
465 fprintf(stderr
, "Non-positive jitter\n");
469 fprintf(stderr
, "Unknown slot parameter: %s\n",
475 matches(*(argv
+1), "packets") == 0) {
477 if (!NEXT_ARG_OK() ||
478 get_s32(&slot
.max_packets
, *(argv
+1), 0)) {
479 explain1("slot packets");
485 matches(*(argv
+1), "bytes") == 0) {
486 unsigned int max_bytes
;
488 if (!NEXT_ARG_OK() ||
489 get_size(&max_bytes
, *(argv
+1))) {
490 explain1("slot bytes");
493 slot
.max_bytes
= (int) max_bytes
;
496 } else if (strcmp(*argv
, "help") == 0) {
500 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
506 tail
= NLMSG_TAIL(n
);
508 if (reorder
.probability
) {
509 if (opt
.latency
== 0) {
510 fprintf(stderr
, "reordering not possible without specifying some delay\n");
516 } else if (opt
.gap
> 0) {
517 fprintf(stderr
, "gap specified without reorder probability\n");
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");
530 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
531 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
536 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
539 if (present
[TCA_NETEM_CORR
] &&
540 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
543 if (present
[TCA_NETEM_REORDER
] &&
544 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
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)
552 if (present
[TCA_NETEM_CORRUPT
] &&
553 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
556 if (present
[TCA_NETEM_SLOT
] &&
557 addattr_l(n
, 1024, TCA_NETEM_SLOT
, &slot
, sizeof(slot
)) < 0)
560 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
561 struct rtattr
*start
;
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)
568 } else if (loss_type
== NETEM_LOSS_GE
) {
569 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
570 &gemodel
, sizeof(gemodel
)) < 0)
573 fprintf(stderr
, "loss in the weeds!\n");
577 addattr_nest_end(n
, start
);
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)
589 if (addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
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)
601 if (slot_dist_data
) {
602 if (addattr_l(n
, MAX_DIST
* sizeof(slot_dist_data
[0]),
604 slot_dist_data
, slot_dist_size
* sizeof(slot_dist_data
[0])) < 0)
606 free(slot_dist_data
);
608 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
612 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
614 const struct tc_netem_corr
*cor
= NULL
;
615 const struct tc_netem_reorder
*reorder
= NULL
;
616 const struct tc_netem_corrupt
*corrupt
= NULL
;
617 const struct tc_netem_gimodel
*gimodel
= NULL
;
618 const struct tc_netem_gemodel
*gemodel
= NULL
;
620 struct tc_netem_qopt qopt
;
621 const struct tc_netem_rate
*rate
= NULL
;
622 const struct tc_netem_slot
*slot
= NULL
;
631 len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
633 fprintf(stderr
, "options size error\n");
636 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
639 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
641 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
644 if (tb
[TCA_NETEM_CORR
]) {
645 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
647 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
649 if (tb
[TCA_NETEM_REORDER
]) {
650 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
652 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
654 if (tb
[TCA_NETEM_CORRUPT
]) {
655 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
657 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
659 if (tb
[TCA_NETEM_LOSS
]) {
660 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
662 parse_rtattr_nested(lb
, NETEM_LOSS_MAX
, tb
[TCA_NETEM_LOSS
]);
663 if (lb
[NETEM_LOSS_GI
])
664 gimodel
= RTA_DATA(lb
[NETEM_LOSS_GI
]);
665 if (lb
[NETEM_LOSS_GE
])
666 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GE
]);
668 if (tb
[TCA_NETEM_RATE
]) {
669 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
671 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
673 if (tb
[TCA_NETEM_ECN
]) {
674 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
676 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
678 if (tb
[TCA_NETEM_RATE64
]) {
679 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE64
]) < sizeof(rate64
))
681 rate64
= rta_getattr_u64(tb
[TCA_NETEM_RATE64
]);
683 if (tb
[TCA_NETEM_SLOT
]) {
684 if (RTA_PAYLOAD(tb
[TCA_NETEM_SLOT
]) < sizeof(*slot
))
686 slot
= RTA_DATA(tb
[TCA_NETEM_SLOT
]);
690 fprintf(f
, "limit %d", qopt
.limit
);
693 fprintf(f
, " delay %s", sprint_ticks(qopt
.latency
, b1
));
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
));
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
));
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
));
717 fprintf(f
, " loss gemodel p %s",
718 sprint_percent(gemodel
->p
, b1
));
719 fprintf(f
, " r %s", sprint_percent(gemodel
->r
, b1
));
720 fprintf(f
, " 1-h %s", sprint_percent(UINT32_MAX
-
722 fprintf(f
, " 1-k %s", sprint_percent(gemodel
->k1
, b1
));
725 if (qopt
.duplicate
) {
726 fprintf(f
, " duplicate %s",
727 sprint_percent(qopt
.duplicate
, b1
));
728 if (cor
&& cor
->dup_corr
)
729 fprintf(f
, " %s", sprint_percent(cor
->dup_corr
, b1
));
732 if (reorder
&& reorder
->probability
) {
733 fprintf(f
, " reorder %s",
734 sprint_percent(reorder
->probability
, b1
));
735 if (reorder
->correlation
)
737 sprint_percent(reorder
->correlation
, b1
));
740 if (corrupt
&& corrupt
->probability
) {
741 fprintf(f
, " corrupt %s",
742 sprint_percent(corrupt
->probability
, b1
));
743 if (corrupt
->correlation
)
745 sprint_percent(corrupt
->correlation
, b1
));
748 if (rate
&& rate
->rate
) {
750 fprintf(f
, " rate %s", sprint_rate(rate64
, b1
));
752 fprintf(f
, " rate %s", sprint_rate(rate
->rate
, b1
));
753 if (rate
->packet_overhead
)
754 fprintf(f
, " packetoverhead %d", rate
->packet_overhead
);
756 fprintf(f
, " cellsize %u", rate
->cell_size
);
757 if (rate
->cell_overhead
)
758 fprintf(f
, " celloverhead %d", rate
->cell_overhead
);
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
));
766 fprintf(f
, " slot %s", sprint_time64(slot
->min_delay
, b1
));
767 fprintf(f
, " %s", sprint_time64(slot
->max_delay
, b1
));
769 if (slot
->max_packets
)
770 fprintf(f
, " packets %d", slot
->max_packets
);
772 fprintf(f
, " bytes %d", slot
->max_bytes
);
779 fprintf(f
, " gap %lu", (unsigned long)qopt
.gap
);
785 struct qdisc_util netem_qdisc_util
= {
787 .parse_qopt
= netem_parse_opt
,
788 .print_qopt
= netem_print_opt
,