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) 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) {
126 if (*line
== '\n' || *line
== '#')
129 for (p
= line
; ; p
= endp
) {
130 x
= strtol(p
, &endp
, 0);
135 fprintf(stderr
, "%s: too much data\n",
149 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
150 #define NEXT_IS_SIGNED_NUMBER() \
151 (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
153 /* Adjust for the fact that psched_ticks aren't always usecs
154 (based on kernel PSCHED_CLOCK configuration */
155 static int get_ticks(__u32
*ticks
, const char *str
)
159 if(get_time(&t
, str
))
162 if (tc_core_time2big(t
)) {
163 fprintf(stderr
, "Illegal %u time (too large)\n", t
);
167 *ticks
= tc_core_time2tick(t
);
171 static int netem_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
,
176 struct tc_netem_qopt opt
= { .limit
= 1000 };
177 struct tc_netem_corr cor
;
178 struct tc_netem_reorder reorder
;
179 struct tc_netem_corrupt corrupt
;
180 struct tc_netem_gimodel gimodel
;
181 struct tc_netem_gemodel gemodel
;
182 struct tc_netem_rate rate
;
183 __s16
*dist_data
= NULL
;
184 __u16 loss_type
= NETEM_LOSS_UNSPEC
;
185 int present
[__TCA_NETEM_MAX
];
188 memset(&cor
, 0, sizeof(cor
));
189 memset(&reorder
, 0, sizeof(reorder
));
190 memset(&corrupt
, 0, sizeof(corrupt
));
191 memset(&rate
, 0, sizeof(rate
));
192 memset(present
, 0, sizeof(present
));
194 for( ; argc
> 0; --argc
, ++argv
) {
195 if (matches(*argv
, "limit") == 0) {
197 if (get_size(&opt
.limit
, *argv
)) {
201 } else if (matches(*argv
, "latency") == 0 ||
202 matches(*argv
, "delay") == 0) {
204 if (get_ticks(&opt
.latency
, *argv
)) {
209 if (NEXT_IS_NUMBER()) {
211 if (get_ticks(&opt
.jitter
, *argv
)) {
216 if (NEXT_IS_NUMBER()) {
218 ++present
[TCA_NETEM_CORR
];
219 if (get_percent(&cor
.delay_corr
, *argv
)) {
225 } else if (matches(*argv
, "loss") == 0 ||
226 matches(*argv
, "drop") == 0) {
227 if (opt
.loss
> 0 || loss_type
!= NETEM_LOSS_UNSPEC
) {
228 explain1("duplicate loss argument\n");
233 /* Old (deprecated) random loss model syntax */
234 if (isdigit(argv
[0][0]))
235 goto random_loss_model
;
237 if (!strcmp(*argv
, "random")) {
240 if (get_percent(&opt
.loss
, *argv
)) {
241 explain1("loss percent");
244 if (NEXT_IS_NUMBER()) {
246 ++present
[TCA_NETEM_CORR
];
247 if (get_percent(&cor
.loss_corr
, *argv
)) {
248 explain1("loss correllation");
252 } else if (!strcmp(*argv
, "state")) {
256 if (parse_percent(&p13
, *argv
)) {
257 explain1("loss p13");
262 set_percent(&gimodel
.p13
, p13
);
263 set_percent(&gimodel
.p31
, 1. - p13
);
264 set_percent(&gimodel
.p32
, 0);
265 set_percent(&gimodel
.p23
, 1.);
266 set_percent(&gimodel
.p14
, 0);
267 loss_type
= NETEM_LOSS_GI
;
269 if (!NEXT_IS_NUMBER())
272 if (get_percent(&gimodel
.p31
, *argv
)) {
273 explain1("loss p31");
277 if (!NEXT_IS_NUMBER())
280 if (get_percent(&gimodel
.p32
, *argv
)) {
281 explain1("loss p32");
285 if (!NEXT_IS_NUMBER())
288 if (get_percent(&gimodel
.p23
, *argv
)) {
289 explain1("loss p23");
292 if (!NEXT_IS_NUMBER())
295 if (get_percent(&gimodel
.p14
, *argv
)) {
296 explain1("loss p14");
300 } else if (!strcmp(*argv
, "gemodel")) {
302 if (get_percent(&gemodel
.p
, *argv
)) {
303 explain1("loss gemodel p");
308 set_percent(&gemodel
.r
, 1.);
309 set_percent(&gemodel
.h
, 0);
310 set_percent(&gemodel
.k1
, 0);
311 loss_type
= NETEM_LOSS_GE
;
313 if (!NEXT_IS_NUMBER())
316 if (get_percent(&gemodel
.r
, *argv
)) {
317 explain1("loss gemodel r");
321 if (!NEXT_IS_NUMBER())
324 if (get_percent(&gemodel
.h
, *argv
)) {
325 explain1("loss gemodel h");
328 /* netem option is "1-h" but kernel
331 gemodel
.h
= max_percent_value
- gemodel
.h
;
333 if (!NEXT_IS_NUMBER())
336 if (get_percent(&gemodel
.k1
, *argv
)) {
337 explain1("loss gemodel k");
341 fprintf(stderr
, "Unknown loss parameter: %s\n",
345 } else if (matches(*argv
, "ecn") == 0) {
346 present
[TCA_NETEM_ECN
] = 1;
347 } else if (matches(*argv
, "reorder") == 0) {
349 present
[TCA_NETEM_REORDER
] = 1;
350 if (get_percent(&reorder
.probability
, *argv
)) {
354 if (NEXT_IS_NUMBER()) {
356 ++present
[TCA_NETEM_CORR
];
357 if (get_percent(&reorder
.correlation
, *argv
)) {
362 } else if (matches(*argv
, "corrupt") == 0) {
364 present
[TCA_NETEM_CORRUPT
] = 1;
365 if (get_percent(&corrupt
.probability
, *argv
)) {
369 if (NEXT_IS_NUMBER()) {
371 ++present
[TCA_NETEM_CORR
];
372 if (get_percent(&corrupt
.correlation
, *argv
)) {
377 } else if (matches(*argv
, "gap") == 0) {
379 if (get_u32(&opt
.gap
, *argv
, 0)) {
383 } else if (matches(*argv
, "duplicate") == 0) {
385 if (get_percent(&opt
.duplicate
, *argv
)) {
386 explain1("duplicate");
389 if (NEXT_IS_NUMBER()) {
391 if (get_percent(&cor
.dup_corr
, *argv
)) {
392 explain1("duplicate");
396 } else if (matches(*argv
, "distribution") == 0) {
398 dist_data
= calloc(sizeof(dist_data
[0]), MAX_DIST
);
399 dist_size
= get_distribution(*argv
, dist_data
, MAX_DIST
);
400 if (dist_size
<= 0) {
404 } else if (matches(*argv
, "rate") == 0) {
405 ++present
[TCA_NETEM_RATE
];
407 if (get_rate64(&rate64
, *argv
)) {
411 if (NEXT_IS_SIGNED_NUMBER()) {
413 if (get_s32(&rate
.packet_overhead
, *argv
, 0)) {
418 if (NEXT_IS_NUMBER()) {
420 if (get_u32(&rate
.cell_size
, *argv
, 0)) {
425 if (NEXT_IS_SIGNED_NUMBER()) {
427 if (get_s32(&rate
.cell_overhead
, *argv
, 0)) {
432 } else if (strcmp(*argv
, "help") == 0) {
436 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
442 tail
= NLMSG_TAIL(n
);
444 if (reorder
.probability
) {
445 if (opt
.latency
== 0) {
446 fprintf(stderr
, "reordering not possible without specifying some delay\n");
452 } else if (opt
.gap
> 0) {
453 fprintf(stderr
, "gap specified without reorder probability\n");
458 if (present
[TCA_NETEM_ECN
]) {
459 if (opt
.loss
<= 0 && loss_type
== NETEM_LOSS_UNSPEC
) {
460 fprintf(stderr
, "ecn requested without loss model\n");
466 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
467 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
472 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
475 if (present
[TCA_NETEM_CORR
] &&
476 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
479 if (present
[TCA_NETEM_REORDER
] &&
480 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
483 if (present
[TCA_NETEM_ECN
] &&
484 addattr_l(n
, 1024, TCA_NETEM_ECN
, &present
[TCA_NETEM_ECN
],
485 sizeof(present
[TCA_NETEM_ECN
])) < 0)
488 if (present
[TCA_NETEM_CORRUPT
] &&
489 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
492 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
493 struct rtattr
*start
;
495 start
= addattr_nest(n
, 1024, TCA_NETEM_LOSS
| NLA_F_NESTED
);
496 if (loss_type
== NETEM_LOSS_GI
) {
497 if (addattr_l(n
, 1024, NETEM_LOSS_GI
,
498 &gimodel
, sizeof(gimodel
)) < 0)
500 } else if (loss_type
== NETEM_LOSS_GE
) {
501 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
502 &gemodel
, sizeof(gemodel
)) < 0)
505 fprintf(stderr
, "loss in the weeds!\n");
509 addattr_nest_end(n
, start
);
512 if (present
[TCA_NETEM_RATE
]) {
513 if (rate64
>= (1ULL << 32)) {
514 if (addattr_l(n
, 1024,
515 TCA_NETEM_RATE64
, &rate64
, sizeof(rate64
)) < 0)
521 if (addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
526 if (addattr_l(n
, MAX_DIST
* sizeof(dist_data
[0]),
527 TCA_NETEM_DELAY_DIST
,
528 dist_data
, dist_size
* sizeof(dist_data
[0])) < 0)
532 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
536 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
538 const struct tc_netem_corr
*cor
= NULL
;
539 const struct tc_netem_reorder
*reorder
= NULL
;
540 const struct tc_netem_corrupt
*corrupt
= NULL
;
541 const struct tc_netem_gimodel
*gimodel
= NULL
;
542 const struct tc_netem_gemodel
*gemodel
= NULL
;
544 struct tc_netem_qopt qopt
;
545 const struct tc_netem_rate
*rate
= NULL
;
546 int len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
554 fprintf(stderr
, "options size error\n");
557 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
560 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
561 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
564 if (tb
[TCA_NETEM_CORR
]) {
565 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
567 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
569 if (tb
[TCA_NETEM_REORDER
]) {
570 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
572 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
574 if (tb
[TCA_NETEM_CORRUPT
]) {
575 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
577 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
579 if (tb
[TCA_NETEM_LOSS
]) {
580 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
582 parse_rtattr_nested(lb
, NETEM_LOSS_MAX
, tb
[TCA_NETEM_LOSS
]);
583 if (lb
[NETEM_LOSS_GI
])
584 gimodel
= RTA_DATA(lb
[NETEM_LOSS_GI
]);
585 if (lb
[NETEM_LOSS_GE
])
586 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GE
]);
588 if (tb
[TCA_NETEM_RATE
]) {
589 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
591 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
593 if (tb
[TCA_NETEM_ECN
]) {
594 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
596 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
598 if (tb
[TCA_NETEM_RATE64
]) {
599 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE64
]) < sizeof(rate64
))
601 rate64
= rta_getattr_u64(tb
[TCA_NETEM_RATE64
]);
605 fprintf(f
, "limit %d", qopt
.limit
);
608 fprintf(f
, " delay %s", sprint_ticks(qopt
.latency
, b1
));
611 fprintf(f
, " %s", sprint_ticks(qopt
.jitter
, b1
));
612 if (cor
&& cor
->delay_corr
)
613 fprintf(f
, " %s", sprint_percent(cor
->delay_corr
, b1
));
618 fprintf(f
, " loss %s", sprint_percent(qopt
.loss
, b1
));
619 if (cor
&& cor
->loss_corr
)
620 fprintf(f
, " %s", sprint_percent(cor
->loss_corr
, b1
));
624 fprintf(f
, " loss state p13 %s", sprint_percent(gimodel
->p13
, b1
));
625 fprintf(f
, " p31 %s", sprint_percent(gimodel
->p31
, b1
));
626 fprintf(f
, " p32 %s", sprint_percent(gimodel
->p32
, b1
));
627 fprintf(f
, " p23 %s", sprint_percent(gimodel
->p23
, b1
));
628 fprintf(f
, " p14 %s", sprint_percent(gimodel
->p14
, b1
));
632 fprintf(f
, " loss gemodel p %s",
633 sprint_percent(gemodel
->p
, b1
));
634 fprintf(f
, " r %s", sprint_percent(gemodel
->r
, b1
));
635 fprintf(f
, " 1-h %s", sprint_percent(max_percent_value
-
637 fprintf(f
, " 1-k %s", sprint_percent(gemodel
->k1
, b1
));
640 if (qopt
.duplicate
) {
641 fprintf(f
, " duplicate %s",
642 sprint_percent(qopt
.duplicate
, b1
));
643 if (cor
&& cor
->dup_corr
)
644 fprintf(f
, " %s", sprint_percent(cor
->dup_corr
, b1
));
647 if (reorder
&& reorder
->probability
) {
648 fprintf(f
, " reorder %s",
649 sprint_percent(reorder
->probability
, b1
));
650 if (reorder
->correlation
)
652 sprint_percent(reorder
->correlation
, b1
));
655 if (corrupt
&& corrupt
->probability
) {
656 fprintf(f
, " corrupt %s",
657 sprint_percent(corrupt
->probability
, b1
));
658 if (corrupt
->correlation
)
660 sprint_percent(corrupt
->correlation
, b1
));
663 if (rate
&& rate
->rate
) {
665 fprintf(f
, " rate %s", sprint_rate(rate64
, b1
));
667 fprintf(f
, " rate %s", sprint_rate(rate
->rate
, b1
));
668 if (rate
->packet_overhead
)
669 fprintf(f
, " packetoverhead %d", rate
->packet_overhead
);
671 fprintf(f
, " cellsize %u", rate
->cell_size
);
672 if (rate
->cell_overhead
)
673 fprintf(f
, " celloverhead %d", rate
->cell_overhead
);
680 fprintf(f
, " gap %lu", (unsigned long)qopt
.gap
);
686 struct qdisc_util netem_qdisc_util
= {
688 .parse_qopt
= netem_parse_opt
,
689 .print_qopt
= netem_print_opt
,