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");
46 static void explain1(const char *arg
)
48 fprintf(stderr
, "Illegal \"%s\"\n", arg
);
51 /* Upper bound on size of distribution
52 * really (TCA_BUF_MAX - other headers) / sizeof (__s16)
54 #define MAX_DIST (16*1024)
56 /* scaled value used to percent of maximum. */
57 static void set_percent(__u32
*percent
, double per
)
59 *percent
= rint(per
* UINT32_MAX
);
62 static int get_percent(__u32
*percent
, const char *str
)
66 if (parse_percent(&per
, str
))
69 set_percent(percent
, per
);
73 static void print_percent(char *buf
, int len
, __u32 per
)
75 snprintf(buf
, len
, "%g%%", (100. * per
) / UINT32_MAX
);
78 static char *sprint_percent(__u32 per
, char *buf
)
80 print_percent(buf
, SPRINT_BSIZE
-1, per
);
85 * Simplistic file parser for distrbution data.
90 static int get_distribution(const char *type
, __s16
*data
, int maxdata
)
99 snprintf(name
, sizeof(name
), "%s/%s.dist", get_tc_lib(), type
);
100 if ((f
= fopen(name
, "r")) == NULL
) {
101 fprintf(stderr
, "No distribution data for %s (%s: %s)\n",
102 type
, name
, strerror(errno
));
107 while (getline(&line
, &len
, f
) != -1) {
110 if (*line
== '\n' || *line
== '#')
113 for (p
= line
; ; p
= endp
) {
114 x
= strtol(p
, &endp
, 0);
119 fprintf(stderr
, "%s: too much data\n",
133 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
134 #define NEXT_IS_SIGNED_NUMBER() \
135 (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
137 /* Adjust for the fact that psched_ticks aren't always usecs
138 (based on kernel PSCHED_CLOCK configuration */
139 static int get_ticks(__u32
*ticks
, const char *str
)
143 if (get_time(&t
, str
))
146 if (tc_core_time2big(t
)) {
147 fprintf(stderr
, "Illegal %u time (too large)\n", t
);
151 *ticks
= tc_core_time2tick(t
);
155 static int netem_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
,
156 struct nlmsghdr
*n
, const char *dev
)
160 struct tc_netem_qopt opt
= { .limit
= 1000 };
161 struct tc_netem_corr cor
= {};
162 struct tc_netem_reorder reorder
= {};
163 struct tc_netem_corrupt corrupt
= {};
164 struct tc_netem_gimodel gimodel
;
165 struct tc_netem_gemodel gemodel
;
166 struct tc_netem_rate rate
= {};
167 __s16
*dist_data
= NULL
;
168 __u16 loss_type
= NETEM_LOSS_UNSPEC
;
169 int present
[__TCA_NETEM_MAX
] = {};
172 for ( ; argc
> 0; --argc
, ++argv
) {
173 if (matches(*argv
, "limit") == 0) {
175 if (get_size(&opt
.limit
, *argv
)) {
179 } else if (matches(*argv
, "latency") == 0 ||
180 matches(*argv
, "delay") == 0) {
182 if (get_ticks(&opt
.latency
, *argv
)) {
187 if (NEXT_IS_NUMBER()) {
189 if (get_ticks(&opt
.jitter
, *argv
)) {
194 if (NEXT_IS_NUMBER()) {
196 ++present
[TCA_NETEM_CORR
];
197 if (get_percent(&cor
.delay_corr
, *argv
)) {
203 } else if (matches(*argv
, "loss") == 0 ||
204 matches(*argv
, "drop") == 0) {
205 if (opt
.loss
> 0 || loss_type
!= NETEM_LOSS_UNSPEC
) {
206 explain1("duplicate loss argument\n");
211 /* Old (deprecated) random loss model syntax */
212 if (isdigit(argv
[0][0]))
213 goto random_loss_model
;
215 if (!strcmp(*argv
, "random")) {
218 if (get_percent(&opt
.loss
, *argv
)) {
219 explain1("loss percent");
222 if (NEXT_IS_NUMBER()) {
224 ++present
[TCA_NETEM_CORR
];
225 if (get_percent(&cor
.loss_corr
, *argv
)) {
226 explain1("loss correllation");
230 } else if (!strcmp(*argv
, "state")) {
234 if (parse_percent(&p13
, *argv
)) {
235 explain1("loss p13");
240 set_percent(&gimodel
.p13
, p13
);
241 set_percent(&gimodel
.p31
, 1. - p13
);
242 set_percent(&gimodel
.p32
, 0);
243 set_percent(&gimodel
.p23
, 1.);
244 set_percent(&gimodel
.p14
, 0);
245 loss_type
= NETEM_LOSS_GI
;
247 if (!NEXT_IS_NUMBER())
250 if (get_percent(&gimodel
.p31
, *argv
)) {
251 explain1("loss p31");
255 if (!NEXT_IS_NUMBER())
258 if (get_percent(&gimodel
.p32
, *argv
)) {
259 explain1("loss p32");
263 if (!NEXT_IS_NUMBER())
266 if (get_percent(&gimodel
.p23
, *argv
)) {
267 explain1("loss p23");
270 if (!NEXT_IS_NUMBER())
273 if (get_percent(&gimodel
.p14
, *argv
)) {
274 explain1("loss p14");
278 } else if (!strcmp(*argv
, "gemodel")) {
280 if (get_percent(&gemodel
.p
, *argv
)) {
281 explain1("loss gemodel p");
286 set_percent(&gemodel
.r
, 1.);
287 set_percent(&gemodel
.h
, 0);
288 set_percent(&gemodel
.k1
, 0);
289 loss_type
= NETEM_LOSS_GE
;
291 if (!NEXT_IS_NUMBER())
294 if (get_percent(&gemodel
.r
, *argv
)) {
295 explain1("loss gemodel r");
299 if (!NEXT_IS_NUMBER())
302 if (get_percent(&gemodel
.h
, *argv
)) {
303 explain1("loss gemodel h");
306 /* netem option is "1-h" but kernel
309 gemodel
.h
= UINT32_MAX
- gemodel
.h
;
311 if (!NEXT_IS_NUMBER())
314 if (get_percent(&gemodel
.k1
, *argv
)) {
315 explain1("loss gemodel k");
319 fprintf(stderr
, "Unknown loss parameter: %s\n",
323 } else if (matches(*argv
, "ecn") == 0) {
324 present
[TCA_NETEM_ECN
] = 1;
325 } else if (matches(*argv
, "reorder") == 0) {
327 present
[TCA_NETEM_REORDER
] = 1;
328 if (get_percent(&reorder
.probability
, *argv
)) {
332 if (NEXT_IS_NUMBER()) {
334 ++present
[TCA_NETEM_CORR
];
335 if (get_percent(&reorder
.correlation
, *argv
)) {
340 } else if (matches(*argv
, "corrupt") == 0) {
342 present
[TCA_NETEM_CORRUPT
] = 1;
343 if (get_percent(&corrupt
.probability
, *argv
)) {
347 if (NEXT_IS_NUMBER()) {
349 ++present
[TCA_NETEM_CORR
];
350 if (get_percent(&corrupt
.correlation
, *argv
)) {
355 } else if (matches(*argv
, "gap") == 0) {
357 if (get_u32(&opt
.gap
, *argv
, 0)) {
361 } else if (matches(*argv
, "duplicate") == 0) {
363 if (get_percent(&opt
.duplicate
, *argv
)) {
364 explain1("duplicate");
367 if (NEXT_IS_NUMBER()) {
369 if (get_percent(&cor
.dup_corr
, *argv
)) {
370 explain1("duplicate");
374 } else if (matches(*argv
, "distribution") == 0) {
376 dist_data
= calloc(sizeof(dist_data
[0]), MAX_DIST
);
377 dist_size
= get_distribution(*argv
, dist_data
, MAX_DIST
);
378 if (dist_size
<= 0) {
382 } else if (matches(*argv
, "rate") == 0) {
383 ++present
[TCA_NETEM_RATE
];
385 if (strchr(*argv
, '%')) {
386 if (get_percent_rate64(&rate64
, *argv
, dev
)) {
390 } else if (get_rate64(&rate64
, *argv
)) {
394 if (NEXT_IS_SIGNED_NUMBER()) {
396 if (get_s32(&rate
.packet_overhead
, *argv
, 0)) {
401 if (NEXT_IS_NUMBER()) {
403 if (get_u32(&rate
.cell_size
, *argv
, 0)) {
408 if (NEXT_IS_SIGNED_NUMBER()) {
410 if (get_s32(&rate
.cell_overhead
, *argv
, 0)) {
415 } else if (strcmp(*argv
, "help") == 0) {
419 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
425 tail
= NLMSG_TAIL(n
);
427 if (reorder
.probability
) {
428 if (opt
.latency
== 0) {
429 fprintf(stderr
, "reordering not possible without specifying some delay\n");
435 } else if (opt
.gap
> 0) {
436 fprintf(stderr
, "gap specified without reorder probability\n");
441 if (present
[TCA_NETEM_ECN
]) {
442 if (opt
.loss
<= 0 && loss_type
== NETEM_LOSS_UNSPEC
) {
443 fprintf(stderr
, "ecn requested without loss model\n");
449 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
450 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
455 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
458 if (present
[TCA_NETEM_CORR
] &&
459 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
462 if (present
[TCA_NETEM_REORDER
] &&
463 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
466 if (present
[TCA_NETEM_ECN
] &&
467 addattr_l(n
, 1024, TCA_NETEM_ECN
, &present
[TCA_NETEM_ECN
],
468 sizeof(present
[TCA_NETEM_ECN
])) < 0)
471 if (present
[TCA_NETEM_CORRUPT
] &&
472 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
475 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
476 struct rtattr
*start
;
478 start
= addattr_nest(n
, 1024, TCA_NETEM_LOSS
| NLA_F_NESTED
);
479 if (loss_type
== NETEM_LOSS_GI
) {
480 if (addattr_l(n
, 1024, NETEM_LOSS_GI
,
481 &gimodel
, sizeof(gimodel
)) < 0)
483 } else if (loss_type
== NETEM_LOSS_GE
) {
484 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
485 &gemodel
, sizeof(gemodel
)) < 0)
488 fprintf(stderr
, "loss in the weeds!\n");
492 addattr_nest_end(n
, start
);
495 if (present
[TCA_NETEM_RATE
]) {
496 if (rate64
>= (1ULL << 32)) {
497 if (addattr_l(n
, 1024,
498 TCA_NETEM_RATE64
, &rate64
, sizeof(rate64
)) < 0)
504 if (addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
509 if (addattr_l(n
, MAX_DIST
* sizeof(dist_data
[0]),
510 TCA_NETEM_DELAY_DIST
,
511 dist_data
, dist_size
* sizeof(dist_data
[0])) < 0)
515 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
519 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
521 const struct tc_netem_corr
*cor
= NULL
;
522 const struct tc_netem_reorder
*reorder
= NULL
;
523 const struct tc_netem_corrupt
*corrupt
= NULL
;
524 const struct tc_netem_gimodel
*gimodel
= NULL
;
525 const struct tc_netem_gemodel
*gemodel
= NULL
;
527 struct tc_netem_qopt qopt
;
528 const struct tc_netem_rate
*rate
= NULL
;
537 len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
539 fprintf(stderr
, "options size error\n");
542 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
545 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
547 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
550 if (tb
[TCA_NETEM_CORR
]) {
551 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
553 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
555 if (tb
[TCA_NETEM_REORDER
]) {
556 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
558 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
560 if (tb
[TCA_NETEM_CORRUPT
]) {
561 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
563 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
565 if (tb
[TCA_NETEM_LOSS
]) {
566 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
568 parse_rtattr_nested(lb
, NETEM_LOSS_MAX
, tb
[TCA_NETEM_LOSS
]);
569 if (lb
[NETEM_LOSS_GI
])
570 gimodel
= RTA_DATA(lb
[NETEM_LOSS_GI
]);
571 if (lb
[NETEM_LOSS_GE
])
572 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GE
]);
574 if (tb
[TCA_NETEM_RATE
]) {
575 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
577 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
579 if (tb
[TCA_NETEM_ECN
]) {
580 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
582 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
584 if (tb
[TCA_NETEM_RATE64
]) {
585 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE64
]) < sizeof(rate64
))
587 rate64
= rta_getattr_u64(tb
[TCA_NETEM_RATE64
]);
591 fprintf(f
, "limit %d", qopt
.limit
);
594 fprintf(f
, " delay %s", sprint_ticks(qopt
.latency
, b1
));
597 fprintf(f
, " %s", sprint_ticks(qopt
.jitter
, b1
));
598 if (cor
&& cor
->delay_corr
)
599 fprintf(f
, " %s", sprint_percent(cor
->delay_corr
, b1
));
604 fprintf(f
, " loss %s", sprint_percent(qopt
.loss
, b1
));
605 if (cor
&& cor
->loss_corr
)
606 fprintf(f
, " %s", sprint_percent(cor
->loss_corr
, b1
));
610 fprintf(f
, " loss state p13 %s", sprint_percent(gimodel
->p13
, b1
));
611 fprintf(f
, " p31 %s", sprint_percent(gimodel
->p31
, b1
));
612 fprintf(f
, " p32 %s", sprint_percent(gimodel
->p32
, b1
));
613 fprintf(f
, " p23 %s", sprint_percent(gimodel
->p23
, b1
));
614 fprintf(f
, " p14 %s", sprint_percent(gimodel
->p14
, b1
));
618 fprintf(f
, " loss gemodel p %s",
619 sprint_percent(gemodel
->p
, b1
));
620 fprintf(f
, " r %s", sprint_percent(gemodel
->r
, b1
));
621 fprintf(f
, " 1-h %s", sprint_percent(UINT32_MAX
-
623 fprintf(f
, " 1-k %s", sprint_percent(gemodel
->k1
, b1
));
626 if (qopt
.duplicate
) {
627 fprintf(f
, " duplicate %s",
628 sprint_percent(qopt
.duplicate
, b1
));
629 if (cor
&& cor
->dup_corr
)
630 fprintf(f
, " %s", sprint_percent(cor
->dup_corr
, b1
));
633 if (reorder
&& reorder
->probability
) {
634 fprintf(f
, " reorder %s",
635 sprint_percent(reorder
->probability
, b1
));
636 if (reorder
->correlation
)
638 sprint_percent(reorder
->correlation
, b1
));
641 if (corrupt
&& corrupt
->probability
) {
642 fprintf(f
, " corrupt %s",
643 sprint_percent(corrupt
->probability
, b1
));
644 if (corrupt
->correlation
)
646 sprint_percent(corrupt
->correlation
, b1
));
649 if (rate
&& rate
->rate
) {
651 fprintf(f
, " rate %s", sprint_rate(rate64
, b1
));
653 fprintf(f
, " rate %s", sprint_rate(rate
->rate
, b1
));
654 if (rate
->packet_overhead
)
655 fprintf(f
, " packetoverhead %d", rate
->packet_overhead
);
657 fprintf(f
, " cellsize %u", rate
->cell_size
);
658 if (rate
->cell_overhead
)
659 fprintf(f
, " celloverhead %d", rate
->cell_overhead
);
666 fprintf(f
, " gap %lu", (unsigned long)qopt
.gap
);
672 struct qdisc_util netem_qdisc_util
= {
674 .parse_qopt
= netem_parse_opt
,
675 .print_qopt
= netem_print_opt
,