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