1/*
2 * lib/route/sch/tbf.c		TBF 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 */
11
12/**
13 * @ingroup qdisc_api
14 * @defgroup tbf Token Bucket Filter (TBF)
15 * @{
16 */
17
18#include <netlink-local.h>
19#include <netlink-tc.h>
20#include <netlink/netlink.h>
21#include <netlink/cache.h>
22#include <netlink/utils.h>
23#include <netlink/route/tc.h>
24#include <netlink/route/qdisc.h>
25#include <netlink/route/qdisc-modules.h>
26#include <netlink/route/class.h>
27#include <netlink/route/class-modules.h>
28#include <netlink/route/link.h>
29#include <netlink/route/sch/tbf.h>
30
31/** @cond SKIP */
32#define TBF_ATTR_LIMIT			0x01
33#define TBF_ATTR_RATE			0x02
34#define TBF_ATTR_PEAKRATE		0x10
35#define TBF_ATTR_MPU			0x80
36/** @endcond */
37
38static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc)
39{
40	return (struct rtnl_tbf *) qdisc->q_subdata;
41}
42
43static inline struct rtnl_tbf *tbf_alloc(struct rtnl_qdisc *qdisc)
44{
45	if (!qdisc->q_subdata)
46		qdisc->q_subdata = calloc(1, sizeof(struct rtnl_tbf));
47
48	return tbf_qdisc(qdisc);
49}
50
51static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = {
52	[TCA_TBF_PARMS]	= { .minlen = sizeof(struct tc_tbf_qopt) },
53};
54
55static int tbf_msg_parser(struct rtnl_qdisc *q)
56{
57	int err;
58	struct nlattr *tb[TCA_TBF_MAX + 1];
59	struct rtnl_tbf *tbf;
60
61	err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy);
62	if (err < 0)
63		return err;
64
65	tbf = tbf_alloc(q);
66	if (!tbf)
67		return -NLE_NOMEM;
68
69	if (tb[TCA_TBF_PARMS]) {
70		struct tc_tbf_qopt opts;
71		int bufsize;
72
73		nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts));
74		tbf->qt_limit = opts.limit;
75		tbf->qt_mpu = opts.rate.mpu;
76
77		rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate);
78		tbf->qt_rate_txtime = opts.buffer;
79		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
80					       opts.rate.rate);
81		tbf->qt_rate_bucket = bufsize;
82
83		rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate);
84		tbf->qt_peakrate_txtime = opts.mtu;
85		bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu),
86					       opts.peakrate.rate);
87		tbf->qt_peakrate_bucket = bufsize;
88
89		tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE |
90				TBF_ATTR_PEAKRATE);
91	}
92
93	return 0;
94}
95
96static void tbf_free_data(struct rtnl_qdisc *qdisc)
97{
98	free(qdisc->q_subdata);
99}
100
101static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
102{
103	double r, rbit, lim;
104	char *ru, *rubit, *limu;
105	struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
106
107	if (!tbf)
108		return;
109
110	r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru);
111	rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit);
112	lim = nl_cancel_down_bytes(tbf->qt_limit, &limu);
113
114	nl_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s",
115		r, ru, rbit, rubit, lim, limu);
116}
117
118static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
119{
120	struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
121
122	if (!tbf)
123		return;
124
125	if (1) {
126		char *bu, *cu;
127		double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu);
128		double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
129						 &cu);
130
131		nl_dump(p, "mpu %u rate-bucket-size %1.f%s "
132			   "rate-cell-size %.1f%s\n",
133			tbf->qt_mpu, bs, bu, cl, cu);
134
135	}
136
137	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
138		char *pru, *prbu, *bsu, *clu;
139		double pr, prb, bs, cl;
140
141		pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru);
142		prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu);
143		bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu);
144		cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log,
145					 &clu);
146
147		nl_dump_line(p, "    peak-rate %.2f%s/s (%.0f%s) "
148				"bucket-size %.1f%s cell-size %.1f%s"
149				"latency %.1f%s",
150			     pr, pru, prb, prbu, bs, bsu, cl, clu);
151	}
152}
153
154static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
155{
156	struct tc_tbf_qopt opts;
157	struct rtnl_tbf *tbf;
158	struct nl_msg *msg;
159	uint32_t rtab[RTNL_TC_RTABLE_SIZE];
160	uint32_t ptab[RTNL_TC_RTABLE_SIZE];
161	int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT;
162
163	memset(&opts, 0, sizeof(opts));
164
165	tbf = tbf_qdisc(qdisc);
166	if (!tbf)
167		return NULL;
168
169	if (!(tbf->qt_mask & required) != required)
170		return NULL;
171
172	opts.limit = tbf->qt_limit;
173	opts.buffer = tbf->qt_rate_txtime;
174	tbf->qt_rate.rs_mpu = tbf->qt_mpu;
175	rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
176
177	rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8,
178				 1 << tbf->qt_rate.rs_cell_log,
179				 tbf->qt_rate.rs_rate);
180
181	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
182		opts.mtu = tbf->qt_peakrate_txtime;
183		tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
184		rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate);
185
186		rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff,
187					 tbf->qt_mpu >> 8,
188					 1 << tbf->qt_peakrate.rs_cell_log,
189					 tbf->qt_peakrate.rs_rate);
190	}
191
192	msg = nlmsg_alloc();
193	if (!msg)
194		goto nla_put_failure;
195
196	NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts);
197	NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab);
198
199	if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
200		NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab);
201
202	return msg;
203
204nla_put_failure:
205	nlmsg_free(msg);
206	return NULL;
207}
208
209/**
210 * @name Attribute Access
211 * @{
212 */
213
214/**
215 * Set limit of TBF qdisc.
216 * @arg qdisc		TBF qdisc to be modified.
217 * @arg limit		New limit in bytes.
218 * @return 0 on success or a negative error code.
219 */
220int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
221{
222	struct rtnl_tbf *tbf;
223
224	tbf = tbf_alloc(qdisc);
225	if (!tbf)
226		return -NLE_NOMEM;
227
228	tbf->qt_limit = limit;
229	tbf->qt_mask |= TBF_ATTR_LIMIT;
230
231	return 0;
232}
233
234static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
235				int bucket)
236{
237	double limit;
238
239	limit = (double) spec->rs_rate * ((double) latency / 1000000.);
240	limit += bucket;
241
242	return limit;
243}
244
245/**
246 * Set limit of TBF qdisc by latency.
247 * @arg qdisc		TBF qdisc to be modified.
248 * @arg latency		Latency in micro seconds.
249 *
250 * Calculates and sets the limit based on the desired latency and the
251 * configured rate and peak rate. In order for this operation to succeed,
252 * the rate and if required the peak rate must have been set in advance.
253 *
254 * @f[
255 *   limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n
256 * @f]
257 * @f[
258 *   limit = min(limit_{rate},limit_{peak})
259 * @f]
260 *
261 * @return 0 on success or a negative error code.
262 */
263int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
264{
265	struct rtnl_tbf *tbf;
266	double limit, limit2;
267
268	tbf = tbf_alloc(qdisc);
269	if (!tbf)
270		return -NLE_NOMEM;
271
272	if (!(tbf->qt_mask & TBF_ATTR_RATE))
273		return -NLE_MISSING_ATTR;
274
275	limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket);
276
277	if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
278		limit2 = calc_limit(&tbf->qt_peakrate, latency,
279				    tbf->qt_peakrate_bucket);
280
281		if (limit2 < limit)
282			limit = limit2;
283	}
284
285	return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
286}
287
288/**
289 * Get limit of TBF qdisc.
290 * @arg qdisc		TBF qdisc.
291 * @return Limit in bytes or a negative error code.
292 */
293int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc)
294{
295	struct rtnl_tbf *tbf;
296
297	tbf = tbf_qdisc(qdisc);
298	if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT))
299		return tbf->qt_limit;
300	else
301		return -NLE_NOATTR;
302}
303
304/**
305 * Set MPU of TBF qdisc.
306 * @arg qdisc		TBF qdisc to be modified.
307 * @arg mpu		New MPU in bytes.
308 * @return 0 on success or a negative error code.
309 */
310int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu)
311{
312	struct rtnl_tbf *tbf;
313
314	tbf = tbf_alloc(qdisc);
315	if (!tbf)
316		return -NLE_NOMEM;
317
318	tbf->qt_mpu = mpu;
319	tbf->qt_mask |= TBF_ATTR_MPU;
320
321	return 0;
322}
323
324/**
325 * Get MPU of TBF qdisc.
326 * @arg qdisc		TBF qdisc.
327 * @return MPU in bytes or a negative error code.
328 */
329int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc)
330{
331	struct rtnl_tbf *tbf;
332
333	tbf = tbf_qdisc(qdisc);
334	if (tbf && (tbf->qt_mask & TBF_ATTR_MPU))
335		return tbf->qt_mpu;
336	else
337		return -NLE_NOATTR;
338}
339
340static inline int calc_cell_log(int cell, int bucket)
341{
342	if (cell > 0)
343		cell = rtnl_tc_calc_cell_log(cell);
344	else {
345		cell = 0;
346
347		if (!bucket)
348			bucket = 2047; /* defaults to cell_log=3 */
349
350		while ((bucket >> cell) > 255)
351			cell++;
352	}
353
354	return cell;
355}
356
357/**
358 * Set rate of TBF qdisc.
359 * @arg qdisc		TBF qdisc to be modified.
360 * @arg rate		New rate in bytes per second.
361 * @arg bucket		Size of bucket in bytes.
362 * @arg cell		Size of a rate cell or 0 to get default value.
363 * @return 0 on success or a negative error code.
364 */
365int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket,
366			    int cell)
367{
368	struct rtnl_tbf *tbf;
369	int cell_log;
370
371	tbf = tbf_alloc(qdisc);
372	if (!tbf)
373		return -NLE_NOMEM;
374
375	cell_log = calc_cell_log(cell, bucket);
376	if (cell_log < 0)
377		return cell_log;
378
379	tbf->qt_rate.rs_rate = rate;
380	tbf->qt_rate_bucket = bucket;
381	tbf->qt_rate.rs_cell_log = cell_log;
382	tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate);
383	tbf->qt_mask |= TBF_ATTR_RATE;
384
385	return 0;
386}
387
388/**
389 * Get rate of TBF qdisc.
390 * @arg qdisc		TBF qdisc.
391 * @return Rate in bytes per seconds or a negative error code.
392 */
393int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc)
394{
395	struct rtnl_tbf *tbf;
396
397	tbf = tbf_qdisc(qdisc);
398	if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
399		return tbf->qt_rate.rs_rate;
400	else
401		return -1;
402}
403
404/**
405 * Get rate bucket size of TBF qdisc.
406 * @arg qdisc		TBF qdisc.
407 * @return Size of rate bucket or a negative error code.
408 */
409int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc)
410{
411	struct rtnl_tbf *tbf;
412
413	tbf = tbf_qdisc(qdisc);
414	if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
415		return tbf->qt_rate_bucket;
416	else
417		return -1;
418}
419
420/**
421 * Get rate cell size of TBF qdisc.
422 * @arg qdisc		TBF qdisc.
423 * @return Size of rate cell in bytes or a negative error code.
424 */
425int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc)
426{
427	struct rtnl_tbf *tbf;
428
429	tbf = tbf_qdisc(qdisc);
430	if (tbf && (tbf->qt_mask & TBF_ATTR_RATE))
431		return (1 << tbf->qt_rate.rs_cell_log);
432	else
433		return -1;
434}
435
436/**
437 * Set peak rate of TBF qdisc.
438 * @arg qdisc		TBF qdisc to be modified.
439 * @arg rate		New peak rate in bytes per second.
440 * @arg bucket		Size of peakrate bucket.
441 * @arg cell		Size of a peakrate cell or 0 to get default value.
442 * @return 0 on success or a negative error code.
443 */
444int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
445				int cell)
446{
447	struct rtnl_tbf *tbf;
448	int cell_log;
449
450	tbf = tbf_alloc(qdisc);
451	if (!tbf)
452		return -NLE_NOMEM;
453
454	cell_log = calc_cell_log(cell, bucket);
455	if (cell_log < 0)
456		return cell_log;
457
458	tbf->qt_peakrate.rs_rate = rate;
459	tbf->qt_peakrate_bucket = bucket;
460	tbf->qt_peakrate.rs_cell_log = cell_log;
461	tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate);
462
463	tbf->qt_mask |= TBF_ATTR_PEAKRATE;
464
465	return 0;
466}
467
468/**
469 * Get peak rate of TBF qdisc.
470 * @arg qdisc		TBF qdisc.
471 * @return Peak rate in bytes per seconds or a negative error code.
472 */
473int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc)
474{
475	struct rtnl_tbf *tbf;
476
477	tbf = tbf_qdisc(qdisc);
478	if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
479		return tbf->qt_peakrate.rs_rate;
480	else
481		return -1;
482}
483
484/**
485 * Get peak rate bucket size of TBF qdisc.
486 * @arg qdisc		TBF qdisc.
487 * @return Size of peak rate bucket or a negative error code.
488 */
489int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc)
490{
491	struct rtnl_tbf *tbf;
492
493	tbf = tbf_qdisc(qdisc);
494	if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
495		return tbf->qt_peakrate_bucket;
496	else
497		return -1;
498}
499
500/**
501 * Get peak rate cell size of TBF qdisc.
502 * @arg qdisc		TBF qdisc.
503 * @return Size of peak rate cell in bytes or a negative error code.
504 */
505int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
506{
507	struct rtnl_tbf *tbf;
508
509	tbf = tbf_qdisc(qdisc);
510	if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE))
511		return (1 << tbf->qt_peakrate.rs_cell_log);
512	else
513		return -1;
514}
515
516/** @} */
517
518static struct rtnl_qdisc_ops tbf_qdisc_ops = {
519	.qo_kind		= "tbf",
520	.qo_msg_parser		= tbf_msg_parser,
521	.qo_dump = {
522	    [NL_DUMP_LINE]	= tbf_dump_line,
523	    [NL_DUMP_DETAILS]	= tbf_dump_details,
524	},
525	.qo_free_data		= tbf_free_data,
526	.qo_get_opts		= tbf_get_opts,
527};
528
529static void __init tbf_init(void)
530{
531	rtnl_qdisc_register(&tbf_qdisc_ops);
532}
533
534static void __exit tbf_exit(void)
535{
536	rtnl_qdisc_unregister(&tbf_qdisc_ops);
537}
538
539/** @} */
540