1 /* 2 * lib/route/sch/netem.c Network Emulator Qdisc 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2008 Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 /** 13 * @ingroup qdisc_api 14 * @defgroup netem Network Emulator 15 * @brief 16 * 17 * For further documentation see http://linux-net.osdl.org/index.php/Netem 18 * @{ 19 */ 20 21 #include <netlink-local.h> 22 #include <netlink-tc.h> 23 #include <netlink/netlink.h> 24 #include <netlink/utils.h> 25 #include <netlink/route/qdisc.h> 26 #include <netlink/route/qdisc-modules.h> 27 #include <netlink/route/sch/netem.h> 28 29 /** @cond SKIP */ 30 #define SCH_NETEM_ATTR_LATENCY 0x0001 31 #define SCH_NETEM_ATTR_LIMIT 0x0002 32 #define SCH_NETEM_ATTR_LOSS 0x0004 33 #define SCH_NETEM_ATTR_GAP 0x0008 34 #define SCH_NETEM_ATTR_DUPLICATE 0x0010 35 #define SCH_NETEM_ATTR_JITTER 0x0020 36 #define SCH_NETEM_ATTR_DELAY_CORR 0x0040 37 #define SCH_NETEM_ATTR_LOSS_CORR 0x0080 38 #define SCH_NETEM_ATTR_DUP_CORR 0x0100 39 #define SCH_NETEM_ATTR_RO_PROB 0x0200 40 #define SCH_NETEM_ATTR_RO_CORR 0x0400 41 #define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800 42 #define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000 43 #define SCH_NETEM_ATTR_DIST 0x2000 44 /** @endcond */ 45 46 static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc) 47 { 48 return (struct rtnl_netem *) qdisc->q_subdata; 49 } 50 51 static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc) 52 { 53 if (!qdisc->q_subdata) 54 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_netem)); 55 56 return netem_qdisc(qdisc); 57 } 58 59 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = { 60 [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) }, 61 [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) }, 62 [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) }, 63 }; 64 65 static int netem_msg_parser(struct rtnl_qdisc *qdisc) 66 { 67 int len, err = 0; 68 struct rtnl_netem *netem; 69 struct tc_netem_qopt *opts; 70 71 if (qdisc->q_opts->d_size < sizeof(*opts)) 72 return -NLE_INVAL; 73 74 netem = netem_alloc(qdisc); 75 if (!netem) 76 return -NLE_NOMEM; 77 78 opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data; 79 netem->qnm_latency = opts->latency; 80 netem->qnm_limit = opts->limit; 81 netem->qnm_loss = opts->loss; 82 netem->qnm_gap = opts->gap; 83 netem->qnm_duplicate = opts->duplicate; 84 netem->qnm_jitter = opts->jitter; 85 86 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT | 87 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP | 88 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER); 89 90 len = qdisc->q_opts->d_size - sizeof(*opts); 91 92 if (len > 0) { 93 struct nlattr *tb[TCA_NETEM_MAX+1]; 94 95 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *) 96 (qdisc->q_opts->d_data + sizeof(*opts)), 97 len, netem_policy); 98 if (err < 0) { 99 free(netem); 100 return err; 101 } 102 103 if (tb[TCA_NETEM_CORR]) { 104 struct tc_netem_corr cor; 105 106 nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor)); 107 netem->qnm_corr.nmc_delay = cor.delay_corr; 108 netem->qnm_corr.nmc_loss = cor.loss_corr; 109 netem->qnm_corr.nmc_duplicate = cor.dup_corr; 110 111 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR | 112 SCH_NETEM_ATTR_LOSS_CORR | 113 SCH_NETEM_ATTR_DUP_CORR); 114 } 115 116 if (tb[TCA_NETEM_REORDER]) { 117 struct tc_netem_reorder ro; 118 119 nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro)); 120 netem->qnm_ro.nmro_probability = ro.probability; 121 netem->qnm_ro.nmro_correlation = ro.correlation; 122 123 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB | 124 SCH_NETEM_ATTR_RO_CORR); 125 } 126 127 if (tb[TCA_NETEM_CORRUPT]) { 128 struct tc_netem_corrupt corrupt; 129 130 nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt)); 131 netem->qnm_crpt.nmcr_probability = corrupt.probability; 132 netem->qnm_crpt.nmcr_correlation = corrupt.correlation; 133 134 netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB | 135 SCH_NETEM_ATTR_CORRUPT_CORR); 136 } 137 138 /* sch_netem does not currently dump TCA_NETEM_DELAY_DIST */ 139 netem->qnm_dist.dist_data = NULL; 140 netem->qnm_dist.dist_size = 0; 141 } 142 143 return 0; 144 } 145 146 static void netem_free_data(struct rtnl_qdisc *qdisc) 147 { 148 struct rtnl_netem *netem; 149 150 if ( ! qdisc ) return; 151 152 netem = netem_qdisc(qdisc); 153 if ( ! netem ) return; 154 155 if ( netem->qnm_dist.dist_data ) 156 free(netem->qnm_dist.dist_data); 157 158 netem = NULL; 159 160 free (qdisc->q_subdata); 161 } 162 163 static void netem_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p) 164 { 165 struct rtnl_netem *netem = netem_qdisc(qdisc); 166 167 if (netem) 168 nl_dump(p, "limit %d", netem->qnm_limit); 169 } 170 171 int netem_build_msg(struct rtnl_qdisc *qdisc, struct nl_msg *msg) 172 { 173 int err = 0; 174 struct tc_netem_qopt opts; 175 struct tc_netem_corr cor; 176 struct tc_netem_reorder reorder; 177 struct tc_netem_corrupt corrupt; 178 struct rtnl_netem *netem; 179 180 unsigned char set_correlation = 0, set_reorder = 0, 181 set_corrupt = 0, set_dist = 0; 182 183 memset(&opts, 0, sizeof(opts)); 184 memset(&cor, 0, sizeof(cor)); 185 memset(&reorder, 0, sizeof(reorder)); 186 memset(&corrupt, 0, sizeof(corrupt)); 187 188 netem = netem_qdisc(qdisc); 189 if (!netem || !msg) 190 return EFAULT; 191 192 msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST; 193 194 if ( netem->qnm_ro.nmro_probability != 0 ) { 195 if (netem->qnm_latency == 0) { 196 return -NLE_MISSING_ATTR; 197 } 198 if (netem->qnm_gap == 0) netem->qnm_gap = 1; 199 } 200 else if ( netem->qnm_gap ) { 201 return -NLE_MISSING_ATTR; 202 } 203 204 if ( netem->qnm_corr.nmc_delay != 0 ) { 205 if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) { 206 return -NLE_MISSING_ATTR; 207 } 208 set_correlation = 1; 209 } 210 211 if ( netem->qnm_corr.nmc_loss != 0 ) { 212 if ( netem->qnm_loss == 0 ) { 213 return -NLE_MISSING_ATTR; 214 } 215 set_correlation = 1; 216 } 217 218 if ( netem->qnm_corr.nmc_duplicate != 0 ) { 219 if ( netem->qnm_duplicate == 0 ) { 220 return -NLE_MISSING_ATTR; 221 } 222 set_correlation = 1; 223 } 224 225 if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1; 226 else if ( netem->qnm_ro.nmro_correlation != 0 ) { 227 return -NLE_MISSING_ATTR; 228 } 229 230 if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1; 231 else if ( netem->qnm_crpt.nmcr_correlation != 0 ) { 232 return -NLE_MISSING_ATTR; 233 } 234 235 if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) { 236 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) { 237 return -NLE_MISSING_ATTR; 238 } 239 else { 240 /* Resize to accomodate the large distribution table */ 241 int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size * 242 sizeof(netem->qnm_dist.dist_data[0]); 243 244 msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len); 245 if ( msg->nm_nlh == NULL ) 246 return -NLE_NOMEM; 247 msg->nm_size = new_msg_len; 248 set_dist = 1; 249 } 250 } 251 252 opts.latency = netem->qnm_latency; 253 opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000; 254 opts.loss = netem->qnm_loss; 255 opts.gap = netem->qnm_gap; 256 opts.duplicate = netem->qnm_duplicate; 257 opts.jitter = netem->qnm_jitter; 258 259 NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts); 260 261 if ( set_correlation ) { 262 cor.delay_corr = netem->qnm_corr.nmc_delay; 263 cor.loss_corr = netem->qnm_corr.nmc_loss; 264 cor.dup_corr = netem->qnm_corr.nmc_duplicate; 265 266 NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor); 267 } 268 269 if ( set_reorder ) { 270 reorder.probability = netem->qnm_ro.nmro_probability; 271 reorder.correlation = netem->qnm_ro.nmro_correlation; 272 273 NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder); 274 } 275 276 if ( set_corrupt ) { 277 corrupt.probability = netem->qnm_crpt.nmcr_probability; 278 corrupt.correlation = netem->qnm_crpt.nmcr_correlation; 279 280 NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); 281 } 282 283 if ( set_dist ) { 284 NLA_PUT(msg, TCA_NETEM_DELAY_DIST, 285 netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]), 286 netem->qnm_dist.dist_data); 287 } 288 289 /* Length specified in the TCA_OPTIONS section must span the entire 290 * remainder of the message. That's just the way that sch_netem expects it. 291 * Maybe there's a more succinct way to do this at a higher level. 292 */ 293 struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) + 294 NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO); 295 296 struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) + 297 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len)); 298 299 int old_len = head->nla_len; 300 head->nla_len = (void *)tail - (void *)head; 301 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len); 302 303 return err; 304 nla_put_failure: 305 return -NLE_MSGSIZE; 306 } 307 308 /** 309 * @name Queue Limit 310 * @{ 311 */ 312 313 /** 314 * Set limit of netem qdisc. 315 * @arg qdisc Netem qdisc to be modified. 316 * @arg limit New limit in bytes. 317 * @return 0 on success or a negative error code. 318 */ 319 int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) 320 { 321 struct rtnl_netem *netem; 322 323 netem = netem_alloc(qdisc); 324 if (!netem) 325 return -NLE_NOMEM; 326 327 netem->qnm_limit = limit; 328 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; 329 330 return 0; 331 } 332 333 /** 334 * Get limit of netem qdisc. 335 * @arg qdisc Netem qdisc. 336 * @return Limit in bytes or a negative error code. 337 */ 338 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) 339 { 340 struct rtnl_netem *netem; 341 342 netem = netem_qdisc(qdisc); 343 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)) 344 return netem->qnm_limit; 345 else 346 return -NLE_NOATTR; 347 } 348 349 /** @} */ 350 351 /** 352 * @name Packet Re-ordering 353 * @{ 354 */ 355 356 /** 357 * Set re-ordering gap of netem qdisc. 358 * @arg qdisc Netem qdisc to be modified. 359 * @arg gap New gap in number of packets. 360 * @return 0 on success or a negative error code. 361 */ 362 int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) 363 { 364 struct rtnl_netem *netem; 365 366 netem = netem_alloc(qdisc); 367 if (!netem) 368 return -NLE_NOMEM; 369 370 netem->qnm_gap = gap; 371 netem->qnm_mask |= SCH_NETEM_ATTR_GAP; 372 373 return 0; 374 } 375 376 /** 377 * Get re-ordering gap of netem qdisc. 378 * @arg qdisc Netem qdisc. 379 * @return Re-ordering gap in packets or a negative error code. 380 */ 381 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) 382 { 383 struct rtnl_netem *netem; 384 385 netem = netem_qdisc(qdisc); 386 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP)) 387 return netem->qnm_gap; 388 else 389 return -NLE_NOATTR; 390 } 391 392 /** 393 * Set re-ordering probability of netem qdisc. 394 * @arg qdisc Netem qdisc to be modified. 395 * @arg prob New re-ordering probability. 396 * @return 0 on success or a negative error code. 397 */ 398 int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) 399 { 400 struct rtnl_netem *netem; 401 402 netem = netem_alloc(qdisc); 403 if (!netem) 404 return -NLE_NOMEM; 405 406 netem->qnm_ro.nmro_probability = prob; 407 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; 408 409 return 0; 410 } 411 412 /** 413 * Get re-ordering probability of netem qdisc. 414 * @arg qdisc Netem qdisc. 415 * @return Re-ordering probability or a negative error code. 416 */ 417 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) 418 { 419 struct rtnl_netem *netem; 420 421 netem = netem_qdisc(qdisc); 422 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)) 423 return netem->qnm_ro.nmro_probability; 424 else 425 return -NLE_NOATTR; 426 } 427 428 /** 429 * Set re-order correlation probability of netem qdisc. 430 * @arg qdisc Netem qdisc to be modified. 431 * @arg prob New re-ordering correlation probability. 432 * @return 0 on success or a negative error code. 433 */ 434 int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) 435 { 436 struct rtnl_netem *netem; 437 438 netem = netem_alloc(qdisc); 439 if (!netem) 440 return -NLE_NOMEM; 441 442 netem->qnm_ro.nmro_correlation = prob; 443 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; 444 445 return 0; 446 } 447 448 /** 449 * Get re-ordering correlation probability of netem qdisc. 450 * @arg qdisc Netem qdisc. 451 * @return Re-ordering correlation probability or a negative error code. 452 */ 453 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) 454 { 455 struct rtnl_netem *netem; 456 457 netem = netem_qdisc(qdisc); 458 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)) 459 return netem->qnm_ro.nmro_correlation; 460 else 461 return -NLE_NOATTR; 462 } 463 464 /** @} */ 465 466 /** 467 * @name Corruption 468 * @{ 469 */ 470 471 /** 472 * Set corruption probability of netem qdisc. 473 * @arg qdisc Netem qdisc to be modified. 474 * @arg prob New corruption probability. 475 * @return 0 on success or a negative error code. 476 */ 477 int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob) 478 { 479 struct rtnl_netem *netem; 480 481 netem = netem_alloc(qdisc); 482 if (!netem) 483 return -NLE_NOMEM; 484 485 netem->qnm_crpt.nmcr_probability = prob; 486 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB; 487 488 return 0; 489 } 490 491 /** 492 * Get corruption probability of netem qdisc. 493 * @arg qdisc Netem qdisc. 494 * @return Corruption probability or a negative error code. 495 */ 496 int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc) 497 { 498 struct rtnl_netem *netem; 499 500 netem = netem_qdisc(qdisc); 501 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)) 502 return netem->qnm_crpt.nmcr_probability; 503 else 504 return -NLE_NOATTR; 505 } 506 507 /** 508 * Set corruption correlation probability of netem qdisc. 509 * @arg qdisc Netem qdisc to be modified. 510 * @arg prob New corruption correlation probability. 511 * @return 0 on success or a negative error code. 512 */ 513 int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob) 514 { 515 struct rtnl_netem *netem; 516 517 netem = netem_alloc(qdisc); 518 if (!netem) 519 return -NLE_NOMEM; 520 521 netem->qnm_crpt.nmcr_correlation = prob; 522 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR; 523 524 return 0; 525 } 526 527 /** 528 * Get corruption correlation probability of netem qdisc. 529 * @arg qdisc Netem qdisc. 530 * @return Corruption correlation probability or a negative error code. 531 */ 532 int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc) 533 { 534 struct rtnl_netem *netem; 535 536 netem = netem_qdisc(qdisc); 537 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)) 538 return netem->qnm_crpt.nmcr_correlation; 539 else 540 return -NLE_NOATTR; 541 } 542 543 /** @} */ 544 545 /** 546 * @name Packet Loss 547 * @{ 548 */ 549 550 /** 551 * Set packet loss probability of netem qdisc. 552 * @arg qdisc Netem qdisc to be modified. 553 * @arg prob New packet loss probability. 554 * @return 0 on success or a negative error code. 555 */ 556 int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) 557 { 558 struct rtnl_netem *netem; 559 560 netem = netem_alloc(qdisc); 561 if (!netem) 562 return -NLE_NOMEM; 563 564 netem->qnm_loss = prob; 565 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; 566 567 return 0; 568 } 569 570 /** 571 * Get packet loss probability of netem qdisc. 572 * @arg qdisc Netem qdisc. 573 * @return Packet loss probability or a negative error code. 574 */ 575 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) 576 { 577 struct rtnl_netem *netem; 578 579 netem = netem_qdisc(qdisc); 580 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)) 581 return netem->qnm_loss; 582 else 583 return -NLE_NOATTR; 584 } 585 586 /** 587 * Set packet loss correlation probability of netem qdisc. 588 * @arg qdisc Netem qdisc to be modified. 589 * @arg prob New packet loss correlation. 590 * @return 0 on success or a negative error code. 591 */ 592 int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) 593 { 594 struct rtnl_netem *netem; 595 596 netem = netem_alloc(qdisc); 597 if (!netem) 598 return -NLE_NOMEM; 599 600 netem->qnm_corr.nmc_loss = prob; 601 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; 602 603 return 0; 604 } 605 606 /** 607 * Get packet loss correlation probability of netem qdisc. 608 * @arg qdisc Netem qdisc. 609 * @return Packet loss correlation probability or a negative error code. 610 */ 611 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) 612 { 613 struct rtnl_netem *netem; 614 615 netem = netem_qdisc(qdisc); 616 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)) 617 return netem->qnm_corr.nmc_loss; 618 else 619 return -NLE_NOATTR; 620 } 621 622 /** @} */ 623 624 /** 625 * @name Packet Duplication 626 * @{ 627 */ 628 629 /** 630 * Set packet duplication probability of netem qdisc. 631 * @arg qdisc Netem qdisc to be modified. 632 * @arg prob New packet duplication probability. 633 * @return 0 on success or a negative error code. 634 */ 635 int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) 636 { 637 struct rtnl_netem *netem; 638 639 netem = netem_alloc(qdisc); 640 if (!netem) 641 return -NLE_NOMEM; 642 643 netem->qnm_duplicate = prob; 644 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; 645 646 return 0; 647 } 648 649 /** 650 * Get packet duplication probability of netem qdisc. 651 * @arg qdisc Netem qdisc. 652 * @return Packet duplication probability or a negative error code. 653 */ 654 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) 655 { 656 struct rtnl_netem *netem; 657 658 netem = netem_qdisc(qdisc); 659 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)) 660 return netem->qnm_duplicate; 661 else 662 return -NLE_NOATTR; 663 } 664 665 /** 666 * Set packet duplication correlation probability of netem qdisc. 667 * @arg qdisc Netem qdisc to be modified. 668 * @arg prob New packet duplication correlation probability. 669 * @return 0 on sucess or a negative error code. 670 */ 671 int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) 672 { 673 struct rtnl_netem *netem; 674 675 netem = netem_alloc(qdisc); 676 if (!netem) 677 return -NLE_NOMEM; 678 679 netem->qnm_corr.nmc_duplicate = prob; 680 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; 681 682 return 0; 683 } 684 685 /** 686 * Get packet duplication correlation probability of netem qdisc. 687 * @arg qdisc Netem qdisc. 688 * @return Packet duplication correlation probability or a negative error code. 689 */ 690 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) 691 { 692 struct rtnl_netem *netem; 693 694 netem = netem_qdisc(qdisc); 695 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)) 696 return netem->qnm_corr.nmc_duplicate; 697 else 698 return -NLE_NOATTR; 699 } 700 701 /** @} */ 702 703 /** 704 * @name Packet Delay 705 * @{ 706 */ 707 708 /** 709 * Set packet delay of netem qdisc. 710 * @arg qdisc Netem qdisc to be modified. 711 * @arg delay New packet delay in micro seconds. 712 * @return 0 on success or a negative error code. 713 */ 714 int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) 715 { 716 struct rtnl_netem *netem; 717 718 netem = netem_alloc(qdisc); 719 if (!netem) 720 return -NLE_NOMEM; 721 722 netem->qnm_latency = nl_us2ticks(delay); 723 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; 724 725 return 0; 726 } 727 728 /** 729 * Get packet delay of netem qdisc. 730 * @arg qdisc Netem qdisc. 731 * @return Packet delay in micro seconds or a negative error code. 732 */ 733 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) 734 { 735 struct rtnl_netem *netem; 736 737 netem = netem_qdisc(qdisc); 738 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)) 739 return nl_ticks2us(netem->qnm_latency); 740 else 741 return -NLE_NOATTR; 742 } 743 744 /** 745 * Set packet delay jitter of netem qdisc. 746 * @arg qdisc Netem qdisc to be modified. 747 * @arg jitter New packet delay jitter in micro seconds. 748 * @return 0 on success or a negative error code. 749 */ 750 int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) 751 { 752 struct rtnl_netem *netem; 753 754 netem = netem_alloc(qdisc); 755 if (!netem) 756 return -NLE_NOMEM; 757 758 netem->qnm_jitter = nl_us2ticks(jitter); 759 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; 760 761 return 0; 762 } 763 764 /** 765 * Get packet delay jitter of netem qdisc. 766 * @arg qdisc Netem qdisc. 767 * @return Packet delay jitter in micro seconds or a negative error code. 768 */ 769 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) 770 { 771 struct rtnl_netem *netem; 772 773 netem = netem_qdisc(qdisc); 774 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)) 775 return nl_ticks2us(netem->qnm_jitter); 776 else 777 return -NLE_NOATTR; 778 } 779 780 /** 781 * Set packet delay correlation probability of netem qdisc. 782 * @arg qdisc Netem qdisc to be modified. 783 * @arg prob New packet delay correlation probability. 784 */ 785 int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) 786 { 787 struct rtnl_netem *netem; 788 789 netem = netem_alloc(qdisc); 790 if (!netem) 791 return -NLE_NOMEM; 792 793 netem->qnm_corr.nmc_delay = prob; 794 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; 795 796 return 0; 797 } 798 799 /** 800 * Get packet delay correlation probability of netem qdisc. 801 * @arg qdisc Netem qdisc. 802 * @return Packet delay correlation probability or a negative error code. 803 */ 804 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) 805 { 806 struct rtnl_netem *netem; 807 808 netem = netem_qdisc(qdisc); 809 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)) 810 return netem->qnm_corr.nmc_delay; 811 else 812 return -NLE_NOATTR; 813 } 814 815 /** 816 * Get the size of the distribution table. 817 * @arg qdisc Netem qdisc. 818 * @return Distribution table size or a negative error code. 819 */ 820 int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc) 821 { 822 struct rtnl_netem *netem; 823 824 netem = netem_qdisc(qdisc); 825 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) 826 return netem->qnm_dist.dist_size; 827 else 828 return -NLE_NOATTR; 829 } 830 831 /** 832 * Get a pointer to the distribution table. 833 * @arg qdisc Netem qdisc. 834 * @arg dist_ptr The pointer to set. 835 * @return Negative error code on failure or 0 on success. 836 */ 837 int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr) 838 { 839 struct rtnl_netem *netem; 840 841 netem = netem_qdisc(qdisc); 842 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) { 843 *dist_ptr = netem->qnm_dist.dist_data; 844 return 0; 845 } 846 else 847 return -NLE_NOATTR; 848 } 849 850 /** 851 * Set the delay distribution. Latency/jitter must be set before applying. 852 * @arg qdisc Netem qdisc. 853 * @arg dist_type The name of the distribution (type, file, path/file). 854 * @return 0 on success, error code on failure. 855 */ 856 int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) { 857 struct rtnl_netem *netem; 858 859 netem = netem_alloc(qdisc); 860 if (!netem) 861 return -NLE_NOMEM; 862 863 FILE *f = NULL; 864 int i, n = 0; 865 size_t len = 2048; 866 char *line; 867 char name[NAME_MAX]; 868 char dist_suffix[] = ".dist"; 869 870 /* If the given filename already ends in .dist, don't append it later */ 871 char *test_suffix = strstr(dist_type, dist_suffix); 872 if (test_suffix != NULL && strlen(test_suffix) == 5) 873 strcpy(dist_suffix, ""); 874 875 /* Check several locations for the dist file */ 876 char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" }; 877 878 for (i = 0; i < sizeof(test_path) && f == NULL; i++) { 879 snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix); 880 f = fopen(name, "r"); 881 } 882 883 if ( f == NULL ) 884 return -nl_syserr2nlerr(errno); 885 886 netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t)); 887 888 line = (char *) calloc (sizeof(char), len + 1); 889 890 while (getline(&line, &len, f) != -1) { 891 char *p, *endp; 892 893 if (*line == '\n' || *line == '#') 894 continue; 895 896 for (p = line; ; p = endp) { 897 long x = strtol(p, &endp, 0); 898 if (endp == p) break; 899 900 if (n >= MAXDIST) { 901 free(line); 902 fclose(f); 903 return -NLE_INVAL; 904 } 905 netem->qnm_dist.dist_data[n++] = x; 906 } 907 } 908 909 free(line); 910 911 netem->qnm_dist.dist_size = n; 912 netem->qnm_mask |= SCH_NETEM_ATTR_DIST; 913 914 fclose(f); 915 return 0; 916 } 917 918 /** @} */ 919 920 static struct rtnl_qdisc_ops netem_ops = { 921 .qo_kind = "netem", 922 .qo_msg_parser = netem_msg_parser, 923 .qo_free_data = netem_free_data, 924 .qo_dump[NL_DUMP_LINE] = netem_dump_line, 925 .qo_get_opts = 0, 926 .qo_build_msg = netem_build_msg 927 }; 928 929 static void __init netem_init(void) 930 { 931 rtnl_qdisc_register(&netem_ops); 932 } 933 934 static void __exit netem_exit(void) 935 { 936 rtnl_qdisc_unregister(&netem_ops); 937 } 938 939 /** @} */ 940