00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <netlink-local.h>
00019 #include <netlink-tc.h>
00020 #include <netlink/netlink.h>
00021 #include <netlink/cache.h>
00022 #include <netlink/utils.h>
00023 #include <netlink/route/tc.h>
00024 #include <netlink/route/qdisc.h>
00025 #include <netlink/route/qdisc-modules.h>
00026 #include <netlink/route/class.h>
00027 #include <netlink/route/class-modules.h>
00028 #include <netlink/route/link.h>
00029 #include <netlink/route/sch/tbf.h>
00030
00031
00032 #define TBF_ATTR_LIMIT 0x01
00033 #define TBF_ATTR_RATE 0x02
00034 #define TBF_ATTR_PEAKRATE 0x10
00035 #define TBF_ATTR_MPU 0x80
00036
00037
00038 static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc)
00039 {
00040 return (struct rtnl_tbf *) qdisc->q_subdata;
00041 }
00042
00043 static inline struct rtnl_tbf *tbf_alloc(struct rtnl_qdisc *qdisc)
00044 {
00045 if (!qdisc->q_subdata)
00046 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_tbf));
00047
00048 return tbf_qdisc(qdisc);
00049 }
00050
00051 static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = {
00052 [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) },
00053 };
00054
00055 static int tbf_msg_parser(struct rtnl_qdisc *q)
00056 {
00057 int err;
00058 struct nlattr *tb[TCA_TBF_MAX + 1];
00059 struct rtnl_tbf *tbf;
00060
00061 err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy);
00062 if (err < 0)
00063 return err;
00064
00065 tbf = tbf_qdisc(q);
00066 if (!tbf)
00067 return nl_errno(ENOMEM);
00068
00069 if (tb[TCA_TBF_PARMS]) {
00070 struct tc_tbf_qopt opts;
00071 int bufsize;
00072
00073 nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
00074 tbf->qt_limit = opts.limit;
00075 tbf->qt_mpu = opts.rate.mpu;
00076
00077 rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
00078 tbf->qt_rate_txtime = opts.buffer;
00079 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
00080 opts.rate.rate);
00081 tbf->qt_rate_bucket = bufsize;
00082
00083 rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
00084 tbf->qt_peakrate_txtime = opts.mtu;
00085 bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu),
00086 opts.peakrate.rate);
00087 tbf->qt_peakrate_bucket = bufsize;
00088
00089 tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE |
00090 TBF_ATTR_PEAKRATE);
00091 }
00092
00093 return 0;
00094 }
00095
00096 static int tbf_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
00097 int line)
00098 {
00099 double r, rbit, lim;
00100 char *ru, *rubit, *limu;
00101 struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
00102
00103 if (!tbf)
00104 goto ignore;
00105
00106 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
00107 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
00108 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
00109
00110 dp_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
00111 r, ru, rbit, rubit, lim, limu);
00112
00113 ignore:
00114 return line;
00115 }
00116
00117 static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
00118 int line)
00119 {
00120 struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
00121
00122 if (!tbf)
00123 goto ignore;
00124
00125 if (1) {
00126 char *bu, *cu;
00127 double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
00128 double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
00129 &cu);
00130
00131 dp_dump(p, "mpu %u rate-bucket-size %1.f%s "
00132 "rate-cell-size %.1f%s\n",
00133 tbf->qt_mpu, bs, bu, cl, cu);
00134
00135 }
00136
00137 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00138 char *pru, *prbu, *bsu, *clu;
00139 double pr, prb, bs, cl;
00140
00141 pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
00142 prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
00143 bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
00144 cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
00145 &clu);
00146
00147 dp_dump_line(p, line++, " peak-rate %.2f%s/s (%.0f%s) "
00148 "bucket-size %.1f%s cell-size %.1f%s",
00149 "latency %.1f%s",
00150 pr, pru, prb, prbu, bs, bsu, cl, clu);
00151 }
00152
00153 ignore:
00154 return line;
00155 }
00156
00157 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
00158 {
00159 struct tc_tbf_qopt opts;
00160 struct rtnl_tbf *tbf;
00161 struct nl_msg *msg;
00162 uint32_t rtab[RTNL_TC_RTABLE_SIZE];
00163 uint32_t ptab[RTNL_TC_RTABLE_SIZE];
00164 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
00165
00166 memset(&opts, 0, sizeof(opts));
00167
00168 tbf = tbf_qdisc(qdisc);
00169 if (!tbf)
00170 return NULL;
00171
00172 if (!(tbf->qt_mask & required) != required)
00173 return NULL;
00174
00175 opts.limit = tbf->qt_limit;
00176 opts.buffer = tbf->qt_rate_txtime;
00177 tbf->qt_rate.rs_mpu = tbf->qt_mpu;
00178 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
00179
00180 rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8,
00181 1 << tbf->qt_rate.rs_cell_log,
00182 tbf->qt_rate.rs_rate);
00183
00184 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00185 opts.mtu = tbf->qt_peakrate_txtime;
00186 tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
00187 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
00188
00189 rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff,
00190 tbf->qt_mpu >> 8,
00191 1 << tbf->qt_peakrate.rs_cell_log,
00192 tbf->qt_peakrate.rs_rate);
00193 }
00194
00195 msg = nlmsg_alloc();
00196 if (!msg)
00197 goto nla_put_failure;
00198
00199 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
00200 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
00201
00202 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
00203 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
00204
00205 return msg;
00206
00207 nla_put_failure:
00208 nlmsg_free(msg);
00209 return NULL;
00210 }
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223 int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
00224 {
00225 struct rtnl_tbf *tbf;
00226
00227 tbf = tbf_alloc(qdisc);
00228 if (!tbf)
00229 return nl_errno(ENOMEM);
00230
00231 tbf->qt_limit = limit;
00232 tbf->qt_mask |= TBF_ATTR_LIMIT;
00233
00234 return 0;
00235 }
00236
00237 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
00238 int bucket)
00239 {
00240 double limit;
00241
00242 limit = (double) spec->rs_rate * ((double) latency / 1000000.);
00243 limit += bucket;
00244
00245 return limit;
00246 }
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
00267 {
00268 struct rtnl_tbf *tbf;
00269 double limit, limit2;
00270
00271 tbf = tbf_alloc(qdisc);
00272 if (!tbf)
00273 return nl_errno(ENOMEM);
00274
00275 if (!(tbf->qt_mask & TBF_ATTR_RATE))
00276 return nl_error(EINVAL, "The rate must be specified before "
00277 "limit can be calculated based on latency.");
00278
00279 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
00280
00281 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00282 limit2 = calc_limit(&tbf->qt_peakrate, latency,
00283 tbf->qt_peakrate_bucket);
00284
00285 if (limit2 < limit)
00286 limit = limit2;
00287 }
00288
00289 return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
00290 }
00291
00292
00293
00294
00295
00296
00297 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
00298 {
00299 struct rtnl_tbf *tbf;
00300
00301 tbf = tbf_qdisc(qdisc);
00302 if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
00303 return tbf->qt_limit;
00304 return
00305 nl_errno(ENOENT);
00306 }
00307
00308
00309
00310
00311
00312
00313
00314 int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
00315 {
00316 struct rtnl_tbf *tbf;
00317
00318 tbf = tbf_alloc(qdisc);
00319 if (!tbf)
00320 return nl_errno(ENOMEM);
00321
00322 tbf->qt_mpu = mpu;
00323 tbf->qt_mask |= TBF_ATTR_MPU;
00324
00325 return 0;
00326 }
00327
00328
00329
00330
00331
00332
00333 int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
00334 {
00335 struct rtnl_tbf *tbf;
00336
00337 tbf = tbf_qdisc(qdisc);
00338 if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
00339 return tbf->qt_mpu;
00340 return
00341 nl_errno(ENOENT);
00342 }
00343
00344 static inline int calc_cell_log(int cell, int bucket)
00345 {
00346 if (cell > 0)
00347 cell = rtnl_tc_calc_cell_log(cell);
00348 else {
00349 cell = 0;
00350
00351 if (!bucket)
00352 bucket = 2047;
00353
00354 while ((bucket >> cell) > 255)
00355 cell++;
00356 }
00357
00358 return cell;
00359 }
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369 int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00370 int cell)
00371 {
00372 struct rtnl_tbf *tbf;
00373 int cell_log;
00374
00375 tbf = tbf_alloc(qdisc);
00376 if (!tbf)
00377 return nl_errno(ENOMEM);
00378
00379 cell_log = calc_cell_log(cell, bucket);
00380 if (cell_log < 0)
00381 return cell_log;
00382
00383 tbf->qt_rate.rs_rate = rate;
00384 tbf->qt_rate_bucket = bucket;
00385 tbf->qt_rate.rs_cell_log = cell_log;
00386 tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00387 tbf->qt_mask |= TBF_ATTR_RATE;
00388
00389 return 0;
00390 }
00391
00392
00393
00394
00395
00396
00397 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
00398 {
00399 struct rtnl_tbf *tbf;
00400
00401 tbf = tbf_qdisc(qdisc);
00402 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00403 return tbf->qt_rate.rs_rate;
00404 else
00405 return -1;
00406 }
00407
00408
00409
00410
00411
00412
00413 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
00414 {
00415 struct rtnl_tbf *tbf;
00416
00417 tbf = tbf_qdisc(qdisc);
00418 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00419 return tbf->qt_rate_bucket;
00420 else
00421 return -1;
00422 }
00423
00424
00425
00426
00427
00428
00429 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
00430 {
00431 struct rtnl_tbf *tbf;
00432
00433 tbf = tbf_qdisc(qdisc);
00434 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00435 return (1 << tbf->qt_rate.rs_cell_log);
00436 else
00437 return -1;
00438 }
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00449 int cell)
00450 {
00451 struct rtnl_tbf *tbf;
00452 int cell_log;
00453
00454 tbf = tbf_alloc(qdisc);
00455 if (!tbf)
00456 return nl_errno(ENOMEM);
00457
00458 cell_log = calc_cell_log(cell, bucket);
00459 if (cell_log < 0)
00460 return cell_log;
00461
00462 tbf->qt_peakrate.rs_rate = rate;
00463 tbf->qt_peakrate_bucket = bucket;
00464 tbf->qt_peakrate.rs_cell_log = cell_log;
00465 tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00466
00467 tbf->qt_mask |= TBF_ATTR_PEAKRATE;
00468
00469 return 0;
00470 }
00471
00472
00473
00474
00475
00476
00477 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
00478 {
00479 struct rtnl_tbf *tbf;
00480
00481 tbf = tbf_qdisc(qdisc);
00482 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00483 return tbf->qt_peakrate.rs_rate;
00484 else
00485 return -1;
00486 }
00487
00488
00489
00490
00491
00492
00493 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
00494 {
00495 struct rtnl_tbf *tbf;
00496
00497 tbf = tbf_qdisc(qdisc);
00498 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00499 return tbf->qt_peakrate_bucket;
00500 else
00501 return -1;
00502 }
00503
00504
00505
00506
00507
00508
00509 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
00510 {
00511 struct rtnl_tbf *tbf;
00512
00513 tbf = tbf_qdisc(qdisc);
00514 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00515 return (1 << tbf->qt_peakrate.rs_cell_log);
00516 else
00517 return -1;
00518 }
00519
00520
00521
00522 static struct rtnl_qdisc_ops tbf_qdisc_ops = {
00523 .qo_kind = "tbf",
00524 .qo_msg_parser = tbf_msg_parser,
00525 .qo_dump[NL_DUMP_BRIEF] = tbf_dump_brief,
00526 .qo_dump[NL_DUMP_FULL] = tbf_dump_full,
00527 .qo_get_opts = tbf_get_opts,
00528 };
00529
00530 static void __init tbf_init(void)
00531 {
00532 rtnl_qdisc_register(&tbf_qdisc_ops);
00533 }
00534
00535 static void __exit tbf_exit(void)
00536 {
00537 rtnl_qdisc_unregister(&tbf_qdisc_ops);
00538 }
00539
00540