]>
Commit | Line | Data |
---|---|---|
07d5ee70 PL |
1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
2 | /* Copyright 2020 NXP */ | |
3 | ||
4 | #include <stdio.h> | |
5 | #include <stdlib.h> | |
6 | #include <unistd.h> | |
7 | #include <string.h> | |
8 | #include <linux/if_ether.h> | |
9 | #include "utils.h" | |
10 | #include "rt_names.h" | |
11 | #include "tc_util.h" | |
12 | #include "list.h" | |
13 | #include <linux/tc_act/tc_gate.h> | |
14 | ||
15 | struct gate_entry { | |
16 | struct list_head list; | |
17 | uint8_t gate_state; | |
18 | uint32_t interval; | |
19 | int32_t ipv; | |
20 | int32_t maxoctets; | |
21 | }; | |
22 | ||
23 | #define CLOCKID_INVALID (-1) | |
24 | static const struct clockid_table { | |
25 | const char *name; | |
26 | clockid_t clockid; | |
27 | } clockt_map[] = { | |
28 | { "REALTIME", CLOCK_REALTIME }, | |
29 | { "TAI", CLOCK_TAI }, | |
30 | { "BOOTTIME", CLOCK_BOOTTIME }, | |
31 | { "MONOTONIC", CLOCK_MONOTONIC }, | |
32 | { NULL } | |
33 | }; | |
34 | ||
35 | static void explain(void) | |
36 | { | |
37 | fprintf(stderr, | |
38 | "Usage: gate [ priority PRIO-SPEC ] [ base-time BASE-TIME ]\n" | |
39 | " [ cycle-time CYCLE-TIME ]\n" | |
40 | " [ cycle-time-ext CYCLE-TIME-EXT ]\n" | |
41 | " [ clockid CLOCKID ] [flags FLAGS]\n" | |
42 | " [ sched-entry GATE0 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n" | |
43 | " [ sched-entry GATE1 INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n" | |
44 | " ......\n" | |
45 | " [ sched-entry GATEn INTERVAL [ INTERNAL-PRIO-VALUE MAX-OCTETS ] ]\n" | |
46 | " [ CONTROL ]\n" | |
47 | " GATEn := open | close\n" | |
48 | " INTERVAL : nanoseconds period of gate slot\n" | |
49 | " INTERNAL-PRIO-VALUE : internal priority decide which\n" | |
50 | " rx queue number direct to.\n" | |
51 | " default to be -1 which means wildcard.\n" | |
52 | " MAX-OCTETS : maximum number of MSDU octets that are\n" | |
53 | " permitted to pas the gate during the\n" | |
54 | " specified TimeInterval.\n" | |
55 | " default to be -1 which means wildcard.\n" | |
56 | " CONTROL := pipe | drop | continue | pass |\n" | |
57 | " goto chain <CHAIN_INDEX>\n"); | |
58 | } | |
59 | ||
60 | static void usage(void) | |
61 | { | |
62 | explain(); | |
63 | exit(-1); | |
64 | } | |
65 | ||
66 | static void explain_entry_format(void) | |
67 | { | |
68 | fprintf(stderr, "Usage: sched-entry <open | close> <interval> [ <interval ipv> <octets max bytes> ]\n"); | |
69 | } | |
70 | ||
71 | static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p, | |
72 | int tca_id, struct nlmsghdr *n); | |
73 | static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg); | |
74 | ||
75 | struct action_util gate_action_util = { | |
76 | .id = "gate", | |
77 | .parse_aopt = parse_gate, | |
78 | .print_aopt = print_gate, | |
79 | }; | |
80 | ||
81 | static int get_clockid(__s32 *val, const char *arg) | |
82 | { | |
83 | const struct clockid_table *c; | |
84 | ||
85 | if (strcasestr(arg, "CLOCK_") != NULL) | |
86 | arg += sizeof("CLOCK_") - 1; | |
87 | ||
88 | for (c = clockt_map; c->name; c++) { | |
89 | if (strcasecmp(c->name, arg) == 0) { | |
90 | *val = c->clockid; | |
91 | return 0; | |
92 | } | |
93 | } | |
94 | ||
95 | return -1; | |
96 | } | |
97 | ||
98 | static const char *get_clock_name(clockid_t clockid) | |
99 | { | |
100 | const struct clockid_table *c; | |
101 | ||
102 | for (c = clockt_map; c->name; c++) { | |
103 | if (clockid == c->clockid) | |
104 | return c->name; | |
105 | } | |
106 | ||
107 | return "invalid"; | |
108 | } | |
109 | ||
110 | static int get_gate_state(__u8 *val, const char *arg) | |
111 | { | |
112 | if (!strcasecmp("OPEN", arg)) { | |
113 | *val = 1; | |
114 | return 0; | |
115 | } | |
116 | ||
117 | if (!strcasecmp("CLOSE", arg)) { | |
118 | *val = 0; | |
119 | return 0; | |
120 | } | |
121 | ||
122 | return -1; | |
123 | } | |
124 | ||
125 | static struct gate_entry *create_gate_entry(uint8_t gate_state, | |
126 | uint32_t interval, | |
127 | int32_t ipv, | |
128 | int32_t maxoctets) | |
129 | { | |
130 | struct gate_entry *e; | |
131 | ||
132 | e = calloc(1, sizeof(*e)); | |
133 | if (!e) | |
134 | return NULL; | |
135 | ||
136 | e->gate_state = gate_state; | |
137 | e->interval = interval; | |
138 | e->ipv = ipv; | |
139 | e->maxoctets = maxoctets; | |
140 | ||
141 | return e; | |
142 | } | |
143 | ||
144 | static int add_gate_list(struct list_head *gate_entries, struct nlmsghdr *n) | |
145 | { | |
146 | struct gate_entry *e; | |
147 | ||
148 | list_for_each_entry(e, gate_entries, list) { | |
149 | struct rtattr *a; | |
150 | ||
151 | a = addattr_nest(n, 1024, TCA_GATE_ONE_ENTRY | NLA_F_NESTED); | |
152 | ||
153 | if (e->gate_state) | |
154 | addattr(n, MAX_MSG, TCA_GATE_ENTRY_GATE); | |
155 | ||
156 | addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_INTERVAL, | |
157 | &e->interval, sizeof(e->interval)); | |
158 | addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_IPV, | |
159 | &e->ipv, sizeof(e->ipv)); | |
160 | addattr_l(n, MAX_MSG, TCA_GATE_ENTRY_MAX_OCTETS, | |
161 | &e->maxoctets, sizeof(e->maxoctets)); | |
162 | ||
163 | addattr_nest_end(n, a); | |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | static void free_entries(struct list_head *gate_entries) | |
170 | { | |
171 | struct gate_entry *e, *n; | |
172 | ||
173 | list_for_each_entry_safe(e, n, gate_entries, list) { | |
174 | list_del(&e->list); | |
175 | free(e); | |
176 | } | |
177 | } | |
178 | ||
179 | static int parse_gate(struct action_util *a, int *argc_p, char ***argv_p, | |
180 | int tca_id, struct nlmsghdr *n) | |
181 | { | |
182 | struct tc_gate parm = {.action = TC_ACT_PIPE}; | |
183 | struct list_head gate_entries; | |
184 | __s32 clockid = CLOCKID_INVALID; | |
185 | struct rtattr *tail, *nle; | |
186 | char **argv = *argv_p; | |
187 | int argc = *argc_p; | |
188 | __s64 base_time = 0; | |
189 | __s64 cycle_time = 0; | |
190 | __s64 cycle_time_ext = 0; | |
191 | int entry_num = 0; | |
192 | char *invalidarg; | |
193 | __u32 flags = 0; | |
194 | int prio = -1; | |
195 | ||
196 | int err; | |
197 | ||
198 | if (matches(*argv, "gate") != 0) | |
199 | return -1; | |
200 | ||
201 | NEXT_ARG(); | |
202 | if (argc <= 0) | |
203 | return -1; | |
204 | ||
205 | INIT_LIST_HEAD(&gate_entries); | |
206 | ||
207 | while (argc > 0) { | |
208 | if (matches(*argv, "index") == 0) { | |
209 | NEXT_ARG(); | |
210 | if (get_u32(&parm.index, *argv, 10)) { | |
211 | invalidarg = "index"; | |
212 | goto err_arg; | |
213 | } | |
214 | } else if (matches(*argv, "priority") == 0) { | |
215 | NEXT_ARG(); | |
216 | if (get_s32(&prio, *argv, 0)) { | |
217 | invalidarg = "priority"; | |
218 | goto err_arg; | |
219 | } | |
220 | } else if (matches(*argv, "base-time") == 0) { | |
221 | NEXT_ARG(); | |
222 | if (get_s64(&base_time, *argv, 10) && | |
223 | get_time64(&base_time, *argv)) { | |
224 | invalidarg = "base-time"; | |
225 | goto err_arg; | |
226 | } | |
227 | } else if (matches(*argv, "cycle-time") == 0) { | |
228 | NEXT_ARG(); | |
229 | if (get_s64(&cycle_time, *argv, 10) && | |
230 | get_time64(&cycle_time, *argv)) { | |
231 | invalidarg = "cycle-time"; | |
232 | goto err_arg; | |
233 | } | |
234 | } else if (matches(*argv, "cycle-time-ext") == 0) { | |
235 | NEXT_ARG(); | |
236 | if (get_s64(&cycle_time_ext, *argv, 10) && | |
237 | get_time64(&cycle_time_ext, *argv)) { | |
238 | invalidarg = "cycle-time-ext"; | |
239 | goto err_arg; | |
240 | } | |
241 | } else if (matches(*argv, "clockid") == 0) { | |
242 | NEXT_ARG(); | |
243 | if (get_clockid(&clockid, *argv)) { | |
244 | invalidarg = "clockid"; | |
245 | goto err_arg; | |
246 | } | |
247 | } else if (matches(*argv, "flags") == 0) { | |
248 | NEXT_ARG(); | |
249 | if (get_u32(&flags, *argv, 0)) { | |
250 | invalidarg = "flags"; | |
251 | goto err_arg; | |
252 | } | |
253 | } else if (matches(*argv, "sched-entry") == 0) { | |
254 | unsigned int maxoctets_uint = 0; | |
255 | int32_t maxoctets = -1; | |
256 | struct gate_entry *e; | |
257 | uint8_t gate_state = 0; | |
258 | __s64 interval_s64 = 0; | |
259 | uint32_t interval = 0; | |
260 | int32_t ipv = -1; | |
261 | ||
262 | if (!NEXT_ARG_OK()) { | |
263 | explain_entry_format(); | |
cbf64817 | 264 | fprintf(stderr, "\"sched-entry\" is incomplete\n"); |
07d5ee70 PL |
265 | free_entries(&gate_entries); |
266 | return -1; | |
267 | } | |
268 | ||
269 | NEXT_ARG(); | |
270 | ||
271 | if (get_gate_state(&gate_state, *argv)) { | |
272 | explain_entry_format(); | |
cbf64817 | 273 | fprintf(stderr, "\"sched-entry\" is incomplete\n"); |
07d5ee70 PL |
274 | free_entries(&gate_entries); |
275 | return -1; | |
276 | } | |
277 | ||
278 | if (!NEXT_ARG_OK()) { | |
279 | explain_entry_format(); | |
cbf64817 | 280 | fprintf(stderr, "\"sched-entry\" is incomplete\n"); |
07d5ee70 PL |
281 | free_entries(&gate_entries); |
282 | return -1; | |
283 | } | |
284 | ||
285 | NEXT_ARG(); | |
286 | ||
287 | if (get_u32(&interval, *argv, 0) && | |
288 | get_time64(&interval_s64, *argv)) { | |
289 | explain_entry_format(); | |
cbf64817 | 290 | fprintf(stderr, "\"sched-entry\" is incomplete\n"); |
07d5ee70 PL |
291 | free_entries(&gate_entries); |
292 | return -1; | |
293 | } | |
294 | ||
295 | if (interval_s64 > UINT_MAX) { | |
296 | fprintf(stderr, "\"interval\" is too large\n"); | |
297 | free_entries(&gate_entries); | |
298 | return -1; | |
299 | } else if (interval_s64) { | |
300 | interval = interval_s64; | |
301 | } | |
302 | ||
303 | if (!NEXT_ARG_OK()) | |
304 | goto create_entry; | |
305 | ||
306 | NEXT_ARG(); | |
307 | ||
308 | if (get_s32(&ipv, *argv, 0)) { | |
309 | PREV_ARG(); | |
310 | goto create_entry; | |
311 | } | |
312 | ||
313 | if (!gate_state) | |
314 | ipv = -1; | |
315 | ||
316 | if (!NEXT_ARG_OK()) | |
317 | goto create_entry; | |
318 | ||
319 | NEXT_ARG(); | |
320 | ||
321 | if (get_s32(&maxoctets, *argv, 0) && | |
322 | get_size(&maxoctets_uint, *argv)) | |
323 | PREV_ARG(); | |
324 | ||
325 | if (maxoctets_uint > INT_MAX) { | |
326 | fprintf(stderr, "\"maxoctets\" is too large\n"); | |
327 | free_entries(&gate_entries); | |
328 | return -1; | |
329 | } else if (maxoctets_uint ) { | |
330 | maxoctets = maxoctets_uint; | |
331 | } | |
332 | ||
333 | if (!gate_state) | |
334 | maxoctets = -1; | |
335 | ||
336 | create_entry: | |
337 | e = create_gate_entry(gate_state, interval, | |
338 | ipv, maxoctets); | |
339 | if (!e) { | |
340 | fprintf(stderr, "gate: not enough memory\n"); | |
341 | free_entries(&gate_entries); | |
342 | return -1; | |
343 | } | |
344 | ||
345 | list_add_tail(&e->list, &gate_entries); | |
346 | entry_num++; | |
347 | } else if (matches(*argv, "help") == 0) { | |
348 | usage(); | |
349 | } else { | |
350 | break; | |
351 | } | |
352 | ||
353 | argc--; | |
354 | argv++; | |
355 | } | |
356 | ||
357 | parse_action_control_dflt(&argc, &argv, &parm.action, | |
358 | false, TC_ACT_PIPE); | |
359 | ||
360 | if (!entry_num && !parm.index) { | |
361 | fprintf(stderr, "gate: must add at least one entry\n"); | |
362 | return -1; | |
363 | } | |
364 | ||
365 | tail = addattr_nest(n, MAX_MSG, tca_id | NLA_F_NESTED); | |
366 | addattr_l(n, MAX_MSG, TCA_GATE_PARMS, &parm, sizeof(parm)); | |
367 | ||
368 | if (prio != -1) | |
369 | addattr_l(n, MAX_MSG, TCA_GATE_PRIORITY, &prio, sizeof(prio)); | |
370 | ||
371 | if (flags) | |
372 | addattr_l(n, MAX_MSG, TCA_GATE_FLAGS, &flags, sizeof(flags)); | |
373 | ||
374 | if (base_time) | |
375 | addattr_l(n, MAX_MSG, TCA_GATE_BASE_TIME, | |
376 | &base_time, sizeof(base_time)); | |
377 | ||
378 | if (cycle_time) | |
379 | addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME, | |
380 | &cycle_time, sizeof(cycle_time)); | |
381 | ||
382 | if (cycle_time_ext) | |
383 | addattr_l(n, MAX_MSG, TCA_GATE_CYCLE_TIME_EXT, | |
384 | &cycle_time_ext, sizeof(cycle_time_ext)); | |
385 | ||
386 | if (clockid != CLOCKID_INVALID) | |
387 | addattr_l(n, MAX_MSG, TCA_GATE_CLOCKID, | |
388 | &clockid, sizeof(clockid)); | |
389 | ||
390 | nle = addattr_nest(n, MAX_MSG, TCA_GATE_ENTRY_LIST | NLA_F_NESTED); | |
391 | err = add_gate_list(&gate_entries, n); | |
392 | if (err < 0) { | |
393 | fprintf(stderr, "Could not add entries to netlink message\n"); | |
394 | free_entries(&gate_entries); | |
395 | return -1; | |
396 | } | |
397 | ||
398 | addattr_nest_end(n, nle); | |
399 | addattr_nest_end(n, tail); | |
400 | free_entries(&gate_entries); | |
401 | *argc_p = argc; | |
402 | *argv_p = argv; | |
403 | ||
404 | return 0; | |
405 | err_arg: | |
406 | invarg(invalidarg, *argv); | |
407 | free_entries(&gate_entries); | |
408 | ||
409 | return -1; | |
410 | } | |
411 | ||
412 | static int print_gate_list(struct rtattr *list) | |
413 | { | |
414 | struct rtattr *item; | |
415 | int rem; | |
416 | ||
417 | rem = RTA_PAYLOAD(list); | |
418 | ||
419 | print_string(PRINT_FP, NULL, "%s", _SL_); | |
420 | print_string(PRINT_FP, NULL, "\tschedule:%s", _SL_); | |
421 | open_json_array(PRINT_JSON, "schedule"); | |
422 | ||
423 | for (item = RTA_DATA(list); | |
424 | RTA_OK(item, rem); | |
425 | item = RTA_NEXT(item, rem)) { | |
426 | struct rtattr *tb[TCA_GATE_ENTRY_MAX + 1]; | |
427 | __u32 index = 0, interval = 0; | |
428 | __u8 gate_state = 0; | |
429 | __s32 ipv = -1, maxoctets = -1; | |
546f7382 | 430 | SPRINT_BUF(buf); |
07d5ee70 PL |
431 | |
432 | parse_rtattr_nested(tb, TCA_GATE_ENTRY_MAX, item); | |
433 | ||
434 | if (tb[TCA_GATE_ENTRY_INDEX]) | |
435 | index = rta_getattr_u32(tb[TCA_GATE_ENTRY_INDEX]); | |
436 | ||
437 | if (tb[TCA_GATE_ENTRY_GATE]) | |
438 | gate_state = 1; | |
439 | ||
440 | if (tb[TCA_GATE_ENTRY_INTERVAL]) | |
441 | interval = rta_getattr_u32(tb[TCA_GATE_ENTRY_INTERVAL]); | |
442 | ||
443 | if (tb[TCA_GATE_ENTRY_IPV]) | |
444 | ipv = rta_getattr_s32(tb[TCA_GATE_ENTRY_IPV]); | |
445 | ||
446 | if (tb[TCA_GATE_ENTRY_MAX_OCTETS]) | |
447 | maxoctets = rta_getattr_s32(tb[TCA_GATE_ENTRY_MAX_OCTETS]); | |
448 | ||
449 | open_json_object(NULL); | |
450 | print_uint(PRINT_ANY, "number", "\t number %4u", index); | |
451 | print_string(PRINT_ANY, "gate_state", "\tgate-state %s ", | |
452 | gate_state ? "open" : "close"); | |
453 | ||
454 | print_uint(PRINT_JSON, "interval", NULL, interval); | |
455 | ||
456 | memset(buf, 0, sizeof(buf)); | |
457 | print_string(PRINT_FP, NULL, "\tinterval %s", | |
458 | sprint_time64(interval, buf)); | |
459 | ||
460 | if (ipv != -1) { | |
461 | print_uint(PRINT_ANY, "ipv", "\t ipv %-10u", ipv); | |
462 | } else { | |
463 | print_int(PRINT_JSON, "ipv", NULL, ipv); | |
464 | print_string(PRINT_FP, NULL, "\t ipv %s", "wildcard"); | |
465 | } | |
466 | ||
467 | if (maxoctets != -1) { | |
adbe5de9 PM |
468 | print_size(PRINT_ANY, "max_octets", "\t max-octets %s", |
469 | maxoctets); | |
07d5ee70 PL |
470 | } else { |
471 | print_string(PRINT_FP, NULL, | |
472 | "\t max-octets %s", "wildcard"); | |
473 | print_int(PRINT_JSON, "max_octets", NULL, maxoctets); | |
474 | } | |
475 | ||
476 | close_json_object(); | |
477 | print_string(PRINT_FP, NULL, "%s", _SL_); | |
478 | } | |
479 | ||
480 | close_json_array(PRINT_ANY, ""); | |
481 | ||
482 | return 0; | |
483 | } | |
484 | ||
485 | static int print_gate(struct action_util *au, FILE *f, struct rtattr *arg) | |
486 | { | |
487 | struct tc_gate *parm; | |
488 | struct rtattr *tb[TCA_GATE_MAX + 1]; | |
489 | __s32 clockid = CLOCKID_INVALID; | |
490 | __s64 base_time = 0; | |
491 | __s64 cycle_time = 0; | |
492 | __s64 cycle_time_ext = 0; | |
546f7382 | 493 | SPRINT_BUF(buf); |
07d5ee70 PL |
494 | int prio = -1; |
495 | ||
496 | if (arg == NULL) | |
497 | return -1; | |
498 | ||
499 | parse_rtattr_nested(tb, TCA_GATE_MAX, arg); | |
500 | ||
501 | if (!tb[TCA_GATE_PARMS]) { | |
502 | fprintf(stderr, "Missing gate parameters\n"); | |
503 | return -1; | |
504 | } | |
505 | ||
506 | print_string(PRINT_FP, NULL, "%s", "\n"); | |
507 | ||
508 | parm = RTA_DATA(tb[TCA_GATE_PARMS]); | |
509 | ||
510 | if (tb[TCA_GATE_PRIORITY]) | |
511 | prio = rta_getattr_s32(tb[TCA_GATE_PRIORITY]); | |
512 | ||
513 | if (prio != -1) { | |
514 | print_int(PRINT_ANY, "priority", "\tpriority %-8d", prio); | |
515 | } else { | |
516 | print_string(PRINT_FP, NULL, "\tpriority %s", "wildcard"); | |
517 | print_int(PRINT_JSON, "priority", NULL, prio); | |
518 | } | |
519 | ||
520 | if (tb[TCA_GATE_CLOCKID]) | |
521 | clockid = rta_getattr_s32(tb[TCA_GATE_CLOCKID]); | |
522 | print_string(PRINT_ANY, "clockid", "\tclockid %s", | |
523 | get_clock_name(clockid)); | |
524 | ||
525 | if (tb[TCA_GATE_FLAGS]) { | |
526 | __u32 flags; | |
527 | ||
528 | flags = rta_getattr_u32(tb[TCA_GATE_FLAGS]); | |
529 | print_0xhex(PRINT_ANY, "flags", "\tflags %#x", flags); | |
530 | } | |
531 | ||
532 | print_string(PRINT_FP, NULL, "%s", "\n"); | |
533 | ||
534 | if (tb[TCA_GATE_BASE_TIME]) | |
535 | base_time = rta_getattr_s64(tb[TCA_GATE_BASE_TIME]); | |
536 | ||
537 | memset(buf, 0, sizeof(buf)); | |
538 | print_string(PRINT_FP, NULL, "\tbase-time %s", | |
539 | sprint_time64(base_time, buf)); | |
540 | print_lluint(PRINT_JSON, "base_time", NULL, base_time); | |
541 | ||
542 | if (tb[TCA_GATE_CYCLE_TIME]) | |
543 | cycle_time = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME]); | |
544 | ||
545 | memset(buf, 0, sizeof(buf)); | |
546 | print_string(PRINT_FP, NULL, | |
547 | "\tcycle-time %s", sprint_time64(cycle_time, buf)); | |
548 | print_lluint(PRINT_JSON, "cycle_time", NULL, cycle_time); | |
549 | ||
550 | if (tb[TCA_GATE_CYCLE_TIME_EXT]) | |
551 | cycle_time_ext = rta_getattr_s64(tb[TCA_GATE_CYCLE_TIME_EXT]); | |
552 | ||
553 | memset(buf, 0, sizeof(buf)); | |
554 | print_string(PRINT_FP, NULL, "\tcycle-time-ext %s", | |
555 | sprint_time64(cycle_time_ext, buf)); | |
556 | print_lluint(PRINT_JSON, "cycle_time_ext", NULL, cycle_time_ext); | |
557 | ||
558 | if (tb[TCA_GATE_ENTRY_LIST]) | |
559 | print_gate_list(tb[TCA_GATE_ENTRY_LIST]); | |
560 | ||
561 | print_action_control(f, "\t", parm->action, ""); | |
562 | ||
563 | print_uint(PRINT_ANY, "index", "\n\t index %u", parm->index); | |
564 | print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt); | |
565 | print_int(PRINT_ANY, "bind", " bind %d", parm->bindcnt); | |
566 | ||
567 | if (show_stats) { | |
568 | if (tb[TCA_GATE_TM]) { | |
569 | struct tcf_t *tm = RTA_DATA(tb[TCA_GATE_TM]); | |
570 | ||
571 | print_tm(f, tm); | |
572 | } | |
573 | } | |
574 | ||
575 | print_string(PRINT_FP, NULL, "%s", "\n"); | |
576 | ||
577 | return 0; | |
578 | } |