blob: 8fe220416225c99df162991192ac5bc6f1c7dac6 [file] [log] [blame]
osdl.net!shemminger309a4c92004-06-28 20:41:55 +00001/*
2 * q_netem.c NETEM.
3 *
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.
8 *
Stephen Hemminger59a935d2011-03-23 12:29:06 -07009 * Authors: Stephen Hemminger <shemminger@linux-foundation.org>
osdl.net!shemminger309a4c92004-06-28 20:41:55 +000010 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080015#include <math.h>
16#include <ctype.h>
osdl.net!shemminger309a4c92004-06-28 20:41:55 +000017#include <unistd.h>
18#include <syslog.h>
19#include <fcntl.h>
20#include <sys/socket.h>
21#include <netinet/in.h>
22#include <arpa/inet.h>
23#include <string.h>
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +000024#include <errno.h>
osdl.net!shemminger309a4c92004-06-28 20:41:55 +000025
26#include "utils.h"
27#include "tc_util.h"
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +000028#include "tc_common.h"
osdl.net!shemminger309a4c92004-06-28 20:41:55 +000029
30static void explain(void)
31{
Stephen Hemmingerae665a52006-12-05 10:10:22 -080032 fprintf(stderr,
Stephen Hemminger32a121c2016-03-21 11:48:36 -070033"Usage: ... netem [ limit PACKETS ]\n" \
shemmingerea8fc102005-06-22 18:27:49 +000034" [ delay TIME [ JITTER [CORRELATION]]]\n" \
35" [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
Stephen Hemminger32a121c2016-03-21 11:48:36 -070036" [ corrupt PERCENT [CORRELATION]]\n" \
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +000037" [ duplicate PERCENT [CORRELATION]]\n" \
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080038" [ loss random PERCENT [CORRELATION]]\n" \
39" [ loss state P13 [P31 [P32 [P23 P14]]]\n" \
40" [ loss gemodel PERCENT [R [1-H [1-K]]]\n" \
Vijay Subramanian10702052012-05-16 13:51:58 +000041" [ ecn ]\n" \
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -080042" [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n" \
43" [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n");
osdl.net!shemminger309a4c92004-06-28 20:41:55 +000044}
45
46static void explain1(const char *arg)
47{
48 fprintf(stderr, "Illegal \"%s\"\n", arg);
49}
50
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080051/* Upper bound on size of distribution
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -080052 * really (TCA_BUF_MAX - other headers) / sizeof (__s16)
53 */
54#define MAX_DIST (16*1024)
55
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080056static const double max_percent_value = 0xffffffff;
57
58/* scaled value used to percent of maximum. */
59static void set_percent(__u32 *percent, double per)
60{
Stephen Hemminger32a121c2016-03-21 11:48:36 -070061 *percent = (unsigned int) rint(per * max_percent_value);
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080062}
63
64
65/* Parse either a fraction '.3' or percent '30%
66 * return: 0 = ok, -1 = error, 1 = out of range
67 */
68static int parse_percent(double *val, const char *str)
69{
70 char *p;
71
72 *val = strtod(str, &p) / 100.;
Stephen Hemminger32a121c2016-03-21 11:48:36 -070073 if (*p && strcmp(p, "%"))
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080074 return -1;
75
76 return 0;
77}
78
79static int get_percent(__u32 *percent, const char *str)
80{
81 double per;
82
83 if (parse_percent(&per, str))
84 return -1;
85
86 set_percent(percent, per);
87 return 0;
88}
89
Stephen Hemmingerd1f28cf2013-02-12 11:09:03 -080090static void print_percent(char *buf, int len, __u32 per)
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080091{
92 snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value);
93}
94
Stephen Hemminger32a121c2016-03-21 11:48:36 -070095static char *sprint_percent(__u32 per, char *buf)
Stephen Hemminger3c7950a2011-12-22 17:08:11 -080096{
97 print_percent(buf, SPRINT_BSIZE-1, per);
98 return buf;
99}
100
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000101/*
102 * Simplistic file parser for distrbution data.
103 * Format is:
104 * # comment line(s)
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800105 * data0 data1 ...
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000106 */
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800107static int get_distribution(const char *type, __s16 *data, int maxdata)
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000108{
109 FILE *f;
110 int n;
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000111 long x;
112 size_t len;
osdl.net!shemmingerfb9b1d02005-02-07 18:15:04 +0000113 char *line = NULL;
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000114 char name[128];
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000115
Stephen Hemmingeraa27f882007-06-20 15:27:22 -0700116 snprintf(name, sizeof(name), "%s/%s.dist", get_tc_lib(), type);
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000117 if ((f = fopen(name, "r")) == NULL) {
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800118 fprintf(stderr, "No distribution data for %s (%s: %s)\n",
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000119 type, name, strerror(errno));
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000120 return -1;
121 }
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800122
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000123 n = 0;
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000124 while (getline(&line, &len, f) != -1) {
125 char *p, *endp;
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700126
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000127 if (*line == '\n' || *line == '#')
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000128 continue;
129
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000130 for (p = line; ; p = endp) {
131 x = strtol(p, &endp, 0);
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800132 if (endp == p)
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000133 break;
134
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800135 if (n >= maxdata) {
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000136 fprintf(stderr, "%s: too much data\n",
137 name);
138 n = -1;
139 goto error;
140 }
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000141 data[n++] = x;
142 }
143 }
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000144 error:
145 free(line);
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000146 fclose(f);
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000147 return n;
148}
149
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800150#define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
Johannes Naabe72ca3f2013-01-23 11:38:19 +0000151#define NEXT_IS_SIGNED_NUMBER() \
152 (NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000153
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800154/* Adjust for the fact that psched_ticks aren't always usecs
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000155 (based on kernel PSCHED_CLOCK configuration */
156static int get_ticks(__u32 *ticks, const char *str)
157{
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700158 unsigned int t;
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000159
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700160 if (get_time(&t, str))
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000161 return -1;
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800162
Patrick McHardy8f34caa2007-03-04 20:15:00 +0100163 if (tc_core_time2big(t)) {
164 fprintf(stderr, "Illegal %u time (too large)\n", t);
Stephen Hemmingerfa565132006-10-19 13:10:26 -0700165 return -1;
166 }
167
Patrick McHardy8f34caa2007-03-04 20:15:00 +0100168 *ticks = tc_core_time2tick(t);
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000169 return 0;
170}
171
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800172static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000173 struct nlmsghdr *n)
174{
Thomas Jaroschfcbd0162011-11-23 22:15:19 +0100175 int dist_size = 0;
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000176 struct rtattr *tail;
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800177 struct tc_netem_qopt opt = { .limit = 1000 };
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000178 struct tc_netem_corr cor;
shemmingerea8fc102005-06-22 18:27:49 +0000179 struct tc_netem_reorder reorder;
shemmingera31a5d52005-12-09 23:27:44 +0000180 struct tc_netem_corrupt corrupt;
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800181 struct tc_netem_gimodel gimodel;
182 struct tc_netem_gemodel gemodel;
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800183 struct tc_netem_rate rate;
shemmingera31a5d52005-12-09 23:27:44 +0000184 __s16 *dist_data = NULL;
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800185 __u16 loss_type = NETEM_LOSS_UNSPEC;
Stephen Hemminger40076f62007-01-09 15:46:55 -0800186 int present[__TCA_NETEM_MAX];
Yang Yingliangdad2f722014-01-16 11:09:14 +0800187 __u64 rate64 = 0;
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000188
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000189 memset(&cor, 0, sizeof(cor));
shemmingerea8fc102005-06-22 18:27:49 +0000190 memset(&reorder, 0, sizeof(reorder));
shemmingera31a5d52005-12-09 23:27:44 +0000191 memset(&corrupt, 0, sizeof(corrupt));
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800192 memset(&rate, 0, sizeof(rate));
Stephen Hemminger40076f62007-01-09 15:46:55 -0800193 memset(present, 0, sizeof(present));
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000194
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700195 for ( ; argc > 0; --argc, ++argv) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000196 if (matches(*argv, "limit") == 0) {
197 NEXT_ARG();
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000198 if (get_size(&opt.limit, *argv)) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000199 explain1("limit");
200 return -1;
201 }
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000202 } else if (matches(*argv, "latency") == 0 ||
203 matches(*argv, "delay") == 0) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000204 NEXT_ARG();
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000205 if (get_ticks(&opt.latency, *argv)) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000206 explain1("latency");
207 return -1;
208 }
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000209
210 if (NEXT_IS_NUMBER()) {
211 NEXT_ARG();
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000212 if (get_ticks(&opt.jitter, *argv)) {
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000213 explain1("latency");
214 return -1;
215 }
216
217 if (NEXT_IS_NUMBER()) {
218 NEXT_ARG();
Stephen Hemminger40076f62007-01-09 15:46:55 -0800219 ++present[TCA_NETEM_CORR];
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800220 if (get_percent(&cor.delay_corr, *argv)) {
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000221 explain1("latency");
222 return -1;
223 }
224 }
225 }
226 } else if (matches(*argv, "loss") == 0 ||
227 matches(*argv, "drop") == 0) {
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800228 if (opt.loss > 0 || loss_type != NETEM_LOSS_UNSPEC) {
229 explain1("duplicate loss argument\n");
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000230 return -1;
231 }
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800232
233 NEXT_ARG();
234 /* Old (deprecated) random loss model syntax */
235 if (isdigit(argv[0][0]))
236 goto random_loss_model;
237
238 if (!strcmp(*argv, "random")) {
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000239 NEXT_ARG();
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700240random_loss_model:
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800241 if (get_percent(&opt.loss, *argv)) {
242 explain1("loss percent");
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000243 return -1;
244 }
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800245 if (NEXT_IS_NUMBER()) {
246 NEXT_ARG();
247 ++present[TCA_NETEM_CORR];
248 if (get_percent(&cor.loss_corr, *argv)) {
249 explain1("loss correllation");
250 return -1;
251 }
252 }
253 } else if (!strcmp(*argv, "state")) {
254 double p13;
255
256 NEXT_ARG();
257 if (parse_percent(&p13, *argv)) {
258 explain1("loss p13");
259 return -1;
260 }
261
262 /* set defaults */
263 set_percent(&gimodel.p13, p13);
264 set_percent(&gimodel.p31, 1. - p13);
265 set_percent(&gimodel.p32, 0);
266 set_percent(&gimodel.p23, 1.);
Jay Vosburgh8f9672a2014-05-08 19:02:20 -0700267 set_percent(&gimodel.p14, 0);
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800268 loss_type = NETEM_LOSS_GI;
269
270 if (!NEXT_IS_NUMBER())
271 continue;
272 NEXT_ARG();
273 if (get_percent(&gimodel.p31, *argv)) {
274 explain1("loss p31");
275 return -1;
276 }
277
278 if (!NEXT_IS_NUMBER())
279 continue;
280 NEXT_ARG();
281 if (get_percent(&gimodel.p32, *argv)) {
282 explain1("loss p32");
283 return -1;
284 }
285
286 if (!NEXT_IS_NUMBER())
287 continue;
288 NEXT_ARG();
289 if (get_percent(&gimodel.p23, *argv)) {
290 explain1("loss p23");
291 return -1;
292 }
Jay Vosburgh8f9672a2014-05-08 19:02:20 -0700293 if (!NEXT_IS_NUMBER())
294 continue;
295 NEXT_ARG();
296 if (get_percent(&gimodel.p14, *argv)) {
297 explain1("loss p14");
298 return -1;
299 }
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800300
301 } else if (!strcmp(*argv, "gemodel")) {
302 NEXT_ARG();
303 if (get_percent(&gemodel.p, *argv)) {
304 explain1("loss gemodel p");
305 return -1;
306 }
307
308 /* set defaults */
309 set_percent(&gemodel.r, 1.);
310 set_percent(&gemodel.h, 0);
Jay Vosburgh37571852014-05-10 13:34:58 -0700311 set_percent(&gemodel.k1, 0);
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800312 loss_type = NETEM_LOSS_GE;
313
314 if (!NEXT_IS_NUMBER())
315 continue;
316 NEXT_ARG();
317 if (get_percent(&gemodel.r, *argv)) {
318 explain1("loss gemodel r");
319 return -1;
320 }
321
322 if (!NEXT_IS_NUMBER())
323 continue;
324 NEXT_ARG();
325 if (get_percent(&gemodel.h, *argv)) {
326 explain1("loss gemodel h");
327 return -1;
328 }
Jay Vosburgh37571852014-05-10 13:34:58 -0700329 /* netem option is "1-h" but kernel
330 * expects "h".
331 */
332 gemodel.h = max_percent_value - gemodel.h;
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800333
334 if (!NEXT_IS_NUMBER())
335 continue;
336 NEXT_ARG();
337 if (get_percent(&gemodel.k1, *argv)) {
338 explain1("loss gemodel k");
339 return -1;
340 }
341 } else {
342 fprintf(stderr, "Unknown loss parameter: %s\n",
343 *argv);
344 return -1;
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000345 }
Vijay Subramanian10702052012-05-16 13:51:58 +0000346 } else if (matches(*argv, "ecn") == 0) {
347 present[TCA_NETEM_ECN] = 1;
shemmingerea8fc102005-06-22 18:27:49 +0000348 } else if (matches(*argv, "reorder") == 0) {
349 NEXT_ARG();
Stephen Hemminger40076f62007-01-09 15:46:55 -0800350 present[TCA_NETEM_REORDER] = 1;
shemmingerea8fc102005-06-22 18:27:49 +0000351 if (get_percent(&reorder.probability, *argv)) {
352 explain1("reorder");
353 return -1;
354 }
355 if (NEXT_IS_NUMBER()) {
356 NEXT_ARG();
Stephen Hemminger40076f62007-01-09 15:46:55 -0800357 ++present[TCA_NETEM_CORR];
shemmingerea8fc102005-06-22 18:27:49 +0000358 if (get_percent(&reorder.correlation, *argv)) {
359 explain1("reorder");
360 return -1;
361 }
362 }
shemmingera31a5d52005-12-09 23:27:44 +0000363 } else if (matches(*argv, "corrupt") == 0) {
364 NEXT_ARG();
Stephen Hemminger40076f62007-01-09 15:46:55 -0800365 present[TCA_NETEM_CORRUPT] = 1;
shemmingera31a5d52005-12-09 23:27:44 +0000366 if (get_percent(&corrupt.probability, *argv)) {
367 explain1("corrupt");
368 return -1;
369 }
370 if (NEXT_IS_NUMBER()) {
371 NEXT_ARG();
Stephen Hemminger40076f62007-01-09 15:46:55 -0800372 ++present[TCA_NETEM_CORR];
shemmingera31a5d52005-12-09 23:27:44 +0000373 if (get_percent(&corrupt.correlation, *argv)) {
374 explain1("corrupt");
375 return -1;
376 }
377 }
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000378 } else if (matches(*argv, "gap") == 0) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000379 NEXT_ARG();
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000380 if (get_u32(&opt.gap, *argv, 0)) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000381 explain1("gap");
382 return -1;
383 }
osdl.net!shemmingerffb79d02004-07-01 11:02:04 +0000384 } else if (matches(*argv, "duplicate") == 0) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000385 NEXT_ARG();
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000386 if (get_percent(&opt.duplicate, *argv)) {
osdl.net!shemmingerffb79d02004-07-01 11:02:04 +0000387 explain1("duplicate");
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000388 return -1;
389 }
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000390 if (NEXT_IS_NUMBER()) {
391 NEXT_ARG();
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000392 if (get_percent(&cor.dup_corr, *argv)) {
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000393 explain1("duplicate");
394 return -1;
395 }
396 }
397 } else if (matches(*argv, "distribution") == 0) {
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000398 NEXT_ARG();
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800399 dist_data = calloc(sizeof(dist_data[0]), MAX_DIST);
400 dist_size = get_distribution(*argv, dist_data, MAX_DIST);
401 if (dist_size <= 0) {
402 free(dist_data);
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000403 return -1;
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800404 }
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800405 } else if (matches(*argv, "rate") == 0) {
406 ++present[TCA_NETEM_RATE];
407 NEXT_ARG();
Yang Yingliangdad2f722014-01-16 11:09:14 +0800408 if (get_rate64(&rate64, *argv)) {
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800409 explain1("rate");
410 return -1;
411 }
Johannes Naabe72ca3f2013-01-23 11:38:19 +0000412 if (NEXT_IS_SIGNED_NUMBER()) {
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800413 NEXT_ARG();
414 if (get_s32(&rate.packet_overhead, *argv, 0)) {
415 explain1("rate");
416 return -1;
417 }
418 }
419 if (NEXT_IS_NUMBER()) {
420 NEXT_ARG();
421 if (get_u32(&rate.cell_size, *argv, 0)) {
422 explain1("rate");
423 return -1;
424 }
425 }
Johannes Naabe72ca3f2013-01-23 11:38:19 +0000426 if (NEXT_IS_SIGNED_NUMBER()) {
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800427 NEXT_ARG();
428 if (get_s32(&rate.cell_overhead, *argv, 0)) {
429 explain1("rate");
430 return -1;
431 }
432 }
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000433 } else if (strcmp(*argv, "help") == 0) {
434 explain();
435 return -1;
436 } else {
437 fprintf(stderr, "What is \"%s\"?\n", *argv);
438 explain();
439 return -1;
440 }
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000441 }
442
17!tgraf1a1d22a2005-01-18 01:24:18 +0000443 tail = NLMSG_TAIL(n);
osdl.net!shemminger025dc692004-08-09 17:12:23 +0000444
shemmingerea8fc102005-06-22 18:27:49 +0000445 if (reorder.probability) {
446 if (opt.latency == 0) {
447 fprintf(stderr, "reordering not possible without specifying some delay\n");
Vijay Subramanian14a1c162012-01-20 09:50:25 -0800448 explain();
449 return -1;
shemmingerea8fc102005-06-22 18:27:49 +0000450 }
451 if (opt.gap == 0)
452 opt.gap = 1;
453 } else if (opt.gap > 0) {
454 fprintf(stderr, "gap specified without reorder probability\n");
455 explain();
456 return -1;
457 }
458
Vijay Subramanian10702052012-05-16 13:51:58 +0000459 if (present[TCA_NETEM_ECN]) {
460 if (opt.loss <= 0 && loss_type == NETEM_LOSS_UNSPEC) {
461 fprintf(stderr, "ecn requested without loss model\n");
462 explain();
463 return -1;
464 }
465 }
466
shemmingera31a5d52005-12-09 23:27:44 +0000467 if (dist_data && (opt.latency == 0 || opt.jitter == 0)) {
shemmingerea8fc102005-06-22 18:27:49 +0000468 fprintf(stderr, "distribution specified but no latency and jitter values\n");
469 explain();
470 return -1;
471 }
472
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800473 if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
shemmingera31a5d52005-12-09 23:27:44 +0000474 return -1;
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000475
Stephen Hemminger40076f62007-01-09 15:46:55 -0800476 if (present[TCA_NETEM_CORR] &&
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800477 addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
shemmingera31a5d52005-12-09 23:27:44 +0000478 return -1;
shemmingera31a5d52005-12-09 23:27:44 +0000479
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800480 if (present[TCA_NETEM_REORDER] &&
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800481 addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
shemmingere9bc3c42005-12-10 00:01:02 +0000482 return -1;
shemmingera31a5d52005-12-09 23:27:44 +0000483
Vijay Subramanian10702052012-05-16 13:51:58 +0000484 if (present[TCA_NETEM_ECN] &&
485 addattr_l(n, 1024, TCA_NETEM_ECN, &present[TCA_NETEM_ECN],
486 sizeof(present[TCA_NETEM_ECN])) < 0)
487 return -1;
488
Stephen Hemminger40076f62007-01-09 15:46:55 -0800489 if (present[TCA_NETEM_CORRUPT] &&
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800490 addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
Stephen Hemminger40076f62007-01-09 15:46:55 -0800491 return -1;
shemmingera31a5d52005-12-09 23:27:44 +0000492
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800493 if (loss_type != NETEM_LOSS_UNSPEC) {
494 struct rtattr *start;
495
496 start = addattr_nest(n, 1024, TCA_NETEM_LOSS | NLA_F_NESTED);
497 if (loss_type == NETEM_LOSS_GI) {
498 if (addattr_l(n, 1024, NETEM_LOSS_GI,
499 &gimodel, sizeof(gimodel)) < 0)
500 return -1;
501 } else if (loss_type == NETEM_LOSS_GE) {
502 if (addattr_l(n, 1024, NETEM_LOSS_GE,
503 &gemodel, sizeof(gemodel)) < 0)
504 return -1;
505 } else {
506 fprintf(stderr, "loss in the weeds!\n");
507 return -1;
508 }
Stephen Hemminger3d0b7432014-12-20 15:47:17 -0800509
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800510 addattr_nest_end(n, start);
511 }
512
Yang Yingliangdad2f722014-01-16 11:09:14 +0800513 if (present[TCA_NETEM_RATE]) {
514 if (rate64 >= (1ULL << 32)) {
515 if (addattr_l(n, 1024,
516 TCA_NETEM_RATE64, &rate64, sizeof(rate64)) < 0)
517 return -1;
518 rate.rate = ~0U;
519 } else {
520 rate.rate = rate64;
521 }
522 if (addattr_l(n, 1024, TCA_NETEM_RATE, &rate, sizeof(rate)) < 0)
523 return -1;
524 }
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800525
shemmingera31a5d52005-12-09 23:27:44 +0000526 if (dist_data) {
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800527 if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
528 TCA_NETEM_DELAY_DIST,
529 dist_data, dist_size * sizeof(dist_data[0])) < 0)
shemmingera31a5d52005-12-09 23:27:44 +0000530 return -1;
Stephen Hemmingerc1b81cb2007-12-12 15:02:51 -0800531 free(dist_data);
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000532 }
17!tgraf1a1d22a2005-01-18 01:24:18 +0000533 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000534 return 0;
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000535}
536
537static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
538{
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000539 const struct tc_netem_corr *cor = NULL;
shemmingerea8fc102005-06-22 18:27:49 +0000540 const struct tc_netem_reorder *reorder = NULL;
shemmingera31a5d52005-12-09 23:27:44 +0000541 const struct tc_netem_corrupt *corrupt = NULL;
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800542 const struct tc_netem_gimodel *gimodel = NULL;
543 const struct tc_netem_gemodel *gemodel = NULL;
Vijay Subramanian10702052012-05-16 13:51:58 +0000544 int *ecn = NULL;
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000545 struct tc_netem_qopt qopt;
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800546 const struct tc_netem_rate *rate = NULL;
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000547 int len = RTA_PAYLOAD(opt) - sizeof(qopt);
Yang Yingliangdad2f722014-01-16 11:09:14 +0800548 __u64 rate64 = 0;
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700549
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000550 SPRINT_BUF(b1);
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000551
552 if (opt == NULL)
553 return 0;
554
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000555 if (len < 0) {
556 fprintf(stderr, "options size error\n");
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000557 return -1;
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000558 }
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000559 memcpy(&qopt, RTA_DATA(opt), sizeof(qopt));
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000560
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000561 if (len > 0) {
net[shemminger]!shemminger1d2d1cb2005-03-10 19:00:42 +0000562 struct rtattr *tb[TCA_NETEM_MAX+1];
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700563
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000564 parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt),
565 len);
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800566
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000567 if (tb[TCA_NETEM_CORR]) {
568 if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor))
569 return -1;
570 cor = RTA_DATA(tb[TCA_NETEM_CORR]);
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000571 }
shemmingerea8fc102005-06-22 18:27:49 +0000572 if (tb[TCA_NETEM_REORDER]) {
573 if (RTA_PAYLOAD(tb[TCA_NETEM_REORDER]) < sizeof(*reorder))
574 return -1;
575 reorder = RTA_DATA(tb[TCA_NETEM_REORDER]);
576 }
shemmingera31a5d52005-12-09 23:27:44 +0000577 if (tb[TCA_NETEM_CORRUPT]) {
578 if (RTA_PAYLOAD(tb[TCA_NETEM_CORRUPT]) < sizeof(*corrupt))
579 return -1;
shemmingere9bc3c42005-12-10 00:01:02 +0000580 corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
shemmingera31a5d52005-12-09 23:27:44 +0000581 }
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800582 if (tb[TCA_NETEM_LOSS]) {
583 struct rtattr *lb[NETEM_LOSS_MAX + 1];
584
585 parse_rtattr_nested(lb, NETEM_LOSS_MAX, tb[TCA_NETEM_LOSS]);
586 if (lb[NETEM_LOSS_GI])
Jay Vosburgh8f9672a2014-05-08 19:02:20 -0700587 gimodel = RTA_DATA(lb[NETEM_LOSS_GI]);
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800588 if (lb[NETEM_LOSS_GE])
589 gemodel = RTA_DATA(lb[NETEM_LOSS_GE]);
Stephen Hemminger3d0b7432014-12-20 15:47:17 -0800590 }
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800591 if (tb[TCA_NETEM_RATE]) {
592 if (RTA_PAYLOAD(tb[TCA_NETEM_RATE]) < sizeof(*rate))
593 return -1;
594 rate = RTA_DATA(tb[TCA_NETEM_RATE]);
595 }
Vijay Subramanian10702052012-05-16 13:51:58 +0000596 if (tb[TCA_NETEM_ECN]) {
597 if (RTA_PAYLOAD(tb[TCA_NETEM_ECN]) < sizeof(*ecn))
598 return -1;
599 ecn = RTA_DATA(tb[TCA_NETEM_ECN]);
600 }
Yang Yingliangdad2f722014-01-16 11:09:14 +0800601 if (tb[TCA_NETEM_RATE64]) {
602 if (RTA_PAYLOAD(tb[TCA_NETEM_RATE64]) < sizeof(rate64))
603 return -1;
604 rate64 = rta_getattr_u64(tb[TCA_NETEM_RATE64]);
605 }
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000606 }
607
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000608 fprintf(f, "limit %d", qopt.limit);
609
610 if (qopt.latency) {
611 fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1));
612
613 if (qopt.jitter) {
614 fprintf(f, " %s", sprint_ticks(qopt.jitter, b1));
615 if (cor && cor->delay_corr)
616 fprintf(f, " %s", sprint_percent(cor->delay_corr, b1));
617 }
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000618 }
619
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000620 if (qopt.loss) {
621 fprintf(f, " loss %s", sprint_percent(qopt.loss, b1));
622 if (cor && cor->loss_corr)
623 fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
624 }
625
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800626 if (gimodel) {
627 fprintf(f, " loss state p13 %s", sprint_percent(gimodel->p13, b1));
628 fprintf(f, " p31 %s", sprint_percent(gimodel->p31, b1));
629 fprintf(f, " p32 %s", sprint_percent(gimodel->p32, b1));
630 fprintf(f, " p23 %s", sprint_percent(gimodel->p23, b1));
631 fprintf(f, " p14 %s", sprint_percent(gimodel->p14, b1));
632 }
633
634 if (gemodel) {
Jay Vosburgh37571852014-05-10 13:34:58 -0700635 fprintf(f, " loss gemodel p %s",
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800636 sprint_percent(gemodel->p, b1));
637 fprintf(f, " r %s", sprint_percent(gemodel->r, b1));
Jay Vosburgh37571852014-05-10 13:34:58 -0700638 fprintf(f, " 1-h %s", sprint_percent(max_percent_value -
639 gemodel->h, b1));
Stephen Hemminger3c7950a2011-12-22 17:08:11 -0800640 fprintf(f, " 1-k %s", sprint_percent(gemodel->k1, b1));
641 }
642
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000643 if (qopt.duplicate) {
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000644 fprintf(f, " duplicate %s",
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000645 sprint_percent(qopt.duplicate, b1));
646 if (cor && cor->dup_corr)
647 fprintf(f, " %s", sprint_percent(cor->dup_corr, b1));
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000648 }
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800649
shemmingerea8fc102005-06-22 18:27:49 +0000650 if (reorder && reorder->probability) {
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800651 fprintf(f, " reorder %s",
shemmingerea8fc102005-06-22 18:27:49 +0000652 sprint_percent(reorder->probability, b1));
653 if (reorder->correlation)
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800654 fprintf(f, " %s",
shemmingerea8fc102005-06-22 18:27:49 +0000655 sprint_percent(reorder->correlation, b1));
656 }
osdl.net!shemmingerb7be3d02004-08-23 20:21:21 +0000657
shemmingera31a5d52005-12-09 23:27:44 +0000658 if (corrupt && corrupt->probability) {
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800659 fprintf(f, " corrupt %s",
shemmingera31a5d52005-12-09 23:27:44 +0000660 sprint_percent(corrupt->probability, b1));
661 if (corrupt->correlation)
Stephen Hemmingerae665a52006-12-05 10:10:22 -0800662 fprintf(f, " %s",
shemmingera31a5d52005-12-09 23:27:44 +0000663 sprint_percent(corrupt->correlation, b1));
664 }
665
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800666 if (rate && rate->rate) {
Yang Yingliangdad2f722014-01-16 11:09:14 +0800667 if (rate64)
668 fprintf(f, " rate %s", sprint_rate(rate64, b1));
669 else
670 fprintf(f, " rate %s", sprint_rate(rate->rate, b1));
Hagen Paul Pfeifer6b8dc4d2012-01-19 14:28:27 -0800671 if (rate->packet_overhead)
672 fprintf(f, " packetoverhead %d", rate->packet_overhead);
673 if (rate->cell_size)
674 fprintf(f, " cellsize %u", rate->cell_size);
675 if (rate->cell_overhead)
676 fprintf(f, " celloverhead %d", rate->cell_overhead);
677 }
678
Vijay Subramanian10702052012-05-16 13:51:58 +0000679 if (ecn)
680 fprintf(f, " ecn ");
681
osdl.net!shemminger2e216552004-08-30 20:54:46 +0000682 if (qopt.gap)
683 fprintf(f, " gap %lu", (unsigned long)qopt.gap);
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000684
Vijay Subramanian10702052012-05-16 13:51:58 +0000685
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000686 return 0;
687}
688
net[shemminger]!kaber95812b52004-09-28 18:35:49 +0000689struct qdisc_util netem_qdisc_util = {
Stephen Hemminger32a121c2016-03-21 11:48:36 -0700690 .id = "netem",
osdl.net!shemminger31fa60e2004-07-12 21:03:29 +0000691 .parse_qopt = netem_parse_opt,
692 .print_qopt = netem_print_opt,
osdl.net!shemminger309a4c92004-06-28 20:41:55 +0000693};