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) 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 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 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. */ 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 */ 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 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 89 void print_percent(char *buf, int len, __u32 per) 90 { 91 snprintf(buf, len, "%g%%", 100. * (double) per / max_percent_value); 92 } 93 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 */ 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 */ 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 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 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