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