Home | History | Annotate | Download | only in tc
      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 (at) osdl.org>
     10  *
     11  */
     12 
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <unistd.h>
     16 #include <syslog.h>
     17 #include <fcntl.h>
     18 #include <sys/socket.h>
     19 #include <netinet/in.h>
     20 #include <arpa/inet.h>
     21 #include <string.h>
     22 #include <errno.h>
     23 
     24 #include "utils.h"
     25 #include "tc_util.h"
     26 #include "tc_common.h"
     27 
     28 static void explain(void)
     29 {
     30 	fprintf(stderr,
     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");
     38 }
     39 
     40 static void explain1(const char *arg)
     41 {
     42 	fprintf(stderr, "Illegal \"%s\"\n", arg);
     43 }
     44 
     45 /* Upper bound on size of distribution
     46  *  really (TCA_BUF_MAX - other headers) / sizeof (__s16)
     47  */
     48 #define MAX_DIST	(16*1024)
     49 
     50 /*
     51  * Simplistic file parser for distrbution data.
     52  * Format is:
     53  *	# comment line(s)
     54  *	data0 data1 ...
     55  */
     56 static int get_distribution(const char *type, __s16 *data, int maxdata)
     57 {
     58 	FILE *f;
     59 	int n;
     60 	long x;
     61 	size_t len;
     62 	char *line = NULL;
     63 	char name[128];
     64 
     65 	snprintf(name, sizeof(name), "%s/%s.dist", get_tc_lib(), type);
     66 	if ((f = fopen(name, "r")) == NULL) {
     67 		fprintf(stderr, "No distribution data for %s (%s: %s)\n",
     68 			type, name, strerror(errno));
     69 		return -1;
     70 	}
     71 
     72 	n = 0;
     73 	while (getline(&line, &len, f) != -1) {
     74 		char *p, *endp;
     75 		if (*line == '\n' || *line == '#')
     76 			continue;
     77 
     78 		for (p = line; ; p = endp) {
     79 			x = strtol(p, &endp, 0);
     80 			if (endp == p)
     81 				break;
     82 
     83 			if (n >= maxdata) {
     84 				fprintf(stderr, "%s: too much data\n",
     85 					name);
     86 				n = -1;
     87 				goto error;
     88 			}
     89 			data[n++] = x;
     90 		}
     91 	}
     92  error:
     93 	free(line);
     94 	fclose(f);
     95 	return n;
     96 }
     97 
     98 static int isnumber(const char *arg)
     99 {
    100 	char *p;
    101 
    102 	return strtod(arg, &p) != 0 || p != arg;
    103 }
    104 
    105 #define NEXT_IS_NUMBER() (NEXT_ARG_OK() && isnumber(argv[1]))
    106 
    107 /* Adjust for the fact that psched_ticks aren't always usecs
    108    (based on kernel PSCHED_CLOCK configuration */
    109 static int get_ticks(__u32 *ticks, const char *str)
    110 {
    111 	unsigned t;
    112 
    113 	if(get_time(&t, str))
    114 		return -1;
    115 
    116 	if (tc_core_time2big(t)) {
    117 		fprintf(stderr, "Illegal %u time (too large)\n", t);
    118 		return -1;
    119 	}
    120 
    121 	*ticks = tc_core_time2tick(t);
    122 	return 0;
    123 }
    124 
    125 static int netem_parse_opt(struct qdisc_util *qu, int argc, char **argv,
    126 			   struct nlmsghdr *n)
    127 {
    128 	size_t dist_size = 0;
    129 	struct rtattr *tail;
    130 	struct tc_netem_qopt opt;
    131 	struct tc_netem_corr cor;
    132 	struct tc_netem_reorder reorder;
    133 	struct tc_netem_corrupt corrupt;
    134 	__s16 *dist_data = NULL;
    135 	int present[__TCA_NETEM_MAX];
    136 
    137 	memset(&opt, 0, sizeof(opt));
    138 	opt.limit = 1000;
    139 	memset(&cor, 0, sizeof(cor));
    140 	memset(&reorder, 0, sizeof(reorder));
    141 	memset(&corrupt, 0, sizeof(corrupt));
    142 	memset(present, 0, sizeof(present));
    143 
    144 	while (argc > 0) {
    145 		if (matches(*argv, "limit") == 0) {
    146 			NEXT_ARG();
    147 			if (get_size(&opt.limit, *argv)) {
    148 				explain1("limit");
    149 				return -1;
    150 			}
    151 		} else if (matches(*argv, "latency") == 0 ||
    152 			   matches(*argv, "delay") == 0) {
    153 			NEXT_ARG();
    154 			if (get_ticks(&opt.latency, *argv)) {
    155 				explain1("latency");
    156 				return -1;
    157 			}
    158 
    159 			if (NEXT_IS_NUMBER()) {
    160 				NEXT_ARG();
    161 				if (get_ticks(&opt.jitter, *argv)) {
    162 					explain1("latency");
    163 					return -1;
    164 				}
    165 
    166 				if (NEXT_IS_NUMBER()) {
    167 					NEXT_ARG();
    168 					++present[TCA_NETEM_CORR];
    169 					if (get_percent(&cor.delay_corr,							*argv)) {
    170 						explain1("latency");
    171 						return -1;
    172 					}
    173 				}
    174 			}
    175 		} else if (matches(*argv, "loss") == 0 ||
    176 			   matches(*argv, "drop") == 0) {
    177 			NEXT_ARG();
    178 			if (get_percent(&opt.loss, *argv)) {
    179 				explain1("loss");
    180 				return -1;
    181 			}
    182 			if (NEXT_IS_NUMBER()) {
    183 				NEXT_ARG();
    184 				++present[TCA_NETEM_CORR];
    185 				if (get_percent(&cor.loss_corr, *argv)) {
    186 					explain1("loss");
    187 					return -1;
    188 				}
    189 			}
    190 		} else if (matches(*argv, "reorder") == 0) {
    191 			NEXT_ARG();
    192 			present[TCA_NETEM_REORDER] = 1;
    193 			if (get_percent(&reorder.probability, *argv)) {
    194 				explain1("reorder");
    195 				return -1;
    196 			}
    197 			if (NEXT_IS_NUMBER()) {
    198 				NEXT_ARG();
    199 				++present[TCA_NETEM_CORR];
    200 				if (get_percent(&reorder.correlation, *argv)) {
    201 					explain1("reorder");
    202 					return -1;
    203 				}
    204 			}
    205 		} else if (matches(*argv, "corrupt") == 0) {
    206 			NEXT_ARG();
    207 			present[TCA_NETEM_CORRUPT] = 1;
    208 			if (get_percent(&corrupt.probability, *argv)) {
    209 				explain1("corrupt");
    210 				return -1;
    211 			}
    212 			if (NEXT_IS_NUMBER()) {
    213 				NEXT_ARG();
    214 				++present[TCA_NETEM_CORR];
    215 				if (get_percent(&corrupt.correlation, *argv)) {
    216 					explain1("corrupt");
    217 					return -1;
    218 				}
    219 			}
    220 		} else if (matches(*argv, "gap") == 0) {
    221 			NEXT_ARG();
    222 			if (get_u32(&opt.gap, *argv, 0)) {
    223 				explain1("gap");
    224 				return -1;
    225 			}
    226 		} else if (matches(*argv, "duplicate") == 0) {
    227 			NEXT_ARG();
    228 			if (get_percent(&opt.duplicate, *argv)) {
    229 				explain1("duplicate");
    230 				return -1;
    231 			}
    232 			if (NEXT_IS_NUMBER()) {
    233 				NEXT_ARG();
    234 				if (get_percent(&cor.dup_corr, *argv)) {
    235 					explain1("duplicate");
    236 					return -1;
    237 				}
    238 			}
    239 		} else if (matches(*argv, "distribution") == 0) {
    240 			NEXT_ARG();
    241 			dist_data = calloc(sizeof(dist_data[0]), MAX_DIST);
    242 			dist_size = get_distribution(*argv, dist_data, MAX_DIST);
    243 			if (dist_size <= 0) {
    244 				free(dist_data);
    245 				return -1;
    246 			}
    247 		} else if (strcmp(*argv, "help") == 0) {
    248 			explain();
    249 			return -1;
    250 		} else {
    251 			fprintf(stderr, "What is \"%s\"?\n", *argv);
    252 			explain();
    253 			return -1;
    254 		}
    255 		argc--; argv++;
    256 	}
    257 
    258 	tail = NLMSG_TAIL(n);
    259 
    260 	if (reorder.probability) {
    261 		if (opt.latency == 0) {
    262 			fprintf(stderr, "reordering not possible without specifying some delay\n");
    263 		}
    264 		if (opt.gap == 0)
    265 			opt.gap = 1;
    266 	} else if (opt.gap > 0) {
    267 		fprintf(stderr, "gap specified without reorder probability\n");
    268 		explain();
    269 		return -1;
    270 	}
    271 
    272 	if (dist_data && (opt.latency == 0 || opt.jitter == 0)) {
    273 		fprintf(stderr, "distribution specified but no latency and jitter values\n");
    274 		explain();
    275 		return -1;
    276 	}
    277 
    278 	if (addattr_l(n, 1024, TCA_OPTIONS, &opt, sizeof(opt)) < 0)
    279 		return -1;
    280 
    281 	if (present[TCA_NETEM_CORR] &&
    282 	    addattr_l(n, 1024, TCA_NETEM_CORR, &cor, sizeof(cor)) < 0)
    283 			return -1;
    284 
    285 	if (present[TCA_NETEM_REORDER] &&
    286 	    addattr_l(n, 1024, TCA_NETEM_REORDER, &reorder, sizeof(reorder)) < 0)
    287 		return -1;
    288 
    289 	if (present[TCA_NETEM_CORRUPT] &&
    290 	    addattr_l(n, 1024, TCA_NETEM_CORRUPT, &corrupt, sizeof(corrupt)) < 0)
    291 		return -1;
    292 
    293 	if (dist_data) {
    294 		if (addattr_l(n, MAX_DIST * sizeof(dist_data[0]),
    295 			      TCA_NETEM_DELAY_DIST,
    296 			      dist_data, dist_size * sizeof(dist_data[0])) < 0)
    297 			return -1;
    298 		free(dist_data);
    299 	}
    300 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    301 	return 0;
    302 }
    303 
    304 static int netem_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
    305 {
    306 	const struct tc_netem_corr *cor = NULL;
    307 	const struct tc_netem_reorder *reorder = NULL;
    308 	const struct tc_netem_corrupt *corrupt = NULL;
    309 	struct tc_netem_qopt qopt;
    310 	int len = RTA_PAYLOAD(opt) - sizeof(qopt);
    311 	SPRINT_BUF(b1);
    312 
    313 	if (opt == NULL)
    314 		return 0;
    315 
    316 	if (len < 0) {
    317 		fprintf(stderr, "options size error\n");
    318 		return -1;
    319 	}
    320 	memcpy(&qopt, RTA_DATA(opt), sizeof(qopt));
    321 
    322 	if (len > 0) {
    323 		struct rtattr *tb[TCA_NETEM_MAX+1];
    324 		parse_rtattr(tb, TCA_NETEM_MAX, RTA_DATA(opt) + sizeof(qopt),
    325 			     len);
    326 
    327 		if (tb[TCA_NETEM_CORR]) {
    328 			if (RTA_PAYLOAD(tb[TCA_NETEM_CORR]) < sizeof(*cor))
    329 				return -1;
    330 			cor = RTA_DATA(tb[TCA_NETEM_CORR]);
    331 		}
    332 		if (tb[TCA_NETEM_REORDER]) {
    333 			if (RTA_PAYLOAD(tb[TCA_NETEM_REORDER]) < sizeof(*reorder))
    334 				return -1;
    335 			reorder = RTA_DATA(tb[TCA_NETEM_REORDER]);
    336 		}
    337 		if (tb[TCA_NETEM_CORRUPT]) {
    338 			if (RTA_PAYLOAD(tb[TCA_NETEM_CORRUPT]) < sizeof(*corrupt))
    339 				return -1;
    340 			corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
    341 		}
    342 	}
    343 
    344 	fprintf(f, "limit %d", qopt.limit);
    345 
    346 	if (qopt.latency) {
    347 		fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1));
    348 
    349 		if (qopt.jitter) {
    350 			fprintf(f, "  %s", sprint_ticks(qopt.jitter, b1));
    351 			if (cor && cor->delay_corr)
    352 				fprintf(f, " %s", sprint_percent(cor->delay_corr, b1));
    353 		}
    354 	}
    355 
    356 	if (qopt.loss) {
    357 		fprintf(f, " loss %s", sprint_percent(qopt.loss, b1));
    358 		if (cor && cor->loss_corr)
    359 			fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
    360 	}
    361 
    362 	if (qopt.duplicate) {
    363 		fprintf(f, " duplicate %s",
    364 			sprint_percent(qopt.duplicate, b1));
    365 		if (cor && cor->dup_corr)
    366 			fprintf(f, " %s", sprint_percent(cor->dup_corr, b1));
    367 	}
    368 
    369 	if (reorder && reorder->probability) {
    370 		fprintf(f, " reorder %s",
    371 			sprint_percent(reorder->probability, b1));
    372 		if (reorder->correlation)
    373 			fprintf(f, " %s",
    374 				sprint_percent(reorder->correlation, b1));
    375 	}
    376 
    377 	if (corrupt && corrupt->probability) {
    378 		fprintf(f, " corrupt %s",
    379 			sprint_percent(corrupt->probability, b1));
    380 		if (corrupt->correlation)
    381 			fprintf(f, " %s",
    382 				sprint_percent(corrupt->correlation, b1));
    383 	}
    384 
    385 	if (qopt.gap)
    386 		fprintf(f, " gap %lu", (unsigned long)qopt.gap);
    387 
    388 	return 0;
    389 }
    390 
    391 struct qdisc_util netem_qdisc_util = {
    392 	.id	   	= "netem",
    393 	.parse_qopt	= netem_parse_opt,
    394 	.print_qopt	= netem_print_opt,
    395 };
    396 
    397