1/*
2 * q_gred.c		GRED.
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:    J Hadi Salim(hadi@nortelnetworks.com)
10 *             code ruthlessly ripped from
11 *	       Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
12 *
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <unistd.h>
18#include <syslog.h>
19#include <fcntl.h>
20#include <sys/socket.h>
21#include <netinet/in.h>
22#include <arpa/inet.h>
23#include <string.h>
24#include <math.h>
25
26#include "utils.h"
27#include "tc_util.h"
28
29#include "tc_red.h"
30
31
32#if 0
33#define DPRINTF(format,args...) fprintf(stderr,format,##args)
34#else
35#define DPRINTF(format,args...)
36#endif
37
38static void explain(void)
39{
40	fprintf(stderr, "Usage: tc qdisc { add | replace | change } ... gred setup vqs NUMBER\n");
41	fprintf(stderr, "           default DEFAULT_VQ [ grio ] [ limit BYTES ]\n");
42	fprintf(stderr, "       tc qdisc change ... gred vq VQ [ prio VALUE ] limit BYTES\n");
43	fprintf(stderr, "           min BYTES max BYTES avpkt BYTES [ burst PACKETS ]\n");
44	fprintf(stderr, "           [ probability PROBABILITY ] [ bandwidth KBPS ]\n");
45}
46
47static int init_gred(struct qdisc_util *qu, int argc, char **argv,
48		     struct nlmsghdr *n)
49{
50
51	struct rtattr *tail;
52	struct tc_gred_sopt opt = { 0 };
53	__u32 limit = 0;
54
55	opt.def_DP = MAX_DPs;
56
57	while (argc > 0) {
58		DPRINTF(stderr,"init_gred: invoked with %s\n",*argv);
59		if (strcmp(*argv, "vqs") == 0 ||
60		    strcmp(*argv, "DPs") == 0) {
61			NEXT_ARG();
62			if (get_unsigned(&opt.DPs, *argv, 10)) {
63				fprintf(stderr, "Illegal \"vqs\"\n");
64				return -1;
65			} else if (opt.DPs > MAX_DPs) {
66				fprintf(stderr, "GRED: only %u VQs are "
67					"currently supported\n", MAX_DPs);
68				return -1;
69			}
70		} else if (strcmp(*argv, "default") == 0) {
71			if (opt.DPs == 0) {
72				fprintf(stderr, "\"default\" must be defined "
73					"after \"vqs\"\n");
74				return -1;
75			}
76			NEXT_ARG();
77			if (get_unsigned(&opt.def_DP, *argv, 10)) {
78				fprintf(stderr, "Illegal \"default\"\n");
79				return -1;
80			} else if (opt.def_DP >= opt.DPs) {
81				fprintf(stderr, "\"default\" must be less than "
82					"\"vqs\"\n");
83				return -1;
84			}
85		} else if (strcmp(*argv, "grio") == 0) {
86			opt.grio = 1;
87		} else if (strcmp(*argv, "limit") == 0) {
88			NEXT_ARG();
89			if (get_size(&limit, *argv)) {
90				fprintf(stderr, "Illegal \"limit\"\n");
91				return -1;
92			}
93		} else if (strcmp(*argv, "help") == 0) {
94			explain();
95			return -1;
96		} else {
97			fprintf(stderr, "What is \"%s\"?\n", *argv);
98			explain();
99			return -1;
100		}
101		argc--; argv++;
102	}
103
104	if (!opt.DPs || opt.def_DP == MAX_DPs) {
105		fprintf(stderr, "Illegal gred setup parameters \n");
106		return -1;
107	}
108
109	DPRINTF("TC_GRED: sending DPs=%u def_DP=%u\n",opt.DPs,opt.def_DP);
110	n->nlmsg_flags|=NLM_F_CREATE;
111	tail = NLMSG_TAIL(n);
112	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
113	addattr_l(n, 1024, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt));
114	if (limit)
115		addattr32(n, 1024, TCA_GRED_LIMIT, limit);
116	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
117	return 0;
118}
119/*
120^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
121*/
122static int gred_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
123{
124	int ok=0;
125	struct tc_gred_qopt opt = { 0 };
126	unsigned burst = 0;
127	unsigned avpkt = 0;
128	double probability = 0.02;
129	unsigned rate = 0;
130	int parm;
131	__u8 sbuf[256];
132	struct rtattr *tail;
133	__u32 max_P;
134
135	opt.DP = MAX_DPs;
136
137	while (argc > 0) {
138		if (strcmp(*argv, "limit") == 0) {
139			NEXT_ARG();
140			if (get_size(&opt.limit, *argv)) {
141				fprintf(stderr, "Illegal \"limit\"\n");
142				return -1;
143			}
144			ok++;
145		} else if (strcmp(*argv, "setup") == 0) {
146			if (ok) {
147				fprintf(stderr, "Illegal \"setup\"\n");
148				return -1;
149			}
150			return init_gred(qu, argc-1, argv+1, n);
151		} else if (strcmp(*argv, "min") == 0) {
152			NEXT_ARG();
153			if (get_size(&opt.qth_min, *argv)) {
154				fprintf(stderr, "Illegal \"min\"\n");
155				return -1;
156			}
157			ok++;
158		} else if (strcmp(*argv, "max") == 0) {
159			NEXT_ARG();
160			if (get_size(&opt.qth_max, *argv)) {
161				fprintf(stderr, "Illegal \"max\"\n");
162				return -1;
163			}
164			ok++;
165		} else if (strcmp(*argv, "vq") == 0 ||
166			   strcmp(*argv, "DP") == 0) {
167			NEXT_ARG();
168			if (get_unsigned(&opt.DP, *argv, 10)) {
169				fprintf(stderr, "Illegal \"vq\"\n");
170				return -1;
171			} else if (opt.DP >= MAX_DPs) {
172				fprintf(stderr, "GRED: only %u VQs are "
173					"currently supported\n", MAX_DPs);
174				return -1;
175			} /* need a better error check */
176			ok++;
177		} else if (strcmp(*argv, "burst") == 0) {
178			NEXT_ARG();
179			if (get_unsigned(&burst, *argv, 0)) {
180				fprintf(stderr, "Illegal \"burst\"\n");
181				return -1;
182			}
183			ok++;
184		} else if (strcmp(*argv, "avpkt") == 0) {
185			NEXT_ARG();
186			if (get_size(&avpkt, *argv)) {
187				fprintf(stderr, "Illegal \"avpkt\"\n");
188				return -1;
189			}
190			ok++;
191		} else if (strcmp(*argv, "probability") == 0) {
192			NEXT_ARG();
193			if (sscanf(*argv, "%lg", &probability) != 1) {
194				fprintf(stderr, "Illegal \"probability\"\n");
195				return -1;
196			}
197			ok++;
198		} else if (strcmp(*argv, "prio") == 0) {
199			NEXT_ARG();
200			opt.prio=strtol(*argv, (char **)NULL, 10);
201			/* some error check here */
202			ok++;
203		} else if (strcmp(*argv, "bandwidth") == 0) {
204			NEXT_ARG();
205			if (get_rate(&rate, *argv)) {
206				fprintf(stderr, "Illegal \"bandwidth\"\n");
207				return -1;
208			}
209			ok++;
210		} else if (strcmp(*argv, "help") == 0) {
211			explain();
212			return -1;
213		} else {
214			fprintf(stderr, "What is \"%s\"?\n", *argv);
215			explain();
216			return -1;
217		}
218		argc--; argv++;
219	}
220
221	if (!ok) {
222		explain();
223		return -1;
224	}
225	if (opt.DP == MAX_DPs || !opt.limit || !opt.qth_min || !opt.qth_max ||
226	    !avpkt) {
227		fprintf(stderr, "Required parameter (vq, limit, min, max, "
228			"avpkt) is missing\n");
229		return -1;
230	}
231	if (!burst) {
232		burst = (2 * opt.qth_min + opt.qth_max) / (3 * avpkt);
233		fprintf(stderr, "GRED: set burst to %u\n", burst);
234	}
235	if (!rate) {
236		get_rate(&rate, "10Mbit");
237		fprintf(stderr, "GRED: set bandwidth to 10Mbit\n");
238	}
239	if ((parm = tc_red_eval_ewma(opt.qth_min, burst, avpkt)) < 0) {
240		fprintf(stderr, "GRED: failed to calculate EWMA constant.\n");
241		return -1;
242	}
243	if (parm >= 10)
244		fprintf(stderr, "GRED: WARNING. Burst %u seems to be too "
245		    "large.\n", burst);
246	opt.Wlog = parm;
247	if ((parm = tc_red_eval_P(opt.qth_min, opt.qth_max, probability)) < 0) {
248		fprintf(stderr, "GRED: failed to calculate probability.\n");
249		return -1;
250	}
251	opt.Plog = parm;
252	if ((parm = tc_red_eval_idle_damping(opt.Wlog, avpkt, rate, sbuf)) < 0)
253	    {
254		fprintf(stderr, "GRED: failed to calculate idle damping "
255		    "table.\n");
256		return -1;
257	}
258	opt.Scell_log = parm;
259
260	tail = NLMSG_TAIL(n);
261	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
262	addattr_l(n, 1024, TCA_GRED_PARMS, &opt, sizeof(opt));
263	addattr_l(n, 1024, TCA_GRED_STAB, sbuf, 256);
264	max_P = probability * pow(2, 32);
265	addattr32(n, 1024, TCA_GRED_MAX_P, max_P);
266	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
267	return 0;
268}
269
270static int gred_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
271{
272	struct rtattr *tb[TCA_GRED_MAX + 1];
273	struct tc_gred_sopt *sopt;
274	struct tc_gred_qopt *qopt;
275	__u32 *max_p = NULL;
276	__u32 *limit = NULL;
277	unsigned i;
278	SPRINT_BUF(b1);
279	SPRINT_BUF(b2);
280	SPRINT_BUF(b3);
281
282	if (opt == NULL)
283		return 0;
284
285	parse_rtattr_nested(tb, TCA_GRED_MAX, opt);
286
287	if (tb[TCA_GRED_PARMS] == NULL)
288		return -1;
289
290	if (tb[TCA_GRED_MAX_P] &&
291	    RTA_PAYLOAD(tb[TCA_GRED_MAX_P]) >= sizeof(__u32) * MAX_DPs)
292		max_p = RTA_DATA(tb[TCA_GRED_MAX_P]);
293
294	if (tb[TCA_GRED_LIMIT] &&
295	    RTA_PAYLOAD(tb[TCA_GRED_LIMIT]) == sizeof(__u32))
296		limit = RTA_DATA(tb[TCA_GRED_LIMIT]);
297
298	sopt = RTA_DATA(tb[TCA_GRED_DPS]);
299	qopt = RTA_DATA(tb[TCA_GRED_PARMS]);
300	if (RTA_PAYLOAD(tb[TCA_GRED_DPS]) < sizeof(*sopt) ||
301	    RTA_PAYLOAD(tb[TCA_GRED_PARMS]) < sizeof(*qopt)*MAX_DPs) {
302		fprintf(f,"\n GRED received message smaller than expected\n");
303		return -1;
304	}
305
306/* Bad hack! should really return a proper message as shown above*/
307
308	fprintf(f, "vqs %u default %u %s",
309		sopt->DPs,
310		sopt->def_DP,
311		sopt->grio ? "grio " : "");
312
313	if (limit)
314		fprintf(f, "limit %s ",
315			sprint_size(*limit, b1));
316
317	for (i=0;i<MAX_DPs;i++, qopt++) {
318		if (qopt->DP >= MAX_DPs) continue;
319		fprintf(f, "\n vq %u prio %hhu limit %s min %s max %s ",
320			qopt->DP,
321			qopt->prio,
322			sprint_size(qopt->limit, b1),
323			sprint_size(qopt->qth_min, b2),
324			sprint_size(qopt->qth_max, b3));
325		if (show_details) {
326			fprintf(f, "ewma %u ", qopt->Wlog);
327			if (max_p)
328				fprintf(f, "probability %lg ", max_p[i] / pow(2, 32));
329			else
330				fprintf(f, "Plog %u ", qopt->Plog);
331			fprintf(f, "Scell_log %u ", qopt->Scell_log);
332		}
333		if (show_stats) {
334			fprintf(f, "\n  Queue size: average %s current %s ",
335				sprint_size(qopt->qave, b1),
336				sprint_size(qopt->backlog, b2));
337			fprintf(f, "\n  Dropped packets: forced %u early %u pdrop %u other %u ",
338				qopt->forced,
339				qopt->early,
340				qopt->pdrop,
341				qopt->other);
342			fprintf(f, "\n  Total packets: %u (%s) ",
343				qopt->packets,
344				sprint_size(qopt->bytesin, b1));
345		}
346	}
347	return 0;
348}
349
350struct qdisc_util gred_qdisc_util = {
351	.id		= "gred",
352	.parse_qopt	= gred_parse_opt,
353	.print_qopt	= gred_print_opt,
354};
355