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
];
187 memset(&cor
, 0, sizeof(cor
));
188 memset(&reorder
, 0, sizeof(reorder
));
189 memset(&corrupt
, 0, sizeof(corrupt
));
190 memset(&rate
, 0, sizeof(rate
));
191 memset(present
, 0, sizeof(present
));
193 for( ; argc
> 0; --argc
, ++argv
) {
194 if (matches(*argv
, "limit") == 0) {
196 if (get_size(&opt
.limit
, *argv
)) {
200 } else if (matches(*argv
, "latency") == 0 ||
201 matches(*argv
, "delay") == 0) {
203 if (get_ticks(&opt
.latency
, *argv
)) {
208 if (NEXT_IS_NUMBER()) {
210 if (get_ticks(&opt
.jitter
, *argv
)) {
215 if (NEXT_IS_NUMBER()) {
217 ++present
[TCA_NETEM_CORR
];
218 if (get_percent(&cor
.delay_corr
, *argv
)) {
224 } else if (matches(*argv
, "loss") == 0 ||
225 matches(*argv
, "drop") == 0) {
226 if (opt
.loss
> 0 || loss_type
!= NETEM_LOSS_UNSPEC
) {
227 explain1("duplicate loss argument\n");
232 /* Old (deprecated) random loss model syntax */
233 if (isdigit(argv
[0][0]))
234 goto random_loss_model
;
236 if (!strcmp(*argv
, "random")) {
239 if (get_percent(&opt
.loss
, *argv
)) {
240 explain1("loss percent");
243 if (NEXT_IS_NUMBER()) {
245 ++present
[TCA_NETEM_CORR
];
246 if (get_percent(&cor
.loss_corr
, *argv
)) {
247 explain1("loss correllation");
251 } else if (!strcmp(*argv
, "state")) {
255 if (parse_percent(&p13
, *argv
)) {
256 explain1("loss p13");
261 set_percent(&gimodel
.p13
, p13
);
262 set_percent(&gimodel
.p31
, 1. - p13
);
263 set_percent(&gimodel
.p32
, 0);
264 set_percent(&gimodel
.p23
, 1.);
265 loss_type
= NETEM_LOSS_GI
;
267 if (!NEXT_IS_NUMBER())
270 if (get_percent(&gimodel
.p31
, *argv
)) {
271 explain1("loss p31");
275 if (!NEXT_IS_NUMBER())
278 if (get_percent(&gimodel
.p32
, *argv
)) {
279 explain1("loss p32");
283 if (!NEXT_IS_NUMBER())
286 if (get_percent(&gimodel
.p23
, *argv
)) {
287 explain1("loss p23");
291 } else if (!strcmp(*argv
, "gemodel")) {
293 if (get_percent(&gemodel
.p
, *argv
)) {
294 explain1("loss gemodel p");
299 set_percent(&gemodel
.r
, 1.);
300 set_percent(&gemodel
.h
, 0);
301 set_percent(&gemodel
.k1
, 1.);
302 loss_type
= NETEM_LOSS_GE
;
304 if (!NEXT_IS_NUMBER())
307 if (get_percent(&gemodel
.r
, *argv
)) {
308 explain1("loss gemodel r");
312 if (!NEXT_IS_NUMBER())
315 if (get_percent(&gemodel
.h
, *argv
)) {
316 explain1("loss gemodel h");
320 if (!NEXT_IS_NUMBER())
323 if (get_percent(&gemodel
.k1
, *argv
)) {
324 explain1("loss gemodel k");
328 fprintf(stderr
, "Unknown loss parameter: %s\n",
332 } else if (matches(*argv
, "ecn") == 0) {
333 present
[TCA_NETEM_ECN
] = 1;
334 } else if (matches(*argv
, "reorder") == 0) {
336 present
[TCA_NETEM_REORDER
] = 1;
337 if (get_percent(&reorder
.probability
, *argv
)) {
341 if (NEXT_IS_NUMBER()) {
343 ++present
[TCA_NETEM_CORR
];
344 if (get_percent(&reorder
.correlation
, *argv
)) {
349 } else if (matches(*argv
, "corrupt") == 0) {
351 present
[TCA_NETEM_CORRUPT
] = 1;
352 if (get_percent(&corrupt
.probability
, *argv
)) {
356 if (NEXT_IS_NUMBER()) {
358 ++present
[TCA_NETEM_CORR
];
359 if (get_percent(&corrupt
.correlation
, *argv
)) {
364 } else if (matches(*argv
, "gap") == 0) {
366 if (get_u32(&opt
.gap
, *argv
, 0)) {
370 } else if (matches(*argv
, "duplicate") == 0) {
372 if (get_percent(&opt
.duplicate
, *argv
)) {
373 explain1("duplicate");
376 if (NEXT_IS_NUMBER()) {
378 if (get_percent(&cor
.dup_corr
, *argv
)) {
379 explain1("duplicate");
383 } else if (matches(*argv
, "distribution") == 0) {
385 dist_data
= calloc(sizeof(dist_data
[0]), MAX_DIST
);
386 dist_size
= get_distribution(*argv
, dist_data
, MAX_DIST
);
387 if (dist_size
<= 0) {
391 } else if (matches(*argv
, "rate") == 0) {
392 ++present
[TCA_NETEM_RATE
];
394 if (get_rate(&rate
.rate
, *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 (strcmp(*argv
, "help") == 0) {
423 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
429 tail
= NLMSG_TAIL(n
);
431 if (reorder
.probability
) {
432 if (opt
.latency
== 0) {
433 fprintf(stderr
, "reordering not possible without specifying some delay\n");
439 } else if (opt
.gap
> 0) {
440 fprintf(stderr
, "gap specified without reorder probability\n");
445 if (present
[TCA_NETEM_ECN
]) {
446 if (opt
.loss
<= 0 && loss_type
== NETEM_LOSS_UNSPEC
) {
447 fprintf(stderr
, "ecn requested without loss model\n");
453 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
454 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
459 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
462 if (present
[TCA_NETEM_CORR
] &&
463 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
466 if (present
[TCA_NETEM_REORDER
] &&
467 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
470 if (present
[TCA_NETEM_ECN
] &&
471 addattr_l(n
, 1024, TCA_NETEM_ECN
, &present
[TCA_NETEM_ECN
],
472 sizeof(present
[TCA_NETEM_ECN
])) < 0)
475 if (present
[TCA_NETEM_CORRUPT
] &&
476 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
479 if (loss_type
!= NETEM_LOSS_UNSPEC
) {
480 struct rtattr
*start
;
482 start
= addattr_nest(n
, 1024, TCA_NETEM_LOSS
| NLA_F_NESTED
);
483 if (loss_type
== NETEM_LOSS_GI
) {
484 if (addattr_l(n
, 1024, NETEM_LOSS_GI
,
485 &gimodel
, sizeof(gimodel
)) < 0)
487 } else if (loss_type
== NETEM_LOSS_GE
) {
488 if (addattr_l(n
, 1024, NETEM_LOSS_GE
,
489 &gemodel
, sizeof(gemodel
)) < 0)
492 fprintf(stderr
, "loss in the weeds!\n");
496 addattr_nest_end(n
, start
);
499 if (present
[TCA_NETEM_RATE
] &&
500 addattr_l(n
, 1024, TCA_NETEM_RATE
, &rate
, sizeof(rate
)) < 0)
504 if (addattr_l(n
, MAX_DIST
* sizeof(dist_data
[0]),
505 TCA_NETEM_DELAY_DIST
,
506 dist_data
, dist_size
* sizeof(dist_data
[0])) < 0)
510 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
514 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
516 const struct tc_netem_corr
*cor
= NULL
;
517 const struct tc_netem_reorder
*reorder
= NULL
;
518 const struct tc_netem_corrupt
*corrupt
= NULL
;
519 const struct tc_netem_gimodel
*gimodel
= NULL
;
520 const struct tc_netem_gemodel
*gemodel
= NULL
;
522 struct tc_netem_qopt qopt
;
523 const struct tc_netem_rate
*rate
= NULL
;
524 int len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
531 fprintf(stderr
, "options size error\n");
534 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
537 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
538 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
541 if (tb
[TCA_NETEM_CORR
]) {
542 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
544 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
546 if (tb
[TCA_NETEM_REORDER
]) {
547 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
549 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
551 if (tb
[TCA_NETEM_CORRUPT
]) {
552 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
554 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
556 if (tb
[TCA_NETEM_LOSS
]) {
557 struct rtattr
*lb
[NETEM_LOSS_MAX
+ 1];
559 parse_rtattr_nested(lb
, NETEM_LOSS_MAX
, tb
[TCA_NETEM_LOSS
]);
560 if (lb
[NETEM_LOSS_GI
])
561 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GI
]);
562 if (lb
[NETEM_LOSS_GE
])
563 gemodel
= RTA_DATA(lb
[NETEM_LOSS_GE
]);
565 if (tb
[TCA_NETEM_RATE
]) {
566 if (RTA_PAYLOAD(tb
[TCA_NETEM_RATE
]) < sizeof(*rate
))
568 rate
= RTA_DATA(tb
[TCA_NETEM_RATE
]);
570 if (tb
[TCA_NETEM_ECN
]) {
571 if (RTA_PAYLOAD(tb
[TCA_NETEM_ECN
]) < sizeof(*ecn
))
573 ecn
= RTA_DATA(tb
[TCA_NETEM_ECN
]);
577 fprintf(f
, "limit %d", qopt
.limit
);
580 fprintf(f
, " delay %s", sprint_ticks(qopt
.latency
, b1
));
583 fprintf(f
, " %s", sprint_ticks(qopt
.jitter
, b1
));
584 if (cor
&& cor
->delay_corr
)
585 fprintf(f
, " %s", sprint_percent(cor
->delay_corr
, b1
));
590 fprintf(f
, " loss %s", sprint_percent(qopt
.loss
, b1
));
591 if (cor
&& cor
->loss_corr
)
592 fprintf(f
, " %s", sprint_percent(cor
->loss_corr
, b1
));
596 fprintf(f
, " loss state p13 %s", sprint_percent(gimodel
->p13
, b1
));
597 fprintf(f
, " p31 %s", sprint_percent(gimodel
->p31
, b1
));
598 fprintf(f
, " p32 %s", sprint_percent(gimodel
->p32
, b1
));
599 fprintf(f
, " p23 %s", sprint_percent(gimodel
->p23
, b1
));
600 fprintf(f
, " p14 %s", sprint_percent(gimodel
->p14
, b1
));
604 fprintf(f
, "loss gemodel p %s",
605 sprint_percent(gemodel
->p
, b1
));
606 fprintf(f
, " r %s", sprint_percent(gemodel
->r
, b1
));
607 fprintf(f
, " 1-h %s", sprint_percent(gemodel
->h
, b1
));
608 fprintf(f
, " 1-k %s", sprint_percent(gemodel
->k1
, b1
));
611 if (qopt
.duplicate
) {
612 fprintf(f
, " duplicate %s",
613 sprint_percent(qopt
.duplicate
, b1
));
614 if (cor
&& cor
->dup_corr
)
615 fprintf(f
, " %s", sprint_percent(cor
->dup_corr
, b1
));
618 if (reorder
&& reorder
->probability
) {
619 fprintf(f
, " reorder %s",
620 sprint_percent(reorder
->probability
, b1
));
621 if (reorder
->correlation
)
623 sprint_percent(reorder
->correlation
, b1
));
626 if (corrupt
&& corrupt
->probability
) {
627 fprintf(f
, " corrupt %s",
628 sprint_percent(corrupt
->probability
, b1
));
629 if (corrupt
->correlation
)
631 sprint_percent(corrupt
->correlation
, b1
));
634 if (rate
&& rate
->rate
) {
635 fprintf(f
, " rate %s", sprint_rate(rate
->rate
, b1
));
636 if (rate
->packet_overhead
)
637 fprintf(f
, " packetoverhead %d", rate
->packet_overhead
);
639 fprintf(f
, " cellsize %u", rate
->cell_size
);
640 if (rate
->cell_overhead
)
641 fprintf(f
, " celloverhead %d", rate
->cell_overhead
);
648 fprintf(f
, " gap %lu", (unsigned long)qopt
.gap
);
654 struct qdisc_util netem_qdisc_util
= {
656 .parse_qopt
= netem_parse_opt
,
657 .print_qopt
= netem_print_opt
,