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 0x001
00031 #define SCH_NETEM_ATTR_LIMIT 0x002
00032 #define SCH_NETEM_ATTR_LOSS 0x004
00033 #define SCH_NETEM_ATTR_GAP 0x008
00034 #define SCH_NETEM_ATTR_DUPLICATE 0x010
00035 #define SCH_NETEM_ATTR_JITTER 0x020
00036 #define SCH_NETEM_ATTR_DELAY_CORR 0x040
00037 #define SCH_NETEM_ATTR_LOSS_CORR 0x080
00038 #define SCH_NETEM_ATTR_DUP_CORR 0x100
00039 #define SCH_NETEM_ATTR_RO_PROB 0x200
00040 #define SCH_NETEM_ATTR_RO_CORR 0x400
00041
00042
00043 static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc)
00044 {
00045 return (struct rtnl_netem *) qdisc->q_subdata;
00046 }
00047
00048 static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc)
00049 {
00050 if (!qdisc->q_subdata)
00051 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_netem));
00052
00053 return netem_qdisc(qdisc);
00054 }
00055
00056 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
00057 [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) },
00058 [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) },
00059 };
00060
00061 static int netem_msg_parser(struct rtnl_qdisc *qdisc)
00062 {
00063 int len, err = 0;
00064 struct rtnl_netem *netem;
00065 struct tc_netem_qopt *opts;
00066
00067 if (qdisc->q_opts->d_size < sizeof(*opts))
00068 return nl_error(EINVAL, "Netem specific options size mismatch");
00069
00070 netem = netem_alloc(qdisc);
00071 if (!netem)
00072 return nl_errno(ENOMEM);
00073
00074 opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data;
00075 netem->qnm_latency = opts->latency;
00076 netem->qnm_limit = opts->limit;
00077 netem->qnm_loss = opts->loss;
00078 netem->qnm_gap = opts->gap;
00079 netem->qnm_duplicate = opts->duplicate;
00080 netem->qnm_jitter = opts->jitter;
00081
00082 netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
00083 SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
00084 SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
00085
00086 len = qdisc->q_opts->d_size - sizeof(*opts);
00087
00088 if (len > 0) {
00089 struct nlattr *tb[TCA_NETEM_MAX+1];
00090
00091 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
00092 qdisc->q_opts->d_data + sizeof(*opts),
00093 len, netem_policy);
00094 if (err < 0) {
00095 free(netem);
00096 return err;
00097 }
00098
00099 if (tb[TCA_NETEM_CORR]) {
00100 struct tc_netem_corr cor;
00101
00102 nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor));
00103 netem->qnm_corr.nmc_delay = cor.delay_corr;
00104 netem->qnm_corr.nmc_loss = cor.loss_corr;
00105 netem->qnm_corr.nmc_duplicate = cor.dup_corr;
00106
00107 netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
00108 SCH_NETEM_ATTR_LOSS_CORR |
00109 SCH_NETEM_ATTR_DELAY_CORR);
00110 }
00111
00112 if (tb[TCA_NETEM_REORDER]) {
00113 struct tc_netem_reorder ro;
00114
00115 nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro));
00116 netem->qnm_ro.nmro_probability = ro.probability;
00117 netem->qnm_ro.nmro_correlation = ro.correlation;
00118
00119 netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
00120 SCH_NETEM_ATTR_RO_CORR);
00121 }
00122 }
00123
00124 return 0;
00125 }
00126
00127 static void netem_free_data(struct rtnl_qdisc *qdisc)
00128 {
00129 free(qdisc->q_subdata);
00130 }
00131
00132 static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
00133 int line)
00134 {
00135 struct rtnl_netem *netem = netem_qdisc(qdisc);
00136
00137 if (netem)
00138 dp_dump(p, "limit %d", netem->qnm_limit);
00139
00140 return line;
00141 }
00142
00143 static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
00144 int line)
00145 {
00146 return line;
00147 }
00148
00149 static struct nl_msg *netem_get_opts(struct rtnl_qdisc *qdisc)
00150 {
00151 return NULL;
00152 }
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165 int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
00166 {
00167 struct rtnl_netem *netem;
00168
00169 netem = netem_alloc(qdisc);
00170 if (!netem)
00171 return nl_errno(ENOMEM);
00172
00173 netem->qnm_limit = limit;
00174 netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
00175
00176 return 0;
00177 }
00178
00179
00180
00181
00182
00183
00184 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
00185 {
00186 struct rtnl_netem *netem;
00187
00188 netem = netem_qdisc(qdisc);
00189 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT))
00190 return netem->qnm_limit;
00191 else
00192 return nl_errno(ENOENT);
00193 }
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208 int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
00209 {
00210 struct rtnl_netem *netem;
00211
00212 netem = netem_alloc(qdisc);
00213 if (!netem)
00214 return nl_errno(ENOMEM);
00215
00216 netem->qnm_gap = gap;
00217 netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
00218
00219 return 0;
00220 }
00221
00222
00223
00224
00225
00226
00227 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
00228 {
00229 struct rtnl_netem *netem;
00230
00231 netem = netem_qdisc(qdisc);
00232 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP))
00233 return netem->qnm_gap;
00234 else
00235 return nl_errno(ENOENT);
00236 }
00237
00238
00239
00240
00241
00242
00243
00244 int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
00245 {
00246 struct rtnl_netem *netem;
00247
00248 netem = netem_alloc(qdisc);
00249 if (!netem)
00250 return nl_errno(ENOMEM);
00251
00252 netem->qnm_ro.nmro_probability = prob;
00253 netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
00254
00255 return 0;
00256 }
00257
00258
00259
00260
00261
00262
00263 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
00264 {
00265 struct rtnl_netem *netem;
00266
00267 netem = netem_qdisc(qdisc);
00268 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB))
00269 return netem->qnm_ro.nmro_probability;
00270 else
00271 return nl_errno(ENOENT);
00272 }
00273
00274
00275
00276
00277
00278
00279
00280 int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
00281 {
00282 struct rtnl_netem *netem;
00283
00284 netem = netem_alloc(qdisc);
00285 if (!netem)
00286 return nl_errno(ENOMEM);
00287
00288 netem->qnm_ro.nmro_correlation = prob;
00289 netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
00290
00291 return 0;
00292 }
00293
00294
00295
00296
00297
00298
00299 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
00300 {
00301 struct rtnl_netem *netem;
00302
00303 netem = netem_qdisc(qdisc);
00304 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR))
00305 return netem->qnm_ro.nmro_correlation;
00306 else
00307 return nl_errno(ENOENT);
00308 }
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323 int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
00324 {
00325 struct rtnl_netem *netem;
00326
00327 netem = netem_alloc(qdisc);
00328 if (!netem)
00329 return nl_errno(ENOMEM);
00330
00331 netem->qnm_loss = prob;
00332 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
00333
00334 return 0;
00335 }
00336
00337
00338
00339
00340
00341
00342 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
00343 {
00344 struct rtnl_netem *netem;
00345
00346 netem = netem_qdisc(qdisc);
00347 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS))
00348 return netem->qnm_loss;
00349 else
00350 return nl_errno(ENOENT);
00351 }
00352
00353
00354
00355
00356
00357
00358
00359 int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
00360 {
00361 struct rtnl_netem *netem;
00362
00363 netem = netem_alloc(qdisc);
00364 if (!netem)
00365 return nl_errno(ENOMEM);
00366
00367 netem->qnm_corr.nmc_loss = prob;
00368 netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
00369
00370 return 0;
00371 }
00372
00373
00374
00375
00376
00377
00378 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
00379 {
00380 struct rtnl_netem *netem;
00381
00382 netem = netem_qdisc(qdisc);
00383 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR))
00384 return netem->qnm_corr.nmc_loss;
00385 else
00386 return nl_errno(ENOENT);
00387 }
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402 int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
00403 {
00404 struct rtnl_netem *netem;
00405
00406 netem = netem_alloc(qdisc);
00407 if (!netem)
00408 return nl_errno(ENOMEM);
00409
00410 netem->qnm_duplicate = prob;
00411 netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
00412
00413 return 0;
00414 }
00415
00416
00417
00418
00419
00420
00421 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
00422 {
00423 struct rtnl_netem *netem;
00424
00425 netem = netem_qdisc(qdisc);
00426 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE))
00427 return netem->qnm_duplicate;
00428 else
00429 return nl_errno(ENOENT);
00430 }
00431
00432
00433
00434
00435
00436
00437
00438 int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
00439 {
00440 struct rtnl_netem *netem;
00441
00442 netem = netem_alloc(qdisc);
00443 if (!netem)
00444 return nl_errno(ENOMEM);
00445
00446 netem->qnm_corr.nmc_duplicate = prob;
00447 netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
00448
00449 return 0;
00450 }
00451
00452
00453
00454
00455
00456
00457 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
00458 {
00459 struct rtnl_netem *netem;
00460
00461 netem = netem_qdisc(qdisc);
00462 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR))
00463 return netem->qnm_corr.nmc_duplicate;
00464 else
00465 return nl_errno(ENOENT);
00466 }
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481 int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
00482 {
00483 struct rtnl_netem *netem;
00484
00485 netem = netem_alloc(qdisc);
00486 if (!netem)
00487 return nl_errno(ENOMEM);
00488
00489 netem->qnm_latency = nl_us2ticks(delay);
00490 netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
00491
00492 return 0;
00493 }
00494
00495
00496
00497
00498
00499
00500 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
00501 {
00502 struct rtnl_netem *netem;
00503
00504 netem = netem_qdisc(qdisc);
00505 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY))
00506 return nl_ticks2us(netem->qnm_latency);
00507 else
00508 return nl_errno(ENOENT);
00509 }
00510
00511
00512
00513
00514
00515
00516
00517 int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
00518 {
00519 struct rtnl_netem *netem;
00520
00521 netem = netem_alloc(qdisc);
00522 if (!netem)
00523 return nl_errno(ENOMEM);
00524
00525 netem->qnm_jitter = nl_us2ticks(jitter);
00526 netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
00527
00528 return 0;
00529 }
00530
00531
00532
00533
00534
00535
00536 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
00537 {
00538 struct rtnl_netem *netem;
00539
00540 netem = netem_qdisc(qdisc);
00541 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER))
00542 return nl_ticks2us(netem->qnm_jitter);
00543 else
00544 return nl_errno(ENOENT);
00545 }
00546
00547
00548
00549
00550
00551
00552 int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
00553 {
00554 struct rtnl_netem *netem;
00555
00556 netem = netem_alloc(qdisc);
00557 if (!netem)
00558 return nl_errno(ENOMEM);
00559
00560 netem->qnm_corr.nmc_delay = prob;
00561 netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
00562
00563 return 0;
00564 }
00565
00566
00567
00568
00569
00570
00571 int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc)
00572 {
00573 struct rtnl_netem *netem;
00574
00575 netem = netem_qdisc(qdisc);
00576 if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR))
00577 return netem->qnm_corr.nmc_delay;
00578 else
00579 return nl_errno(ENOENT);
00580 }
00581
00582
00583
00584 static struct rtnl_qdisc_ops netem_ops = {
00585 .qo_kind = "netem",
00586 .qo_msg_parser = netem_msg_parser,
00587 .qo_free_data = netem_free_data,
00588 .qo_dump[NL_DUMP_BRIEF] = netem_dump_brief,
00589 .qo_dump[NL_DUMP_FULL] = netem_dump_full,
00590 .qo_get_opts = netem_get_opts,
00591 };
00592
00593 static void __init netem_init(void)
00594 {
00595 rtnl_qdisc_register(&netem_ops);
00596 }
00597
00598 static void __exit netem_exit(void)
00599 {
00600 rtnl_qdisc_unregister(&netem_ops);
00601 }
00602
00603