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 void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc,
00140 struct nl_dump_params *p)
00141 {
00142 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00143
00144 if (d == NULL)
00145 return;
00146
00147 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00148 nl_dump(p, " r2q %u", d->qh_rate2quantum);
00149
00150 if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
00151 char buf[32];
00152 nl_dump(p, " default %s",
00153 rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
00154 }
00155 }
00156
00157 static void htb_class_dump_line(struct rtnl_class *class,
00158 struct nl_dump_params *p)
00159 {
00160 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00161
00162 if (d == NULL)
00163 return;
00164
00165 if (d->ch_mask & SCH_HTB_HAS_RATE) {
00166 double r, rbit;
00167 char *ru, *rubit;
00168
00169 r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru);
00170 rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit);
00171
00172 nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
00173 r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
00174 }
00175 }
00176
00177 static void htb_class_dump_details(struct rtnl_class *class,
00178 struct nl_dump_params *p)
00179 {
00180 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00181
00182 if (d == NULL)
00183 return;
00184
00185
00186 if (d->ch_mask & SCH_HTB_HAS_CEIL) {
00187 double r, rbit;
00188 char *ru, *rubit;
00189
00190 r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru);
00191 rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit);
00192
00193 nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
00194 r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
00195 }
00196
00197 if (d->ch_mask & SCH_HTB_HAS_PRIO)
00198 nl_dump(p, " prio %u", d->ch_prio);
00199
00200 if (d->ch_mask & SCH_HTB_HAS_MTU)
00201 nl_dump(p, " mtu %u", d->ch_mtu);
00202
00203 if (d->ch_mask & SCH_HTB_HAS_RBUFFER) {
00204 double b;
00205 char *bu;
00206
00207 b = nl_cancel_down_bytes(d->ch_rbuffer, &bu);
00208 nl_dump(p, " rbuffer %.2f%s", b, bu);
00209 }
00210
00211 if (d->ch_mask & SCH_HTB_HAS_CBUFFER) {
00212 double b;
00213 char *bu;
00214
00215 b = nl_cancel_down_bytes(d->ch_cbuffer, &bu);
00216 nl_dump(p, " cbuffer %.2f%s", b, bu);
00217 }
00218
00219 if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
00220 nl_dump(p, " quantum %u", d->ch_quantum);
00221
00222 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
00223 nl_dump(p, " overhead %u", d->ch_overhead);
00224
00225 if (d->ch_mask & SCH_HTB_HAS_MPU)
00226 nl_dump(p, " mpu %u", d->ch_mpu);
00227 }
00228
00229 static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc)
00230 {
00231 struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
00232 struct tc_htb_glob opts;
00233 struct nl_msg *msg;
00234
00235 if (d == NULL)
00236 return NULL;
00237
00238 msg = nlmsg_alloc();
00239 if (msg == NULL)
00240 return NULL;
00241
00242 memset(&opts, 0, sizeof(opts));
00243 opts.version = TC_HTB_PROTOVER;
00244
00245 if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
00246 opts.rate2quantum = d->qh_rate2quantum;
00247 if (d->qh_mask & SCH_HTB_HAS_DEFCLS)
00248 opts.defcls = d->qh_defcls;
00249
00250 nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
00251
00252 return msg;
00253 }
00254
00255 static uint8_t compute_cell(uint32_t rate, uint32_t mtu)
00256 {
00257 uint8_t cell_log = 0;
00258 while (mtu > 255) {
00259 mtu >>= 1;
00260 cell_log++;
00261 }
00262
00263 return cell_log;
00264 }
00265
00266 static struct nl_msg *htb_class_get_opts(struct rtnl_class *class)
00267 {
00268 struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
00269 uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
00270 struct tc_htb_opt opts;
00271 struct nl_msg *msg;
00272 int buffer, cbuffer;
00273 uint8_t overhead = 0, mpu = 0;
00274
00275 if (d == NULL)
00276 return NULL;
00277
00278 msg = nlmsg_alloc();
00279 memset(&opts, 0, sizeof(opts));
00280
00281
00282 if (d->ch_mask & SCH_HTB_HAS_PRIO)
00283 opts.prio = d->ch_prio;
00284
00285 if (d->ch_mask & SCH_HTB_HAS_MTU)
00286 mtu = d->ch_mtu;
00287 else
00288 mtu = 1600;
00289
00290 if (!(d->ch_mask & SCH_HTB_HAS_RATE))
00291 BUG();
00292
00293 rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate);
00294
00295 if (opts.rate.cell_log == UINT8_MAX)
00296 opts.rate.cell_log = compute_cell(opts.rate.rate, mtu);
00297
00298
00299 if (d->ch_mask & SCH_HTB_HAS_CEIL)
00300 rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil);
00301 else
00302 memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
00303
00304 if (opts.ceil.cell_log == UINT8_MAX)
00305 opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu);
00306
00307 if (d->ch_mask & SCH_HTB_HAS_RBUFFER)
00308 buffer = d->ch_rbuffer;
00309 else
00310 buffer = opts.rate.rate / nl_get_hz() + mtu;
00311
00312 opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate);
00313
00314 if (d->ch_mask & SCH_HTB_HAS_CBUFFER)
00315 cbuffer = d->ch_cbuffer;
00316 else
00317 cbuffer = opts.ceil.rate / nl_get_hz() + mtu;
00318
00319 opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate);
00320
00321 if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
00322 opts.quantum = d->ch_quantum;
00323
00324 if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
00325 overhead = d->ch_overhead;
00326
00327 if (d->ch_mask & SCH_HTB_HAS_MPU)
00328 mpu = d->ch_mpu;
00329
00330 opts.rate.mpu = mpu | (overhead << 8);
00331 opts.ceil.mpu = mpu | (overhead << 8);
00332
00333 nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
00334
00335 rtnl_tc_build_rate_table(rtable, mpu, overhead,
00336 1 << opts.rate.cell_log,
00337 opts.rate.rate);
00338 nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
00339
00340 rtnl_tc_build_rate_table(ctable, mpu, overhead,
00341 1 << opts.ceil.cell_log,
00342 opts.ceil.rate);
00343 nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
00344
00345 return msg;
00346 }
00347
00348
00349
00350
00351
00352
00353 void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
00354 {
00355 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
00356 if (d == NULL)
00357 return;
00358
00359 d->qh_rate2quantum = rate2quantum;
00360 d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
00361 }
00362
00363
00364
00365
00366
00367
00368 void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
00369 {
00370 struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
00371 if (d == NULL)
00372 return;
00373
00374 d->qh_defcls = defcls;
00375 d->qh_mask |= SCH_HTB_HAS_DEFCLS;
00376 }
00377
00378 void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
00379 {
00380 struct rtnl_htb_class *d = htb_class(class);
00381 if (d == NULL)
00382 return;
00383
00384 d->ch_prio = prio;
00385 d->ch_mask |= SCH_HTB_HAS_PRIO;
00386 }
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396 void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu)
00397 {
00398 struct rtnl_htb_class *d = htb_class(class);
00399 if (d == NULL)
00400 return;
00401
00402 d->ch_mtu = mtu;
00403 d->ch_mask |= SCH_HTB_HAS_MTU;
00404 }
00405
00406
00407
00408
00409
00410
00411 void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
00412 {
00413 struct rtnl_htb_class *d = htb_class(class);
00414 if (d == NULL)
00415 return;
00416
00417 d->ch_rate.rs_cell_log = UINT8_MAX;
00418 d->ch_rate.rs_rate = rate;
00419 d->ch_mask |= SCH_HTB_HAS_RATE;
00420 }
00421
00422
00423
00424
00425
00426
00427 void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
00428 {
00429 struct rtnl_htb_class *d = htb_class(class);
00430 if (d == NULL)
00431 return;
00432
00433 d->ch_ceil.rs_cell_log = UINT8_MAX;
00434 d->ch_ceil.rs_rate = ceil;
00435 d->ch_mask |= SCH_HTB_HAS_CEIL;
00436 }
00437
00438
00439
00440
00441
00442
00443 void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
00444 {
00445 struct rtnl_htb_class *d = htb_class(class);
00446 if (d == NULL)
00447 return;
00448
00449 d->ch_rbuffer = rbuffer;
00450 d->ch_mask |= SCH_HTB_HAS_RBUFFER;
00451 }
00452
00453
00454
00455
00456
00457
00458 void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
00459 {
00460 struct rtnl_htb_class *d = htb_class(class);
00461 if (d == NULL)
00462 return;
00463
00464 d->ch_cbuffer = cbuffer;
00465 d->ch_mask |= SCH_HTB_HAS_CBUFFER;
00466 }
00467
00468
00469
00470
00471
00472
00473 void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
00474 {
00475 struct rtnl_htb_class *d = htb_class(class);
00476 if (d == NULL)
00477 return;
00478
00479 d->ch_quantum = quantum;
00480 d->ch_mask |= SCH_HTB_HAS_QUANTUM;
00481 }
00482
00483
00484
00485
00486
00487
00488 void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead)
00489 {
00490 struct rtnl_htb_class *d = htb_class(class);
00491 if (d == NULL)
00492 return;
00493
00494 d->ch_overhead = overhead;
00495 d->ch_mask |= SCH_HTB_HAS_OVERHEAD;
00496 }
00497
00498
00499
00500
00501
00502
00503 void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu)
00504 {
00505 struct rtnl_htb_class *d = htb_class(class);
00506 if (d == NULL)
00507 return;
00508
00509 d->ch_mpu = mpu;
00510 d->ch_mask |= SCH_HTB_HAS_MPU;
00511 }
00512
00513
00514
00515 static struct rtnl_qdisc_ops htb_qdisc_ops = {
00516 .qo_kind = "htb",
00517 .qo_msg_parser = htb_qdisc_msg_parser,
00518 .qo_free_data = htb_qdisc_free_data,
00519 .qo_dump[NL_DUMP_LINE] = htb_qdisc_dump_line,
00520 .qo_get_opts = htb_qdisc_get_opts,
00521 };
00522
00523 static struct rtnl_class_ops htb_class_ops = {
00524 .co_kind = "htb",
00525 .co_msg_parser = htb_class_msg_parser,
00526 .co_free_data = htb_class_free_data,
00527 .co_dump = {
00528 [NL_DUMP_LINE] = htb_class_dump_line,
00529 [NL_DUMP_DETAILS] = htb_class_dump_details,
00530 },
00531 .co_get_opts = htb_class_get_opts,
00532 };
00533
00534 static void __init htb_init(void)
00535 {
00536 rtnl_qdisc_register(&htb_qdisc_ops);
00537 rtnl_class_register(&htb_class_ops);
00538 }
00539
00540 static void __exit htb_exit(void)
00541 {
00542 rtnl_qdisc_unregister(&htb_qdisc_ops);
00543 rtnl_class_unregister(&htb_class_ops);
00544 }
00545
00546