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