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/cache.h>
00025 #include <netlink/utils.h>
00026 #include <netlink/route/tc.h>
00027 #include <netlink/route/qdisc.h>
00028 #include <netlink/route/qdisc-modules.h>
00029 #include <netlink/route/class.h>
00030 #include <netlink/route/class-modules.h>
00031 #include <netlink/route/link.h>
00032 #include <netlink/route/sch/htb.h>
00033
00034
00035 #define SCH_HTB_HAS_RATE2QUANTUM 0x01
00036 #define SCH_HTB_HAS_DEFCLS 0x02
00037
00038 #define SCH_HTB_HAS_PRIO 0x01
00039 #define SCH_HTB_HAS_MTU 0x02
00040 #define SCH_HTB_HAS_RATE 0x04
00041 #define SCH_HTB_HAS_CEIL 0x08
00042 #define SCH_HTB_HAS_RBUFFER 0x10
00043 #define SCH_HTB_HAS_CBUFFER 0x20
00044
00045
00046 static inline struct rtnl_htb_qdisc *htb_qdisc(struct rtnl_qdisc *qdisc)
00047 {
00048 if (qdisc->q_subdata == NULL)
00049 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_htb_qdisc));
00050
00051 return (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00052 }
00053
00054 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = {
00055 [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) },
00056 [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) },
00057 };
00058
00059 static int htb_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
00060 {
00061 int err;
00062 struct nlattr *tb[TCA_HTB_MAX + 1];
00063 struct rtnl_htb_qdisc *d;
00064
00065 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) qdisc, htb_policy);
00066 if (err < 0)
00067 return err;
00068
00069 d = htb_qdisc(qdisc);
00070
00071 if (tb[TCA_HTB_INIT]) {
00072 struct tc_htb_glob opts;
00073
00074 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts));
00075 d->qh_rate2quantum = opts.rate2quantum;
00076 d->qh_defcls = opts.defcls;
00077
00078 d->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS);
00079 }
00080
00081 return 0;
00082 }
00083
00084 static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc)
00085 {
00086 free(qdisc->q_subdata);
00087 }
00088
00089 static inline struct rtnl_htb_class *htb_class(struct rtnl_class *class)
00090 {
00091 if (class->c_subdata == NULL)
00092 class->c_subdata = calloc(1, sizeof(struct rtnl_htb_class));
00093
00094 return (struct rtnl_htb_class *) class->c_subdata;
00095 }
00096
00097 static int htb_class_msg_parser(struct rtnl_class *class)
00098 {
00099 int err;
00100 struct nlattr *tb[TCA_HTB_MAX + 1];
00101 struct rtnl_htb_class *d;
00102
00103 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) class, htb_policy);
00104 if (err < 0)
00105 return err;
00106
00107 d = htb_class(class);
00108
00109 if (tb[TCA_HTB_PARMS]) {
00110 struct tc_htb_opt opts;
00111
00112 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts));
00113 d->ch_prio = opts.prio;
00114 rtnl_copy_ratespec(&d->ch_rate, &opts.rate);
00115 rtnl_copy_ratespec(&d->ch_ceil, &opts.ceil);
00116 d->ch_rbuffer = opts.buffer;
00117 d->ch_cbuffer = opts.cbuffer;
00118
00119 d->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE |
00120 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER |
00121 SCH_HTB_HAS_CBUFFER);
00122 }
00123
00124 return 0;
00125 }
00126
00127 static void htb_class_free_data(struct rtnl_class *class)
00128 {
00129 free(class->c_subdata);
00130 }
00131
00132 static int htb_qdisc_dump_brief(struct rtnl_qdisc *qdisc,
00133 struct nl_dump_params *p, int line)
00134 {
00135 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00136
00137 if (d == NULL)
00138 goto ignore;
00139
00140 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00141 dp_dump(p, " r2q %u", d->qh_rate2quantum);
00142
00143 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
00144 char buf[32];
00145 dp_dump(p, " default %s",
00146 rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
00147 }
00148
00149 ignore:
00150 return line;
00151 }
00152
00153 static int htb_class_dump_brief(struct rtnl_class *class,
00154 struct nl_dump_params *p, int line)
00155 {
00156 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00157
00158 if (d == NULL)
00159 goto ignore;
00160
00161 if (d->ch_mask & SCH_HTB_HAS_RATE) {
00162 double r, rbit;
00163 char *ru, *rubit;
00164
00165 r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru);
00166 rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit);
00167
00168 dp_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
00169 r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
00170 }
00171
00172 ignore:
00173 return line;
00174 }
00175
00176 static int htb_class_dump_full(struct rtnl_class *class,
00177 struct nl_dump_params *p, int line)
00178 {
00179 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00180
00181 if (d == NULL)
00182 goto ignore;
00183
00184
00185 if (d->ch_mask & SCH_HTB_HAS_CEIL) {
00186 double r, rbit;
00187 char *ru, *rubit;
00188
00189 r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru);
00190 rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit);
00191
00192 dp_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
00193 r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
00194 }
00195
00196 if (d->ch_mask & SCH_HTB_HAS_PRIO)
00197 dp_dump(p, " prio %u", d->ch_prio);
00198 if (d->ch_mask & SCH_HTB_HAS_RBUFFER)
00199 dp_dump(p, " rbuffer %u", d->ch_rbuffer);
00200 if (d->ch_mask & SCH_HTB_HAS_CBUFFER)
00201 dp_dump(p, " cbuffer %u", d->ch_cbuffer);
00202
00203 ignore:
00204 return line;
00205 }
00206
00207 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc)
00208 {
00209 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00210 struct tc_htb_glob opts;
00211 struct nl_msg *msg;
00212
00213 if (d == NULL)
00214 return NULL;
00215
00216 msg = nlmsg_build(NULL);
00217 if (msg == NULL)
00218 return NULL;
00219
00220 memset(&opts, 0, sizeof(opts));
00221 opts.version = TC_HTB_PROTOVER;
00222
00223 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00224 opts.rate2quantum = d->qh_rate2quantum;
00225 if (d->qh_mask & SCH_HTB_HAS_DEFCLS)
00226 opts.defcls = d->qh_defcls;
00227
00228 nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
00229
00230 return msg;
00231 }
00232
00233 static inline uint32_t compute_burst(uint32_t rate, uint32_t mtu)
00234 {
00235 return rtnl_tc_calc_txtime(rate / nl_get_hz() + mtu, rate);
00236 }
00237
00238 static uint8_t compute_cell(uint32_t rate, uint32_t mtu)
00239 {
00240 uint8_t cell_log = 0;
00241 while (mtu > 255) {
00242 mtu >>= 1;
00243 cell_log++;
00244 }
00245
00246 return cell_log;
00247 }
00248
00249 static struct nl_msg *htb_class_get_opts(struct rtnl_class *class)
00250 {
00251 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00252 uint32_t rtable[256], ctable[256];
00253 struct tc_htb_opt opts;
00254 struct nl_msg *msg;
00255
00256
00257 if (d == NULL)
00258 return NULL;
00259
00260 msg = nlmsg_build(NULL);
00261 memset(&opts, 0, sizeof(opts));
00262
00263
00264 if (d->ch_mask & SCH_HTB_HAS_PRIO)
00265 opts.prio = d->ch_prio;
00266
00267 if (!(d->ch_mask & SCH_HTB_HAS_RATE))
00268 BUG();
00269
00270 rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate);
00271
00272 if (opts.rate.cell_log == UINT8_MAX)
00273 {
00274 if(!(d->ch_mask & SCH_HTB_HAS_MTU))
00275 BUG();
00276 opts.rate.cell_log = compute_cell(opts.rate.rate, d->ch_mtu);
00277 }
00278
00279
00280 if (d->ch_mask & SCH_HTB_HAS_CEIL)
00281 rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil);
00282 else
00283 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
00284
00285 if (opts.ceil.cell_log == UINT8_MAX)
00286 {
00287 if(!(d->ch_mask & SCH_HTB_HAS_MTU))
00288 BUG();
00289 opts.ceil.cell_log = compute_cell(opts.ceil.rate, d->ch_mtu);
00290 }
00291
00292 if (d->ch_mask & SCH_HTB_HAS_RBUFFER)
00293 opts.buffer = d->ch_rbuffer;
00294 else
00295 {
00296 if(!(d->ch_mask & SCH_HTB_HAS_MTU))
00297 BUG();
00298 opts.buffer = compute_burst(opts.rate.rate, d->ch_mtu);
00299 }
00300
00301 if (d->ch_mask & SCH_HTB_HAS_CBUFFER)
00302 opts.cbuffer = d->ch_cbuffer;
00303 else
00304 {
00305 if(!(d->ch_mask & SCH_HTB_HAS_MTU))
00306 BUG();
00307 opts.cbuffer = compute_burst(opts.ceil.rate, d->ch_mtu);
00308 }
00309
00310 nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
00311 rtnl_tc_build_rate_table(rtable, opts.rate.mpu & 0xff,
00312 opts.rate.mpu >> 8, 1 << opts.rate.cell_log,
00313 opts.rate.rate);
00314 nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
00315 rtnl_tc_build_rate_table(ctable, opts.ceil.mpu & 0xff,
00316 opts.ceil.mpu >> 8, 1 << opts.ceil.cell_log,
00317 opts.ceil.rate);
00318 nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
00319
00320 return msg;
00321 }
00322
00323
00324
00325
00326
00327
00328 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
00329 {
00330 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
00331 if (d == NULL)
00332 return;
00333
00334 d->qh_rate2quantum = rate2quantum;
00335 d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
00336 }
00337
00338
00339
00340
00341
00342
00343 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
00344 {
00345 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
00346 if (d == NULL)
00347 return;
00348
00349 d->qh_defcls = defcls;
00350 d->qh_mask |= SCH_HTB_HAS_DEFCLS;
00351 }
00352
00353 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
00354 {
00355 struct rtnl_htb_class *d = htb_class(class);
00356 if (d == NULL)
00357 return;
00358
00359 d->ch_prio = prio;
00360 d->ch_mask |= SCH_HTB_HAS_PRIO;
00361 }
00362
00363 void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu)
00364 {
00365 struct rtnl_htb_class *d = htb_class(class);
00366 if (d == NULL)
00367 return;
00368
00369 d->ch_mtu = mtu;
00370 d->ch_mask |= SCH_HTB_HAS_MTU;
00371 }
00372
00373 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
00374 {
00375 struct rtnl_htb_class *d = htb_class(class);
00376 if (d == NULL)
00377 return;
00378
00379 d->ch_rate.rs_cell_log = UINT8_MAX;
00380 d->ch_rate.rs_rate = rate;
00381 d->ch_mask |= SCH_HTB_HAS_RATE;
00382 }
00383
00384 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
00385 {
00386 struct rtnl_htb_class *d = htb_class(class);
00387 if (d == NULL)
00388 return;
00389
00390 d->ch_ceil.rs_cell_log = UINT8_MAX;
00391 d->ch_ceil.rs_rate = ceil;
00392 d->ch_mask |= SCH_HTB_HAS_CEIL;
00393 }
00394
00395
00396
00397 static struct rtnl_qdisc_ops htb_qdisc_ops = {
00398 .qo_kind = "htb",
00399 .qo_msg_parser = htb_qdisc_msg_parser,
00400 .qo_free_data = htb_qdisc_free_data,
00401 .qo_dump[NL_DUMP_BRIEF] = htb_qdisc_dump_brief,
00402 .qo_get_opts = htb_qdisc_get_opts,
00403 };
00404
00405 static struct rtnl_class_ops htb_class_ops = {
00406 .co_kind = "htb",
00407 .co_msg_parser = htb_class_msg_parser,
00408 .co_free_data = htb_class_free_data,
00409 .co_dump[NL_DUMP_BRIEF] = htb_class_dump_brief,
00410 .co_dump[NL_DUMP_FULL] = htb_class_dump_full,
00411 .co_get_opts = htb_class_get_opts,
00412 };
00413
00414 static void __init htb_init(void)
00415 {
00416 rtnl_qdisc_register(&htb_qdisc_ops);
00417 rtnl_class_register(&htb_class_ops);
00418 }
00419
00420 static void __exit htb_exit(void)
00421 {
00422 rtnl_qdisc_unregister(&htb_qdisc_ops);
00423 rtnl_class_unregister(&htb_class_ops);
00424 }
00425
00426