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