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