]> rtime.felk.cvut.cz Git - lisovros/iproute2_canprio.git/blob - tc/q_cbq.c
iproute2: proper detection of libxtables position and flags
[lisovros/iproute2_canprio.git] / tc / q_cbq.c
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
27 static 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
38 static 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
44 static void explain1(char *arg)
45 {
46         fprintf(stderr, "Illegal \"%s\"\n", arg);
47 }
48
49
50 static 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
181 static 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
437 static 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
556 static 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
572 struct 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