typo fix in comment
[oweals/busybox.git] / networking / tc.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4  *
5  * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6  *
7  * Bernhard Reutner-Fischer adjusted for busybox
8  */
9
10 //usage:#define tc_trivial_usage
11 /* //usage: "[OPTIONS] OBJECT CMD [dev STRING]" */
12 //usage:        "OBJECT CMD [dev STRING]"
13 //usage:#define tc_full_usage "\n\n"
14 //usage:        "OBJECT: {qdisc|class|filter}\n"
15 //usage:        "CMD: {add|del|change|replace|show}\n"
16 //usage:        "\n"
17 //usage:        "qdisc [ handle QHANDLE ] [ root |"IF_FEATURE_TC_INGRESS(" ingress |")" parent CLASSID ]\n"
18 /* //usage: "[ estimator INTERVAL TIME_CONSTANT ]\n" */
19 //usage:        "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
20 //usage:        "       QDISC_KIND := { [p|b]fifo | tbf | prio | cbq | red | etc. }\n"
21 //usage:        "qdisc show [ dev STRING ]"IF_FEATURE_TC_INGRESS(" [ingress]")"\n"
22 //usage:        "class [ classid CLASSID ] [ root | parent CLASSID ]\n"
23 //usage:        "       [ [ QDISC_KIND ] [ help | OPTIONS ] ]\n"
24 //usage:        "class show [ dev STRING ] [ root | parent CLASSID ]\n"
25 //usage:        "filter [ pref PRIO ] [ protocol PROTO ]\n"
26 /* //usage: "\t[ estimator INTERVAL TIME_CONSTANT ]\n" */
27 //usage:        "       [ root | classid CLASSID ] [ handle FILTERID ]\n"
28 //usage:        "       [ [ FILTER_TYPE ] [ help | OPTIONS ] ]\n"
29 //usage:        "filter show [ dev STRING ] [ root | parent CLASSID ]"
30
31 #include "libbb.h"
32 #include "common_bufsiz.h"
33
34 #include "libiproute/utils.h"
35 #include "libiproute/ip_common.h"
36 #include "libiproute/rt_names.h"
37 #include <linux/pkt_sched.h> /* for the TC_H_* macros */
38
39 #define parse_rtattr_nested(tb, max, rta) \
40         (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
41
42 /* nullifies tb on error */
43 #define __parse_rtattr_nested_compat(tb, max, rta, len) \
44         ({if ((RTA_PAYLOAD(rta) >= len) && \
45                  (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr))) { \
46                         rta = RTA_DATA(rta) + RTA_ALIGN(len); \
47                         parse_rtattr_nested(tb, max, rta); \
48           } else \
49                         memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); \
50         })
51
52 #define parse_rtattr_nested_compat(tb, max, rta, data, len) \
53         ({data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
54         __parse_rtattr_nested_compat(tb, max, rta, len); })
55
56 #define show_details (0) /* not implemented. Does anyone need it? */
57 #define use_iec (0) /* not currently documented in the upstream manpage */
58
59
60 struct globals {
61         int filter_ifindex;
62         uint32_t filter_qdisc;
63         uint32_t filter_parent;
64         uint32_t filter_prio;
65         uint32_t filter_proto;
66 } FIX_ALIASING;
67 #define G (*(struct globals*)bb_common_bufsiz1)
68 #define filter_ifindex (G.filter_ifindex)
69 #define filter_qdisc (G.filter_qdisc)
70 #define filter_parent (G.filter_parent)
71 #define filter_prio (G.filter_prio)
72 #define filter_proto (G.filter_proto)
73 #define INIT_G() do { \
74         setup_common_bufsiz(); \
75         BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
76 } while (0)
77
78 /* Allocates a buffer containing the name of a class id.
79  * The caller must free the returned memory.  */
80 static char* print_tc_classid(uint32_t cid)
81 {
82 #if 0 /* IMPOSSIBLE */
83         if (cid == TC_H_ROOT)
84                 return xasprintf("root");
85         else
86 #endif
87         if (cid == TC_H_UNSPEC)
88                 return xasprintf("none");
89         else if (TC_H_MAJ(cid) == 0)
90                 return xasprintf(":%x", TC_H_MIN(cid));
91         else if (TC_H_MIN(cid) == 0)
92                 return xasprintf("%x:", TC_H_MAJ(cid)>>16);
93         else
94                 return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid));
95 }
96
97 /* Get a qdisc handle.  Return 0 on success, !0 otherwise.  */
98 static int get_qdisc_handle(uint32_t *h, const char *str) {
99         uint32_t maj;
100         char *p;
101
102         maj = TC_H_UNSPEC;
103         if (!strcmp(str, "none"))
104                 goto ok;
105         maj = strtoul(str, &p, 16);
106         if (p == str)
107                 return 1;
108         maj <<= 16;
109         if (*p != ':' && *p != '\0')
110                 return 1;
111  ok:
112         *h = maj;
113         return 0;
114 }
115
116 /* Get class ID.  Return 0 on success, !0 otherwise.  */
117 static int get_tc_classid(uint32_t *h, const char *str) {
118         uint32_t maj, min;
119         char *p;
120
121         maj = TC_H_ROOT;
122         if (!strcmp(str, "root"))
123                 goto ok;
124         maj = TC_H_UNSPEC;
125         if (!strcmp(str, "none"))
126                 goto ok;
127         maj = strtoul(str, &p, 16);
128         if (p == str) {
129                 if (*p != ':')
130                         return 1;
131                 maj = 0;
132         }
133         if (*p == ':') {
134                 if (maj >= (1<<16))
135                         return 1;
136                 maj <<= 16;
137                 str = p + 1;
138                 min = strtoul(str, &p, 16);
139 //FIXME: check for "" too?
140                 if (*p != '\0' || min >= (1<<16))
141                         return 1;
142                 maj |= min;
143         } else if (*p != 0)
144                 return 1;
145  ok:
146         *h = maj;
147         return 0;
148 }
149
150 static void print_rate(char *buf, int len, uint32_t rate)
151 {
152         double tmp = (double)rate*8;
153
154         if (use_iec) {
155                 if (tmp >= 1000*1024*1024)
156                         snprintf(buf, len, "%.0fMibit", tmp/(1024*1024));
157                 else if (tmp >= 1000*1024)
158                         snprintf(buf, len, "%.0fKibit", tmp/1024);
159                 else
160                         snprintf(buf, len, "%.0fbit", tmp);
161         } else {
162                 if (tmp >= 1000*1000000)
163                         snprintf(buf, len, "%.0fMbit", tmp/1000000);
164                 else if (tmp >= 1000*1000)
165                         snprintf(buf, len, "%.0fKbit", tmp/1000);
166                 else
167                         snprintf(buf, len, "%.0fbit",  tmp);
168         }
169 }
170
171 /* This is "pfifo_fast".  */
172 static int prio_parse_opt(int argc, char **argv, struct nlmsghdr *n)
173 {
174         return 0;
175 }
176 static int prio_print_opt(struct rtattr *opt)
177 {
178         int i;
179         struct tc_prio_qopt *qopt;
180         struct rtattr *tb[TCA_PRIO_MAX+1];
181
182         if (opt == NULL)
183                 return 0;
184         parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, sizeof(*qopt));
185         if (tb == NULL)
186                 return 0;
187         printf("bands %u priomap ", qopt->bands);
188         for (i=0; i<=TC_PRIO_MAX; i++)
189                 printf(" %d", qopt->priomap[i]);
190
191         if (tb[TCA_PRIO_MQ])
192                 printf(" multiqueue: o%s ",
193                     *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "n" : "ff");
194
195         return 0;
196 }
197
198 /* Class Based Queue */
199 static int cbq_parse_opt(int argc, char **argv, struct nlmsghdr *n)
200 {
201         return 0;
202 }
203 static int cbq_print_opt(struct rtattr *opt)
204 {
205         struct rtattr *tb[TCA_CBQ_MAX+1];
206         struct tc_ratespec *r = NULL;
207         struct tc_cbq_lssopt *lss = NULL;
208         struct tc_cbq_wrropt *wrr = NULL;
209         struct tc_cbq_fopt *fopt = NULL;
210         struct tc_cbq_ovl *ovl = NULL;
211         const char *const error = "CBQ: too short %s opt";
212         char buf[64];
213
214         if (opt == NULL)
215                 goto done;
216         parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
217
218         if (tb[TCA_CBQ_RATE]) {
219                 if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
220                         bb_error_msg(error, "rate");
221                 else
222                         r = RTA_DATA(tb[TCA_CBQ_RATE]);
223         }
224         if (tb[TCA_CBQ_LSSOPT]) {
225                 if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
226                         bb_error_msg(error, "lss");
227                 else
228                         lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
229         }
230         if (tb[TCA_CBQ_WRROPT]) {
231                 if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
232                         bb_error_msg(error, "wrr");
233                 else
234                         wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
235         }
236         if (tb[TCA_CBQ_FOPT]) {
237                 if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
238                         bb_error_msg(error, "fopt");
239                 else
240                         fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
241         }
242         if (tb[TCA_CBQ_OVL_STRATEGY]) {
243                 if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
244                         bb_error_msg("CBQ: too short overlimit strategy %u/%u",
245                                 (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
246                                 (unsigned) sizeof(*ovl));
247                 else
248                         ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
249         }
250
251         if (r) {
252                 print_rate(buf, sizeof(buf), r->rate);
253                 printf("rate %s ", buf);
254                 if (show_details) {
255                         printf("cell %ub ", 1<<r->cell_log);
256                         if (r->mpu)
257                                 printf("mpu %ub ", r->mpu);
258                         if (r->overhead)
259                                 printf("overhead %ub ", r->overhead);
260                 }
261         }
262         if (lss && lss->flags) {
263                 bool comma = false;
264                 bb_putchar('(');
265                 if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
266                         printf("bounded");
267                         comma = true;
268                 }
269                 if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
270                         if (comma)
271                                 bb_putchar(',');
272                         printf("isolated");
273                 }
274                 printf(") ");
275         }
276         if (wrr) {
277                 if (wrr->priority != TC_CBQ_MAXPRIO)
278                         printf("prio %u", wrr->priority);
279                 else
280                         printf("prio no-transmit");
281                 if (show_details) {
282                         printf("/%u ", wrr->cpriority);
283                         if (wrr->weight != 1) {
284                                 print_rate(buf, sizeof(buf), wrr->weight);
285                                 printf("weight %s ", buf);
286                         }
287                         if (wrr->allot)
288                                 printf("allot %ub ", wrr->allot);
289                 }
290         }
291  done:
292         return 0;
293 }
294
295 static int print_qdisc(const struct sockaddr_nl *who UNUSED_PARAM,
296                                                 struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
297 {
298         struct tcmsg *msg = NLMSG_DATA(hdr);
299         int len = hdr->nlmsg_len;
300         struct rtattr * tb[TCA_MAX+1];
301         char *name;
302
303         if (hdr->nlmsg_type != RTM_NEWQDISC && hdr->nlmsg_type != RTM_DELQDISC) {
304                 /* bb_error_msg("not a qdisc"); */
305                 return 0; /* ??? mimic upstream; should perhaps return -1 */
306         }
307         len -= NLMSG_LENGTH(sizeof(*msg));
308         if (len < 0) {
309                 /* bb_error_msg("wrong len %d", len); */
310                 return -1;
311         }
312         /* not the desired interface? */
313         if (filter_ifindex && filter_ifindex != msg->tcm_ifindex)
314                 return 0;
315         memset (tb, 0, sizeof(tb));
316         parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len);
317         if (tb[TCA_KIND] == NULL) {
318                 /* bb_error_msg("%s: NULL kind", "qdisc"); */
319                 return -1;
320         }
321         if (hdr->nlmsg_type == RTM_DELQDISC)
322                 printf("deleted ");
323         name = (char*)RTA_DATA(tb[TCA_KIND]);
324         printf("qdisc %s %x: ", name, msg->tcm_handle>>16);
325         if (filter_ifindex == 0)
326                 printf("dev %s ", ll_index_to_name(msg->tcm_ifindex));
327         if (msg->tcm_parent == TC_H_ROOT)
328                 printf("root ");
329         else if (msg->tcm_parent) {
330                 char *classid = print_tc_classid(msg->tcm_parent);
331                 printf("parent %s ", classid);
332                 if (ENABLE_FEATURE_CLEAN_UP)
333                         free(classid);
334         }
335         if (msg->tcm_info != 1)
336                 printf("refcnt %d ", msg->tcm_info);
337         if (tb[TCA_OPTIONS]) {
338                 static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0";
339                 int qqq = index_in_strings(_q_, name);
340                 if (qqq == 0) { /* pfifo_fast aka prio */
341                         prio_print_opt(tb[TCA_OPTIONS]);
342                 } else if (qqq == 1) { /* class based queuing */
343                         cbq_print_opt(tb[TCA_OPTIONS]);
344                 } else
345                         bb_error_msg("unknown %s", name);
346         }
347         bb_putchar('\n');
348         return 0;
349 }
350
351 static int print_class(const struct sockaddr_nl *who UNUSED_PARAM,
352                                                 struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
353 {
354         struct tcmsg *msg = NLMSG_DATA(hdr);
355         int len = hdr->nlmsg_len;
356         struct rtattr * tb[TCA_MAX+1];
357         char *name, *classid;
358
359         /*XXX Eventually factor out common code */
360
361         if (hdr->nlmsg_type != RTM_NEWTCLASS && hdr->nlmsg_type != RTM_DELTCLASS) {
362                 /* bb_error_msg("not a class"); */
363                 return 0; /* ??? mimic upstream; should perhaps return -1 */
364         }
365         len -= NLMSG_LENGTH(sizeof(*msg));
366         if (len < 0) {
367                 /* bb_error_msg("wrong len %d", len); */
368                 return -1;
369         }
370         /* not the desired interface? */
371         if (filter_qdisc && TC_H_MAJ(msg->tcm_handle^filter_qdisc))
372                 return 0;
373         memset (tb, 0, sizeof(tb));
374         parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len);
375         if (tb[TCA_KIND] == NULL) {
376                 /* bb_error_msg("%s: NULL kind", "class"); */
377                 return -1;
378         }
379         if (hdr->nlmsg_type == RTM_DELTCLASS)
380                 printf("deleted ");
381
382         name = (char*)RTA_DATA(tb[TCA_KIND]);
383         classid = !msg->tcm_handle ? NULL : print_tc_classid(
384                                 filter_qdisc ? TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
385         printf ("class %s %s", name, classid);
386         if (ENABLE_FEATURE_CLEAN_UP)
387                 free(classid);
388
389         if (filter_ifindex == 0)
390                 printf("dev %s ", ll_index_to_name(msg->tcm_ifindex));
391         if (msg->tcm_parent == TC_H_ROOT)
392                 printf("root ");
393         else if (msg->tcm_parent) {
394                 classid = print_tc_classid(filter_qdisc ?
395                                 TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
396                 printf("parent %s ", classid);
397                 if (ENABLE_FEATURE_CLEAN_UP)
398                         free(classid);
399         }
400         if (msg->tcm_info)
401                 printf("leaf %x ", msg->tcm_info >> 16);
402         /* Do that get_qdisc_kind(RTA_DATA(tb[TCA_KIND])).  */
403         if (tb[TCA_OPTIONS]) {
404                 static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0";
405                 int qqq = index_in_strings(_q_, name);
406                 if (qqq == 0) { /* pfifo_fast aka prio */
407                         /* nothing. */ /*prio_print_opt(tb[TCA_OPTIONS]);*/
408                 } else if (qqq == 1) { /* class based queuing */
409                         /* cbq_print_copt() is identical to cbq_print_opt(). */
410                         cbq_print_opt(tb[TCA_OPTIONS]);
411                 } else
412                         bb_error_msg("unknown %s", name);
413         }
414         bb_putchar('\n');
415
416         return 0;
417 }
418
419 static int print_filter(const struct sockaddr_nl *who UNUSED_PARAM,
420                                                 struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
421 {
422         return 0;
423 }
424
425 int tc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
426 int tc_main(int argc UNUSED_PARAM, char **argv)
427 {
428         static const char objects[] ALIGN1 =
429                 "qdisc\0""class\0""filter\0"
430                 ;
431         enum { OBJ_qdisc = 0, OBJ_class, OBJ_filter };
432         static const char commands[] ALIGN1 =
433                 "add\0""delete\0""change\0"
434                 "link\0" /* only qdisc */
435                 "replace\0"
436                 "show\0""list\0"
437                 ;
438         static const char args[] ALIGN1 =
439                 "dev\0" /* qdisc, class, filter */
440                 "root\0" /* class, filter */
441                 "parent\0" /* class, filter */
442                 "qdisc\0" /* class */
443                 "handle\0" /* change: qdisc, class(classid) list: filter */
444                 "classid\0" /* change: for class use "handle" */
445                 "preference\0""priority\0""protocol\0" /* filter */
446                 ;
447         enum { CMD_add = 0, CMD_del, CMD_change, CMD_link, CMD_replace, CMD_show };
448         enum { ARG_dev = 0, ARG_root, ARG_parent, ARG_qdisc,
449                         ARG_handle, ARG_classid, ARG_pref, ARG_prio, ARG_proto};
450         struct rtnl_handle rth;
451         struct tcmsg msg;
452         int ret, obj, cmd, arg;
453         char *dev = NULL;
454
455         INIT_G();
456
457         if (!*++argv)
458                 bb_show_usage();
459         xrtnl_open(&rth);
460         ret = EXIT_SUCCESS;
461
462         obj = index_in_substrings(objects, *argv++);
463
464         if (obj < 0)
465                 bb_show_usage();
466         if (!*argv)
467                 cmd = CMD_show; /* list is the default */
468         else {
469                 cmd = index_in_substrings(commands, *argv);
470                 if (cmd < 0)
471                         invarg_1_to_2(*argv, argv[-1]);
472                 argv++;
473         }
474         memset(&msg, 0, sizeof(msg));
475         msg.tcm_family = AF_UNSPEC;
476         ll_init_map(&rth);
477         while (*argv) {
478                 arg = index_in_substrings(args, *argv);
479                 if (arg == ARG_dev) {
480                         NEXT_ARG();
481                         if (dev)
482                                 duparg2("dev", *argv);
483                         dev = *argv++;
484                         msg.tcm_ifindex = xll_name_to_index(dev);
485                         if (cmd >= CMD_show)
486                                 filter_ifindex = msg.tcm_ifindex;
487                 } else
488                 if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show)
489                  || (arg == ARG_handle && obj == OBJ_qdisc && cmd == CMD_change)
490                 ) {
491                         NEXT_ARG();
492                         /* We don't care about duparg2("qdisc handle",*argv) for now */
493                         if (get_qdisc_handle(&filter_qdisc, *argv))
494                                 invarg_1_to_2(*argv, "qdisc");
495                 } else
496                 if (obj != OBJ_qdisc
497                  && (arg == ARG_root
498                     || arg == ARG_parent
499                     || (obj == OBJ_filter && arg >= ARG_pref)
500                     )
501                 ) {
502                         /* nothing */
503                 } else {
504                         invarg_1_to_2(*argv, "command");
505                 }
506                 NEXT_ARG();
507                 if (arg == ARG_root) {
508                         if (msg.tcm_parent)
509                                 duparg("parent", *argv);
510                         msg.tcm_parent = TC_H_ROOT;
511                         if (obj == OBJ_filter)
512                                 filter_parent = TC_H_ROOT;
513                 } else if (arg == ARG_parent) {
514                         uint32_t handle;
515                         if (msg.tcm_parent)
516                                 duparg(*argv, "parent");
517                         if (get_tc_classid(&handle, *argv))
518                                 invarg_1_to_2(*argv, "parent");
519                         msg.tcm_parent = handle;
520                         if (obj == OBJ_filter)
521                                 filter_parent = handle;
522                 } else if (arg == ARG_handle) { /* filter::list */
523                         if (msg.tcm_handle)
524                                 duparg(*argv, "handle");
525                         /* reject LONG_MIN || LONG_MAX */
526                         /* TODO: for fw
527                         slash = strchr(handle, '/');
528                         if (slash != NULL)
529                                    *slash = '\0';
530                          */
531                         msg.tcm_handle = get_u32(*argv, "handle");
532                         /* if (slash) {if (get_u32(uint32_t &mask, slash+1, NULL)) inv mask; addattr32(n, MAX_MSG, TCA_FW_MASK, mask); */
533                 } else if (arg == ARG_classid && obj == OBJ_class && cmd == CMD_change){
534                 } else if (arg == ARG_pref || arg == ARG_prio) { /* filter::list */
535                         if (filter_prio)
536                                 duparg(*argv, "priority");
537                         filter_prio = get_u32(*argv, "priority");
538                 } else if (arg == ARG_proto) { /* filter::list */
539                         uint16_t tmp;
540                         if (filter_proto)
541                                 duparg(*argv, "protocol");
542                         if (ll_proto_a2n(&tmp, *argv))
543                                 invarg_1_to_2(*argv, "protocol");
544                         filter_proto = tmp;
545                 }
546         }
547         if (cmd >= CMD_show) { /* show or list */
548                 if (obj == OBJ_filter)
549                         msg.tcm_info = TC_H_MAKE(filter_prio<<16, filter_proto);
550                 if (rtnl_dump_request(&rth, obj == OBJ_qdisc ? RTM_GETQDISC :
551                                                 obj == OBJ_class ? RTM_GETTCLASS : RTM_GETTFILTER,
552                                                 &msg, sizeof(msg)) < 0)
553                         bb_simple_perror_msg_and_die("can't send dump request");
554
555                 xrtnl_dump_filter(&rth, obj == OBJ_qdisc ? print_qdisc :
556                                                 obj == OBJ_class ? print_class : print_filter,
557                                                 NULL);
558         }
559         if (ENABLE_FEATURE_CLEAN_UP) {
560                 rtnl_close(&rth);
561         }
562         return ret;
563 }