libnl 2.0

/tmp/buildd/libnl2-2.0/lib/route/sch/tbf.c

00001 /*
00002  * lib/route/sch/tbf.c          TBF Qdisc
00003  *
00004  *      This library is free software; you can redistribute it and/or
00005  *      modify it under the terms of the GNU Lesser General Public
00006  *      License as published by the Free Software Foundation version 2.1
00007  *      of the License.
00008  *
00009  * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
00010  */
00011 
00012 /**
00013  * @ingroup qdisc_api
00014  * @defgroup tbf Token Bucket Filter (TBF)
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 /** @cond SKIP */
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 /** @endcond */
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_free_data(struct rtnl_qdisc *qdisc)
00097 {
00098         free(qdisc->q_subdata);
00099 }
00100 
00101 static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
00102 {
00103         double r, rbit, lim;
00104         char *ru, *rubit, *limu;
00105         struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
00106 
00107         if (!tbf)
00108                 return;
00109 
00110         r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
00111         rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
00112         lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
00113 
00114         nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
00115                 r, ru, rbit, rubit, lim, limu);
00116 }
00117 
00118 static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
00119 {
00120         struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
00121 
00122         if (!tbf)
00123                 return;
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                 nl_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                 nl_dump_line(p, "    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 
00154 static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
00155 {
00156         struct tc_tbf_qopt opts;
00157         struct rtnl_tbf *tbf;
00158         struct nl_msg *msg;
00159         uint32_t rtab[RTNL_TC_RTABLE_SIZE];
00160         uint32_t ptab[RTNL_TC_RTABLE_SIZE];
00161         int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
00162 
00163         memset(&opts, 0, sizeof(opts));
00164 
00165         tbf = tbf_qdisc(qdisc);
00166         if (!tbf)
00167                 return NULL;
00168 
00169         if (!(tbf->qt_mask & required) != required)
00170                 return NULL;
00171 
00172         opts.limit = tbf->qt_limit;
00173         opts.buffer = tbf->qt_rate_txtime;
00174         tbf->qt_rate.rs_mpu = tbf->qt_mpu;
00175         rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
00176 
00177         rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8,
00178                                  1 << tbf->qt_rate.rs_cell_log,
00179                                  tbf->qt_rate.rs_rate);
00180 
00181         if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00182                 opts.mtu = tbf->qt_peakrate_txtime;
00183                 tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
00184                 rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
00185 
00186                 rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff,
00187                                          tbf->qt_mpu >> 8,
00188                                          1 << tbf->qt_peakrate.rs_cell_log,
00189                                          tbf->qt_peakrate.rs_rate);
00190         }
00191 
00192         msg = nlmsg_alloc();
00193         if (!msg)
00194                 goto nla_put_failure;
00195 
00196         NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
00197         NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
00198 
00199         if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
00200                 NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
00201 
00202         return msg;
00203 
00204 nla_put_failure:
00205         nlmsg_free(msg);
00206         return NULL;
00207 }
00208 
00209 /**
00210  * @name Attribute Access
00211  * @{
00212  */
00213 
00214 /**
00215  * Set limit of TBF qdisc.
00216  * @arg qdisc           TBF qdisc to be modified.
00217  * @arg limit           New limit in bytes.
00218  * @return 0 on success or a negative error code.
00219  */
00220 int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
00221 {
00222         struct rtnl_tbf *tbf;
00223         
00224         tbf = tbf_alloc(qdisc);
00225         if (!tbf)
00226                 return -NLE_NOMEM;
00227 
00228         tbf->qt_limit = limit;
00229         tbf->qt_mask |= TBF_ATTR_LIMIT;
00230 
00231         return 0;
00232 }
00233 
00234 static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
00235                                 int bucket)
00236 {
00237         double limit;
00238 
00239         limit = (double) spec->rs_rate * ((double) latency / 1000000.);
00240         limit += bucket;
00241 
00242         return limit;
00243 }
00244 
00245 /**
00246  * Set limit of TBF qdisc by latency.
00247  * @arg qdisc           TBF qdisc to be modified.
00248  * @arg latency         Latency in micro seconds.
00249  *
00250  * Calculates and sets the limit based on the desired latency and the
00251  * configured rate and peak rate. In order for this operation to succeed,
00252  * the rate and if required the peak rate must have been set in advance.
00253  *
00254  * @f[
00255  *   limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n
00256  * @f]
00257  * @f[
00258  *   limit = min(limit_{rate},limit_{peak})
00259  * @f]
00260  * 
00261  * @return 0 on success or a negative error code.
00262  */
00263 int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
00264 {
00265         struct rtnl_tbf *tbf;
00266         double limit, limit2;
00267 
00268         tbf = tbf_alloc(qdisc);
00269         if (!tbf)
00270                 return -NLE_NOMEM;
00271 
00272         if (!(tbf->qt_mask & TBF_ATTR_RATE))
00273                 return -NLE_MISSING_ATTR;
00274 
00275         limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
00276 
00277         if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
00278                 limit2 = calc_limit(&tbf->qt_peakrate, latency,
00279                                     tbf->qt_peakrate_bucket);
00280 
00281                 if (limit2 < limit)
00282                         limit = limit2;
00283         }
00284 
00285         return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
00286 }
00287 
00288 /**
00289  * Get limit of TBF qdisc.
00290  * @arg qdisc           TBF qdisc.
00291  * @return Limit in bytes or a negative error code.
00292  */
00293 int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
00294 {
00295         struct rtnl_tbf *tbf;
00296         
00297         tbf = tbf_qdisc(qdisc);
00298         if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
00299                 return tbf->qt_limit;
00300         else
00301                 return -NLE_NOATTR;
00302 }
00303 
00304 /**
00305  * Set MPU of TBF qdisc.
00306  * @arg qdisc           TBF qdisc to be modified.
00307  * @arg mpu             New MPU in bytes.
00308  * @return 0 on success or a negative error code.
00309  */
00310 int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
00311 {
00312         struct rtnl_tbf *tbf;
00313         
00314         tbf = tbf_alloc(qdisc);
00315         if (!tbf)
00316                 return -NLE_NOMEM;
00317 
00318         tbf->qt_mpu = mpu;
00319         tbf->qt_mask |= TBF_ATTR_MPU;
00320 
00321         return 0;
00322 }
00323 
00324 /**
00325  * Get MPU of TBF qdisc.
00326  * @arg qdisc           TBF qdisc.
00327  * @return MPU in bytes or a negative error code.
00328  */
00329 int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
00330 {
00331         struct rtnl_tbf *tbf;
00332         
00333         tbf = tbf_qdisc(qdisc);
00334         if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
00335                 return tbf->qt_mpu;
00336         else
00337                 return -NLE_NOATTR;
00338 }
00339 
00340 static inline int calc_cell_log(int cell, int bucket)
00341 {
00342         if (cell > 0)
00343                 cell = rtnl_tc_calc_cell_log(cell);
00344         else {
00345                 cell = 0;
00346 
00347                 if (!bucket)
00348                         bucket = 2047; /* defaults to cell_log=3 */
00349 
00350                 while ((bucket >> cell) > 255)
00351                         cell++;
00352         }
00353 
00354         return cell;
00355 }
00356 
00357 /**
00358  * Set rate of TBF qdisc.
00359  * @arg qdisc           TBF qdisc to be modified.
00360  * @arg rate            New rate in bytes per second.
00361  * @arg bucket          Size of bucket in bytes.
00362  * @arg cell            Size of a rate cell or 0 to get default value.
00363  * @return 0 on success or a negative error code.
00364  */
00365 int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00366                             int cell)
00367 {
00368         struct rtnl_tbf *tbf;
00369         int cell_log;
00370         
00371         tbf = tbf_alloc(qdisc);
00372         if (!tbf)
00373                 return -NLE_NOMEM;
00374 
00375         cell_log = calc_cell_log(cell, bucket);
00376         if (cell_log < 0)
00377                 return cell_log;
00378 
00379         tbf->qt_rate.rs_rate = rate;
00380         tbf->qt_rate_bucket = bucket;
00381         tbf->qt_rate.rs_cell_log = cell_log;
00382         tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00383         tbf->qt_mask |= TBF_ATTR_RATE;
00384 
00385         return 0;
00386 }
00387 
00388 /**
00389  * Get rate of TBF qdisc.
00390  * @arg qdisc           TBF qdisc.
00391  * @return Rate in bytes per seconds or a negative error code.
00392  */
00393 int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
00394 {
00395         struct rtnl_tbf *tbf;
00396 
00397         tbf = tbf_qdisc(qdisc);
00398         if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00399                 return tbf->qt_rate.rs_rate;
00400         else
00401                 return -1;
00402 }
00403 
00404 /**
00405  * Get rate bucket size of TBF qdisc.
00406  * @arg qdisc           TBF qdisc.
00407  * @return Size of rate bucket or a negative error code.
00408  */
00409 int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
00410 {
00411         struct rtnl_tbf *tbf;
00412 
00413         tbf = tbf_qdisc(qdisc);
00414         if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00415                 return tbf->qt_rate_bucket;
00416         else
00417                 return -1;
00418 }
00419 
00420 /**
00421  * Get rate cell size of TBF qdisc.
00422  * @arg qdisc           TBF qdisc.
00423  * @return Size of rate cell in bytes or a negative error code.
00424  */
00425 int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
00426 {
00427         struct rtnl_tbf *tbf;
00428 
00429         tbf = tbf_qdisc(qdisc);
00430         if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
00431                 return (1 << tbf->qt_rate.rs_cell_log);
00432         else
00433                 return -1;
00434 }
00435 
00436 /**
00437  * Set peak rate of TBF qdisc.
00438  * @arg qdisc           TBF qdisc to be modified.
00439  * @arg rate            New peak rate in bytes per second.
00440  * @arg bucket          Size of peakrate bucket.
00441  * @arg cell            Size of a peakrate cell or 0 to get default value.
00442  * @return 0 on success or a negative error code.
00443  */
00444 int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
00445                                 int cell)
00446 {
00447         struct rtnl_tbf *tbf;
00448         int cell_log;
00449         
00450         tbf = tbf_alloc(qdisc);
00451         if (!tbf)
00452                 return -NLE_NOMEM;
00453 
00454         cell_log = calc_cell_log(cell, bucket);
00455         if (cell_log < 0)
00456                 return cell_log;
00457 
00458         tbf->qt_peakrate.rs_rate = rate;
00459         tbf->qt_peakrate_bucket = bucket;
00460         tbf->qt_peakrate.rs_cell_log = cell_log;
00461         tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
00462         
00463         tbf->qt_mask |= TBF_ATTR_PEAKRATE;
00464 
00465         return 0;
00466 }
00467 
00468 /**
00469  * Get peak rate of TBF qdisc.
00470  * @arg qdisc           TBF qdisc.
00471  * @return Peak rate in bytes per seconds or a negative error code.
00472  */
00473 int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
00474 {
00475         struct rtnl_tbf *tbf;
00476 
00477         tbf = tbf_qdisc(qdisc);
00478         if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00479                 return tbf->qt_peakrate.rs_rate;
00480         else
00481                 return -1;
00482 }
00483 
00484 /**
00485  * Get peak rate bucket size of TBF qdisc.
00486  * @arg qdisc           TBF qdisc.
00487  * @return Size of peak rate bucket or a negative error code.
00488  */
00489 int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
00490 {
00491         struct rtnl_tbf *tbf;
00492 
00493         tbf = tbf_qdisc(qdisc);
00494         if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00495                 return tbf->qt_peakrate_bucket;
00496         else
00497                 return -1;
00498 }
00499 
00500 /**
00501  * Get peak rate cell size of TBF qdisc.
00502  * @arg qdisc           TBF qdisc.
00503  * @return Size of peak rate cell in bytes or a negative error code.
00504  */
00505 int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
00506 {
00507         struct rtnl_tbf *tbf;
00508 
00509         tbf = tbf_qdisc(qdisc);
00510         if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
00511                 return (1 << tbf->qt_peakrate.rs_cell_log);
00512         else
00513                 return -1;
00514 }
00515 
00516 /** @} */
00517 
00518 static struct rtnl_qdisc_ops tbf_qdisc_ops = {
00519         .qo_kind                = "tbf",
00520         .qo_msg_parser          = tbf_msg_parser,
00521         .qo_dump = {
00522             [NL_DUMP_LINE]      = tbf_dump_line,
00523             [NL_DUMP_DETAILS]   = tbf_dump_details,
00524         },
00525         .qo_free_data           = tbf_free_data,
00526         .qo_get_opts            = tbf_get_opts,
00527 };
00528 
00529 static void __init tbf_init(void)
00530 {
00531         rtnl_qdisc_register(&tbf_qdisc_ops);
00532 }
00533 
00534 static void __exit tbf_exit(void)
00535 {
00536         rtnl_qdisc_unregister(&tbf_qdisc_ops);
00537 }
00538 
00539 /** @} */