1/*
2 * lib/route/sch/htb.c	HTB Qdisc
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10 * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
11 * Copyright (c) 2005-2006 Siemens AG Oesterreich
12 */
13
14/**
15 * @ingroup qdisc_api
16 * @ingroup class_api
17 * @defgroup htb Hierachical Token Bucket (HTB)
18 * @{
19 */
20
21#include <netlink-local.h>
22#include <netlink-tc.h>
23#include <netlink/netlink.h>
24#include <netlink/cache.h>
25#include <netlink/utils.h>
26#include <netlink/route/tc.h>
27#include <netlink/route/qdisc.h>
28#include <netlink/route/qdisc-modules.h>
29#include <netlink/route/class.h>
30#include <netlink/route/class-modules.h>
31#include <netlink/route/link.h>
32#include <netlink/route/sch/htb.h>
33
34/** @cond SKIP */
35#define SCH_HTB_HAS_RATE2QUANTUM	0x01
36#define SCH_HTB_HAS_DEFCLS		0x02
37
38#define SCH_HTB_HAS_PRIO		0x001
39#define SCH_HTB_HAS_MTU			0x002
40#define SCH_HTB_HAS_RATE		0x004
41#define SCH_HTB_HAS_CEIL		0x008
42#define SCH_HTB_HAS_RBUFFER		0x010
43#define SCH_HTB_HAS_CBUFFER		0x020
44#define SCH_HTB_HAS_QUANTUM		0x040
45#define SCH_HTB_HAS_OVERHEAD		0x080
46#define SCH_HTB_HAS_MPU			0x100
47/** @endcond */
48
49static inline struct rtnl_htb_qdisc *htb_qdisc(struct rtnl_qdisc *qdisc)
50{
51	if (qdisc->q_subdata == NULL)
52		qdisc->q_subdata = calloc(1, sizeof(struct rtnl_htb_qdisc));
53
54	return (struct rtnl_htb_qdisc *) qdisc->q_subdata;
55}
56
57static struct nla_policy htb_policy[TCA_HTB_MAX+1] = {
58	[TCA_HTB_INIT]	= { .minlen = sizeof(struct tc_htb_glob) },
59	[TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) },
60};
61
62static int htb_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
63{
64	int err;
65	struct nlattr *tb[TCA_HTB_MAX + 1];
66	struct rtnl_htb_qdisc *d;
67
68	err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) qdisc, htb_policy);
69	if (err < 0)
70		return err;
71
72	d = htb_qdisc(qdisc);
73
74	if (tb[TCA_HTB_INIT]) {
75		struct tc_htb_glob opts;
76
77		nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts));
78		d->qh_rate2quantum = opts.rate2quantum;
79		d->qh_defcls = opts.defcls;
80
81		d->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS);
82	}
83
84	return 0;
85}
86
87static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc)
88{
89	free(qdisc->q_subdata);
90}
91
92static inline struct rtnl_htb_class *htb_class(struct rtnl_class *class)
93{
94	if (class->c_subdata == NULL)
95		class->c_subdata = calloc(1, sizeof(struct rtnl_htb_class));
96
97	return (struct rtnl_htb_class *) class->c_subdata;
98}
99
100static int htb_class_msg_parser(struct rtnl_class *class)
101{
102	int err;
103	struct nlattr *tb[TCA_HTB_MAX + 1];
104	struct rtnl_htb_class *d;
105
106	err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) class, htb_policy);
107	if (err < 0)
108		return err;
109
110	d = htb_class(class);
111
112	if (tb[TCA_HTB_PARMS]) {
113		struct tc_htb_opt opts;
114
115		nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts));
116		d->ch_prio = opts.prio;
117		rtnl_copy_ratespec(&d->ch_rate, &opts.rate);
118		rtnl_copy_ratespec(&d->ch_ceil, &opts.ceil);
119		d->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate);
120		d->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate);
121		d->ch_quantum = opts.quantum;
122		d->ch_overhead = (opts.rate.mpu >> 8) & 0xff;
123		d->ch_mpu = opts.rate.mpu & 0xff;
124
125		d->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE |
126			SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER |
127			SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM |
128			SCH_HTB_HAS_OVERHEAD | SCH_HTB_HAS_MPU);
129	}
130
131	return 0;
132}
133
134static void htb_class_free_data(struct rtnl_class *class)
135{
136	free(class->c_subdata);
137}
138
139static void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc,
140				struct nl_dump_params *p)
141{
142	struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
143
144	if (d == NULL)
145		return;
146
147	if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
148		nl_dump(p, " r2q %u", d->qh_rate2quantum);
149
150	if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
151		char buf[32];
152		nl_dump(p, " default %s",
153			rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
154	}
155}
156
157static void htb_class_dump_line(struct rtnl_class *class,
158				struct nl_dump_params *p)
159{
160	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
161
162	if (d == NULL)
163		return;
164
165	if (d->ch_mask & SCH_HTB_HAS_RATE) {
166		double r, rbit;
167		char *ru, *rubit;
168
169		r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru);
170		rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit);
171
172		nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
173			r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
174	}
175}
176
177static void htb_class_dump_details(struct rtnl_class *class,
178				   struct nl_dump_params *p)
179{
180	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
181
182	if (d == NULL)
183		return;
184
185	/* line 1 */
186	if (d->ch_mask & SCH_HTB_HAS_CEIL) {
187		double r, rbit;
188		char *ru, *rubit;
189
190		r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru);
191		rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit);
192
193		nl_dump(p, "    ceil %.2f%s/s (%.0f%s) log %u",
194			r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
195	}
196
197	if (d->ch_mask & SCH_HTB_HAS_PRIO)
198		nl_dump(p, " prio %u", d->ch_prio);
199
200	if (d->ch_mask & SCH_HTB_HAS_MTU)
201		nl_dump(p, " mtu %u", d->ch_mtu);
202
203	if (d->ch_mask & SCH_HTB_HAS_RBUFFER) {
204		double b;
205		char *bu;
206
207		b = nl_cancel_down_bytes(d->ch_rbuffer, &bu);
208		nl_dump(p, " rbuffer %.2f%s", b, bu);
209	}
210
211	if (d->ch_mask & SCH_HTB_HAS_CBUFFER) {
212		double b;
213		char *bu;
214
215		b = nl_cancel_down_bytes(d->ch_cbuffer, &bu);
216		nl_dump(p, " cbuffer %.2f%s", b, bu);
217	}
218
219	if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
220		nl_dump(p, " quantum %u", d->ch_quantum);
221
222	if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
223		nl_dump(p, " overhead %u", d->ch_overhead);
224
225	if (d->ch_mask & SCH_HTB_HAS_MPU)
226		nl_dump(p, " mpu %u", d->ch_mpu);
227}
228
229static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc)
230{
231	struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
232	struct tc_htb_glob opts;
233	struct nl_msg *msg;
234
235	if (d == NULL)
236		return NULL;
237
238	msg = nlmsg_alloc();
239	if (msg == NULL)
240		return NULL;
241
242	memset(&opts, 0, sizeof(opts));
243	opts.version = TC_HTB_PROTOVER;
244
245	if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
246		opts.rate2quantum = d->qh_rate2quantum;
247	if (d->qh_mask & SCH_HTB_HAS_DEFCLS)
248		opts.defcls = d->qh_defcls;
249
250	nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
251
252	return msg;
253}
254
255static uint8_t compute_cell(uint32_t rate, uint32_t mtu)
256{
257	uint8_t cell_log = 0;
258	while (mtu > 255) {
259		mtu >>= 1;
260		cell_log++;
261	}
262
263	return cell_log;
264}
265
266static struct nl_msg *htb_class_get_opts(struct rtnl_class *class)
267{
268	struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
269	uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
270	struct tc_htb_opt opts;
271	struct nl_msg *msg;
272	int buffer, cbuffer;
273	uint8_t overhead = 0, mpu = 0;
274
275	if (d == NULL)
276		return NULL;
277
278	msg = nlmsg_alloc();
279	memset(&opts, 0, sizeof(opts));
280
281	/* if not set, zero (0) is used as priority */
282	if (d->ch_mask & SCH_HTB_HAS_PRIO)
283		opts.prio = d->ch_prio;
284
285	if (d->ch_mask & SCH_HTB_HAS_MTU)
286		mtu = d->ch_mtu;
287	else
288		mtu = 1600; /* eth packet len */
289
290	if (!(d->ch_mask & SCH_HTB_HAS_RATE))
291		BUG();
292
293	rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate);
294	/* if cell_log not set, compute default value */
295	if (opts.rate.cell_log == UINT8_MAX)
296		opts.rate.cell_log = compute_cell(opts.rate.rate, mtu);
297
298	/* if not set, configured rate is used as ceil, which implies no borrowing */
299	if (d->ch_mask & SCH_HTB_HAS_CEIL)
300		rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil);
301	else
302		memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
303	/* if cell_log not set, compute default value */
304	if (opts.ceil.cell_log == UINT8_MAX)
305		opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu);
306
307	if (d->ch_mask & SCH_HTB_HAS_RBUFFER)
308		buffer = d->ch_rbuffer;
309	else
310		buffer = opts.rate.rate / nl_get_hz() + mtu;
311
312	opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate);
313
314	if (d->ch_mask & SCH_HTB_HAS_CBUFFER)
315		cbuffer = d->ch_cbuffer;
316	else
317		cbuffer = opts.ceil.rate / nl_get_hz() + mtu;
318
319	opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate);
320
321	if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
322		opts.quantum = d->ch_quantum;
323
324	if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
325		overhead = d->ch_overhead;
326
327	if (d->ch_mask & SCH_HTB_HAS_MPU)
328		mpu = d->ch_mpu;
329
330	opts.rate.mpu = mpu | (overhead << 8);
331	opts.ceil.mpu = mpu | (overhead << 8);
332
333	nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
334
335	rtnl_tc_build_rate_table(rtable, mpu, overhead,
336				 1 << opts.rate.cell_log,
337				 opts.rate.rate);
338	nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
339
340	rtnl_tc_build_rate_table(ctable, mpu, overhead,
341				 1 << opts.ceil.cell_log,
342				 opts.ceil.rate);
343	nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
344
345	return msg;
346}
347
348/**
349 * @name Attribute Modifications
350 * @{
351 */
352
353void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
354{
355	struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
356	if (d == NULL)
357		return;
358
359	d->qh_rate2quantum = rate2quantum;
360	d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
361}
362
363/**
364 * Set default class of the htb qdisc to the specified value
365 * @arg qdisc		qdisc to change
366 * @arg defcls		new default class
367 */
368void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
369{
370	struct rtnl_htb_qdisc *d = htb_qdisc(qdisc);
371	if (d == NULL)
372		return;
373
374	d->qh_defcls = defcls;
375	d->qh_mask |= SCH_HTB_HAS_DEFCLS;
376}
377
378void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
379{
380	struct rtnl_htb_class *d = htb_class(class);
381	if (d == NULL)
382		return;
383
384	d->ch_prio = prio;
385	d->ch_mask |= SCH_HTB_HAS_PRIO;
386}
387
388/**
389 * Set MTU of the data link.
390 * @arg class		HTB class to be modified.
391 * @arg mtu		New MTU in bytes.
392 *
393 * Sets MTU of the data link controlled by the HTB class.
394 * If not set, the Ethernet MTU (1600) is used.
395 */
396void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu)
397{
398	struct rtnl_htb_class *d = htb_class(class);
399	if (d == NULL)
400		return;
401
402	d->ch_mtu = mtu;
403	d->ch_mask |= SCH_HTB_HAS_MTU;
404}
405
406/**
407 * Set rate of HTB class.
408 * @arg class		HTB class to be modified.
409 * @arg rate		New rate in bytes per second.
410 */
411void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
412{
413	struct rtnl_htb_class *d = htb_class(class);
414	if (d == NULL)
415		return;
416
417	d->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */
418	d->ch_rate.rs_rate = rate;
419	d->ch_mask |= SCH_HTB_HAS_RATE;
420}
421
422/**
423 * Set ceil of HTB class.
424 * @arg class		HTB class to be modified.
425 * @arg ceil		New ceil in bytes per second.
426 */
427void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
428{
429	struct rtnl_htb_class *d = htb_class(class);
430	if (d == NULL)
431		return;
432
433	d->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */
434	d->ch_ceil.rs_rate = ceil;
435	d->ch_mask |= SCH_HTB_HAS_CEIL;
436}
437
438/**
439 * Set size of the rate bucket of HTB class.
440 * @arg class		HTB class to be modified.
441 * @arg rbuffer		New size in bytes.
442 */
443void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
444{
445	struct rtnl_htb_class *d = htb_class(class);
446	if (d == NULL)
447		return;
448
449	d->ch_rbuffer = rbuffer;
450	d->ch_mask |= SCH_HTB_HAS_RBUFFER;
451}
452
453/**
454 * Set size of the ceil bucket of HTB class.
455 * @arg class		HTB class to be modified.
456 * @arg cbuffer		New size in bytes.
457 */
458void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
459{
460	struct rtnl_htb_class *d = htb_class(class);
461	if (d == NULL)
462		return;
463
464	d->ch_cbuffer = cbuffer;
465	d->ch_mask |= SCH_HTB_HAS_CBUFFER;
466}
467
468/**
469 * Set how much bytes to serve from leaf at once of HTB class {use r2q}.
470 * @arg class		HTB class to be modified.
471 * @arg quantum		New size in bytes.
472 */
473void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
474{
475	struct rtnl_htb_class *d = htb_class(class);
476	if (d == NULL)
477		return;
478
479	d->ch_quantum = quantum;
480	d->ch_mask |= SCH_HTB_HAS_QUANTUM;
481}
482
483/**
484 * Set per-packet size overhead used in rate computations of HTB class.
485 * @arg class		HTB class to be modified.
486 * @arg overhead		Size in bytes.
487 */
488void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead)
489{
490	struct rtnl_htb_class *d = htb_class(class);
491	if (d == NULL)
492		return;
493
494	d->ch_overhead = overhead;
495	d->ch_mask |= SCH_HTB_HAS_OVERHEAD;
496}
497
498/**
499 * Set the minimum packet size used in rate computations of HTB class.
500 * @arg class		HTB class to be modified.
501 * @arg mpu		Size in bytes.
502 */
503void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu)
504{
505	struct rtnl_htb_class *d = htb_class(class);
506	if (d == NULL)
507		return;
508
509	d->ch_mpu = mpu;
510	d->ch_mask |= SCH_HTB_HAS_MPU;
511}
512
513/** @} */
514
515static struct rtnl_qdisc_ops htb_qdisc_ops = {
516	.qo_kind		= "htb",
517	.qo_msg_parser		= htb_qdisc_msg_parser,
518	.qo_free_data		= htb_qdisc_free_data,
519	.qo_dump[NL_DUMP_LINE]	= htb_qdisc_dump_line,
520	.qo_get_opts		= htb_qdisc_get_opts,
521};
522
523static struct rtnl_class_ops htb_class_ops = {
524	.co_kind		= "htb",
525	.co_msg_parser		= htb_class_msg_parser,
526	.co_free_data		= htb_class_free_data,
527	.co_dump = {
528	    [NL_DUMP_LINE]	= htb_class_dump_line,
529	    [NL_DUMP_DETAILS]	= htb_class_dump_details,
530	},
531	.co_get_opts		= htb_class_get_opts,
532};
533
534static void __init htb_init(void)
535{
536	rtnl_qdisc_register(&htb_qdisc_ops);
537	rtnl_class_register(&htb_class_ops);
538}
539
540static void __exit htb_exit(void)
541{
542	rtnl_qdisc_unregister(&htb_qdisc_ops);
543	rtnl_class_unregister(&htb_class_ops);
544}
545
546/** @} */
547