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 0x001
00039 #define SCH_HTB_HAS_MTU 0x002
00040 #define SCH_HTB_HAS_RATE 0x004
00041 #define SCH_HTB_HAS_CEIL 0x008
00042 #define SCH_HTB_HAS_RBUFFER 0x010
00043 #define SCH_HTB_HAS_CBUFFER 0x020
00044 #define SCH_HTB_HAS_QUANTUM 0x040
00045 #define SCH_HTB_HAS_OVERHEAD 0x080
00046 #define SCH_HTB_HAS_MPU 0x100
00047
00048
00049 static inline struct rtnl_htb_qdisc *htb_qdisc(struct rtnl_qdisc *qdisc)
00050 {
00051 if (qdisc->q_subdata == NULL)
00052 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_htb_qdisc));
00053
00054 return (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00055 }
00056
00057 static struct nla_policy htb_policy[TCA_HTB_MAX+1] = {
00058 [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) },
00059 [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) },
00060 };
00061
00062 static int htb_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
00063 {
00064 int err;
00065 struct nlattr *tb[TCA_HTB_MAX + 1];
00066 struct rtnl_htb_qdisc *d;
00067
00068 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) qdisc, htb_policy);
00069 if (err < 0)
00070 return err;
00071
00072 d = htb_qdisc(qdisc);
00073
00074 if (tb[TCA_HTB_INIT]) {
00075 struct tc_htb_glob opts;
00076
00077 nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts));
00078 d->qh_rate2quantum = opts.rate2quantum;
00079 d->qh_defcls = opts.defcls;
00080
00081 d->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS);
00082 }
00083
00084 return 0;
00085 }
00086
00087 static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc)
00088 {
00089 free(qdisc->q_subdata);
00090 }
00091
00092 static inline struct rtnl_htb_class *htb_class(struct rtnl_class *class)
00093 {
00094 if (class->c_subdata == NULL)
00095 class->c_subdata = calloc(1, sizeof(struct rtnl_htb_class));
00096
00097 return (struct rtnl_htb_class *) class->c_subdata;
00098 }
00099
00100 static int htb_class_msg_parser(struct rtnl_class *class)
00101 {
00102 int err;
00103 struct nlattr *tb[TCA_HTB_MAX + 1];
00104 struct rtnl_htb_class *d;
00105
00106 err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) class, htb_policy);
00107 if (err < 0)
00108 return err;
00109
00110 d = htb_class(class);
00111
00112 if (tb[TCA_HTB_PARMS]) {
00113 struct tc_htb_opt opts;
00114
00115 nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts));
00116 d->ch_prio = opts.prio;
00117 rtnl_copy_ratespec(&d->ch_rate, &opts.rate);
00118 rtnl_copy_ratespec(&d->ch_ceil, &opts.ceil);
00119 d->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate);
00120 d->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate);
00121 d->ch_quantum = opts.quantum;
00122 d->ch_overhead = (opts.rate.mpu >> 8) & 0xff;
00123 d->ch_mpu = opts.rate.mpu & 0xff;
00124
00125 d->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE |
00126 SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER |
00127 SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM |
00128 SCH_HTB_HAS_OVERHEAD | SCH_HTB_HAS_MPU);
00129 }
00130
00131 return 0;
00132 }
00133
00134 static void htb_class_free_data(struct rtnl_class *class)
00135 {
00136 free(class->c_subdata);
00137 }
00138
00139 static int htb_qdisc_dump_brief(struct rtnl_qdisc *qdisc,
00140 struct nl_dump_params *p, int line)
00141 {
00142 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00143
00144 if (d == NULL)
00145 goto ignore;
00146
00147 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00148 dp_dump(p, " r2q %u", d->qh_rate2quantum);
00149
00150 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
00151 char buf[32];
00152 dp_dump(p, " default %s",
00153 rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
00154 }
00155
00156 ignore:
00157 return line;
00158 }
00159
00160 static int htb_class_dump_brief(struct rtnl_class *class,
00161 struct nl_dump_params *p, int line)
00162 {
00163 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00164
00165 if (d == NULL)
00166 goto ignore;
00167
00168 if (d->ch_mask & SCH_HTB_HAS_RATE) {
00169 double r, rbit;
00170 char *ru, *rubit;
00171
00172 r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru);
00173 rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit);
00174
00175 dp_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
00176 r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
00177 }
00178
00179 ignore:
00180 return line;
00181 }
00182
00183 static int htb_class_dump_full(struct rtnl_class *class,
00184 struct nl_dump_params *p, int line)
00185 {
00186 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00187
00188 if (d == NULL)
00189 goto ignore;
00190
00191
00192 if (d->ch_mask & SCH_HTB_HAS_CEIL) {
00193 double r, rbit;
00194 char *ru, *rubit;
00195
00196 r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru);
00197 rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit);
00198
00199 dp_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
00200 r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
00201 }
00202
00203 if (d->ch_mask & SCH_HTB_HAS_PRIO)
00204 dp_dump(p, " prio %u", d->ch_prio);
00205
00206 if (d->ch_mask & SCH_HTB_HAS_MTU)
00207 dp_dump(p, " mtu %u", d->ch_mtu);
00208
00209 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) {
00210 double b;
00211 char *bu;
00212
00213 b = nl_cancel_down_bytes(d->ch_rbuffer, &bu);
00214 dp_dump(p, " rbuffer %.2f%s", b, bu);
00215 }
00216
00217 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) {
00218 double b;
00219 char *bu;
00220
00221 b = nl_cancel_down_bytes(d->ch_cbuffer, &bu);
00222 dp_dump(p, " cbuffer %.2f%s", b, bu);
00223 }
00224
00225 if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
00226 dp_dump(p, " quantum %u", d->ch_quantum);
00227
00228 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
00229 dp_dump(p, " overhead %u", d->ch_overhead);
00230
00231 if (d->ch_mask & SCH_HTB_HAS_MPU)
00232 dp_dump(p, " mpu %u", d->ch_mpu);
00233
00234 ignore:
00235 return line;
00236 }
00237
00238 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc)
00239 {
00240 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00241 struct tc_htb_glob opts;
00242 struct nl_msg *msg;
00243
00244 if (d == NULL)
00245 return NULL;
00246
00247 msg = nlmsg_alloc();
00248 if (msg == NULL)
00249 return NULL;
00250
00251 memset(&opts, 0, sizeof(opts));
00252 opts.version = TC_HTB_PROTOVER;
00253
00254 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00255 opts.rate2quantum = d->qh_rate2quantum;
00256 if (d->qh_mask & SCH_HTB_HAS_DEFCLS)
00257 opts.defcls = d->qh_defcls;
00258
00259 nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
00260
00261 return msg;
00262 }
00263
00264 static uint8_t compute_cell(uint32_t rate, uint32_t mtu)
00265 {
00266 uint8_t cell_log = 0;
00267 while (mtu > 255) {
00268 mtu >>= 1;
00269 cell_log++;
00270 }
00271
00272 return cell_log;
00273 }
00274
00275 static struct nl_msg *htb_class_get_opts(struct rtnl_class *class)
00276 {
00277 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00278 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
00279 struct tc_htb_opt opts;
00280 struct nl_msg *msg;
00281 int buffer, cbuffer;
00282 uint8_t overhead = 0, mpu = 0;
00283
00284 if (d == NULL)
00285 return NULL;
00286
00287 msg = nlmsg_alloc();
00288 memset(&opts, 0, sizeof(opts));
00289
00290
00291 if (d->ch_mask & SCH_HTB_HAS_PRIO)
00292 opts.prio = d->ch_prio;
00293
00294 if (d->ch_mask & SCH_HTB_HAS_MTU)
00295 mtu = d->ch_mtu;
00296 else
00297 mtu = 1600;
00298
00299 if (!(d->ch_mask & SCH_HTB_HAS_RATE))
00300 BUG();
00301
00302 rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate);
00303
00304 if (opts.rate.cell_log == UINT8_MAX)
00305 opts.rate.cell_log = compute_cell(opts.rate.rate, mtu);
00306
00307
00308 if (d->ch_mask & SCH_HTB_HAS_CEIL)
00309 rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil);
00310 else
00311 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
00312
00313 if (opts.ceil.cell_log == UINT8_MAX)
00314 opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu);
00315
00316 if (d->ch_mask & SCH_HTB_HAS_RBUFFER)
00317 buffer = d->ch_rbuffer;
00318 else
00319 buffer = opts.rate.rate / nl_get_hz() + mtu;
00320
00321 opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate);
00322
00323 if (d->ch_mask & SCH_HTB_HAS_CBUFFER)
00324 cbuffer = d->ch_cbuffer;
00325 else
00326 cbuffer = opts.ceil.rate / nl_get_hz() + mtu;
00327
00328 opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate);
00329
00330 if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
00331 opts.quantum = d->ch_quantum;
00332
00333 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
00334 overhead = d->ch_overhead;
00335
00336 if (d->ch_mask & SCH_HTB_HAS_MPU)
00337 mpu = d->ch_mpu;
00338
00339 opts.rate.mpu = mpu | (overhead << 8);
00340 opts.ceil.mpu = mpu | (overhead << 8);
00341
00342 nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
00343
00344 rtnl_tc_build_rate_table(rtable, mpu, overhead,
00345 1 << opts.rate.cell_log,
00346 opts.rate.rate);
00347 nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
00348
00349 rtnl_tc_build_rate_table(ctable, mpu, overhead,
00350 1 << opts.ceil.cell_log,
00351 opts.ceil.rate);
00352 nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
00353
00354 return msg;
00355 }
00356
00357
00358
00359
00360
00361
00362 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
00363 {
00364 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
00365 if (d == NULL)
00366 return;
00367
00368 d->qh_rate2quantum = rate2quantum;
00369 d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
00370 }
00371
00372
00373
00374
00375
00376
00377 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
00378 {
00379 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
00380 if (d == NULL)
00381 return;
00382
00383 d->qh_defcls = defcls;
00384 d->qh_mask |= SCH_HTB_HAS_DEFCLS;
00385 }
00386
00387 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
00388 {
00389 struct rtnl_htb_class *d = htb_class(class);
00390 if (d == NULL)
00391 return;
00392
00393 d->ch_prio = prio;
00394 d->ch_mask |= SCH_HTB_HAS_PRIO;
00395 }
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405 void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu)
00406 {
00407 struct rtnl_htb_class *d = htb_class(class);
00408 if (d == NULL)
00409 return;
00410
00411 d->ch_mtu = mtu;
00412 d->ch_mask |= SCH_HTB_HAS_MTU;
00413 }
00414
00415
00416
00417
00418
00419
00420 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
00421 {
00422 struct rtnl_htb_class *d = htb_class(class);
00423 if (d == NULL)
00424 return;
00425
00426 d->ch_rate.rs_cell_log = UINT8_MAX;
00427 d->ch_rate.rs_rate = rate;
00428 d->ch_mask |= SCH_HTB_HAS_RATE;
00429 }
00430
00431
00432
00433
00434
00435
00436 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
00437 {
00438 struct rtnl_htb_class *d = htb_class(class);
00439 if (d == NULL)
00440 return;
00441
00442 d->ch_ceil.rs_cell_log = UINT8_MAX;
00443 d->ch_ceil.rs_rate = ceil;
00444 d->ch_mask |= SCH_HTB_HAS_CEIL;
00445 }
00446
00447
00448
00449
00450
00451
00452 void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
00453 {
00454 struct rtnl_htb_class *d = htb_class(class);
00455 if (d == NULL)
00456 return;
00457
00458 d->ch_rbuffer = rbuffer;
00459 d->ch_mask |= SCH_HTB_HAS_RBUFFER;
00460 }
00461
00462
00463
00464
00465
00466
00467 void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
00468 {
00469 struct rtnl_htb_class *d = htb_class(class);
00470 if (d == NULL)
00471 return;
00472
00473 d->ch_cbuffer = cbuffer;
00474 d->ch_mask |= SCH_HTB_HAS_CBUFFER;
00475 }
00476
00477
00478
00479
00480
00481
00482 void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
00483 {
00484 struct rtnl_htb_class *d = htb_class(class);
00485 if (d == NULL)
00486 return;
00487
00488 d->ch_quantum = quantum;
00489 d->ch_mask |= SCH_HTB_HAS_QUANTUM;
00490 }
00491
00492
00493
00494
00495
00496
00497 void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead)
00498 {
00499 struct rtnl_htb_class *d = htb_class(class);
00500 if (d == NULL)
00501 return;
00502
00503 d->ch_overhead = overhead;
00504 d->ch_mask |= SCH_HTB_HAS_OVERHEAD;
00505 }
00506
00507
00508
00509
00510
00511
00512 void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu)
00513 {
00514 struct rtnl_htb_class *d = htb_class(class);
00515 if (d == NULL)
00516 return;
00517
00518 d->ch_mpu = mpu;
00519 d->ch_mask |= SCH_HTB_HAS_MPU;
00520 }
00521
00522
00523
00524 static struct rtnl_qdisc_ops htb_qdisc_ops = {
00525 .qo_kind = "htb",
00526 .qo_msg_parser = htb_qdisc_msg_parser,
00527 .qo_free_data = htb_qdisc_free_data,
00528 .qo_dump[NL_DUMP_BRIEF] = htb_qdisc_dump_brief,
00529 .qo_get_opts = htb_qdisc_get_opts,
00530 };
00531
00532 static struct rtnl_class_ops htb_class_ops = {
00533 .co_kind = "htb",
00534 .co_msg_parser = htb_class_msg_parser,
00535 .co_free_data = htb_class_free_data,
00536 .co_dump[NL_DUMP_BRIEF] = htb_class_dump_brief,
00537 .co_dump[NL_DUMP_FULL] = htb_class_dump_full,
00538 .co_get_opts = htb_class_get_opts,
00539 };
00540
00541 static void __init htb_init(void)
00542 {
00543 rtnl_qdisc_register(&htb_qdisc_ops);
00544 rtnl_class_register(&htb_class_ops);
00545 }
00546
00547 static void __exit htb_exit(void)
00548 {
00549 rtnl_qdisc_unregister(&htb_qdisc_ops);
00550 rtnl_class_unregister(&htb_class_ops);
00551 }
00552
00553