• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
explain(void)38 static 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 
init_gred(struct qdisc_util * qu,int argc,char ** argv,struct nlmsghdr * n)47 static 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 */
gred_parse_opt(struct qdisc_util * qu,int argc,char ** argv,struct nlmsghdr * n)122 static 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 
gred_print_opt(struct qdisc_util * qu,FILE * f,struct rtattr * opt)270 static 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 
350 struct qdisc_util gred_qdisc_util = {
351 	.id		= "gred",
352 	.parse_qopt	= gred_parse_opt,
353 	.print_qopt	= gred_print_opt,
354 };
355