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" \
49 static void explain1(const char *arg
)
51 fprintf(stderr
, "Illegal \"%s\"\n", arg
);
54 /* Upper bound on size of distribution
55 * really (TCA_BUF_MAX - other headers) / sizeof (__s16)
57 #define MAX_DIST (16*1024)
59 /* scaled value used to percent of maximum. */
60 static void set_percent(__u32
*percent
, double per
)
62 *percent
= rint(per
* UINT32_MAX
);
65 static int get_percent(__u32
*percent
, const char *str
)
69 if (parse_percent(&per
, str
))
72 set_percent(percent
, per
);
76 static void print_percent(char *buf
, int len
, __u32 per
)
78 snprintf(buf
, len
, "%g%%", (100. * per
) / UINT32_MAX
);
81 static char *sprint_percent(__u32 per
, char *buf
)
83 print_percent(buf
, SPRINT_BSIZE
-1, per
);
88 * Simplistic file parser for distrbution data.
93 static int get_distribution(const char *type
, __s16
*data
, int maxdata
)
102 snprintf(name
, sizeof(name
), "%s/%s.dist", get_tc_lib(), type
);
103 if ((f
= fopen(name
, "r")) == NULL
) {
104 fprintf(stderr
, "No distribution data for %s (%s: %s)\n",
105 type
, name
, strerror(errno
));
110 while (getline(&line
, &len
, f
) != -1) {
113 if (*line
== '\n' || *line
== '#')
116 for (p
= line
; ; p
= endp
) {
117 x
= strtol(p
, &endp
, 0);
122 fprintf(stderr
, "%s: too much data\n",
136 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
137 #define NEXT_IS_SIGNED_NUMBER() \
138 (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
140 /* Adjust for the fact that psched_ticks aren't always usecs
141 (based on kernel PSCHED_CLOCK configuration */
142 static int get_ticks(__u32
*ticks
, const char *str
)
146 if (get_time(&t
, str
))
149 if (tc_core_time2big(t
)) {
150 fprintf(stderr
, "Illegal %u time (too large)\n", t
);
154 *ticks
= tc_core_time2tick(t
);
158 static int netem_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
,
159 struct nlmsghdr
*n
, const char *dev
)
163 struct tc_netem_qopt opt
= { .limit
= 1000 };
164 struct tc_netem_corr cor
= {};
165 struct tc_netem_reorder reorder
= {};
166 struct tc_netem_corrupt corrupt
= {};
167 struct tc_netem_gimodel gimodel
;
168 struct tc_netem_gemodel gemodel
;
169 struct tc_netem_rate rate
= {};
170 struct tc_netem_slot slot
= {};
171 __s16
*dist_data
= NULL
;
172 __u16 loss_type
= NETEM_LOSS_UNSPEC
;
173 int present
[__TCA_NETEM_MAX
] = {};
176 for ( ; argc
> 0; --argc
, ++argv
) {
177 if (matches(*argv
, "limit") == 0) {
179 if (get_size(&opt
.limit
, *argv
)) {
183 } else if (matches(*argv
, "latency") == 0 ||
184 matches(*argv
, "delay") == 0) {
186 if (get_ticks(&opt
.latency
, *argv
)) {
191 if (NEXT_IS_NUMBER()) {
193 if (get_ticks(&opt
.jitter
, *argv
)) {
198 if (NEXT_IS_NUMBER()) {
200 ++present
[TCA_NETEM_CORR
];
201 if (get_percent(&cor
.delay_corr
, *argv
)) {
207 } else if (matches(*argv
, "loss") == 0 ||
208 matches(*argv
, "drop") == 0) {
209 if (opt
.loss
> 0 || loss_type
!= NETEM_LOSS_UNSPEC
) {
210 explain1("duplicate loss argument\n");
215 /* Old (deprecated) random loss model syntax */
216 if (isdigit(argv
[0][0]))
217 goto random_loss_model
;
219 if (!strcmp(*argv
, "random")) {
222 if (get_percent(&opt
.loss
, *argv
)) {
223 explain1("loss percent");
226 if (NEXT_IS_NUMBER()) {
228 ++present
[TCA_NETEM_CORR
];
229 if (get_percent(&cor
.loss_corr
, *argv
)) {
230 explain1("loss correllation");
234 } else if (!strcmp(*argv
, "state")) {
238 if (parse_percent(&p13
, *argv
)) {
239 explain1("loss p13");
244 set_percent(&gimodel
.p13
, p13
);
245 set_percent(&gimodel
.p31
, 1. - p13
);
246 set_percent(&gimodel
.p32
, 0);
247 set_percent(&gimodel
.p23
, 1.);
248 set_percent(&gimodel
.p14
, 0);
249 loss_type
= NETEM_LOSS_GI
;
251 if (!NEXT_IS_NUMBER())
254 if (get_percent(&gimodel
.p31
, *argv
)) {
255 explain1("loss p31");
259 if (!NEXT_IS_NUMBER())
262 if (get_percent(&gimodel
.p32
, *argv
)) {
263 explain1("loss p32");
267 if (!NEXT_IS_NUMBER())
270 if (get_percent(&gimodel
.p23
, *argv
)) {
271 explain1("loss p23");
274 if (!NEXT_IS_NUMBER())
277 if (get_percent(&gimodel
.p14
, *argv
)) {
278 explain1("loss p14");
282 } else if (!strcmp(*argv
, "gemodel")) {
284 if (get_percent(&gemodel
.p
, *argv
)) {
285 explain1("loss gemodel p");
290 set_percent(&gemodel
.r
, 1.);
291 set_percent(&gemodel
.h
, 0);
292 set_percent(&gemodel
.k1
, 0);
293 loss_type
= NETEM_LOSS_GE
;
295 if (!NEXT_IS_NUMBER())
298 if (get_percent(&gemodel
.r
, *argv
)) {
299 explain1("loss gemodel r");
303 if (!NEXT_IS_NUMBER())
306 if (get_percent(&gemodel
.h
, *argv
)) {
307 explain1("loss gemodel h");
310 /* netem option is "1-h" but kernel
313 gemodel
.h
= UINT32_MAX
- gemodel
.h
;
315 if (!NEXT_IS_NUMBER())
318 if (get_percent(&gemodel
.k1
, *argv
)) {
319 explain1("loss gemodel k");
323 fprintf(stderr
, "Unknown loss parameter: %s\n",
327 } else if (matches(*argv
, "ecn") == 0) {
328 present
[TCA_NETEM_ECN
] = 1;
329 } else if (matches(*argv
, "reorder") == 0) {
331 present
[TCA_NETEM_REORDER
] = 1;
332 if (get_percent(&reorder
.probability
, *argv
)) {
336 if (NEXT_IS_NUMBER()) {
338 ++present
[TCA_NETEM_CORR
];
339 if (get_percent(&reorder
.correlation
, *argv
)) {
344 } else if (matches(*argv
, "corrupt") == 0) {
346 present
[TCA_NETEM_CORRUPT
] = 1;
347 if (get_percent(&corrupt
.probability
, *argv
)) {
351 if (NEXT_IS_NUMBER()) {
353 ++present
[TCA_NETEM_CORR
];
354 if (get_percent(&corrupt
.correlation
, *argv
)) {
359 } else if (matches(*argv
, "gap") == 0) {
361 if (get_u32(&opt
.gap
, *argv
, 0)) {
365 } else if (matches(*argv
, "duplicate") == 0) {
367 if (get_percent(&opt
.duplicate
, *argv
)) {
368 explain1("duplicate");
371 if (NEXT_IS_NUMBER()) {
373 if (get_percent(&cor
.dup_corr
, *argv
)) {
374 explain1("duplicate");
378 } else if (matches(*argv
, "distribution") == 0) {
380 dist_data
= calloc(sizeof(dist_data
[0]), MAX_DIST
);
381 dist_size
= get_distribution(*argv
, dist_data
, MAX_DIST
);
382 if (dist_size
<= 0) {
386 } else if (matches(*argv
, "rate") == 0) {
387 ++present
[TCA_NETEM_RATE
];
389 if (strchr(*argv
, '%')) {
390 if (get_percent_rate64(&rate64
, *argv
, dev
)) {
394 } else if (get_rate64(&rate64
, *argv
)) {
398 if (NEXT_IS_SIGNED_NUMBER()) {
400 if (get_s32(&rate
.packet_overhead
, *argv
, 0)) {
405 if (NEXT_IS_NUMBER()) {
407 if (get_u32(&rate
.cell_size
, *argv
, 0)) {
412 if (NEXT_IS_SIGNED_NUMBER()) {
414 if (get_s32(&rate
.cell_overhead
, *argv
, 0)) {
419 } else if (matches(*argv
, "slot") == 0) {
421 present
[TCA_NETEM_SLOT
] = 1;
422 if (get_time64(&slot
.min_delay
, *argv
)) {
423 explain1("slot min_delay");
426 if (NEXT_IS_NUMBER()) {
428 if (get_time64(&slot
.max_delay
, *argv
) ||
429 slot
.max_delay
< slot
.min_delay
) {
430 explain1("slot max_delay");
434 slot
.max_delay
= slot
.min_delay
;
437 matches(*(argv
+1), "packets") == 0) {
439 if (!NEXT_ARG_OK() ||
440 get_s32(&slot
.max_packets
, *(argv
+1), 0)) {
441 explain1("slot packets");
447 matches(*(argv
+1), "bytes") == 0) {
448 unsigned int max_bytes
;
450 if (!NEXT_ARG_OK() ||
451 get_size(&max_bytes
, *(argv
+1))) {
452 explain1("slot bytes");
455 slot
.max_bytes
= (int) max_bytes
;
458 } else if (strcmp(*argv
, "help") == 0) {
462 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
468 tail
= NLMSG_TAIL(n
);
470 if (reorder
.probability
) {
471 if (opt
.latency
== 0) {
472 fprintf(stderr
, "reordering not possible without specifying some delay\n");
478 } else if (opt
.gap
> 0) {
479 fprintf(stderr
, "gap specified without reorder probability\n");
484 if (present
[TCA_NETEM_ECN
]) {
485 if (opt
.loss
<= 0 && loss_type
== NETEM_LOSS_UNSPEC
) {
486 fprintf(stderr
, "ecn requested without loss model\n");
492 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
493 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
498 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
501 if (present
[TCA_NETEM_CORR
] &&
502 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
505 if (present
[TCA_NETEM_REORDER
] &&
506 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
509 if (present
[TCA_NETEM_ECN
] &&
510 addattr_l(n
, 1024, TCA_NETEM_ECN
, &present
[TCA_NETEM_ECN
],
511 sizeof(present
[TCA_NETEM_ECN
])) < 0)
514 if (present
[TCA_NETEM_CORRUPT
] &&
515 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
518 if (present
[TCA_NETEM_SLOT
] &&
519 addattr_l(n
, 1024, TCA_NETEM_SLOT
, &slot
, sizeof(slot
)) < 0)
522 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
523 struct rtattr
*start
;
525 start
= addattr_nest(n
, 1024, TCA_NETEM_LOSS
| NLA_F_NESTED
);
526 if (loss_type
== NETEM_LOSS_GI
) {
527 if (addattr_l(n
, 1024, NETEM_LOSS_GI
,
528 &gimodel
, sizeof(gimodel
)) < 0)
530 } else if (loss_type
== NETEM_LOSS_GE
) {
531 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
532 &gemodel
, sizeof(gemodel
)) < 0)
535 fprintf(stderr
, "loss in the weeds!\n");
539 addattr_nest_end(n
, start
);
542 if (present
[TCA_NETEM_RATE
]) {
543 if (rate64
>= (1ULL << 32)) {
544 if (addattr_l(n
, 1024,
545 TCA_NETEM_RATE64
, &rate64
, sizeof(rate64
)) < 0)
551 if (addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
556 if (addattr_l(n
, MAX_DIST
* sizeof(dist_data
[0]),
557 TCA_NETEM_DELAY_DIST
,
558 dist_data
, dist_size
* sizeof(dist_data
[0])) < 0)
562 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
566 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
568 const struct tc_netem_corr
*cor
= NULL
;
569 const struct tc_netem_reorder
*reorder
= NULL
;
570 const struct tc_netem_corrupt
*corrupt
= NULL
;
571 const struct tc_netem_gimodel
*gimodel
= NULL
;
572 const struct tc_netem_gemodel
*gemodel
= NULL
;
574 struct tc_netem_qopt qopt
;
575 const struct tc_netem_rate
*rate
= NULL
;
576 const struct tc_netem_slot
*slot
= NULL
;
585 len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
587 fprintf(stderr
, "options size error\n");
590 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
593 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
595 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
598 if (tb
[TCA_NETEM_CORR
]) {
599 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
601 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
603 if (tb
[TCA_NETEM_REORDER
]) {
604 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
606 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
608 if (tb
[TCA_NETEM_CORRUPT
]) {
609 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
611 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
613 if (tb
[TCA_NETEM_LOSS
]) {
614 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
616 parse_rtattr_nested(lb
, NETEM_LOSS_MAX
, tb
[TCA_NETEM_LOSS
]);
617 if (lb
[NETEM_LOSS_GI
])
618 gimodel
= RTA_DATA(lb
[NETEM_LOSS_GI
]);
619 if (lb
[NETEM_LOSS_GE
])
620 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GE
]);
622 if (tb
[TCA_NETEM_RATE
]) {
623 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
625 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
627 if (tb
[TCA_NETEM_ECN
]) {
628 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
630 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
632 if (tb
[TCA_NETEM_RATE64
]) {
633 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE64
]) < sizeof(rate64
))
635 rate64
= rta_getattr_u64(tb
[TCA_NETEM_RATE64
]);
637 if (tb
[TCA_NETEM_SLOT
]) {
638 if (RTA_PAYLOAD(tb
[TCA_NETEM_SLOT
]) < sizeof(*slot
))
640 slot
= RTA_DATA(tb
[TCA_NETEM_SLOT
]);
644 fprintf(f
, "limit %d", qopt
.limit
);
647 fprintf(f
, " delay %s", sprint_ticks(qopt
.latency
, b1
));
650 fprintf(f
, " %s", sprint_ticks(qopt
.jitter
, b1
));
651 if (cor
&& cor
->delay_corr
)
652 fprintf(f
, " %s", sprint_percent(cor
->delay_corr
, b1
));
657 fprintf(f
, " loss %s", sprint_percent(qopt
.loss
, b1
));
658 if (cor
&& cor
->loss_corr
)
659 fprintf(f
, " %s", sprint_percent(cor
->loss_corr
, b1
));
663 fprintf(f
, " loss state p13 %s", sprint_percent(gimodel
->p13
, b1
));
664 fprintf(f
, " p31 %s", sprint_percent(gimodel
->p31
, b1
));
665 fprintf(f
, " p32 %s", sprint_percent(gimodel
->p32
, b1
));
666 fprintf(f
, " p23 %s", sprint_percent(gimodel
->p23
, b1
));
667 fprintf(f
, " p14 %s", sprint_percent(gimodel
->p14
, b1
));
671 fprintf(f
, " loss gemodel p %s",
672 sprint_percent(gemodel
->p
, b1
));
673 fprintf(f
, " r %s", sprint_percent(gemodel
->r
, b1
));
674 fprintf(f
, " 1-h %s", sprint_percent(UINT32_MAX
-
676 fprintf(f
, " 1-k %s", sprint_percent(gemodel
->k1
, b1
));
679 if (qopt
.duplicate
) {
680 fprintf(f
, " duplicate %s",
681 sprint_percent(qopt
.duplicate
, b1
));
682 if (cor
&& cor
->dup_corr
)
683 fprintf(f
, " %s", sprint_percent(cor
->dup_corr
, b1
));
686 if (reorder
&& reorder
->probability
) {
687 fprintf(f
, " reorder %s",
688 sprint_percent(reorder
->probability
, b1
));
689 if (reorder
->correlation
)
691 sprint_percent(reorder
->correlation
, b1
));
694 if (corrupt
&& corrupt
->probability
) {
695 fprintf(f
, " corrupt %s",
696 sprint_percent(corrupt
->probability
, b1
));
697 if (corrupt
->correlation
)
699 sprint_percent(corrupt
->correlation
, b1
));
702 if (rate
&& rate
->rate
) {
704 fprintf(f
, " rate %s", sprint_rate(rate64
, b1
));
706 fprintf(f
, " rate %s", sprint_rate(rate
->rate
, b1
));
707 if (rate
->packet_overhead
)
708 fprintf(f
, " packetoverhead %d", rate
->packet_overhead
);
710 fprintf(f
, " cellsize %u", rate
->cell_size
);
711 if (rate
->cell_overhead
)
712 fprintf(f
, " celloverhead %d", rate
->cell_overhead
);
716 fprintf(f
, " slot %s", sprint_time64(slot
->min_delay
, b1
));
717 fprintf(f
, " %s", sprint_time64(slot
->max_delay
, b1
));
718 if (slot
->max_packets
)
719 fprintf(f
, " packets %d", slot
->max_packets
);
721 fprintf(f
, " bytes %d", slot
->max_bytes
);
728 fprintf(f
, " gap %lu", (unsigned long)qopt
.gap
);
734 struct qdisc_util netem_qdisc_util
= {
736 .parse_qopt
= netem_parse_opt
,
737 .print_qopt
= netem_print_opt
,