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