1
2
3
4
5
6
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <fcntl.h>
12#include <sys/socket.h>
13#include <sys/param.h>
14#include <netinet/in.h>
15#include <arpa/inet.h>
16#include <string.h>
17#include <math.h>
18#include <errno.h>
19
20#include "utils.h"
21#include "names.h"
22#include "tc_util.h"
23#include "tc_common.h"
24
25#ifndef LIBDIR
26#define LIBDIR "/usr/lib"
27#endif
28
29static struct db_names *cls_names;
30
31#define NAMES_DB_USR CONF_USR_DIR "/tc_cls"
32#define NAMES_DB_ETC CONF_ETC_DIR "/tc_cls"
33
34int cls_names_init(char *path)
35{
36 int ret;
37
38 cls_names = db_names_alloc();
39 if (!cls_names)
40 return -1;
41
42 if (path) {
43 ret = db_names_load(cls_names, path);
44 if (ret == -ENOENT) {
45 fprintf(stderr, "Can't open class names file: %s\n", path);
46 return -1;
47 }
48 }
49
50 ret = db_names_load(cls_names, NAMES_DB_ETC);
51 if (ret == -ENOENT)
52 ret = db_names_load(cls_names, NAMES_DB_USR);
53
54 if (ret) {
55 db_names_free(cls_names);
56 cls_names = NULL;
57 }
58
59 return 0;
60}
61
62void cls_names_uninit(void)
63{
64 db_names_free(cls_names);
65}
66
67const char *get_tc_lib(void)
68{
69 const char *lib_dir;
70
71 lib_dir = getenv("TC_LIB_DIR");
72 if (!lib_dir)
73 lib_dir = LIBDIR "/tc/";
74
75 return lib_dir;
76}
77
78int get_qdisc_handle(__u32 *h, const char *str)
79{
80 unsigned long maj;
81 char *p;
82
83 maj = TC_H_UNSPEC;
84 if (strcmp(str, "none") == 0)
85 goto ok;
86 maj = strtoul(str, &p, 16);
87 if (p == str || maj >= (1 << 16))
88 return -1;
89 maj <<= 16;
90 if (*p != ':' && *p != 0)
91 return -1;
92ok:
93 *h = maj;
94 return 0;
95}
96
97int get_tc_classid(__u32 *h, const char *str)
98{
99 unsigned long maj, min;
100 char *p;
101
102 maj = TC_H_ROOT;
103 if (strcmp(str, "root") == 0)
104 goto ok;
105 maj = TC_H_UNSPEC;
106 if (strcmp(str, "none") == 0)
107 goto ok;
108 maj = strtoul(str, &p, 16);
109 if (p == str) {
110 maj = 0;
111 if (*p != ':')
112 return -1;
113 }
114 if (*p == ':') {
115 if (maj >= (1<<16))
116 return -1;
117 maj <<= 16;
118 str = p+1;
119 min = strtoul(str, &p, 16);
120 if (*p != 0)
121 return -1;
122 if (min >= (1<<16))
123 return -1;
124 maj |= min;
125 } else if (*p != 0)
126 return -1;
127
128ok:
129 *h = maj;
130 return 0;
131}
132
133int print_tc_classid(char *buf, int blen, __u32 h)
134{
135 SPRINT_BUF(handle) = {};
136 int hlen = SPRINT_BSIZE - 1;
137
138 if (h == TC_H_ROOT)
139 sprintf(handle, "root");
140 else if (h == TC_H_UNSPEC)
141 snprintf(handle, hlen, "none");
142 else if (TC_H_MAJ(h) == 0)
143 snprintf(handle, hlen, ":%x", TC_H_MIN(h));
144 else if (TC_H_MIN(h) == 0)
145 snprintf(handle, hlen, "%x:", TC_H_MAJ(h) >> 16);
146 else
147 snprintf(handle, hlen, "%x:%x", TC_H_MAJ(h) >> 16, TC_H_MIN(h));
148
149 if (use_names) {
150 char clname[IDNAME_MAX] = {};
151
152 if (id_to_name(cls_names, h, clname))
153 snprintf(buf, blen, "%s#%s", clname, handle);
154 else
155 snprintf(buf, blen, "%s", handle);
156 } else {
157 snprintf(buf, blen, "%s", handle);
158 }
159
160 return 0;
161}
162
163char *sprint_tc_classid(__u32 h, char *buf)
164{
165 if (print_tc_classid(buf, SPRINT_BSIZE-1, h))
166 strcpy(buf, "???");
167 return buf;
168}
169
170
171
172
173int parse_percent(double *val, const char *str)
174{
175 char *p;
176
177 *val = strtod(str, &p) / 100.;
178 if (*val > 1.0 || *val < 0.0)
179 return 1;
180 if (*p && strcmp(p, "%"))
181 return -1;
182
183 return 0;
184}
185
186static int parse_percent_rate(char *rate, size_t len,
187 const char *str, const char *dev)
188{
189 long dev_mbit;
190 int ret;
191 double perc, rate_bit;
192 char *str_perc = NULL;
193
194 if (!dev[0]) {
195 fprintf(stderr, "No device specified; specify device to rate limit by percentage\n");
196 return -1;
197 }
198
199 if (read_prop(dev, "speed", &dev_mbit))
200 return -1;
201
202 ret = sscanf(str, "%m[0-9.%]", &str_perc);
203 if (ret != 1)
204 goto malf;
205
206 ret = parse_percent(&perc, str_perc);
207 if (ret == 1) {
208 fprintf(stderr, "Invalid rate specified; should be between [0,100]%% but is %s\n", str);
209 goto err;
210 } else if (ret == -1) {
211 goto malf;
212 }
213
214 free(str_perc);
215
216 rate_bit = perc * dev_mbit * 1000 * 1000;
217
218 ret = snprintf(rate, len, "%lf", rate_bit);
219 if (ret <= 0 || ret >= len) {
220 fprintf(stderr, "Unable to parse calculated rate\n");
221 return -1;
222 }
223
224 return 0;
225
226malf:
227 fprintf(stderr, "Specified rate value could not be read or is malformed\n");
228err:
229 free(str_perc);
230 return -1;
231}
232
233int get_percent_rate(unsigned int *rate, const char *str, const char *dev)
234{
235 char r_str[20];
236
237 if (parse_percent_rate(r_str, sizeof(r_str), str, dev))
238 return -1;
239
240 return get_rate(rate, r_str);
241}
242
243int get_percent_rate64(__u64 *rate, const char *str, const char *dev)
244{
245 char r_str[20];
246
247 if (parse_percent_rate(r_str, sizeof(r_str), str, dev))
248 return -1;
249
250 return get_rate64(rate, r_str);
251}
252
253void __attribute__((format(printf, 3, 0)))
254tc_print_rate(enum output_type t, const char *key, const char *fmt,
255 unsigned long long rate)
256{
257 print_rate(use_iec, t, key, fmt, rate);
258}
259
260int get_size_and_cell(unsigned int *size, int *cell_log, char *str)
261{
262 char *slash = strchr(str, '/');
263
264 if (slash)
265 *slash = 0;
266
267 if (get_size(size, str))
268 return -1;
269
270 if (slash) {
271 int cell;
272 int i;
273
274 if (get_integer(&cell, slash+1, 0))
275 return -1;
276 *slash = '/';
277
278 for (i = 0; i < 32; i++) {
279 if ((1<<i) == cell) {
280 *cell_log = i;
281 return 0;
282 }
283 }
284 return -1;
285 }
286 return 0;
287}
288
289void print_devname(enum output_type type, int ifindex)
290{
291 const char *ifname = ll_index_to_name(ifindex);
292
293 if (!is_json_context())
294 printf("dev ");
295
296 print_color_string(type, COLOR_IFNAME,
297 "dev", "%s ", ifname);
298}
299
300static const char *action_n2a(int action)
301{
302 static char buf[64];
303
304 if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN))
305 return "goto";
306 if (TC_ACT_EXT_CMP(action, TC_ACT_JUMP))
307 return "jump";
308 switch (action) {
309 case TC_ACT_UNSPEC:
310 return "continue";
311 case TC_ACT_OK:
312 return "pass";
313 case TC_ACT_SHOT:
314 return "drop";
315 case TC_ACT_RECLASSIFY:
316 return "reclassify";
317 case TC_ACT_PIPE:
318 return "pipe";
319 case TC_ACT_STOLEN:
320 return "stolen";
321 case TC_ACT_TRAP:
322 return "trap";
323 default:
324 snprintf(buf, 64, "%d", action);
325 return buf;
326 }
327}
328
329
330
331
332
333
334
335
336
337
338int action_a2n(char *arg, int *result, bool allow_num)
339{
340 int n;
341 char dummy;
342 struct {
343 const char *a;
344 int n;
345 } a2n[] = {
346 {"continue", TC_ACT_UNSPEC},
347 {"drop", TC_ACT_SHOT},
348 {"shot", TC_ACT_SHOT},
349 {"pass", TC_ACT_OK},
350 {"ok", TC_ACT_OK},
351 {"reclassify", TC_ACT_RECLASSIFY},
352 {"pipe", TC_ACT_PIPE},
353 {"goto", TC_ACT_GOTO_CHAIN},
354 {"jump", TC_ACT_JUMP},
355 {"trap", TC_ACT_TRAP},
356 { NULL },
357 }, *iter;
358
359 for (iter = a2n; iter->a; iter++) {
360 if (matches(arg, iter->a) != 0)
361 continue;
362 n = iter->n;
363 goto out_ok;
364 }
365 if (!allow_num || sscanf(arg, "%d%c", &n, &dummy) != 1)
366 return -1;
367
368out_ok:
369 if (result)
370 *result = n;
371 return 0;
372}
373
374static int __parse_action_control(int *argc_p, char ***argv_p, int *result_p,
375 bool allow_num, bool ignore_a2n_miss)
376{
377 int argc = *argc_p;
378 char **argv = *argv_p;
379 int result;
380
381 if (!argc)
382 return -1;
383 if (action_a2n(*argv, &result, allow_num) == -1) {
384 if (!ignore_a2n_miss)
385 fprintf(stderr, "Bad action type %s\n", *argv);
386 return -1;
387 }
388 if (result == TC_ACT_GOTO_CHAIN) {
389 __u32 chain_index;
390
391 NEXT_ARG();
392 if (matches(*argv, "chain") != 0) {
393 fprintf(stderr, "\"chain index\" expected\n");
394 return -1;
395 }
396 NEXT_ARG();
397 if (get_u32(&chain_index, *argv, 10) ||
398 chain_index > TC_ACT_EXT_VAL_MASK) {
399 fprintf(stderr, "Illegal \"chain index\"\n");
400 return -1;
401 }
402 result |= chain_index;
403 }
404 if (result == TC_ACT_JUMP) {
405 __u32 jump_cnt = 0;
406
407 NEXT_ARG();
408 if (get_u32(&jump_cnt, *argv, 10) ||
409 jump_cnt > TC_ACT_EXT_VAL_MASK) {
410 fprintf(stderr, "Invalid \"jump count\" (%s)\n", *argv);
411 return -1;
412 }
413 result |= jump_cnt;
414 }
415 NEXT_ARG_FWD();
416 *argc_p = argc;
417 *argv_p = argv;
418 *result_p = result;
419 return 0;
420}
421
422
423
424
425
426
427
428
429
430
431
432int parse_action_control(int *argc_p, char ***argv_p,
433 int *result_p, bool allow_num)
434{
435 return __parse_action_control(argc_p, argv_p, result_p,
436 allow_num, false);
437}
438
439
440
441
442
443
444
445
446
447
448
449
450void parse_action_control_dflt(int *argc_p, char ***argv_p,
451 int *result_p, bool allow_num,
452 int default_result)
453{
454 if (__parse_action_control(argc_p, argv_p, result_p, allow_num, true))
455 *result_p = default_result;
456}
457
458static int parse_action_control_slash_spaces(int *argc_p, char ***argv_p,
459 int *result1_p, int *result2_p,
460 bool allow_num)
461{
462 int argc = *argc_p;
463 char **argv = *argv_p;
464 int result1 = -1, result2 = -1;
465 int *result_p = &result1;
466 int ok = 0;
467 int ret;
468
469 while (argc > 0) {
470 switch (ok) {
471 case 1:
472 if (strcmp(*argv, "/") != 0)
473 goto out;
474 result_p = &result2;
475 NEXT_ARG();
476
477 case 0:
478 ret = parse_action_control(&argc, &argv,
479 result_p, allow_num);
480 if (ret)
481 return ret;
482 ok++;
483 break;
484 default:
485 goto out;
486 }
487 }
488out:
489 *result1_p = result1;
490 if (ok == 2)
491 *result2_p = result2;
492 *argc_p = argc;
493 *argv_p = argv;
494 return 0;
495}
496
497
498
499
500
501
502
503
504
505
506
507
508int parse_action_control_slash(int *argc_p, char ***argv_p,
509 int *result1_p, int *result2_p, bool allow_num)
510{
511 int result1, result2, argc = *argc_p;
512 char **argv = *argv_p;
513 char *p = strchr(*argv, '/');
514
515 if (!p)
516 return parse_action_control_slash_spaces(argc_p, argv_p,
517 result1_p, result2_p,
518 allow_num);
519 *p = 0;
520 if (action_a2n(*argv, &result1, allow_num)) {
521 *p = '/';
522 return -1;
523 }
524
525 *p = '/';
526 if (action_a2n(p + 1, &result2, allow_num))
527 return -1;
528
529 *result1_p = result1;
530 *result2_p = result2;
531 NEXT_ARG_FWD();
532 *argc_p = argc;
533 *argv_p = argv;
534 return 0;
535}
536
537void print_action_control(const char *prefix, int action, const char *suffix)
538{
539 print_string(PRINT_FP, NULL, "%s", prefix);
540 open_json_object("control_action");
541 print_string(PRINT_ANY, "type", "%s", action_n2a(action));
542 if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN))
543 print_uint(PRINT_ANY, "chain", " chain %u",
544 action & TC_ACT_EXT_VAL_MASK);
545 if (TC_ACT_EXT_CMP(action, TC_ACT_JUMP))
546 print_uint(PRINT_ANY, "jump", " %u",
547 action & TC_ACT_EXT_VAL_MASK);
548 close_json_object();
549 print_string(PRINT_FP, NULL, "%s", suffix);
550}
551
552int get_linklayer(unsigned int *val, const char *arg)
553{
554 int res;
555
556 if (matches(arg, "ethernet") == 0)
557 res = LINKLAYER_ETHERNET;
558 else if (matches(arg, "atm") == 0)
559 res = LINKLAYER_ATM;
560 else if (matches(arg, "adsl") == 0)
561 res = LINKLAYER_ATM;
562 else
563 return -1;
564
565 *val = res;
566 return 0;
567}
568
569static void print_linklayer(char *buf, int len, unsigned int linklayer)
570{
571 switch (linklayer) {
572 case LINKLAYER_UNSPEC:
573 snprintf(buf, len, "%s", "unspec");
574 return;
575 case LINKLAYER_ETHERNET:
576 snprintf(buf, len, "%s", "ethernet");
577 return;
578 case LINKLAYER_ATM:
579 snprintf(buf, len, "%s", "atm");
580 return;
581 default:
582 snprintf(buf, len, "%s", "unknown");
583 return;
584 }
585}
586
587char *sprint_linklayer(unsigned int linklayer, char *buf)
588{
589 print_linklayer(buf, SPRINT_BSIZE-1, linklayer);
590 return buf;
591}
592
593
594
595
596
597
598static const struct clockid_table {
599 const char *name;
600 clockid_t clockid;
601} clockt_map[] = {
602#ifdef CLOCK_BOOTTIME
603 { "BOOTTIME", CLOCK_BOOTTIME },
604#endif
605#ifdef CLOCK_MONOTONIC
606 { "MONOTONIC", CLOCK_MONOTONIC },
607#endif
608#ifdef CLOCK_REALTIME
609 { "REALTIME", CLOCK_REALTIME },
610#endif
611#ifdef CLOCK_TAI
612 { "TAI", CLOCK_TAI },
613#endif
614 { NULL }
615};
616
617int get_clockid(__s32 *val, const char *arg)
618{
619 const struct clockid_table *c;
620
621
622 if (strcasestr(arg, "CLOCK_") != NULL)
623 arg += sizeof("CLOCK_") - 1;
624
625 for (c = clockt_map; c->name; c++) {
626 if (strcasecmp(c->name, arg) == 0) {
627 *val = c->clockid;
628 return 0;
629 }
630 }
631
632 return -1;
633}
634
635const char *get_clock_name(clockid_t clockid)
636{
637 const struct clockid_table *c;
638
639 for (c = clockt_map; c->name; c++) {
640 if (clockid == c->clockid)
641 return c->name;
642 }
643
644 return "invalid";
645}
646
647void print_tm(const struct tcf_t *tm)
648{
649 int hz = get_user_hz();
650
651 if (tm->install != 0)
652 print_uint(PRINT_ANY, "installed", " installed %u sec",
653 tm->install / hz);
654
655 if (tm->lastuse != 0)
656 print_uint(PRINT_ANY, "last_used", " used %u sec",
657 tm->lastuse / hz);
658
659 if (tm->firstuse != 0)
660 print_uint(PRINT_ANY, "first_used", " firstused %u sec",
661 tm->firstuse / hz);
662
663 if (tm->expires != 0)
664 print_uint(PRINT_ANY, "expires", " expires %u sec",
665 tm->expires / hz);
666}
667
668static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix,
669 __u64 packets64, __u64 packets64_hw)
670{
671 struct gnet_stats_basic bs_hw;
672
673 if (!tbs[TCA_STATS_BASIC_HW])
674 return;
675
676 memcpy(&bs_hw, RTA_DATA(tbs[TCA_STATS_BASIC_HW]),
677 MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC_HW]), sizeof(bs_hw)));
678 packets64_hw = packets64_hw ? : bs_hw.packets;
679
680 if (bs_hw.bytes == 0 && packets64_hw == 0)
681 return;
682
683 if (tbs[TCA_STATS_BASIC]) {
684 struct gnet_stats_basic bs;
685
686 memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]),
687 MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]),
688 sizeof(bs)));
689 packets64 = packets64 ? : bs.packets;
690
691 if (bs.bytes >= bs_hw.bytes && packets64 >= packets64_hw) {
692 print_nl();
693 print_string(PRINT_FP, NULL, "%s", prefix);
694 print_lluint(PRINT_ANY, "sw_bytes",
695 "Sent software %llu bytes",
696 bs.bytes - bs_hw.bytes);
697 print_lluint(PRINT_ANY, "sw_packets", " %llu pkt",
698 packets64 - packets64_hw);
699 }
700 }
701
702 print_nl();
703 print_string(PRINT_FP, NULL, "%s", prefix);
704 print_lluint(PRINT_ANY, "hw_bytes", "Sent hardware %llu bytes",
705 bs_hw.bytes);
706 print_lluint(PRINT_ANY, "hw_packets", " %llu pkt", packets64_hw);
707}
708
709static void parse_packets64(const struct rtattr *nest, __u64 *p_packets64,
710 __u64 *p_packets64_hw)
711{
712 unsigned short prev_type = __TCA_STATS_MAX;
713 const struct rtattr *pos;
714
715
716
717
718
719 rtattr_for_each_nested(pos, nest) {
720 if (pos->rta_type == TCA_STATS_PKT64 &&
721 prev_type == TCA_STATS_BASIC)
722 *p_packets64 = rta_getattr_u64(pos);
723 else if (pos->rta_type == TCA_STATS_PKT64 &&
724 prev_type == TCA_STATS_BASIC_HW)
725 *p_packets64_hw = rta_getattr_u64(pos);
726 prev_type = pos->rta_type;
727 }
728}
729
730void print_tcstats2_attr(struct rtattr *rta, const char *prefix, struct rtattr **xstats)
731{
732 struct rtattr *tbs[TCA_STATS_MAX + 1];
733 __u64 packets64 = 0, packets64_hw = 0;
734
735 parse_rtattr_nested(tbs, TCA_STATS_MAX, rta);
736 parse_packets64(rta, &packets64, &packets64_hw);
737
738 if (tbs[TCA_STATS_BASIC]) {
739 struct gnet_stats_basic bs = {0};
740
741 memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]),
742 MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs)));
743 print_string(PRINT_FP, NULL, "%s", prefix);
744 print_lluint(PRINT_ANY, "bytes", "Sent %llu bytes", bs.bytes);
745 if (packets64)
746 print_lluint(PRINT_ANY, "packets",
747 " %llu pkt", packets64);
748 else
749 print_uint(PRINT_ANY, "packets",
750 " %u pkt", bs.packets);
751 }
752
753 if (tbs[TCA_STATS_QUEUE]) {
754 struct gnet_stats_queue q = {0};
755
756 memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]),
757 MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q)));
758 print_uint(PRINT_ANY, "drops", " (dropped %u", q.drops);
759 print_uint(PRINT_ANY, "overlimits", ", overlimits %u",
760 q.overlimits);
761 print_uint(PRINT_ANY, "requeues", " requeues %u) ", q.requeues);
762 }
763
764 if (tbs[TCA_STATS_BASIC_HW])
765 print_tcstats_basic_hw(tbs, prefix, packets64, packets64_hw);
766
767 if (tbs[TCA_STATS_RATE_EST64]) {
768 struct gnet_stats_rate_est64 re = {0};
769
770 memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST64]),
771 MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST64]),
772 sizeof(re)));
773 print_string(PRINT_FP, NULL, "\n%s", prefix);
774 print_lluint(PRINT_JSON, "rate", NULL, re.bps);
775 tc_print_rate(PRINT_FP, NULL, "rate %s", re.bps);
776 print_lluint(PRINT_ANY, "pps", " %llupps", re.pps);
777 } else if (tbs[TCA_STATS_RATE_EST]) {
778 struct gnet_stats_rate_est re = {0};
779
780 memcpy(&re, RTA_DATA(tbs[TCA_STATS_RATE_EST]),
781 MIN(RTA_PAYLOAD(tbs[TCA_STATS_RATE_EST]), sizeof(re)));
782 print_string(PRINT_FP, NULL, "\n%s", prefix);
783 print_uint(PRINT_JSON, "rate", NULL, re.bps);
784 tc_print_rate(PRINT_FP, NULL, "rate %s", re.bps);
785 print_uint(PRINT_ANY, "pps", " %upps", re.pps);
786 }
787
788 if (tbs[TCA_STATS_QUEUE]) {
789 struct gnet_stats_queue q = {0};
790
791 memcpy(&q, RTA_DATA(tbs[TCA_STATS_QUEUE]),
792 MIN(RTA_PAYLOAD(tbs[TCA_STATS_QUEUE]), sizeof(q)));
793 if (!tbs[TCA_STATS_RATE_EST])
794 print_nl();
795 print_string(PRINT_FP, NULL, "%s", prefix);
796 print_size(PRINT_ANY, "backlog", "backlog %s", q.backlog);
797 print_uint(PRINT_ANY, "qlen", " %up", q.qlen);
798 print_uint(PRINT_FP, NULL, " requeues %u", q.requeues);
799 }
800
801 if (xstats)
802 *xstats = tbs[TCA_STATS_APP] ? : NULL;
803}
804
805void print_tcstats_attr(FILE *fp, struct rtattr *tb[], const char *prefix,
806 struct rtattr **xstats)
807{
808 if (tb[TCA_STATS2]) {
809 print_tcstats2_attr(tb[TCA_STATS2], prefix, xstats);
810 if (xstats && !*xstats)
811 goto compat_xstats;
812 return;
813 }
814
815 if (tb[TCA_STATS]) {
816 struct tc_stats st = {};
817
818
819 memcpy(&st, RTA_DATA(tb[TCA_STATS]),
820 MIN(RTA_PAYLOAD(tb[TCA_STATS]), sizeof(st)));
821
822 print_string(PRINT_FP, NULL, "%s", prefix);
823 print_lluint(PRINT_ANY, "bytes", "Sent %llu bytes",
824 (unsigned long long)st.bytes);
825 print_uint(PRINT_ANY, "packets", " %u pkts", st.packets);
826 print_uint(PRINT_ANY, "dropped", " (dropped %u,", st.drops);
827 print_uint(PRINT_ANY, "overlimits", " overlimits %u) ", st.overlimits);
828
829 if (st.bps || st.pps || st.qlen || st.backlog) {
830 print_nl();
831 print_string(PRINT_FP, NULL, "%s", prefix);
832
833 if (st.bps || st.pps) {
834 print_string(PRINT_FP, NULL, "rate ", NULL);
835 if (st.bps)
836 tc_print_rate(PRINT_ANY, "rate", "%s ", st.bps);
837 if (st.pps)
838 print_uint(PRINT_ANY, "pps", "%upps ", st.pps);
839 }
840 if (st.qlen || st.backlog) {
841 print_string(PRINT_FP, NULL, "backlog ", NULL);
842 if (st.backlog)
843 print_size(PRINT_ANY, "backlog", "%s ", st.backlog);
844 if (st.qlen)
845 print_uint(PRINT_ANY, "qlen", "%up ", st.qlen);
846 }
847 }
848 }
849
850compat_xstats:
851 if (tb[TCA_XSTATS] && xstats)
852 *xstats = tb[TCA_XSTATS];
853}
854
855static void print_masked_type(__u32 type_max,
856 __u32 (*rta_getattr_type)(const struct rtattr *),
857 const char *name, struct rtattr *attr,
858 struct rtattr *mask_attr, bool newline)
859{
860 __u32 value, mask;
861
862 if (!attr)
863 return;
864
865 value = rta_getattr_type(attr);
866 mask = mask_attr ? rta_getattr_type(mask_attr) : type_max;
867
868 if (newline)
869 print_string(PRINT_FP, NULL, "%s ", _SL_);
870 else
871 print_string(PRINT_FP, NULL, " ", _SL_);
872
873 print_uint_name_value(name, value);
874
875 if (mask != type_max) {
876 char mask_name[SPRINT_BSIZE-6];
877
878 snprintf(mask_name, sizeof(mask_name), "%s_mask", name);
879 print_hex(PRINT_ANY, mask_name, "/0x%x", mask);
880 }
881}
882
883void print_masked_u32(const char *name, struct rtattr *attr,
884 struct rtattr *mask_attr, bool newline)
885{
886 print_masked_type(UINT32_MAX, rta_getattr_u32, name, attr, mask_attr,
887 newline);
888}
889
890static __u32 __rta_getattr_u16_u32(const struct rtattr *attr)
891{
892 return rta_getattr_u16(attr);
893}
894
895void print_masked_u16(const char *name, struct rtattr *attr,
896 struct rtattr *mask_attr, bool newline)
897{
898 print_masked_type(UINT16_MAX, __rta_getattr_u16_u32, name, attr,
899 mask_attr, newline);
900}
901
902static __u32 __rta_getattr_u8_u32(const struct rtattr *attr)
903{
904 return rta_getattr_u8(attr);
905}
906
907void print_masked_u8(const char *name, struct rtattr *attr,
908 struct rtattr *mask_attr, bool newline)
909{
910 print_masked_type(UINT8_MAX, __rta_getattr_u8_u32, name, attr,
911 mask_attr, newline);
912}
913
914static __u32 __rta_getattr_be16_u32(const struct rtattr *attr)
915{
916 return rta_getattr_be16(attr);
917}
918
919void print_masked_be16(const char *name, struct rtattr *attr,
920 struct rtattr *mask_attr, bool newline)
921{
922 print_masked_type(UINT16_MAX, __rta_getattr_be16_u32, name, attr,
923 mask_attr, newline);
924}
925
926void print_ext_msg(struct rtattr **tb)
927{
928 if (!tb[TCA_EXT_WARN_MSG])
929 return;
930
931 print_string(PRINT_ANY, "warn", "%s", rta_getattr_str(tb[TCA_EXT_WARN_MSG]));
932 print_nl();
933}
934