• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  *
9  * Authors:	Stephen Hemminger <shemminger@linux-foundation.org>
10  *
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <math.h>
16 #include <ctype.h>
17 #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>
24 #include <errno.h>
25 
26 #include "utils.h"
27 #include "tc_util.h"
28 #include "tc_common.h"
29 
explain(void)30 static void explain(void)
31 {
32 	fprintf(stderr,
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" \
41 "                 [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n" \
42 "                 [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n");
43 }
44 
explain1(const char * arg)45 static void explain1(const char *arg)
46 {
47 	fprintf(stderr, "Illegal \"%s\"\n", arg);
48 }
49 
50 /* Upper bound on size of distribution
51  *  really (TCA_BUF_MAX - other headers) / sizeof (__s16)
52  */
53 #define MAX_DIST	(16*1024)
54 
55 static const double max_percent_value = 0xffffffff;
56 
57 /* scaled value used to percent of maximum. */
set_percent(__u32 * percent,double per)58 static void set_percent(__u32 *percent, double per)
59 {
60 	*percent = (unsigned) rint(per * max_percent_value);
61 }
62 
63 
64 /* Parse either a fraction '.3' or percent '30%
65  * return: 0 = ok, -1 = error, 1 = out of range
66  */
parse_percent(double * val,const char * str)67 static int parse_percent(double *val, const char *str)
68 {
69 	char *p;
70 
71 	*val = strtod(str, &p) / 100.;
72 	if (*p && strcmp(p, "%") )
73 		return -1;
74 
75 	return 0;
76 }
77 
get_percent(__u32 * percent,const char * str)78 static int get_percent(__u32 *percent, const char *str)
79 {
80 	double per;
81 
82 	if (parse_percent(&per, str))
83 		return -1;
84 
85 	set_percent(percent, per);
86 	return 0;
87 }
88 
print_percent(char * buf,int len,__u32 per)89 void print_percent(char *buf, int len, __u32 per)
90 {
91 	snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value);
92 }
93 
sprint_percent(__u32 per,char * buf)94 char * sprint_percent(__u32 per, char *buf)
95 {
96 	print_percent(buf, SPRINT_BSIZE-1, per);
97 	return buf;
98 }
99 
100 /*
101  * Simplistic file parser for distrbution data.
102  * Format is:
103  *	# comment line(s)
104  *	data0 data1 ...
105  */
get_distribution(const char * type,__s16 * data,int maxdata)106 static int get_distribution(const char *type, __s16 *data, int maxdata)
107 {
108 	FILE *f;
109 	int n;
110 	long x;
111 	size_t len;
112 	char *line = NULL;
113 	char name[128];
114 
115 	snprintf(name, sizeof(name), "%s/%s.dist", get_tc_lib(), type);
116 	if ((f = fopen(name, "r")) == NULL) {
117 		fprintf(stderr, "No distribution data for %s (%s: %s)\n",
118 			type, name, strerror(errno));
119 		return -1;
120 	}
121 
122 	n = 0;
123 	while (getline(&line, &len, f) != -1) {
124 		char *p, *endp;
125 		if (*line == '\n' || *line == '#')
126 			continue;
127 
128 		for (p = line; ; p = endp) {
129 			x = strtol(p, &endp, 0);
130 			if (endp == p)
131 				break;
132 
133 			if (n >= maxdata) {
134 				fprintf(stderr, "%s: too much data\n",
135 					name);
136 				n = -1;
137 				goto error;
138 			}
139 			data[n++] = x;
140 		}
141 	}
142  error:
143 	free(line);
144 	fclose(f);
145 	return n;
146 }
147 
148 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isdigit(argv[1][0]))
149 
150 /* Adjust for the fact that psched_ticks aren't always usecs
151    (based on kernel PSCHED_CLOCK configuration */
get_ticks(__u32 * ticks,const char * str)152 static int get_ticks(__u32 *ticks, const char *str)
153 {
154 	unsigned t;
155 
156 	if(get_time(&t, str))
157 		return -1;
158 
159 	if (tc_core_time2big(t)) {
160 		fprintf(stderr, "Illegal %u time (too large)\n", t);
161 		return -1;
162 	}
163 
164 	*ticks = tc_core_time2tick(t);
165 	return 0;
166 }
167 
netem_parse_opt(struct qdisc_util * qu,int argc,char ** argv,struct nlmsghdr * n)168 static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
169 			   struct nlmsghdr *n)
170 {
171 	int dist_size = 0;
172 	struct rtattr *tail;
173 	struct tc_netem_qopt opt = { .limit = 1000 };
174 	struct tc_netem_corr cor;
175 	struct tc_netem_reorder reorder;
176 	struct tc_netem_corrupt corrupt;
177 	struct tc_netem_gimodel gimodel;
178 	struct tc_netem_gemodel gemodel;
179 	struct tc_netem_rate rate;
180 	__s16 *dist_data = NULL;
181 	__u16 loss_type = NETEM_LOSS_UNSPEC;
182 	int present[__TCA_NETEM_MAX];
183 
184 	memset(&cor, 0, sizeof(cor));
185 	memset(&reorder, 0, sizeof(reorder));
186 	memset(&corrupt, 0, sizeof(corrupt));
187 	memset(&rate, 0, sizeof(rate));
188 	memset(present, 0, sizeof(present));
189 
190 	for( ; argc > 0; --argc, ++argv) {
191 		if (matches(*argv, "limit") == 0) {
192 			NEXT_ARG();
193 			if (get_size(&opt.limit, *argv)) {
194 				explain1("limit");
195 				return -1;
196 			}
197 		} else if (matches(*argv, "latency") == 0 ||
198 			   matches(*argv, "delay") == 0) {
199 			NEXT_ARG();
200 			if (get_ticks(&opt.latency, *argv)) {
201 				explain1("latency");
202 				return -1;
203 			}
204 
205 			if (NEXT_IS_NUMBER()) {
206 				NEXT_ARG();
207 				if (get_ticks(&opt.jitter, *argv)) {
208 					explain1("latency");
209 					return -1;
210 				}
211 
212 				if (NEXT_IS_NUMBER()) {
213 					NEXT_ARG();
214 					++present[TCA_NETEM_CORR];
215 					if (get_percent(&cor.delay_corr, *argv)) {
216 						explain1("latency");
217 						return -1;
218 					}
219 				}
220 			}
221 		} else if (matches(*argv, "loss") == 0 ||
222 			   matches(*argv, "drop") == 0) {
223 			if (opt.loss > 0 || loss_type != NETEM_LOSS_UNSPEC) {
224 				explain1("duplicate loss argument\n");
225 				return -1;
226 			}
227 
228 			NEXT_ARG();
229 			/* Old (deprecated) random loss model syntax */
230 			if (isdigit(argv[0][0]))
231 				goto random_loss_model;
232 
233 			if (!strcmp(*argv, "random")) {
234 				NEXT_ARG();
235 	random_loss_model:
236 				if (get_percent(&opt.loss, *argv)) {
237 					explain1("loss percent");
238 					return -1;
239 				}
240 				if (NEXT_IS_NUMBER()) {
241 					NEXT_ARG();
242 					++present[TCA_NETEM_CORR];
243 					if (get_percent(&cor.loss_corr, *argv)) {
244 						explain1("loss correllation");
245 						return -1;
246 					}
247 				}
248 			} else if (!strcmp(*argv, "state")) {
249 				double p13;
250 
251 				NEXT_ARG();
252 				if (parse_percent(&p13, *argv)) {
253 					explain1("loss p13");
254 					return -1;
255 				}
256 
257 				/* set defaults */
258 				set_percent(&gimodel.p13, p13);
259 				set_percent(&gimodel.p31, 1. - p13);
260 				set_percent(&gimodel.p32, 0);
261 				set_percent(&gimodel.p23, 1.);
262 				loss_type = NETEM_LOSS_GI;
263 
264 				if (!NEXT_IS_NUMBER())
265 					continue;
266 				NEXT_ARG();
267 				if (get_percent(&gimodel.p31, *argv)) {
268 					explain1("loss p31");
269 					return -1;
270 				}
271 
272 				if (!NEXT_IS_NUMBER())
273 					continue;
274 				NEXT_ARG();
275 				if (get_percent(&gimodel.p32, *argv)) {
276 					explain1("loss p32");
277 					return -1;
278 				}
279 
280 				if (!NEXT_IS_NUMBER())
281 					continue;
282 				NEXT_ARG();
283 				if (get_percent(&gimodel.p23, *argv)) {
284 					explain1("loss p23");
285 					return -1;
286 				}
287 
288 			} else if (!strcmp(*argv, "gemodel")) {
289 				NEXT_ARG();
290 				if (get_percent(&gemodel.p, *argv)) {
291 					explain1("loss gemodel p");
292 					return -1;
293 				}
294 
295 				/* set defaults */
296 				set_percent(&gemodel.r, 1.);
297 				set_percent(&gemodel.h, 0);
298 				set_percent(&gemodel.k1, 1.);
299 				loss_type = NETEM_LOSS_GE;
300 
301 				if (!NEXT_IS_NUMBER())
302 					continue;
303 				NEXT_ARG();
304 				if (get_percent(&gemodel.r, *argv)) {
305 					explain1("loss gemodel r");
306 					return -1;
307 				}
308 
309 				if (!NEXT_IS_NUMBER())
310 					continue;
311 				NEXT_ARG();
312 				if (get_percent(&gemodel.h, *argv)) {
313 					explain1("loss gemodel h");
314 					return -1;
315 				}
316 
317 				if (!NEXT_IS_NUMBER())
318 					continue;
319 				NEXT_ARG();
320 				if (get_percent(&gemodel.k1, *argv)) {
321 					explain1("loss gemodel k");
322 					return -1;
323 				}
324 			} else {
325 				fprintf(stderr, "Unknown loss parameter: %s\n",
326 					*argv);
327 				return -1;
328 			}
329 		} else if (matches(*argv, "reorder") == 0) {
330 			NEXT_ARG();
331 			present[TCA_NETEM_REORDER] = 1;
332 			if (get_percent(&reorder.probability, *argv)) {
333 				explain1("reorder");
334 				return -1;
335 			}
336 			if (NEXT_IS_NUMBER()) {
337 				NEXT_ARG();
338 				++present[TCA_NETEM_CORR];
339 				if (get_percent(&reorder.correlation, *argv)) {
340 					explain1("reorder");
341 					return -1;
342 				}
343 			}
344 		} else if (matches(*argv, "corrupt") == 0) {
345 			NEXT_ARG();
346 			present[TCA_NETEM_CORRUPT] = 1;
347 			if (get_percent(&corrupt.probability, *argv)) {
348 				explain1("corrupt");
349 				return -1;
350 			}
351 			if (NEXT_IS_NUMBER()) {
352 				NEXT_ARG();
353 				++present[TCA_NETEM_CORR];
354 				if (get_percent(&corrupt.correlation, *argv)) {
355 					explain1("corrupt");
356 					return -1;
357 				}
358 			}
359 		} else if (matches(*argv, "gap") == 0) {
360 			NEXT_ARG();
361 			if (get_u32(&opt.gap, *argv, 0)) {
362 				explain1("gap");
363 				return -1;
364 			}
365 		} else if (matches(*argv, "duplicate") == 0) {
366 			NEXT_ARG();
367 			if (get_percent(&opt.duplicate, *argv)) {
368 				explain1("duplicate");
369 				return -1;
370 			}
371 			if (NEXT_IS_NUMBER()) {
372 				NEXT_ARG();
373 				if (get_percent(&cor.dup_corr, *argv)) {
374 					explain1("duplicate");
375 					return -1;
376 				}
377 			}
378 		} else if (matches(*argv, "distribution") == 0) {
379 			NEXT_ARG();
380 			dist_data = calloc(sizeof(dist_data[0]), MAX_DIST);
381 			dist_size = get_distribution(*argv, dist_data, MAX_DIST);
382 			if (dist_size <= 0) {
383 				free(dist_data);
384 				return -1;
385 			}
386 		} else if (matches(*argv, "rate") == 0) {
387 			++present[TCA_NETEM_RATE];
388 			NEXT_ARG();
389 			if (get_rate(&rate.rate, *argv)) {
390 				explain1("rate");
391 				return -1;
392 			}
393 			if (NEXT_IS_NUMBER()) {
394 				NEXT_ARG();
395 				if (get_s32(&rate.packet_overhead, *argv, 0)) {
396 					explain1("rate");
397 					return -1;
398 				}
399 			}
400 			if (NEXT_IS_NUMBER()) {
401 				NEXT_ARG();
402 				if (get_u32(&rate.cell_size, *argv, 0)) {
403 					explain1("rate");
404 					return -1;
405 				}
406 			}
407 			if (NEXT_IS_NUMBER()) {
408 				NEXT_ARG();
409 				if (get_s32(&rate.cell_overhead, *argv, 0)) {
410 					explain1("rate");
411 					return -1;
412 				}
413 			}
414 		} else if (strcmp(*argv, "help") == 0) {
415 			explain();
416 			return -1;
417 		} else {
418 			fprintf(stderr, "What is \"%s\"?\n", *argv);
419 			explain();
420 			return -1;
421 		}
422 	}
423 
424 	tail = NLMSG_TAIL(n);
425 
426 	if (reorder.probability) {
427 		if (opt.latency == 0) {
428 			fprintf(stderr, "reordering not possible without specifying some delay\n");
429 			explain();
430 			return -1;
431 		}
432 		if (opt.gap == 0)
433 			opt.gap = 1;
434 	} else if (opt.gap > 0) {
435 		fprintf(stderr, "gap specified without reorder probability\n");
436 		explain();
437 		return -1;
438 	}
439 
440 	if (dist_data && (opt.latency == 0 || opt.jitter == 0)) {
441 		fprintf(stderr, "distribution specified but no latency and jitter values\n");
442 		explain();
443 		return -1;
444 	}
445 
446 	if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
447 		return -1;
448 
449 	if (present[TCA_NETEM_CORR] &&
450 	    addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
451 			return -1;
452 
453 	if (present[TCA_NETEM_REORDER] &&
454 	    addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
455 		return -1;
456 
457 	if (present[TCA_NETEM_CORRUPT] &&
458 	    addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
459 		return -1;
460 
461 	if (loss_type != NETEM_LOSS_UNSPEC) {
462 		struct rtattr *start;
463 
464 		start = addattr_nest(n, 1024, TCA_NETEM_LOSS | NLA_F_NESTED);
465 		if (loss_type == NETEM_LOSS_GI) {
466 			if (addattr_l(n, 1024, NETEM_LOSS_GI,
467 				      &gimodel, sizeof(gimodel)) < 0)
468 			    return -1;
469 		} else if (loss_type == NETEM_LOSS_GE) {
470 			if (addattr_l(n, 1024, NETEM_LOSS_GE,
471 				      &gemodel, sizeof(gemodel)) < 0)
472 			    return -1;
473 		} else {
474 			fprintf(stderr, "loss in the weeds!\n");
475 			return -1;
476 		}
477 
478 		addattr_nest_end(n, start);
479 	}
480 
481 	if (present[TCA_NETEM_RATE] &&
482 	    addattr_l(n, 1024, TCA_NETEM_RATE, &rate, sizeof(rate)) < 0)
483 		return -1;
484 
485 	if (dist_data) {
486 		if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
487 			      TCA_NETEM_DELAY_DIST,
488 			      dist_data, dist_size * sizeof(dist_data[0])) < 0)
489 			return -1;
490 		free(dist_data);
491 	}
492 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
493 	return 0;
494 }
495 
netem_print_opt(struct qdisc_util * qu,FILE * f,struct rtattr * opt)496 static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
497 {
498 	const struct tc_netem_corr *cor = NULL;
499 	const struct tc_netem_reorder *reorder = NULL;
500 	const struct tc_netem_corrupt *corrupt = NULL;
501 	const struct tc_netem_gimodel *gimodel = NULL;
502 	const struct tc_netem_gemodel *gemodel = NULL;
503 	struct tc_netem_qopt qopt;
504 	const struct tc_netem_rate *rate = NULL;
505 	int len = RTA_PAYLOAD(opt) - sizeof(qopt);
506 	SPRINT_BUF(b1);
507 
508 	if (opt == NULL)
509 		return 0;
510 
511 	if (len < 0) {
512 		fprintf(stderr, "options size error\n");
513 		return -1;
514 	}
515 	memcpy(&qopt, RTA_DATA(opt), sizeof(qopt));
516 
517 	if (len > 0) {
518 		struct rtattr *tb[TCA_NETEM_MAX+1];
519 		parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt),
520 			     len);
521 
522 		if (tb[TCA_NETEM_CORR]) {
523 			if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor))
524 				return -1;
525 			cor = RTA_DATA(tb[TCA_NETEM_CORR]);
526 		}
527 		if (tb[TCA_NETEM_REORDER]) {
528 			if (RTA_PAYLOAD(tb[TCA_NETEM_REORDER]) < sizeof(*reorder))
529 				return -1;
530 			reorder = RTA_DATA(tb[TCA_NETEM_REORDER]);
531 		}
532 		if (tb[TCA_NETEM_CORRUPT]) {
533 			if (RTA_PAYLOAD(tb[TCA_NETEM_CORRUPT]) < sizeof(*corrupt))
534 				return -1;
535 			corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
536 		}
537 		if (tb[TCA_NETEM_LOSS]) {
538 			struct rtattr *lb[NETEM_LOSS_MAX + 1];
539 
540 			parse_rtattr_nested(lb, NETEM_LOSS_MAX, tb[TCA_NETEM_LOSS]);
541 			if (lb[NETEM_LOSS_GI])
542 				gemodel = RTA_DATA(lb[NETEM_LOSS_GI]);
543 			if (lb[NETEM_LOSS_GE])
544 				gemodel = RTA_DATA(lb[NETEM_LOSS_GE]);
545 		}
546 		if (tb[TCA_NETEM_RATE]) {
547 			if (RTA_PAYLOAD(tb[TCA_NETEM_RATE]) < sizeof(*rate))
548 				return -1;
549 			rate = RTA_DATA(tb[TCA_NETEM_RATE]);
550 		}
551 	}
552 
553 	fprintf(f, "limit %d", qopt.limit);
554 
555 	if (qopt.latency) {
556 		fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1));
557 
558 		if (qopt.jitter) {
559 			fprintf(f, "  %s", sprint_ticks(qopt.jitter, b1));
560 			if (cor && cor->delay_corr)
561 				fprintf(f, " %s", sprint_percent(cor->delay_corr, b1));
562 		}
563 	}
564 
565 	if (qopt.loss) {
566 		fprintf(f, " loss %s", sprint_percent(qopt.loss, b1));
567 		if (cor && cor->loss_corr)
568 			fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
569 	}
570 
571 	if (gimodel) {
572 		fprintf(f, " loss state p13 %s", sprint_percent(gimodel->p13, b1));
573 		fprintf(f, " p31 %s", sprint_percent(gimodel->p31, b1));
574 		fprintf(f, " p32 %s", sprint_percent(gimodel->p32, b1));
575 		fprintf(f, " p23 %s", sprint_percent(gimodel->p23, b1));
576 		fprintf(f, " p14 %s", sprint_percent(gimodel->p14, b1));
577 	}
578 
579 	if (gemodel) {
580 		fprintf(f, "loss gemodel p %s",
581 			sprint_percent(gemodel->p, b1));
582 		fprintf(f, " r %s", sprint_percent(gemodel->r, b1));
583 		fprintf(f, " 1-h %s", sprint_percent(gemodel->h, b1));
584 		fprintf(f, " 1-k %s", sprint_percent(gemodel->k1, b1));
585 	}
586 
587 	if (qopt.duplicate) {
588 		fprintf(f, " duplicate %s",
589 			sprint_percent(qopt.duplicate, b1));
590 		if (cor && cor->dup_corr)
591 			fprintf(f, " %s", sprint_percent(cor->dup_corr, b1));
592 	}
593 
594 	if (reorder && reorder->probability) {
595 		fprintf(f, " reorder %s",
596 			sprint_percent(reorder->probability, b1));
597 		if (reorder->correlation)
598 			fprintf(f, " %s",
599 				sprint_percent(reorder->correlation, b1));
600 	}
601 
602 	if (corrupt && corrupt->probability) {
603 		fprintf(f, " corrupt %s",
604 			sprint_percent(corrupt->probability, b1));
605 		if (corrupt->correlation)
606 			fprintf(f, " %s",
607 				sprint_percent(corrupt->correlation, b1));
608 	}
609 
610 	if (rate && rate->rate) {
611 		fprintf(f, " rate %s", sprint_rate(rate->rate, b1));
612 		if (rate->packet_overhead)
613 			fprintf(f, " packetoverhead %d", rate->packet_overhead);
614 		if (rate->cell_size)
615 			fprintf(f, " cellsize %u", rate->cell_size);
616 		if (rate->cell_overhead)
617 			fprintf(f, " celloverhead %d", rate->cell_overhead);
618 	}
619 
620 	if (qopt.gap)
621 		fprintf(f, " gap %lu", (unsigned long)qopt.gap);
622 
623 	return 0;
624 }
625 
626 struct qdisc_util netem_qdisc_util = {
627 	.id	   	= "netem",
628 	.parse_qopt	= netem_parse_opt,
629 	.print_qopt	= netem_print_opt,
630 };
631 
632