1/*
2 * q_cbq.c		CBQ.
3 *
4 *		This program is free software; you can redistribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21#include <string.h>
22
23#include "utils.h"
24#include "tc_util.h"
25#include "tc_cbq.h"
26
27static void explain_class(void)
28{
29	fprintf(stderr, "Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]\n");
30	fprintf(stderr, "               [ minburst PKTS ] [ bounded ] [ isolated ]\n");
31	fprintf(stderr, "               [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]\n");
32	fprintf(stderr, "               [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]\n");
33	fprintf(stderr, "               [ estimator INTERVAL TIME_CONSTANT ]\n");
34	fprintf(stderr, "               [ split CLASSID ] [ defmap MASK/CHANGE ]\n");
35}
36
37static void explain(void)
38{
39	fprintf(stderr, "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n");
40	fprintf(stderr, "               [ cell BYTES ] [ ewma LOG ]\n");
41}
42
43static void explain1(char *arg)
44{
45	fprintf(stderr, "Illegal \"%s\"\n", arg);
46}
47
48#define usage() return(-1)
49
50static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
51{
52	struct tc_ratespec r;
53	struct tc_cbq_lssopt lss;
54	__u32 rtab[256];
55	unsigned mpu=0, avpkt=0, allot=0;
56	int cell_log=-1;
57	int ewma_log=-1;
58	struct rtattr *tail;
59
60	memset(&lss, 0, sizeof(lss));
61	memset(&r, 0, sizeof(r));
62
63	while (argc > 0) {
64		if (strcmp(*argv, "bandwidth") == 0 ||
65		    strcmp(*argv, "rate") == 0) {
66			NEXT_ARG();
67			if (get_rate(&r.rate, *argv)) {
68				explain1("bandwidth");
69				return -1;
70			}
71		} else if (strcmp(*argv, "ewma") == 0) {
72			NEXT_ARG();
73			if (get_integer(&ewma_log, *argv, 0)) {
74				explain1("ewma");
75				return -1;
76			}
77			if (ewma_log > 31) {
78				fprintf(stderr, "ewma_log must be < 32\n");
79				return -1;
80			}
81		} else if (strcmp(*argv, "cell") == 0) {
82			unsigned cell;
83			int i;
84			NEXT_ARG();
85			if (get_size(&cell, *argv)) {
86				explain1("cell");
87				return -1;
88			}
89			for (i=0; i<32; i++)
90				if ((1<<i) == cell)
91					break;
92			if (i>=32) {
93				fprintf(stderr, "cell must be 2^n\n");
94				return -1;
95			}
96			cell_log = i;
97		} else if (strcmp(*argv, "avpkt") == 0) {
98			NEXT_ARG();
99			if (get_size(&avpkt, *argv)) {
100				explain1("avpkt");
101				return -1;
102			}
103		} else if (strcmp(*argv, "mpu") == 0) {
104			NEXT_ARG();
105			if (get_size(&mpu, *argv)) {
106				explain1("mpu");
107				return -1;
108			}
109		} else if (strcmp(*argv, "allot") == 0) {
110			NEXT_ARG();
111			/* Accept and ignore "allot" for backward compatibility */
112			if (get_size(&allot, *argv)) {
113				explain1("allot");
114				return -1;
115			}
116		} else if (strcmp(*argv, "help") == 0) {
117			explain();
118			return -1;
119		} else {
120			fprintf(stderr, "What is \"%s\"?\n", *argv);
121			explain();
122			return -1;
123		}
124		argc--; argv++;
125	}
126
127	/* OK. All options are parsed. */
128
129	if (r.rate == 0) {
130		fprintf(stderr, "CBQ: bandwidth is required parameter.\n");
131		return -1;
132	}
133	if (avpkt == 0) {
134		fprintf(stderr, "CBQ: \"avpkt\" is required.\n");
135		return -1;
136	}
137	if (allot < (avpkt*3)/2)
138		allot = (avpkt*3)/2;
139
140	if ((cell_log = tc_calc_rtable(r.rate, rtab, cell_log, allot, mpu)) < 0) {
141		fprintf(stderr, "CBQ: failed to calculate rate table.\n");
142		return -1;
143	}
144	r.cell_log = cell_log;
145	r.mpu = mpu;
146
147	if (ewma_log < 0)
148		ewma_log = TC_CBQ_DEF_EWMA;
149	lss.ewma_log = ewma_log;
150	lss.maxidle = tc_cbq_calc_maxidle(r.rate, r.rate, avpkt, lss.ewma_log, 0);
151	lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
152	lss.avpkt = avpkt;
153
154	tail = NLMSG_TAIL(n);
155	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
156	addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
157	addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
158	addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
159	if (show_raw) {
160		int i;
161		for (i=0; i<256; i++)
162			printf("%u ", rtab[i]);
163		printf("\n");
164	}
165	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
166	return 0;
167}
168
169static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
170{
171	int wrr_ok=0, fopt_ok=0;
172	struct tc_ratespec r;
173	struct tc_cbq_lssopt lss;
174	struct tc_cbq_wrropt wrr;
175	struct tc_cbq_fopt fopt;
176	struct tc_cbq_ovl ovl;
177	__u32 rtab[256];
178	unsigned mpu=0;
179	int cell_log=-1;
180	int ewma_log=-1;
181	unsigned bndw = 0;
182	unsigned minburst=0, maxburst=0;
183	struct rtattr *tail;
184
185	memset(&r, 0, sizeof(r));
186	memset(&lss, 0, sizeof(lss));
187	memset(&wrr, 0, sizeof(wrr));
188	memset(&fopt, 0, sizeof(fopt));
189	memset(&ovl, 0, sizeof(ovl));
190
191	while (argc > 0) {
192		if (strcmp(*argv, "rate") == 0) {
193			NEXT_ARG();
194			if (get_rate(&r.rate, *argv)) {
195				explain1("rate");
196				return -1;
197			}
198		} else if (strcmp(*argv, "bandwidth") == 0) {
199			NEXT_ARG();
200			if (get_rate(&bndw, *argv)) {
201				explain1("bandwidth");
202				return -1;
203			}
204		} else if (strcmp(*argv, "minidle") == 0) {
205			NEXT_ARG();
206			if (get_u32(&lss.minidle, *argv, 0)) {
207				explain1("minidle");
208				return -1;
209			}
210			lss.change |= TCF_CBQ_LSS_MINIDLE;
211		} else if (strcmp(*argv, "minburst") == 0) {
212			NEXT_ARG();
213			if (get_u32(&minburst, *argv, 0)) {
214				explain1("minburst");
215				return -1;
216			}
217			lss.change |= TCF_CBQ_LSS_OFFTIME;
218		} else if (strcmp(*argv, "maxburst") == 0) {
219			NEXT_ARG();
220			if (get_u32(&maxburst, *argv, 0)) {
221				explain1("maxburst");
222				return -1;
223			}
224			lss.change |= TCF_CBQ_LSS_MAXIDLE;
225		} else if (strcmp(*argv, "bounded") == 0) {
226			lss.flags |= TCF_CBQ_LSS_BOUNDED;
227			lss.change |= TCF_CBQ_LSS_FLAGS;
228		} else if (strcmp(*argv, "borrow") == 0) {
229			lss.flags &= ~TCF_CBQ_LSS_BOUNDED;
230			lss.change |= TCF_CBQ_LSS_FLAGS;
231		} else if (strcmp(*argv, "isolated") == 0) {
232			lss.flags |= TCF_CBQ_LSS_ISOLATED;
233			lss.change |= TCF_CBQ_LSS_FLAGS;
234		} else if (strcmp(*argv, "sharing") == 0) {
235			lss.flags &= ~TCF_CBQ_LSS_ISOLATED;
236			lss.change |= TCF_CBQ_LSS_FLAGS;
237		} else if (strcmp(*argv, "ewma") == 0) {
238			NEXT_ARG();
239			if (get_integer(&ewma_log, *argv, 0)) {
240				explain1("ewma");
241				return -1;
242			}
243			if (ewma_log > 31) {
244				fprintf(stderr, "ewma_log must be < 32\n");
245				return -1;
246			}
247			lss.change |= TCF_CBQ_LSS_EWMA;
248		} else if (strcmp(*argv, "cell") == 0) {
249			unsigned cell;
250			int i;
251			NEXT_ARG();
252			if (get_size(&cell, *argv)) {
253				explain1("cell");
254				return -1;
255			}
256			for (i=0; i<32; i++)
257				if ((1<<i) == cell)
258					break;
259			if (i>=32) {
260				fprintf(stderr, "cell must be 2^n\n");
261				return -1;
262			}
263			cell_log = i;
264		} else if (strcmp(*argv, "prio") == 0) {
265			unsigned prio;
266			NEXT_ARG();
267			if (get_u32(&prio, *argv, 0)) {
268				explain1("prio");
269				return -1;
270			}
271			if (prio > TC_CBQ_MAXPRIO) {
272				fprintf(stderr, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO);
273				return -1;
274			}
275			wrr.priority = prio;
276			wrr_ok++;
277		} else if (strcmp(*argv, "allot") == 0) {
278			NEXT_ARG();
279			if (get_size(&wrr.allot, *argv)) {
280				explain1("allot");
281				return -1;
282			}
283		} else if (strcmp(*argv, "avpkt") == 0) {
284			NEXT_ARG();
285			if (get_size(&lss.avpkt, *argv)) {
286				explain1("avpkt");
287				return -1;
288			}
289			lss.change |= TCF_CBQ_LSS_AVPKT;
290		} else if (strcmp(*argv, "mpu") == 0) {
291			NEXT_ARG();
292			if (get_size(&mpu, *argv)) {
293				explain1("mpu");
294				return -1;
295			}
296		} else if (strcmp(*argv, "weight") == 0) {
297			NEXT_ARG();
298			if (get_size(&wrr.weight, *argv)) {
299				explain1("weight");
300				return -1;
301			}
302			wrr_ok++;
303		} else if (strcmp(*argv, "split") == 0) {
304			NEXT_ARG();
305			if (get_tc_classid(&fopt.split, *argv)) {
306				fprintf(stderr, "Invalid split node ID.\n");
307				usage();
308			}
309			fopt_ok++;
310		} else if (strcmp(*argv, "defmap") == 0) {
311			int err;
312			NEXT_ARG();
313			err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange);
314			if (err < 1) {
315				fprintf(stderr, "Invalid defmap, should be MASK32[/MASK]\n");
316				return -1;
317			}
318			if (err == 1)
319				fopt.defchange = ~0;
320			fopt_ok++;
321		} else if (strcmp(*argv, "help") == 0) {
322			explain_class();
323			return -1;
324		} else {
325			fprintf(stderr, "What is \"%s\"?\n", *argv);
326			explain_class();
327			return -1;
328		}
329		argc--; argv++;
330	}
331
332	/* OK. All options are parsed. */
333
334	/* 1. Prepare link sharing scheduler parameters */
335	if (r.rate) {
336		unsigned pktsize = wrr.allot;
337		if (wrr.allot < (lss.avpkt*3)/2)
338			wrr.allot = (lss.avpkt*3)/2;
339		if ((cell_log = tc_calc_rtable(r.rate, rtab, cell_log, pktsize, mpu)) < 0) {
340			fprintf(stderr, "CBQ: failed to calculate rate table.\n");
341			return -1;
342		}
343		r.cell_log = cell_log;
344		r.mpu = mpu;
345	}
346	if (ewma_log < 0)
347		ewma_log = TC_CBQ_DEF_EWMA;
348	lss.ewma_log = ewma_log;
349	if (lss.change&(TCF_CBQ_LSS_OFFTIME|TCF_CBQ_LSS_MAXIDLE)) {
350		if (lss.avpkt == 0) {
351			fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n");
352			return -1;
353		}
354		if (bndw==0 || r.rate == 0) {
355			fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n");
356			return -1;
357		}
358	}
359	if (wrr.priority == 0 && (n->nlmsg_flags&NLM_F_EXCL)) {
360		wrr_ok = 1;
361		wrr.priority = TC_CBQ_MAXPRIO;
362		if (wrr.allot == 0)
363			wrr.allot = (lss.avpkt*3)/2;
364	}
365	if (wrr_ok) {
366		if (wrr.weight == 0)
367			wrr.weight = (wrr.priority == TC_CBQ_MAXPRIO) ? 1 : r.rate;
368		if (wrr.allot == 0) {
369			fprintf(stderr, "CBQ: \"allot\" is required to set WRR parameters.\n");
370			return -1;
371		}
372	}
373	if (lss.change&TCF_CBQ_LSS_MAXIDLE) {
374		lss.maxidle = tc_cbq_calc_maxidle(bndw, r.rate, lss.avpkt, ewma_log, maxburst);
375		lss.change |= TCF_CBQ_LSS_MAXIDLE;
376		lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
377	}
378	if (lss.change&TCF_CBQ_LSS_OFFTIME) {
379		lss.offtime = tc_cbq_calc_offtime(bndw, r.rate, lss.avpkt, ewma_log, minburst);
380		lss.change |= TCF_CBQ_LSS_OFFTIME;
381		lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
382	}
383	if (lss.change&TCF_CBQ_LSS_MINIDLE) {
384		lss.minidle <<= lss.ewma_log;
385		lss.change |= TCF_CBQ_LSS_EWMA;
386	}
387
388	tail = NLMSG_TAIL(n);
389	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
390	if (lss.change) {
391		lss.change |= TCF_CBQ_LSS_FLAGS;
392		addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
393	}
394	if (wrr_ok)
395		addattr_l(n, 1024, TCA_CBQ_WRROPT, &wrr, sizeof(wrr));
396	if (fopt_ok)
397		addattr_l(n, 1024, TCA_CBQ_FOPT, &fopt, sizeof(fopt));
398	if (r.rate) {
399		addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
400		addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
401		if (show_raw) {
402			int i;
403			for (i=0; i<256; i++)
404				printf("%u ", rtab[i]);
405			printf("\n");
406		}
407	}
408	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
409	return 0;
410}
411
412
413static int cbq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
414{
415	struct rtattr *tb[TCA_CBQ_MAX+1];
416	struct tc_ratespec *r = NULL;
417	struct tc_cbq_lssopt *lss = NULL;
418	struct tc_cbq_wrropt *wrr = NULL;
419	struct tc_cbq_fopt *fopt = NULL;
420	struct tc_cbq_ovl *ovl = NULL;
421
422	if (opt == NULL)
423		return 0;
424
425	parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
426
427	if (tb[TCA_CBQ_RATE]) {
428		if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
429			fprintf(stderr, "CBQ: too short rate opt\n");
430		else
431			r = RTA_DATA(tb[TCA_CBQ_RATE]);
432	}
433	if (tb[TCA_CBQ_LSSOPT]) {
434		if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
435			fprintf(stderr, "CBQ: too short lss opt\n");
436		else
437			lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
438	}
439	if (tb[TCA_CBQ_WRROPT]) {
440		if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
441			fprintf(stderr, "CBQ: too short wrr opt\n");
442		else
443			wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
444	}
445	if (tb[TCA_CBQ_FOPT]) {
446		if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
447			fprintf(stderr, "CBQ: too short fopt\n");
448		else
449			fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
450	}
451	if (tb[TCA_CBQ_OVL_STRATEGY]) {
452		if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
453			fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n",
454				(unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
455				(unsigned) sizeof(*ovl));
456		else
457			ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
458	}
459
460	if (r) {
461		char buf[64];
462		print_rate(buf, sizeof(buf), r->rate);
463		fprintf(f, "rate %s ", buf);
464		if (show_details) {
465			fprintf(f, "cell %ub ", 1<<r->cell_log);
466			if (r->mpu)
467				fprintf(f, "mpu %ub ", r->mpu);
468		}
469	}
470	if (lss && lss->flags) {
471		int comma=0;
472		fprintf(f, "(");
473		if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
474			fprintf(f, "bounded");
475			comma=1;
476		}
477		if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
478			if (comma)
479				fprintf(f, ",");
480			fprintf(f, "isolated");
481		}
482		fprintf(f, ") ");
483	}
484	if (wrr) {
485		if (wrr->priority != TC_CBQ_MAXPRIO)
486			fprintf(f, "prio %u", wrr->priority);
487		else
488			fprintf(f, "prio no-transmit");
489		if (show_details) {
490			char buf[64];
491			fprintf(f, "/%u ", wrr->cpriority);
492			if (wrr->weight != 1) {
493				print_rate(buf, sizeof(buf), wrr->weight);
494				fprintf(f, "weight %s ", buf);
495			}
496			if (wrr->allot)
497				fprintf(f, "allot %ub ", wrr->allot);
498		}
499	}
500	if (lss && show_details) {
501		fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt);
502		if (lss->maxidle) {
503			fprintf(f, "maxidle %luus ", tc_core_tick2usec(lss->maxidle>>lss->ewma_log));
504			if (show_raw)
505				fprintf(f, "[%08x] ", lss->maxidle);
506		}
507		if (lss->minidle!=0x7fffffff) {
508			fprintf(f, "minidle %luus ", tc_core_tick2usec(lss->minidle>>lss->ewma_log));
509			if (show_raw)
510				fprintf(f, "[%08x] ", lss->minidle);
511		}
512		if (lss->offtime) {
513			fprintf(f, "offtime %luus ", tc_core_tick2usec(lss->offtime));
514			if (show_raw)
515				fprintf(f, "[%08x] ", lss->offtime);
516		}
517	}
518	if (fopt && show_details) {
519		char buf[64];
520		print_tc_classid(buf, sizeof(buf), fopt->split);
521		fprintf(f, "\nsplit %s ", buf);
522		if (fopt->defmap) {
523			fprintf(f, "defmap %08x", fopt->defmap);
524		}
525	}
526	return 0;
527}
528
529static int cbq_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
530{
531	struct tc_cbq_xstats *st;
532
533	if (xstats == NULL)
534		return 0;
535
536	if (RTA_PAYLOAD(xstats) < sizeof(*st))
537		return -1;
538
539	st = RTA_DATA(xstats);
540	fprintf(f, "  borrowed %u overactions %u avgidle %g undertime %g", st->borrows,
541		st->overactions, (double)st->avgidle, (double)st->undertime);
542	return 0;
543}
544
545struct qdisc_util cbq_qdisc_util = {
546	.id		= "cbq",
547	.parse_qopt	= cbq_parse_opt,
548	.print_qopt	= cbq_print_opt,
549	.print_xstats	= cbq_print_xstats,
550	.parse_copt	= cbq_parse_class_opt,
551	.print_copt	= cbq_print_opt,
552};
553
554