00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <netlink-local.h>
00022 #include <netlink-tc.h>
00023 #include <netlink/netlink.h>
00024 #include <netlink/utils.h>
00025 #include <netlink/route/qdisc.h>
00026 #include <netlink/route/qdisc-modules.h>
00027 #include <netlink/route/sch/netem.h>
00028
00029
00030 #define SCH_NETEM_ATTR_LATENCY 0x0001
00031 #define SCH_NETEM_ATTR_LIMIT 0x0002
00032 #define SCH_NETEM_ATTR_LOSS 0x0004
00033 #define SCH_NETEM_ATTR_GAP 0x0008
00034 #define SCH_NETEM_ATTR_DUPLICATE 0x0010
00035 #define SCH_NETEM_ATTR_JITTER 0x0020
00036 #define SCH_NETEM_ATTR_DELAY_CORR 0x0040
00037 #define SCH_NETEM_ATTR_LOSS_CORR 0x0080
00038 #define SCH_NETEM_ATTR_DUP_CORR 0x0100
00039 #define SCH_NETEM_ATTR_RO_PROB 0x0200
00040 #define SCH_NETEM_ATTR_RO_CORR 0x0400
00041 #define SCH_NETEM_ATTR_CORRUPT_PROB 0x0800
00042 #define SCH_NETEM_ATTR_CORRUPT_CORR 0x1000
00043 #define SCH_NETEM_ATTR_DIST 0x2000
00044
00045
00046 static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc)
00047 {
00048 return (struct rtnl_netem *) qdisc->q_subdata;
00049 }
00050
00051 static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc)
00052 {
00053 if (!qdisc->q_subdata)
00054 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_netem));
00055
00056 return netem_qdisc(qdisc);
00057 }
00058
00059 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
00060 [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) },
00061 [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) },
00062 [TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) },
00063 };
00064
00065 static int netem_msg_parser(struct rtnl_qdisc *qdisc)
00066 {
00067 int len, err = 0;
00068 struct rtnl_netem *netem;
00069 struct tc_netem_qopt *opts;
00070
00071 if (qdisc->q_opts->d_size < sizeof(*opts))
00072 return -NLE_INVAL;
00073
00074 netem = netem_alloc(qdisc);
00075 if (!netem)
00076 return -NLE_NOMEM;
00077
00078 opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data;
00079 netem->qnm_latency = opts->latency;
00080 netem->qnm_limit = opts->limit;
00081 netem->qnm_loss = opts->loss;
00082 netem->qnm_gap = opts->gap;
00083 netem->qnm_duplicate = opts->duplicate;
00084 netem->qnm_jitter = opts->jitter;
00085
00086 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
00087 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
00088 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
00089
00090 len = qdisc->q_opts->d_size - sizeof(*opts);
00091
00092 if (len > 0) {
00093 struct nlattr *tb[TCA_NETEM_MAX+1];
00094
00095 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
00096 (qdisc->q_opts->d_data + sizeof(*opts)),
00097 len, netem_policy);
00098 if (err < 0) {
00099 free(netem);
00100 return err;
00101 }
00102
00103 if (tb[TCA_NETEM_CORR]) {
00104 struct tc_netem_corr cor;
00105
00106 nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor));
00107 netem->qnm_corr.nmc_delay = cor.delay_corr;
00108 netem->qnm_corr.nmc_loss = cor.loss_corr;
00109 netem->qnm_corr.nmc_duplicate = cor.dup_corr;
00110
00111 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
00112 SCH_NETEM_ATTR_LOSS_CORR |
00113 SCH_NETEM_ATTR_DUP_CORR);
00114 }
00115
00116 if (tb[TCA_NETEM_REORDER]) {
00117 struct tc_netem_reorder ro;
00118
00119 nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro));
00120 netem->qnm_ro.nmro_probability = ro.probability;
00121 netem->qnm_ro.nmro_correlation = ro.correlation;
00122
00123 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
00124 SCH_NETEM_ATTR_RO_CORR);
00125 }
00126
00127 if (tb[TCA_NETEM_CORRUPT]) {
00128 struct tc_netem_corrupt corrupt;
00129
00130 nla_memcpy(&corrupt, tb[TCA_NETEM_CORRUPT], sizeof(corrupt));
00131 netem->qnm_crpt.nmcr_probability = corrupt.probability;
00132 netem->qnm_crpt.nmcr_correlation = corrupt.correlation;
00133
00134 netem->qnm_mask |= (SCH_NETEM_ATTR_CORRUPT_PROB |
00135 SCH_NETEM_ATTR_CORRUPT_CORR);
00136 }
00137
00138
00139 netem->qnm_dist.dist_data = NULL;
00140 netem->qnm_dist.dist_size = 0;
00141 }
00142
00143 return 0;
00144 }
00145
00146 static void netem_free_data(struct rtnl_qdisc *qdisc)
00147 {
00148 struct rtnl_netem *netem;
00149
00150 if ( ! qdisc ) return;
00151
00152 netem = netem_qdisc(qdisc);
00153 if ( ! netem ) return;
00154
00155 if ( netem->qnm_dist.dist_data )
00156 free(netem->qnm_dist.dist_data);
00157
00158 netem = NULL;
00159
00160 free (qdisc->q_subdata);
00161 }
00162
00163 static void netem_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
00164 {
00165 struct rtnl_netem *netem = netem_qdisc(qdisc);
00166
00167 if (netem)
00168 nl_dump(p, "limit %d", netem->qnm_limit);
00169 }
00170
00171 int netem_build_msg(struct rtnl_qdisc *qdisc, struct nl_msg *msg)
00172 {
00173 int err = 0;
00174 struct tc_netem_qopt opts;
00175 struct tc_netem_corr cor;
00176 struct tc_netem_reorder reorder;
00177 struct tc_netem_corrupt corrupt;
00178 struct rtnl_netem *netem;
00179
00180 unsigned char set_correlation = 0, set_reorder = 0,
00181 set_corrupt = 0, set_dist = 0;
00182
00183 memset(&opts, 0, sizeof(opts));
00184 memset(&cor, 0, sizeof(cor));
00185 memset(&reorder, 0, sizeof(reorder));
00186 memset(&corrupt, 0, sizeof(corrupt));
00187
00188 netem = netem_qdisc(qdisc);
00189 if (!netem || !msg)
00190 return EFAULT;
00191
00192 msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
00193
00194 if ( netem->qnm_ro.nmro_probability != 0 ) {
00195 if (netem->qnm_latency == 0) {
00196 return -NLE_MISSING_ATTR;
00197 }
00198 if (netem->qnm_gap == 0) netem->qnm_gap = 1;
00199 }
00200 else if ( netem->qnm_gap ) {
00201 return -NLE_MISSING_ATTR;
00202 }
00203
00204 if ( netem->qnm_corr.nmc_delay != 0 ) {
00205 if ( netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
00206 return -NLE_MISSING_ATTR;
00207 }
00208 set_correlation = 1;
00209 }
00210
00211 if ( netem->qnm_corr.nmc_loss != 0 ) {
00212 if ( netem->qnm_loss == 0 ) {
00213 return -NLE_MISSING_ATTR;
00214 }
00215 set_correlation = 1;
00216 }
00217
00218 if ( netem->qnm_corr.nmc_duplicate != 0 ) {
00219 if ( netem->qnm_duplicate == 0 ) {
00220 return -NLE_MISSING_ATTR;
00221 }
00222 set_correlation = 1;
00223 }
00224
00225 if ( netem->qnm_ro.nmro_probability != 0 ) set_reorder = 1;
00226 else if ( netem->qnm_ro.nmro_correlation != 0 ) {
00227 return -NLE_MISSING_ATTR;
00228 }
00229
00230 if ( netem->qnm_crpt.nmcr_probability != 0 ) set_corrupt = 1;
00231 else if ( netem->qnm_crpt.nmcr_correlation != 0 ) {
00232 return -NLE_MISSING_ATTR;
00233 }
00234
00235 if ( netem->qnm_dist.dist_data && netem->qnm_dist.dist_size ) {
00236 if (netem->qnm_latency == 0 || netem->qnm_jitter == 0) {
00237 return -NLE_MISSING_ATTR;
00238 }
00239 else {
00240
00241 int new_msg_len = msg->nm_size + netem->qnm_dist.dist_size *
00242 sizeof(netem->qnm_dist.dist_data[0]);
00243
00244 msg->nm_nlh = (struct nlmsghdr *) realloc(msg->nm_nlh, new_msg_len);
00245 if ( msg->nm_nlh == NULL )
00246 return -NLE_NOMEM;
00247 msg->nm_size = new_msg_len;
00248 set_dist = 1;
00249 }
00250 }
00251
00252 opts.latency = netem->qnm_latency;
00253 opts.limit = netem->qnm_limit ? netem->qnm_limit : 1000;
00254 opts.loss = netem->qnm_loss;
00255 opts.gap = netem->qnm_gap;
00256 opts.duplicate = netem->qnm_duplicate;
00257 opts.jitter = netem->qnm_jitter;
00258
00259 NLA_PUT(msg, TCA_OPTIONS, sizeof(opts), &opts);
00260
00261 if ( set_correlation ) {
00262 cor.delay_corr = netem->qnm_corr.nmc_delay;
00263 cor.loss_corr = netem->qnm_corr.nmc_loss;
00264 cor.dup_corr = netem->qnm_corr.nmc_duplicate;
00265
00266 NLA_PUT(msg, TCA_NETEM_CORR, sizeof(cor), &cor);
00267 }
00268
00269 if ( set_reorder ) {
00270 reorder.probability = netem->qnm_ro.nmro_probability;
00271 reorder.correlation = netem->qnm_ro.nmro_correlation;
00272
00273 NLA_PUT(msg, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
00274 }
00275
00276 if ( set_corrupt ) {
00277 corrupt.probability = netem->qnm_crpt.nmcr_probability;
00278 corrupt.correlation = netem->qnm_crpt.nmcr_correlation;
00279
00280 NLA_PUT(msg, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
00281 }
00282
00283 if ( set_dist ) {
00284 NLA_PUT(msg, TCA_NETEM_DELAY_DIST,
00285 netem->qnm_dist.dist_size * sizeof(netem->qnm_dist.dist_data[0]),
00286 netem->qnm_dist.dist_data);
00287 }
00288
00289
00290
00291
00292
00293 struct nlattr* head = (struct nlattr *)(NLMSG_DATA(msg->nm_nlh) +
00294 NLMSG_LENGTH(sizeof(struct tcmsg)) - NLMSG_ALIGNTO);
00295
00296 struct nlattr* tail = (struct nlattr *)(((void *) (msg->nm_nlh)) +
00297 NLMSG_ALIGN(msg->nm_nlh->nlmsg_len));
00298
00299 int old_len = head->nla_len;
00300 head->nla_len = (void *)tail - (void *)head;
00301 msg->nm_nlh->nlmsg_len += (head->nla_len - old_len);
00302
00303 return err;
00304 nla_put_failure:
00305 return -NLE_MSGSIZE;
00306 }
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
00320 {
00321 struct rtnl_netem *netem;
00322
00323 netem = netem_alloc(qdisc);
00324 if (!netem)
00325 return -NLE_NOMEM;
00326
00327 netem->qnm_limit = limit;
00328 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
00329
00330 return 0;
00331 }
00332
00333
00334
00335
00336
00337
00338 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
00339 {
00340 struct rtnl_netem *netem;
00341
00342 netem = netem_qdisc(qdisc);
00343 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT))
00344 return netem->qnm_limit;
00345 else
00346 return -NLE_NOATTR;
00347 }
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362 int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
00363 {
00364 struct rtnl_netem *netem;
00365
00366 netem = netem_alloc(qdisc);
00367 if (!netem)
00368 return -NLE_NOMEM;
00369
00370 netem->qnm_gap = gap;
00371 netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
00372
00373 return 0;
00374 }
00375
00376
00377
00378
00379
00380
00381 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
00382 {
00383 struct rtnl_netem *netem;
00384
00385 netem = netem_qdisc(qdisc);
00386 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP))
00387 return netem->qnm_gap;
00388 else
00389 return -NLE_NOATTR;
00390 }
00391
00392
00393
00394
00395
00396
00397
00398 int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
00399 {
00400 struct rtnl_netem *netem;
00401
00402 netem = netem_alloc(qdisc);
00403 if (!netem)
00404 return -NLE_NOMEM;
00405
00406 netem->qnm_ro.nmro_probability = prob;
00407 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
00408
00409 return 0;
00410 }
00411
00412
00413
00414
00415
00416
00417 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
00418 {
00419 struct rtnl_netem *netem;
00420
00421 netem = netem_qdisc(qdisc);
00422 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB))
00423 return netem->qnm_ro.nmro_probability;
00424 else
00425 return -NLE_NOATTR;
00426 }
00427
00428
00429
00430
00431
00432
00433
00434 int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
00435 {
00436 struct rtnl_netem *netem;
00437
00438 netem = netem_alloc(qdisc);
00439 if (!netem)
00440 return -NLE_NOMEM;
00441
00442 netem->qnm_ro.nmro_correlation = prob;
00443 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
00444
00445 return 0;
00446 }
00447
00448
00449
00450
00451
00452
00453 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
00454 {
00455 struct rtnl_netem *netem;
00456
00457 netem = netem_qdisc(qdisc);
00458 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR))
00459 return netem->qnm_ro.nmro_correlation;
00460 else
00461 return -NLE_NOATTR;
00462 }
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477 int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
00478 {
00479 struct rtnl_netem *netem;
00480
00481 netem = netem_alloc(qdisc);
00482 if (!netem)
00483 return -NLE_NOMEM;
00484
00485 netem->qnm_crpt.nmcr_probability = prob;
00486 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
00487
00488 return 0;
00489 }
00490
00491
00492
00493
00494
00495
00496 int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
00497 {
00498 struct rtnl_netem *netem;
00499
00500 netem = netem_qdisc(qdisc);
00501 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB))
00502 return netem->qnm_crpt.nmcr_probability;
00503 else
00504 return -NLE_NOATTR;
00505 }
00506
00507
00508
00509
00510
00511
00512
00513 int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
00514 {
00515 struct rtnl_netem *netem;
00516
00517 netem = netem_alloc(qdisc);
00518 if (!netem)
00519 return -NLE_NOMEM;
00520
00521 netem->qnm_crpt.nmcr_correlation = prob;
00522 netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
00523
00524 return 0;
00525 }
00526
00527
00528
00529
00530
00531
00532 int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
00533 {
00534 struct rtnl_netem *netem;
00535
00536 netem = netem_qdisc(qdisc);
00537 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR))
00538 return netem->qnm_crpt.nmcr_correlation;
00539 else
00540 return -NLE_NOATTR;
00541 }
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556 int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
00557 {
00558 struct rtnl_netem *netem;
00559
00560 netem = netem_alloc(qdisc);
00561 if (!netem)
00562 return -NLE_NOMEM;
00563
00564 netem->qnm_loss = prob;
00565 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
00566
00567 return 0;
00568 }
00569
00570
00571
00572
00573
00574
00575 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
00576 {
00577 struct rtnl_netem *netem;
00578
00579 netem = netem_qdisc(qdisc);
00580 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS))
00581 return netem->qnm_loss;
00582 else
00583 return -NLE_NOATTR;
00584 }
00585
00586
00587
00588
00589
00590
00591
00592 int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
00593 {
00594 struct rtnl_netem *netem;
00595
00596 netem = netem_alloc(qdisc);
00597 if (!netem)
00598 return -NLE_NOMEM;
00599
00600 netem->qnm_corr.nmc_loss = prob;
00601 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
00602
00603 return 0;
00604 }
00605
00606
00607
00608
00609
00610
00611 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
00612 {
00613 struct rtnl_netem *netem;
00614
00615 netem = netem_qdisc(qdisc);
00616 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR))
00617 return netem->qnm_corr.nmc_loss;
00618 else
00619 return -NLE_NOATTR;
00620 }
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635 int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
00636 {
00637 struct rtnl_netem *netem;
00638
00639 netem = netem_alloc(qdisc);
00640 if (!netem)
00641 return -NLE_NOMEM;
00642
00643 netem->qnm_duplicate = prob;
00644 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
00645
00646 return 0;
00647 }
00648
00649
00650
00651
00652
00653
00654 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
00655 {
00656 struct rtnl_netem *netem;
00657
00658 netem = netem_qdisc(qdisc);
00659 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE))
00660 return netem->qnm_duplicate;
00661 else
00662 return -NLE_NOATTR;
00663 }
00664
00665
00666
00667
00668
00669
00670
00671 int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
00672 {
00673 struct rtnl_netem *netem;
00674
00675 netem = netem_alloc(qdisc);
00676 if (!netem)
00677 return -NLE_NOMEM;
00678
00679 netem->qnm_corr.nmc_duplicate = prob;
00680 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
00681
00682 return 0;
00683 }
00684
00685
00686
00687
00688
00689
00690 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
00691 {
00692 struct rtnl_netem *netem;
00693
00694 netem = netem_qdisc(qdisc);
00695 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR))
00696 return netem->qnm_corr.nmc_duplicate;
00697 else
00698 return -NLE_NOATTR;
00699 }
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714 int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
00715 {
00716 struct rtnl_netem *netem;
00717
00718 netem = netem_alloc(qdisc);
00719 if (!netem)
00720 return -NLE_NOMEM;
00721
00722 netem->qnm_latency = nl_us2ticks(delay);
00723 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
00724
00725 return 0;
00726 }
00727
00728
00729
00730
00731
00732
00733 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
00734 {
00735 struct rtnl_netem *netem;
00736
00737 netem = netem_qdisc(qdisc);
00738 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY))
00739 return nl_ticks2us(netem->qnm_latency);
00740 else
00741 return -NLE_NOATTR;
00742 }
00743
00744
00745
00746
00747
00748
00749
00750 int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
00751 {
00752 struct rtnl_netem *netem;
00753
00754 netem = netem_alloc(qdisc);
00755 if (!netem)
00756 return -NLE_NOMEM;
00757
00758 netem->qnm_jitter = nl_us2ticks(jitter);
00759 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
00760
00761 return 0;
00762 }
00763
00764
00765
00766
00767
00768
00769 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
00770 {
00771 struct rtnl_netem *netem;
00772
00773 netem = netem_qdisc(qdisc);
00774 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER))
00775 return nl_ticks2us(netem->qnm_jitter);
00776 else
00777 return -NLE_NOATTR;
00778 }
00779
00780
00781
00782
00783
00784
00785 int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
00786 {
00787 struct rtnl_netem *netem;
00788
00789 netem = netem_alloc(qdisc);
00790 if (!netem)
00791 return -NLE_NOMEM;
00792
00793 netem->qnm_corr.nmc_delay = prob;
00794 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
00795
00796 return 0;
00797 }
00798
00799
00800
00801
00802
00803
00804 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
00805 {
00806 struct rtnl_netem *netem;
00807
00808 netem = netem_qdisc(qdisc);
00809 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR))
00810 return netem->qnm_corr.nmc_delay;
00811 else
00812 return -NLE_NOATTR;
00813 }
00814
00815
00816
00817
00818
00819
00820 int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
00821 {
00822 struct rtnl_netem *netem;
00823
00824 netem = netem_qdisc(qdisc);
00825 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST))
00826 return netem->qnm_dist.dist_size;
00827 else
00828 return -NLE_NOATTR;
00829 }
00830
00831
00832
00833
00834
00835
00836
00837 int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_ptr)
00838 {
00839 struct rtnl_netem *netem;
00840
00841 netem = netem_qdisc(qdisc);
00842 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) {
00843 *dist_ptr = netem->qnm_dist.dist_data;
00844 return 0;
00845 }
00846 else
00847 return -NLE_NOATTR;
00848 }
00849
00850
00851
00852
00853
00854
00855
00856 int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
00857 struct rtnl_netem *netem;
00858
00859 netem = netem_alloc(qdisc);
00860 if (!netem)
00861 return -NLE_NOMEM;
00862
00863 FILE *f = NULL;
00864 int i, n = 0;
00865 size_t len = 2048;
00866 char *line;
00867 char name[NAME_MAX];
00868 char dist_suffix[] = ".dist";
00869
00870
00871 char *test_suffix = strstr(dist_type, dist_suffix);
00872 if (test_suffix != NULL && strlen(test_suffix) == 5)
00873 strcpy(dist_suffix, "");
00874
00875
00876 char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" };
00877
00878 for (i = 0; i < sizeof(test_path) && f == NULL; i++) {
00879 snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
00880 f = fopen(name, "r");
00881 }
00882
00883 if ( f == NULL )
00884 return -nl_syserr2nlerr(errno);
00885
00886 netem->qnm_dist.dist_data = (int16_t *) calloc (MAXDIST, sizeof(int16_t));
00887
00888 line = (char *) calloc (sizeof(char), len + 1);
00889
00890 while (getline(&line, &len, f) != -1) {
00891 char *p, *endp;
00892
00893 if (*line == '\n' || *line == '#')
00894 continue;
00895
00896 for (p = line; ; p = endp) {
00897 long x = strtol(p, &endp, 0);
00898 if (endp == p) break;
00899
00900 if (n >= MAXDIST) {
00901 free(line);
00902 fclose(f);
00903 return -NLE_INVAL;
00904 }
00905 netem->qnm_dist.dist_data[n++] = x;
00906 }
00907 }
00908
00909 free(line);
00910
00911 netem->qnm_dist.dist_size = n;
00912 netem->qnm_mask |= SCH_NETEM_ATTR_DIST;
00913
00914 fclose(f);
00915 return 0;
00916 }
00917
00918
00919
00920 static struct rtnl_qdisc_ops netem_ops = {
00921 .qo_kind = "netem",
00922 .qo_msg_parser = netem_msg_parser,
00923 .qo_free_data = netem_free_data,
00924 .qo_dump[NL_DUMP_LINE] = netem_dump_line,
00925 .qo_get_opts = 0,
00926 .qo_build_msg = netem_build_msg
00927 };
00928
00929 static void __init netem_init(void)
00930 {
00931 rtnl_qdisc_register(&netem_ops);
00932 }
00933
00934 static void __exit netem_exit(void)
00935 {
00936 rtnl_qdisc_unregister(&netem_ops);
00937 }
00938
00939