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 static const double max_percent_value
= 0xffffffff;
58 /* scaled value used to percent of maximum. */
59 static void set_percent(__u32
*percent
, double per
)
61 *percent
= (unsigned int) rint(per
* max_percent_value
);
65 /* Parse either a fraction '.3' or percent '30%
66 * return: 0 = ok, -1 = error, 1 = out of range
68 static int parse_percent(double *val
, const char *str
)
72 *val
= strtod(str
, &p
) / 100.;
73 if (*p
&& strcmp(p
, "%"))
79 static int get_percent(__u32
*percent
, const char *str
)
83 if (parse_percent(&per
, str
))
86 set_percent(percent
, per
);
90 static void print_percent(char *buf
, int len
, __u32 per
)
92 snprintf(buf
, len
, "%g%%", 100. * (double) per
/ max_percent_value
);
95 static char *sprint_percent(__u32 per
, char *buf
)
97 print_percent(buf
, SPRINT_BSIZE
-1, per
);
102 * Simplistic file parser for distrbution data.
107 static int get_distribution(const char *type
, __s16
*data
, int maxdata
)
116 snprintf(name
, sizeof(name
), "%s/%s.dist", get_tc_lib(), type
);
117 if ((f
= fopen(name
, "r")) == NULL
) {
118 fprintf(stderr
, "No distribution data for %s (%s: %s)\n",
119 type
, name
, strerror(errno
));
124 while (getline(&line
, &len
, f
) != -1) {
127 if (*line
== '\n' || *line
== '#')
130 for (p
= line
; ; p
= endp
) {
131 x
= strtol(p
, &endp
, 0);
136 fprintf(stderr
, "%s: too much data\n",
150 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
151 #define NEXT_IS_SIGNED_NUMBER() \
152 (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
154 /* Adjust for the fact that psched_ticks aren't always usecs
155 (based on kernel PSCHED_CLOCK configuration */
156 static int get_ticks(__u32
*ticks
, const char *str
)
160 if (get_time(&t
, str
))
163 if (tc_core_time2big(t
)) {
164 fprintf(stderr
, "Illegal %u time (too large)\n", t
);
168 *ticks
= tc_core_time2tick(t
);
172 static int netem_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
,
177 struct tc_netem_qopt opt
= { .limit
= 1000 };
178 struct tc_netem_corr cor
= {};
179 struct tc_netem_reorder reorder
= {};
180 struct tc_netem_corrupt corrupt
= {};
181 struct tc_netem_gimodel gimodel
;
182 struct tc_netem_gemodel gemodel
;
183 struct tc_netem_rate rate
= {};
184 __s16
*dist_data
= NULL
;
185 __u16 loss_type
= NETEM_LOSS_UNSPEC
;
186 int present
[__TCA_NETEM_MAX
] = {};
189 for ( ; argc
> 0; --argc
, ++argv
) {
190 if (matches(*argv
, "limit") == 0) {
192 if (get_size(&opt
.limit
, *argv
)) {
196 } else if (matches(*argv
, "latency") == 0 ||
197 matches(*argv
, "delay") == 0) {
199 if (get_ticks(&opt
.latency
, *argv
)) {
204 if (NEXT_IS_NUMBER()) {
206 if (get_ticks(&opt
.jitter
, *argv
)) {
211 if (NEXT_IS_NUMBER()) {
213 ++present
[TCA_NETEM_CORR
];
214 if (get_percent(&cor
.delay_corr
, *argv
)) {
220 } else if (matches(*argv
, "loss") == 0 ||
221 matches(*argv
, "drop") == 0) {
222 if (opt
.loss
> 0 || loss_type
!= NETEM_LOSS_UNSPEC
) {
223 explain1("duplicate loss argument\n");
228 /* Old (deprecated) random loss model syntax */
229 if (isdigit(argv
[0][0]))
230 goto random_loss_model
;
232 if (!strcmp(*argv
, "random")) {
235 if (get_percent(&opt
.loss
, *argv
)) {
236 explain1("loss percent");
239 if (NEXT_IS_NUMBER()) {
241 ++present
[TCA_NETEM_CORR
];
242 if (get_percent(&cor
.loss_corr
, *argv
)) {
243 explain1("loss correllation");
247 } else if (!strcmp(*argv
, "state")) {
251 if (parse_percent(&p13
, *argv
)) {
252 explain1("loss p13");
257 set_percent(&gimodel
.p13
, p13
);
258 set_percent(&gimodel
.p31
, 1. - p13
);
259 set_percent(&gimodel
.p32
, 0);
260 set_percent(&gimodel
.p23
, 1.);
261 set_percent(&gimodel
.p14
, 0);
262 loss_type
= NETEM_LOSS_GI
;
264 if (!NEXT_IS_NUMBER())
267 if (get_percent(&gimodel
.p31
, *argv
)) {
268 explain1("loss p31");
272 if (!NEXT_IS_NUMBER())
275 if (get_percent(&gimodel
.p32
, *argv
)) {
276 explain1("loss p32");
280 if (!NEXT_IS_NUMBER())
283 if (get_percent(&gimodel
.p23
, *argv
)) {
284 explain1("loss p23");
287 if (!NEXT_IS_NUMBER())
290 if (get_percent(&gimodel
.p14
, *argv
)) {
291 explain1("loss p14");
295 } else if (!strcmp(*argv
, "gemodel")) {
297 if (get_percent(&gemodel
.p
, *argv
)) {
298 explain1("loss gemodel p");
303 set_percent(&gemodel
.r
, 1.);
304 set_percent(&gemodel
.h
, 0);
305 set_percent(&gemodel
.k1
, 0);
306 loss_type
= NETEM_LOSS_GE
;
308 if (!NEXT_IS_NUMBER())
311 if (get_percent(&gemodel
.r
, *argv
)) {
312 explain1("loss gemodel r");
316 if (!NEXT_IS_NUMBER())
319 if (get_percent(&gemodel
.h
, *argv
)) {
320 explain1("loss gemodel h");
323 /* netem option is "1-h" but kernel
326 gemodel
.h
= max_percent_value
- gemodel
.h
;
328 if (!NEXT_IS_NUMBER())
331 if (get_percent(&gemodel
.k1
, *argv
)) {
332 explain1("loss gemodel k");
336 fprintf(stderr
, "Unknown loss parameter: %s\n",
340 } else if (matches(*argv
, "ecn") == 0) {
341 present
[TCA_NETEM_ECN
] = 1;
342 } else if (matches(*argv
, "reorder") == 0) {
344 present
[TCA_NETEM_REORDER
] = 1;
345 if (get_percent(&reorder
.probability
, *argv
)) {
349 if (NEXT_IS_NUMBER()) {
351 ++present
[TCA_NETEM_CORR
];
352 if (get_percent(&reorder
.correlation
, *argv
)) {
357 } else if (matches(*argv
, "corrupt") == 0) {
359 present
[TCA_NETEM_CORRUPT
] = 1;
360 if (get_percent(&corrupt
.probability
, *argv
)) {
364 if (NEXT_IS_NUMBER()) {
366 ++present
[TCA_NETEM_CORR
];
367 if (get_percent(&corrupt
.correlation
, *argv
)) {
372 } else if (matches(*argv
, "gap") == 0) {
374 if (get_u32(&opt
.gap
, *argv
, 0)) {
378 } else if (matches(*argv
, "duplicate") == 0) {
380 if (get_percent(&opt
.duplicate
, *argv
)) {
381 explain1("duplicate");
384 if (NEXT_IS_NUMBER()) {
386 if (get_percent(&cor
.dup_corr
, *argv
)) {
387 explain1("duplicate");
391 } else if (matches(*argv
, "distribution") == 0) {
393 dist_data
= calloc(sizeof(dist_data
[0]), MAX_DIST
);
394 dist_size
= get_distribution(*argv
, dist_data
, MAX_DIST
);
395 if (dist_size
<= 0) {
399 } else if (matches(*argv
, "rate") == 0) {
400 ++present
[TCA_NETEM_RATE
];
402 if (get_rate64(&rate64
, *argv
)) {
406 if (NEXT_IS_SIGNED_NUMBER()) {
408 if (get_s32(&rate
.packet_overhead
, *argv
, 0)) {
413 if (NEXT_IS_NUMBER()) {
415 if (get_u32(&rate
.cell_size
, *argv
, 0)) {
420 if (NEXT_IS_SIGNED_NUMBER()) {
422 if (get_s32(&rate
.cell_overhead
, *argv
, 0)) {
427 } else if (strcmp(*argv
, "help") == 0) {
431 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
437 tail
= NLMSG_TAIL(n
);
439 if (reorder
.probability
) {
440 if (opt
.latency
== 0) {
441 fprintf(stderr
, "reordering not possible without specifying some delay\n");
447 } else if (opt
.gap
> 0) {
448 fprintf(stderr
, "gap specified without reorder probability\n");
453 if (present
[TCA_NETEM_ECN
]) {
454 if (opt
.loss
<= 0 && loss_type
== NETEM_LOSS_UNSPEC
) {
455 fprintf(stderr
, "ecn requested without loss model\n");
461 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
462 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
467 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
470 if (present
[TCA_NETEM_CORR
] &&
471 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
474 if (present
[TCA_NETEM_REORDER
] &&
475 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
478 if (present
[TCA_NETEM_ECN
] &&
479 addattr_l(n
, 1024, TCA_NETEM_ECN
, &present
[TCA_NETEM_ECN
],
480 sizeof(present
[TCA_NETEM_ECN
])) < 0)
483 if (present
[TCA_NETEM_CORRUPT
] &&
484 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
487 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
488 struct rtattr
*start
;
490 start
= addattr_nest(n
, 1024, TCA_NETEM_LOSS
| NLA_F_NESTED
);
491 if (loss_type
== NETEM_LOSS_GI
) {
492 if (addattr_l(n
, 1024, NETEM_LOSS_GI
,
493 &gimodel
, sizeof(gimodel
)) < 0)
495 } else if (loss_type
== NETEM_LOSS_GE
) {
496 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
497 &gemodel
, sizeof(gemodel
)) < 0)
500 fprintf(stderr
, "loss in the weeds!\n");
504 addattr_nest_end(n
, start
);
507 if (present
[TCA_NETEM_RATE
]) {
508 if (rate64
>= (1ULL << 32)) {
509 if (addattr_l(n
, 1024,
510 TCA_NETEM_RATE64
, &rate64
, sizeof(rate64
)) < 0)
516 if (addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
521 if (addattr_l(n
, MAX_DIST
* sizeof(dist_data
[0]),
522 TCA_NETEM_DELAY_DIST
,
523 dist_data
, dist_size
* sizeof(dist_data
[0])) < 0)
527 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
531 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
533 const struct tc_netem_corr
*cor
= NULL
;
534 const struct tc_netem_reorder
*reorder
= NULL
;
535 const struct tc_netem_corrupt
*corrupt
= NULL
;
536 const struct tc_netem_gimodel
*gimodel
= NULL
;
537 const struct tc_netem_gemodel
*gemodel
= NULL
;
539 struct tc_netem_qopt qopt
;
540 const struct tc_netem_rate
*rate
= NULL
;
541 int len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
550 fprintf(stderr
, "options size error\n");
553 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
556 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
558 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
561 if (tb
[TCA_NETEM_CORR
]) {
562 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
564 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
566 if (tb
[TCA_NETEM_REORDER
]) {
567 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
569 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
571 if (tb
[TCA_NETEM_CORRUPT
]) {
572 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
574 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
576 if (tb
[TCA_NETEM_LOSS
]) {
577 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
579 parse_rtattr_nested(lb
, NETEM_LOSS_MAX
, tb
[TCA_NETEM_LOSS
]);
580 if (lb
[NETEM_LOSS_GI
])
581 gimodel
= RTA_DATA(lb
[NETEM_LOSS_GI
]);
582 if (lb
[NETEM_LOSS_GE
])
583 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GE
]);
585 if (tb
[TCA_NETEM_RATE
]) {
586 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
588 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
590 if (tb
[TCA_NETEM_ECN
]) {
591 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
593 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
595 if (tb
[TCA_NETEM_RATE64
]) {
596 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE64
]) < sizeof(rate64
))
598 rate64
= rta_getattr_u64(tb
[TCA_NETEM_RATE64
]);
602 fprintf(f
, "limit %d", qopt
.limit
);
605 fprintf(f
, " delay %s", sprint_ticks(qopt
.latency
, b1
));
608 fprintf(f
, " %s", sprint_ticks(qopt
.jitter
, b1
));
609 if (cor
&& cor
->delay_corr
)
610 fprintf(f
, " %s", sprint_percent(cor
->delay_corr
, b1
));
615 fprintf(f
, " loss %s", sprint_percent(qopt
.loss
, b1
));
616 if (cor
&& cor
->loss_corr
)
617 fprintf(f
, " %s", sprint_percent(cor
->loss_corr
, b1
));
621 fprintf(f
, " loss state p13 %s", sprint_percent(gimodel
->p13
, b1
));
622 fprintf(f
, " p31 %s", sprint_percent(gimodel
->p31
, b1
));
623 fprintf(f
, " p32 %s", sprint_percent(gimodel
->p32
, b1
));
624 fprintf(f
, " p23 %s", sprint_percent(gimodel
->p23
, b1
));
625 fprintf(f
, " p14 %s", sprint_percent(gimodel
->p14
, b1
));
629 fprintf(f
, " loss gemodel p %s",
630 sprint_percent(gemodel
->p
, b1
));
631 fprintf(f
, " r %s", sprint_percent(gemodel
->r
, b1
));
632 fprintf(f
, " 1-h %s", sprint_percent(max_percent_value
-
634 fprintf(f
, " 1-k %s", sprint_percent(gemodel
->k1
, b1
));
637 if (qopt
.duplicate
) {
638 fprintf(f
, " duplicate %s",
639 sprint_percent(qopt
.duplicate
, b1
));
640 if (cor
&& cor
->dup_corr
)
641 fprintf(f
, " %s", sprint_percent(cor
->dup_corr
, b1
));
644 if (reorder
&& reorder
->probability
) {
645 fprintf(f
, " reorder %s",
646 sprint_percent(reorder
->probability
, b1
));
647 if (reorder
->correlation
)
649 sprint_percent(reorder
->correlation
, b1
));
652 if (corrupt
&& corrupt
->probability
) {
653 fprintf(f
, " corrupt %s",
654 sprint_percent(corrupt
->probability
, b1
));
655 if (corrupt
->correlation
)
657 sprint_percent(corrupt
->correlation
, b1
));
660 if (rate
&& rate
->rate
) {
662 fprintf(f
, " rate %s", sprint_rate(rate64
, b1
));
664 fprintf(f
, " rate %s", sprint_rate(rate
->rate
, b1
));
665 if (rate
->packet_overhead
)
666 fprintf(f
, " packetoverhead %d", rate
->packet_overhead
);
668 fprintf(f
, " cellsize %u", rate
->cell_size
);
669 if (rate
->cell_overhead
)
670 fprintf(f
, " celloverhead %d", rate
->cell_overhead
);
677 fprintf(f
, " gap %lu", (unsigned long)qopt
.gap
);
683 struct qdisc_util netem_qdisc_util
= {
685 .parse_qopt
= netem_parse_opt
,
686 .print_qopt
= netem_print_opt
,