netem.c revision 861901c55bd9e2f84e7c8de0da5ea6179867907d
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-2006 Thomas Graf <tgraf@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 46static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc) 47{ 48 return (struct rtnl_netem *) qdisc->q_subdata; 49} 50 51static 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 59static 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 65static 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 nl_error(EINVAL, "Netem specific options size mismatch"); 73 74 netem = netem_alloc(qdisc); 75 if (!netem) 76 return nl_errno(ENOMEM); 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 146static 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 163static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, 164 int line) 165{ 166 struct rtnl_netem *netem = netem_qdisc(qdisc); 167 168 if (netem) 169 dp_dump(p, "limit %d", netem->qnm_limit); 170 171 return line; 172} 173 174static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, 175 int line) 176{ 177 return line; 178} 179 180int netem_build_msg(struct rtnl_qdisc *qdisc, struct nl_msg *msg) 181{ 182 int err = 0; 183 struct tc_netem_qopt opts; 184 struct tc_netem_corr cor; 185 struct tc_netem_reorder reorder; 186 struct tc_netem_corrupt corrupt; 187 struct rtnl_netem *netem; 188 189 unsigned char set_correlation = 0, set_reorder = 0, 190 set_corrupt = 0, set_dist = 0; 191 192 memset(&opts, 0, sizeof(opts)); 193 memset(&cor, 0, sizeof(cor)); 194 memset(&reorder, 0, sizeof(reorder)); 195 memset(&corrupt, 0, sizeof(corrupt)); 196 197 netem = netem_qdisc(qdisc); 198 if (!netem || !msg) 199 return EFAULT; 200 201 msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST; 202 203 if ( netem->qnm_ro.nmro_probability != 0 ) { 204 if (netem->qnm_latency == 0) { 205 return nl_error(EINVAL, 206 "netem: Specified reorder gap without latency."); 207 } 208 if (netem->qnm_gap == 0) netem->qnm_gap = 1; 209 } 210 else if ( netem->qnm_gap ) { 211 return nl_error(EINVAL, 212 "netem: Specified reorder gap without reorder probability."); 213 } 214 215 if ( netem->qnm_corr.nmc_delay != 0 ) { 216 if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) { 217 return nl_error(EINVAL, 218 "netem: Specified delay correlation without delay size / jitter."); 219 } 220 set_correlation = 1; 221 } 222 223 if ( netem->qnm_corr.nmc_loss != 0 ) { 224 if ( netem->qnm_loss == 0 ) { 225 return nl_error(EINVAL, 226 "netem: Specified loss correlation without loss probability."); 227 } 228 set_correlation = 1; 229 } 230 231 if ( netem->qnm_corr.nmc_duplicate != 0 ) { 232 if ( netem->qnm_duplicate == 0 ) { 233 return nl_error(EINVAL, 234 "netem: Specified dup correlation without duplication probability."); 235 } 236 set_correlation = 1; 237 } 238 239 if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1; 240 else if ( netem->qnm_ro.nmro_correlation != 0 ) { 241 return nl_error(EINVAL, 242 "netem: Specified reorder correlation without reorder probability."); 243 } 244 245 if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1; 246 else if ( netem->qnm_crpt.nmcr_correlation != 0 ) { 247 return nl_error(EINVAL, 248 "netem: Specified corrupt correlation without corrupt probability."); 249 } 250 251 if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) { 252 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) { 253 return nl_error(EINVAL, 254 "netem: Distribution specified with empty latency and jitter values."); 255 } 256 else { 257 /* Resize to accomodate the large distribution table */ 258 int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size * 259 sizeof(netem->qnm_dist.dist_data[0]); 260 261 msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len); 262 if ( msg->nm_nlh == NULL ) 263 return nl_error(ENOMEM, 264 "netem: Unable to reallocate message size to contain delay distribution data."); 265 msg->nm_size = new_msg_len; 266 set_dist = 1; 267 } 268 } 269 270 opts.latency = netem->qnm_latency; 271 opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000; 272 opts.loss = netem->qnm_loss; 273 opts.gap = netem->qnm_gap; 274 opts.duplicate = netem->qnm_duplicate; 275 opts.jitter = netem->qnm_jitter; 276 277 err = nla_put(msg, TCA_OPTIONS, sizeof(opts), &opts); 278 if (err) 279 return nl_error(err, "netem: Unable to add TCA_OPTIONS to nl_msg."); 280 281 if ( set_correlation ) { 282 cor.delay_corr = netem->qnm_corr.nmc_delay; 283 cor.loss_corr = netem->qnm_corr.nmc_loss; 284 cor.dup_corr = netem->qnm_corr.nmc_duplicate; 285 286 err = nla_put(msg, TCA_NETEM_CORR, sizeof(cor), &cor); 287 if (err) 288 return nl_error(err, "netem: Unable to add TCA_NETEM_CORR to nl_msg."); 289 } 290 291 if ( set_reorder ) { 292 reorder.probability = netem->qnm_ro.nmro_probability; 293 reorder.correlation = netem->qnm_ro.nmro_correlation; 294 295 err = nla_put(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder); 296 if (err) 297 return nl_error(err, "netem: Unable to add TCA_NETEM_REORDER to nl_msg."); 298 } 299 300 if ( set_corrupt ) { 301 corrupt.probability = netem->qnm_crpt.nmcr_probability; 302 corrupt.correlation = netem->qnm_crpt.nmcr_correlation; 303 304 err = nla_put(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt); 305 if (err) 306 return nl_error(err, "netem: Unable to add TCA_NETEM_CORRUPT to nl_msg."); 307 } 308 309 if ( set_dist ) { 310 err = nla_put(msg, TCA_NETEM_DELAY_DIST, 311 netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]), 312 netem->qnm_dist.dist_data); 313 if (err) 314 return nl_error(err, "netem: Unable to add TCA_NETEM_DELAY_DIST to nl_msg."); 315 } 316 317 /* Length specified in the TCA_OPTIONS section must span the entire 318 * remainder of the message. That's just the way that sch_netem expects it. 319 * Maybe there's a more succinct way to do this at a higher level. 320 */ 321 struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) + 322 NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO); 323 324 struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) + 325 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len)); 326 327 int old_len = head->nla_len; 328 head->nla_len = (void *)tail - (void *)head; 329 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len); 330 331 return err; 332} 333 334/** 335 * @name Queue Limit 336 * @{ 337 */ 338 339/** 340 * Set limit of netem qdisc. 341 * @arg qdisc Netem qdisc to be modified. 342 * @arg limit New limit in bytes. 343 * @return 0 on success or a negative error code. 344 */ 345int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) 346{ 347 struct rtnl_netem *netem; 348 349 netem = netem_alloc(qdisc); 350 if (!netem) 351 return nl_errno(ENOMEM); 352 353 netem->qnm_limit = limit; 354 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; 355 356 return 0; 357} 358 359/** 360 * Get limit of netem qdisc. 361 * @arg qdisc Netem qdisc. 362 * @return Limit in bytes or a negative error code. 363 */ 364int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) 365{ 366 struct rtnl_netem *netem; 367 368 netem = netem_qdisc(qdisc); 369 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)) 370 return netem->qnm_limit; 371 else 372 return nl_errno(ENOENT); 373} 374 375/** @} */ 376 377/** 378 * @name Packet Re-ordering 379 * @{ 380 */ 381 382/** 383 * Set re-ordering gap of netem qdisc. 384 * @arg qdisc Netem qdisc to be modified. 385 * @arg gap New gap in number of packets. 386 * @return 0 on success or a negative error code. 387 */ 388int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) 389{ 390 struct rtnl_netem *netem; 391 392 netem = netem_alloc(qdisc); 393 if (!netem) 394 return nl_errno(ENOMEM); 395 396 netem->qnm_gap = gap; 397 netem->qnm_mask |= SCH_NETEM_ATTR_GAP; 398 399 return 0; 400} 401 402/** 403 * Get re-ordering gap of netem qdisc. 404 * @arg qdisc Netem qdisc. 405 * @return Re-ordering gap in packets or a negative error code. 406 */ 407int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) 408{ 409 struct rtnl_netem *netem; 410 411 netem = netem_qdisc(qdisc); 412 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP)) 413 return netem->qnm_gap; 414 else 415 return nl_errno(ENOENT); 416} 417 418/** 419 * Set re-ordering probability of netem qdisc. 420 * @arg qdisc Netem qdisc to be modified. 421 * @arg prob New re-ordering probability. 422 * @return 0 on success or a negative error code. 423 */ 424int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) 425{ 426 struct rtnl_netem *netem; 427 428 netem = netem_alloc(qdisc); 429 if (!netem) 430 return nl_errno(ENOMEM); 431 432 netem->qnm_ro.nmro_probability = prob; 433 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; 434 435 return 0; 436} 437 438/** 439 * Get re-ordering probability of netem qdisc. 440 * @arg qdisc Netem qdisc. 441 * @return Re-ordering probability or a negative error code. 442 */ 443int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) 444{ 445 struct rtnl_netem *netem; 446 447 netem = netem_qdisc(qdisc); 448 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)) 449 return netem->qnm_ro.nmro_probability; 450 else 451 return nl_errno(ENOENT); 452} 453 454/** 455 * Set re-order correlation probability of netem qdisc. 456 * @arg qdisc Netem qdisc to be modified. 457 * @arg prob New re-ordering correlation probability. 458 * @return 0 on success or a negative error code. 459 */ 460int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) 461{ 462 struct rtnl_netem *netem; 463 464 netem = netem_alloc(qdisc); 465 if (!netem) 466 return nl_errno(ENOMEM); 467 468 netem->qnm_ro.nmro_correlation = prob; 469 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; 470 471 return 0; 472} 473 474/** 475 * Get re-ordering correlation probability of netem qdisc. 476 * @arg qdisc Netem qdisc. 477 * @return Re-ordering correlation probability or a negative error code. 478 */ 479int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) 480{ 481 struct rtnl_netem *netem; 482 483 netem = netem_qdisc(qdisc); 484 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)) 485 return netem->qnm_ro.nmro_correlation; 486 else 487 return nl_errno(ENOENT); 488} 489 490/** @} */ 491 492/** 493 * @name Corruption 494 * @{ 495 */ 496 497/** 498 * Set corruption probability of netem qdisc. 499 * @arg qdisc Netem qdisc to be modified. 500 * @arg prob New corruption probability. 501 * @return 0 on success or a negative error code. 502 */ 503int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob) 504{ 505 struct rtnl_netem *netem; 506 507 netem = netem_alloc(qdisc); 508 if (!netem) 509 return nl_errno(ENOMEM); 510 511 netem->qnm_crpt.nmcr_probability = prob; 512 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB; 513 514 return 0; 515} 516 517/** 518 * Get corruption probability of netem qdisc. 519 * @arg qdisc Netem qdisc. 520 * @return Corruption probability or a negative error code. 521 */ 522int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc) 523{ 524 struct rtnl_netem *netem; 525 526 netem = netem_qdisc(qdisc); 527 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)) 528 return netem->qnm_crpt.nmcr_probability; 529 else 530 return nl_errno(ENOENT); 531} 532 533/** 534 * Set corruption correlation probability of netem qdisc. 535 * @arg qdisc Netem qdisc to be modified. 536 * @arg prob New corruption correlation probability. 537 * @return 0 on success or a negative error code. 538 */ 539int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob) 540{ 541 struct rtnl_netem *netem; 542 543 netem = netem_alloc(qdisc); 544 if (!netem) 545 return nl_errno(ENOMEM); 546 547 netem->qnm_crpt.nmcr_correlation = prob; 548 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR; 549 550 return 0; 551} 552 553/** 554 * Get corruption correlation probability of netem qdisc. 555 * @arg qdisc Netem qdisc. 556 * @return Corruption correlation probability or a negative error code. 557 */ 558int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc) 559{ 560 struct rtnl_netem *netem; 561 562 netem = netem_qdisc(qdisc); 563 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)) 564 return netem->qnm_crpt.nmcr_correlation; 565 else 566 return nl_errno(ENOENT); 567} 568 569/** @} */ 570 571/** 572 * @name Packet Loss 573 * @{ 574 */ 575 576/** 577 * Set packet loss probability of netem qdisc. 578 * @arg qdisc Netem qdisc to be modified. 579 * @arg prob New packet loss probability. 580 * @return 0 on success or a negative error code. 581 */ 582int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) 583{ 584 struct rtnl_netem *netem; 585 586 netem = netem_alloc(qdisc); 587 if (!netem) 588 return nl_errno(ENOMEM); 589 590 netem->qnm_loss = prob; 591 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; 592 593 return 0; 594} 595 596/** 597 * Get packet loss probability of netem qdisc. 598 * @arg qdisc Netem qdisc. 599 * @return Packet loss probability or a negative error code. 600 */ 601int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) 602{ 603 struct rtnl_netem *netem; 604 605 netem = netem_qdisc(qdisc); 606 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)) 607 return netem->qnm_loss; 608 else 609 return nl_errno(ENOENT); 610} 611 612/** 613 * Set packet loss correlation probability of netem qdisc. 614 * @arg qdisc Netem qdisc to be modified. 615 * @arg prob New packet loss correlation. 616 * @return 0 on success or a negative error code. 617 */ 618int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) 619{ 620 struct rtnl_netem *netem; 621 622 netem = netem_alloc(qdisc); 623 if (!netem) 624 return nl_errno(ENOMEM); 625 626 netem->qnm_corr.nmc_loss = prob; 627 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; 628 629 return 0; 630} 631 632/** 633 * Get packet loss correlation probability of netem qdisc. 634 * @arg qdisc Netem qdisc. 635 * @return Packet loss correlation probability or a negative error code. 636 */ 637int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) 638{ 639 struct rtnl_netem *netem; 640 641 netem = netem_qdisc(qdisc); 642 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)) 643 return netem->qnm_corr.nmc_loss; 644 else 645 return nl_errno(ENOENT); 646} 647 648/** @} */ 649 650/** 651 * @name Packet Duplication 652 * @{ 653 */ 654 655/** 656 * Set packet duplication probability of netem qdisc. 657 * @arg qdisc Netem qdisc to be modified. 658 * @arg prob New packet duplication probability. 659 * @return 0 on success or a negative error code. 660 */ 661int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) 662{ 663 struct rtnl_netem *netem; 664 665 netem = netem_alloc(qdisc); 666 if (!netem) 667 return nl_errno(ENOMEM); 668 669 netem->qnm_duplicate = prob; 670 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; 671 672 return 0; 673} 674 675/** 676 * Get packet duplication probability of netem qdisc. 677 * @arg qdisc Netem qdisc. 678 * @return Packet duplication probability or a negative error code. 679 */ 680int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) 681{ 682 struct rtnl_netem *netem; 683 684 netem = netem_qdisc(qdisc); 685 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)) 686 return netem->qnm_duplicate; 687 else 688 return nl_errno(ENOENT); 689} 690 691/** 692 * Set packet duplication correlation probability of netem qdisc. 693 * @arg qdisc Netem qdisc to be modified. 694 * @arg prob New packet duplication correlation probability. 695 * @return 0 on sucess or a negative error code. 696 */ 697int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) 698{ 699 struct rtnl_netem *netem; 700 701 netem = netem_alloc(qdisc); 702 if (!netem) 703 return nl_errno(ENOMEM); 704 705 netem->qnm_corr.nmc_duplicate = prob; 706 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; 707 708 return 0; 709} 710 711/** 712 * Get packet duplication correlation probability of netem qdisc. 713 * @arg qdisc Netem qdisc. 714 * @return Packet duplication correlation probability or a negative error code. 715 */ 716int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) 717{ 718 struct rtnl_netem *netem; 719 720 netem = netem_qdisc(qdisc); 721 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)) 722 return netem->qnm_corr.nmc_duplicate; 723 else 724 return nl_errno(ENOENT); 725} 726 727/** @} */ 728 729/** 730 * @name Packet Delay 731 * @{ 732 */ 733 734/** 735 * Set packet delay of netem qdisc. 736 * @arg qdisc Netem qdisc to be modified. 737 * @arg delay New packet delay in micro seconds. 738 * @return 0 on success or a negative error code. 739 */ 740int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) 741{ 742 struct rtnl_netem *netem; 743 744 netem = netem_alloc(qdisc); 745 if (!netem) 746 return nl_errno(ENOMEM); 747 748 netem->qnm_latency = nl_us2ticks(delay); 749 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; 750 751 return 0; 752} 753 754/** 755 * Get packet delay of netem qdisc. 756 * @arg qdisc Netem qdisc. 757 * @return Packet delay in micro seconds or a negative error code. 758 */ 759int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) 760{ 761 struct rtnl_netem *netem; 762 763 netem = netem_qdisc(qdisc); 764 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)) 765 return nl_ticks2us(netem->qnm_latency); 766 else 767 return nl_errno(ENOENT); 768} 769 770/** 771 * Set packet delay jitter of netem qdisc. 772 * @arg qdisc Netem qdisc to be modified. 773 * @arg jitter New packet delay jitter in micro seconds. 774 * @return 0 on success or a negative error code. 775 */ 776int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) 777{ 778 struct rtnl_netem *netem; 779 780 netem = netem_alloc(qdisc); 781 if (!netem) 782 return nl_errno(ENOMEM); 783 784 netem->qnm_jitter = nl_us2ticks(jitter); 785 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; 786 787 return 0; 788} 789 790/** 791 * Get packet delay jitter of netem qdisc. 792 * @arg qdisc Netem qdisc. 793 * @return Packet delay jitter in micro seconds or a negative error code. 794 */ 795int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) 796{ 797 struct rtnl_netem *netem; 798 799 netem = netem_qdisc(qdisc); 800 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)) 801 return nl_ticks2us(netem->qnm_jitter); 802 else 803 return nl_errno(ENOENT); 804} 805 806/** 807 * Set packet delay correlation probability of netem qdisc. 808 * @arg qdisc Netem qdisc to be modified. 809 * @arg prob New packet delay correlation probability. 810 */ 811int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) 812{ 813 struct rtnl_netem *netem; 814 815 netem = netem_alloc(qdisc); 816 if (!netem) 817 return nl_errno(ENOMEM); 818 819 netem->qnm_corr.nmc_delay = prob; 820 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; 821 822 return 0; 823} 824 825/** 826 * Get packet delay correlation probability of netem qdisc. 827 * @arg qdisc Netem qdisc. 828 * @return Packet delay correlation probability or a negative error code. 829 */ 830int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) 831{ 832 struct rtnl_netem *netem; 833 834 netem = netem_qdisc(qdisc); 835 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)) 836 return netem->qnm_corr.nmc_delay; 837 else 838 return nl_errno(ENOENT); 839} 840 841/** 842 * Get the size of the distribution table. 843 * @arg qdisc Netem qdisc. 844 * @return Distribution table size or a negative error code. 845 */ 846int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc) 847{ 848 struct rtnl_netem *netem; 849 850 netem = netem_qdisc(qdisc); 851 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) 852 return netem->qnm_dist.dist_size; 853 else 854 return nl_errno(ENOENT); 855} 856 857/** 858 * Get a pointer to the distribution table. 859 * @arg qdisc Netem qdisc. 860 * @arg dist_ptr The pointer to set. 861 * @return Negative error code on failure or 0 on success. 862 */ 863int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr) 864{ 865 struct rtnl_netem *netem; 866 867 netem = netem_qdisc(qdisc); 868 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) { 869 *dist_ptr = netem->qnm_dist.dist_data; 870 return 0; 871 } 872 else 873 return nl_errno(ENOENT); 874} 875 876/** 877 * Set the delay distribution. Latency/jitter must be set before applying. 878 * @arg qdisc Netem qdisc. 879 * @arg dist_type The name of the distribution (type, file, path/file). 880 * @return 0 on success, error code on failure. 881 */ 882int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) { 883 struct rtnl_netem *netem; 884 885 netem = netem_alloc(qdisc); 886 if (!netem) 887 return nl_errno(ENOMEM); 888 889 FILE *f = NULL; 890 int i, n = 0; 891 size_t len = 2048; 892 char *line; 893 char name[NAME_MAX]; 894 char dist_suffix[] = ".dist"; 895 896 /* If the given filename already ends in .dist, don't append it later */ 897 char *test_suffix = strstr(dist_type, dist_suffix); 898 if (test_suffix != NULL && strlen(test_suffix) == 5) 899 strcpy(dist_suffix, ""); 900 901 /* Check several locations for the dist file */ 902 char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" }; 903 904 for (i = 0; i < sizeof(test_path) && f == NULL; i++) { 905 snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix); 906 f = fopen(name, "r"); 907 } 908 909 if ( f == NULL ) return nl_error(errno, "netem: Unable to open distribution file."); 910 911 netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t)); 912 913 line = (char *) calloc (sizeof(char), len + 1); 914 915 while (getline(&line, &len, f) != -1) { 916 char *p, *endp; 917 918 if (*line == '\n' || *line == '#') 919 continue; 920 921 for (p = line; ; p = endp) { 922 long x = strtol(p, &endp, 0); 923 if (endp == p) break; 924 925 if (n >= MAXDIST) { 926 free(line); 927 fclose(f); 928 return nl_error(EINVAL, "netem: Distribution file too long."); 929 } 930 netem->qnm_dist.dist_data[n++] = x; 931 } 932 } 933 934 free(line); 935 936 netem->qnm_dist.dist_size = n; 937 netem->qnm_mask |= SCH_NETEM_ATTR_DIST; 938 939 fclose(f); 940 return 0; 941} 942 943/** @} */ 944 945static struct rtnl_qdisc_ops netem_ops = { 946 .qo_kind = "netem", 947 .qo_msg_parser = netem_msg_parser, 948 .qo_free_data = netem_free_data, 949 .qo_dump[NL_DUMP_BRIEF] = netem_dump_brief, 950 .qo_dump[NL_DUMP_FULL] = netem_dump_full, 951 .qo_get_opts = 0, 952 .qo_build_msg = netem_build_msg 953}; 954 955static void __init netem_init(void) 956{ 957 rtnl_qdisc_register(&netem_ops); 958} 959 960static void __exit netem_exit(void) 961{ 962 rtnl_qdisc_unregister(&netem_ops); 963} 964 965/** @} */ 966