source: MondoRescue/branches/3.3/mindi-busybox/networking/tc.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 15.3 KB
Line 
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
60struct 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. */
80static 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. */
98static 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. */
117static 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
150static 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". */
172static int prio_parse_opt(int argc, char **argv, struct nlmsghdr *n)
173{
174 return 0;
175}
176static 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 */
199static int cbq_parse_opt(int argc, char **argv, struct nlmsghdr *n)
200{
201 return 0;
202}
203static 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
295static 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
351static 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
419static int print_filter(const struct sockaddr_nl *who UNUSED_PARAM,
420 struct nlmsghdr *hdr, void *arg UNUSED_PARAM)
421{
422 return 0;
423}
424
425int tc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
426int 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}
Note: See TracBrowser for help on using the repository browser.