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_alloc(q);
00066 if (!tbf)
00067 return -NLE_NOMEM;
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 void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
00097 {
00098 double r, rbit, lim;
00099 char *ru, *rubit, *limu;
00100 struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
00101
00102 if (!tbf)
00103 return;
00104
00105 r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
00106 rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
00107 lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
00108
00109 nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
00110 r, ru, rbit, rubit, lim, limu);
00111 }
00112
00113 static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
00114 {
00115 struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
00116
00117 if (!tbf)
00118 return;
00119
00120 if (1) {
00121 char *bu, *cu;
00122 double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
00123 double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
00124 &cu);
00125
00126 nl_dump(p, "mpu %u rate-bucket-size %1.f%s "
00127 "rate-cell-size %.1f%s\n",
00128 tbf->qt_mpu, bs, bu, cl, cu);
00129
00130 }
00131
00132 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00133 char *pru, *prbu, *bsu, *clu;
00134 double pr, prb, bs, cl;
00135
00136 pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
00137 prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
00138 bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
00139 cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
00140 &clu);
00141
00142 nl_dump_line(p, " peak-rate %.2f%s/s (%.0f%s) "
00143 "bucket-size %.1f%s cell-size %.1f%s"
00144 "latency %.1f%s",
00145 pr, pru, prb, prbu, bs, bsu, cl, clu);
00146 }
00147 }
00148
00149 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
00150 {
00151 struct tc_tbf_qopt opts;
00152 struct rtnl_tbf *tbf;
00153 struct nl_msg *msg;
00154 uint32_t rtab[RTNL_TC_RTABLE_SIZE];
00155 uint32_t ptab[RTNL_TC_RTABLE_SIZE];
00156 int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
00157
00158 memset(&opts, 0, sizeof(opts));
00159
00160 tbf = tbf_qdisc(qdisc);
00161 if (!tbf)
00162 return NULL;
00163
00164 if (!(tbf->qt_mask & required) != required)
00165 return NULL;
00166
00167 opts.limit = tbf->qt_limit;
00168 opts.buffer = tbf->qt_rate_txtime;
00169 tbf->qt_rate.rs_mpu = tbf->qt_mpu;
00170 rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
00171
00172 rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8,
00173 1 << tbf->qt_rate.rs_cell_log,
00174 tbf->qt_rate.rs_rate);
00175
00176 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00177 opts.mtu = tbf->qt_peakrate_txtime;
00178 tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
00179 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
00180
00181 rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff,
00182 tbf->qt_mpu >> 8,
00183 1 << tbf->qt_peakrate.rs_cell_log,
00184 tbf->qt_peakrate.rs_rate);
00185 }
00186
00187 msg = nlmsg_alloc();
00188 if (!msg)
00189 goto nla_put_failure;
00190
00191 NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
00192 NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
00193
00194 if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
00195 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
00196
00197 return msg;
00198
00199 nla_put_failure:
00200 nlmsg_free(msg);
00201 return NULL;
00202 }
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215 int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
00216 {
00217 struct rtnl_tbf *tbf;
00218
00219 tbf = tbf_alloc(qdisc);
00220 if (!tbf)
00221 return -NLE_NOMEM;
00222
00223 tbf->qt_limit = limit;
00224 tbf->qt_mask |= TBF_ATTR_LIMIT;
00225
00226 return 0;
00227 }
00228
00229 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
00230 int bucket)
00231 {
00232 double limit;
00233
00234 limit = (double) spec->rs_rate * ((double) latency / 1000000.);
00235 limit += bucket;
00236
00237 return limit;
00238 }
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
00259 {
00260 struct rtnl_tbf *tbf;
00261 double limit, limit2;
00262
00263 tbf = tbf_alloc(qdisc);
00264 if (!tbf)
00265 return -NLE_NOMEM;
00266
00267 if (!(tbf->qt_mask & TBF_ATTR_RATE))
00268 return -NLE_MISSING_ATTR;
00269
00270 limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
00271
00272 if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00273 limit2 = calc_limit(&tbf->qt_peakrate, latency,
00274 tbf->qt_peakrate_bucket);
00275
00276 if (limit2 < limit)
00277 limit = limit2;
00278 }
00279
00280 return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
00281 }
00282
00283
00284
00285
00286
00287
00288 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
00289 {
00290 struct rtnl_tbf *tbf;
00291
00292 tbf = tbf_qdisc(qdisc);
00293 if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
00294 return tbf->qt_limit;
00295 else
00296 return -NLE_NOATTR;
00297 }
00298
00299
00300
00301
00302
00303
00304
00305 int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
00306 {
00307 struct rtnl_tbf *tbf;
00308
00309 tbf = tbf_alloc(qdisc);
00310 if (!tbf)
00311 return -NLE_NOMEM;
00312
00313 tbf->qt_mpu = mpu;
00314 tbf->qt_mask |= TBF_ATTR_MPU;
00315
00316 return 0;
00317 }
00318
00319
00320
00321
00322
00323
00324 int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
00325 {
00326 struct rtnl_tbf *tbf;
00327
00328 tbf = tbf_qdisc(qdisc);
00329 if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
00330 return tbf->qt_mpu;
00331 else
00332 return -NLE_NOATTR;
00333 }
00334
00335 static inline int calc_cell_log(int cell, int bucket)
00336 {
00337 if (cell > 0)
00338 cell = rtnl_tc_calc_cell_log(cell);
00339 else {
00340 cell = 0;
00341
00342 if (!bucket)
00343 bucket = 2047;
00344
00345 while ((bucket >> cell) > 255)
00346 cell++;
00347 }
00348
00349 return cell;
00350 }
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360 int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00361 int cell)
00362 {
00363 struct rtnl_tbf *tbf;
00364 int cell_log;
00365
00366 tbf = tbf_alloc(qdisc);
00367 if (!tbf)
00368 return -NLE_NOMEM;
00369
00370 cell_log = calc_cell_log(cell, bucket);
00371 if (cell_log < 0)
00372 return cell_log;
00373
00374 tbf->qt_rate.rs_rate = rate;
00375 tbf->qt_rate_bucket = bucket;
00376 tbf->qt_rate.rs_cell_log = cell_log;
00377 tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00378 tbf->qt_mask |= TBF_ATTR_RATE;
00379
00380 return 0;
00381 }
00382
00383
00384
00385
00386
00387
00388 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
00389 {
00390 struct rtnl_tbf *tbf;
00391
00392 tbf = tbf_qdisc(qdisc);
00393 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00394 return tbf->qt_rate.rs_rate;
00395 else
00396 return -1;
00397 }
00398
00399
00400
00401
00402
00403
00404 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
00405 {
00406 struct rtnl_tbf *tbf;
00407
00408 tbf = tbf_qdisc(qdisc);
00409 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00410 return tbf->qt_rate_bucket;
00411 else
00412 return -1;
00413 }
00414
00415
00416
00417
00418
00419
00420 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
00421 {
00422 struct rtnl_tbf *tbf;
00423
00424 tbf = tbf_qdisc(qdisc);
00425 if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00426 return (1 << tbf->qt_rate.rs_cell_log);
00427 else
00428 return -1;
00429 }
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00440 int cell)
00441 {
00442 struct rtnl_tbf *tbf;
00443 int cell_log;
00444
00445 tbf = tbf_alloc(qdisc);
00446 if (!tbf)
00447 return -NLE_NOMEM;
00448
00449 cell_log = calc_cell_log(cell, bucket);
00450 if (cell_log < 0)
00451 return cell_log;
00452
00453 tbf->qt_peakrate.rs_rate = rate;
00454 tbf->qt_peakrate_bucket = bucket;
00455 tbf->qt_peakrate.rs_cell_log = cell_log;
00456 tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00457
00458 tbf->qt_mask |= TBF_ATTR_PEAKRATE;
00459
00460 return 0;
00461 }
00462
00463
00464
00465
00466
00467
00468 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
00469 {
00470 struct rtnl_tbf *tbf;
00471
00472 tbf = tbf_qdisc(qdisc);
00473 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00474 return tbf->qt_peakrate.rs_rate;
00475 else
00476 return -1;
00477 }
00478
00479
00480
00481
00482
00483
00484 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
00485 {
00486 struct rtnl_tbf *tbf;
00487
00488 tbf = tbf_qdisc(qdisc);
00489 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00490 return tbf->qt_peakrate_bucket;
00491 else
00492 return -1;
00493 }
00494
00495
00496
00497
00498
00499
00500 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
00501 {
00502 struct rtnl_tbf *tbf;
00503
00504 tbf = tbf_qdisc(qdisc);
00505 if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00506 return (1 << tbf->qt_peakrate.rs_cell_log);
00507 else
00508 return -1;
00509 }
00510
00511
00512
00513 static struct rtnl_qdisc_ops tbf_qdisc_ops = {
00514 .qo_kind = "tbf",
00515 .qo_msg_parser = tbf_msg_parser,
00516 .qo_dump = {
00517 [NL_DUMP_LINE] = tbf_dump_line,
00518 [NL_DUMP_DETAILS] = tbf_dump_details,
00519 },
00520 .qo_get_opts = tbf_get_opts,
00521 };
00522
00523 static void __init tbf_init(void)
00524 {
00525 rtnl_qdisc_register(&tbf_qdisc_ops);
00526 }
00527
00528 static void __exit tbf_exit(void)
00529 {
00530 rtnl_qdisc_unregister(&tbf_qdisc_ops);
00531 }
00532
00533