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