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@osdl.org>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
26 #include "tc_common.h"
28 static void explain(void)
31 "Usage: ... netem [ limit PACKETS ] \n" \
32 " [ delay TIME [ JITTER [CORRELATION]]]\n" \
33 " [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
34 " [ drop PERCENT [CORRELATION]] \n" \
35 " [ corrupt PERCENT [CORRELATION]] \n" \
36 " [ duplicate PERCENT [CORRELATION]]\n" \
37 " [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n");
40 static void explain1(const char *arg
)
42 fprintf(stderr
, "Illegal \"%s\"\n", arg
);
45 #define usage() return(-1)
47 /* Upper bound on size of distribution
48 * really (TCA_BUF_MAX - other headers) / sizeof (__s16)
50 #define MAX_DIST (16*1024)
53 * Simplistic file parser for distrbution data.
58 static int get_distribution(const char *type
, __s16
*data
, int maxdata
)
67 snprintf(name
, sizeof(name
), "%s/%s.dist", get_tc_lib(), type
);
68 if ((f
= fopen(name
, "r")) == NULL
) {
69 fprintf(stderr
, "No distribution data for %s (%s: %s)\n",
70 type
, name
, strerror(errno
));
75 while (getline(&line
, &len
, f
) != -1) {
77 if (*line
== '\n' || *line
== '#')
80 for (p
= line
; ; p
= endp
) {
81 x
= strtol(p
, &endp
, 0);
86 fprintf(stderr
, "%s: too much data\n",
100 static int isnumber(const char *arg
)
103 (void) strtod(arg
, &p
);
107 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isnumber(argv[1]))
109 /* Adjust for the fact that psched_ticks aren't always usecs
110 (based on kernel PSCHED_CLOCK configuration */
111 static int get_ticks(__u32
*ticks
, const char *str
)
115 if(get_time(&t
, str
))
118 if (tc_core_time2big(t
)) {
119 fprintf(stderr
, "Illegal %u time (too large)\n", t
);
123 *ticks
= tc_core_time2tick(t
);
127 static int netem_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
,
130 size_t dist_size
= 0;
132 struct tc_netem_qopt opt
;
133 struct tc_netem_corr cor
;
134 struct tc_netem_reorder reorder
;
135 struct tc_netem_corrupt corrupt
;
136 __s16
*dist_data
= NULL
;
137 int present
[__TCA_NETEM_MAX
];
139 memset(&opt
, 0, sizeof(opt
));
141 memset(&cor
, 0, sizeof(cor
));
142 memset(&reorder
, 0, sizeof(reorder
));
143 memset(&corrupt
, 0, sizeof(corrupt
));
144 memset(present
, 0, sizeof(present
));
147 if (matches(*argv
, "limit") == 0) {
149 if (get_size(&opt
.limit
, *argv
)) {
153 } else if (matches(*argv
, "latency") == 0 ||
154 matches(*argv
, "delay") == 0) {
156 if (get_ticks(&opt
.latency
, *argv
)) {
161 if (NEXT_IS_NUMBER()) {
163 if (get_ticks(&opt
.jitter
, *argv
)) {
168 if (NEXT_IS_NUMBER()) {
170 ++present
[TCA_NETEM_CORR
];
171 if (get_percent(&cor
.delay_corr
, *argv
)) {
177 } else if (matches(*argv
, "loss") == 0 ||
178 matches(*argv
, "drop") == 0) {
180 if (get_percent(&opt
.loss
, *argv
)) {
184 if (NEXT_IS_NUMBER()) {
186 ++present
[TCA_NETEM_CORR
];
187 if (get_percent(&cor
.loss_corr
, *argv
)) {
192 } else if (matches(*argv
, "reorder") == 0) {
194 present
[TCA_NETEM_REORDER
] = 1;
195 if (get_percent(&reorder
.probability
, *argv
)) {
199 if (NEXT_IS_NUMBER()) {
201 ++present
[TCA_NETEM_CORR
];
202 if (get_percent(&reorder
.correlation
, *argv
)) {
207 } else if (matches(*argv
, "corrupt") == 0) {
209 present
[TCA_NETEM_CORRUPT
] = 1;
210 if (get_percent(&corrupt
.probability
, *argv
)) {
214 if (NEXT_IS_NUMBER()) {
216 ++present
[TCA_NETEM_CORR
];
217 if (get_percent(&corrupt
.correlation
, *argv
)) {
222 } else if (matches(*argv
, "gap") == 0) {
224 if (get_u32(&opt
.gap
, *argv
, 0)) {
228 } else if (matches(*argv
, "duplicate") == 0) {
230 if (get_percent(&opt
.duplicate
, *argv
)) {
231 explain1("duplicate");
234 if (NEXT_IS_NUMBER()) {
236 if (get_percent(&cor
.dup_corr
, *argv
)) {
237 explain1("duplicate");
241 } else if (matches(*argv
, "distribution") == 0) {
243 dist_data
= calloc(sizeof(dist_data
[0]), MAX_DIST
);
244 dist_size
= get_distribution(*argv
, dist_data
, MAX_DIST
);
245 if (dist_size
<= 0) {
249 } else if (strcmp(*argv
, "help") == 0) {
253 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
260 tail
= NLMSG_TAIL(n
);
262 if (reorder
.probability
) {
263 if (opt
.latency
== 0) {
264 fprintf(stderr
, "reordering not possible without specifying some delay\n");
268 } else if (opt
.gap
> 0) {
269 fprintf(stderr
, "gap specified without reorder probability\n");
274 if (dist_data
&& (opt
.latency
== 0 || opt
.jitter
== 0)) {
275 fprintf(stderr
, "distribution specified but no latency and jitter values\n");
280 if (addattr_l(n
, 1024, TCA_OPTIONS
, &opt
, sizeof(opt
)) < 0)
283 if (present
[TCA_NETEM_CORR
] &&
284 addattr_l(n
, 1024, TCA_NETEM_CORR
, &cor
, sizeof(cor
)) < 0)
287 if (present
[TCA_NETEM_REORDER
] &&
288 addattr_l(n
, 1024, TCA_NETEM_REORDER
, &reorder
, sizeof(reorder
)) < 0)
291 if (present
[TCA_NETEM_CORRUPT
] &&
292 addattr_l(n
, 1024, TCA_NETEM_CORRUPT
, &corrupt
, sizeof(corrupt
)) < 0)
296 if (addattr_l(n
, MAX_DIST
* sizeof(dist_data
[0]),
297 TCA_NETEM_DELAY_DIST
,
298 dist_data
, dist_size
* sizeof(dist_data
[0])) < 0)
302 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
306 static int netem_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
308 const struct tc_netem_corr
*cor
= NULL
;
309 const struct tc_netem_reorder
*reorder
= NULL
;
310 const struct tc_netem_corrupt
*corrupt
= NULL
;
311 struct tc_netem_qopt qopt
;
312 int len
= RTA_PAYLOAD(opt
) - sizeof(qopt
);
319 fprintf(stderr
, "options size error\n");
322 memcpy(&qopt
, RTA_DATA(opt
), sizeof(qopt
));
325 struct rtattr
*tb
[TCA_NETEM_MAX
+1];
326 parse_rtattr(tb
, TCA_NETEM_MAX
, RTA_DATA(opt
) + sizeof(qopt
),
329 if (tb
[TCA_NETEM_CORR
]) {
330 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORR
]) < sizeof(*cor
))
332 cor
= RTA_DATA(tb
[TCA_NETEM_CORR
]);
334 if (tb
[TCA_NETEM_REORDER
]) {
335 if (RTA_PAYLOAD(tb
[TCA_NETEM_REORDER
]) < sizeof(*reorder
))
337 reorder
= RTA_DATA(tb
[TCA_NETEM_REORDER
]);
339 if (tb
[TCA_NETEM_CORRUPT
]) {
340 if (RTA_PAYLOAD(tb
[TCA_NETEM_CORRUPT
]) < sizeof(*corrupt
))
342 corrupt
= RTA_DATA(tb
[TCA_NETEM_CORRUPT
]);
346 fprintf(f
, "limit %d", qopt
.limit
);
349 fprintf(f
, " delay %s", sprint_ticks(qopt
.latency
, b1
));
352 fprintf(f
, " %s", sprint_ticks(qopt
.jitter
, b1
));
353 if (cor
&& cor
->delay_corr
)
354 fprintf(f
, " %s", sprint_percent(cor
->delay_corr
, b1
));
359 fprintf(f
, " loss %s", sprint_percent(qopt
.loss
, b1
));
360 if (cor
&& cor
->loss_corr
)
361 fprintf(f
, " %s", sprint_percent(cor
->loss_corr
, b1
));
364 if (qopt
.duplicate
) {
365 fprintf(f
, " duplicate %s",
366 sprint_percent(qopt
.duplicate
, b1
));
367 if (cor
&& cor
->dup_corr
)
368 fprintf(f
, " %s", sprint_percent(cor
->dup_corr
, b1
));
371 if (reorder
&& reorder
->probability
) {
372 fprintf(f
, " reorder %s",
373 sprint_percent(reorder
->probability
, b1
));
374 if (reorder
->correlation
)
376 sprint_percent(reorder
->correlation
, b1
));
379 if (corrupt
&& corrupt
->probability
) {
380 fprintf(f
, " corrupt %s",
381 sprint_percent(corrupt
->probability
, b1
));
382 if (corrupt
->correlation
)
384 sprint_percent(corrupt
->correlation
, b1
));
388 fprintf(f
, " gap %lu", (unsigned long)qopt
.gap
);
393 struct qdisc_util netem_qdisc_util
= {
395 .parse_qopt
= netem_parse_opt
,
396 .print_qopt
= netem_print_opt
,