1
2#include <assert.h>
3#include <errno.h>
4#include <stdio.h>
5#include <string.h>
6#include <sys/param.h>
7#include <sys/socket.h>
8
9#include "list.h"
10#include "utils.h"
11#include "ip_common.h"
12
13struct ipstats_stat_dump_filters {
14
15
16
17 __u32 mask[IFLA_STATS_MAX + 1];
18};
19
20static void
21ipstats_stat_desc_enable_bit(struct ipstats_stat_dump_filters *filters,
22 unsigned int group, unsigned int subgroup)
23{
24 filters->mask[0] |= IFLA_STATS_FILTER_BIT(group);
25 if (subgroup)
26 filters->mask[group] |= IFLA_STATS_FILTER_BIT(subgroup);
27}
28
29struct ipstats_stat_show_attrs {
30 struct if_stats_msg *ifsm;
31 int len;
32
33
34
35
36 struct rtattr **tbs[IFLA_STATS_MAX + 1];
37};
38
39static const char *const ipstats_levels[] = {
40 "group",
41 "subgroup",
42 "suite",
43};
44
45enum {
46 IPSTATS_LEVELS_COUNT = ARRAY_SIZE(ipstats_levels),
47};
48
49struct ipstats_sel {
50 const char *sel[IPSTATS_LEVELS_COUNT];
51};
52
53struct ipstats_stat_enabled_one {
54 const struct ipstats_stat_desc *desc;
55 struct ipstats_sel sel;
56};
57
58struct ipstats_stat_enabled {
59 struct ipstats_stat_enabled_one *enabled;
60 size_t nenabled;
61};
62
63static const unsigned int ipstats_stat_ifla_max[] = {
64 [0] = IFLA_STATS_MAX,
65 [IFLA_STATS_LINK_XSTATS] = LINK_XSTATS_TYPE_MAX,
66 [IFLA_STATS_LINK_XSTATS_SLAVE] = LINK_XSTATS_TYPE_MAX,
67 [IFLA_STATS_LINK_OFFLOAD_XSTATS] = IFLA_OFFLOAD_XSTATS_MAX,
68 [IFLA_STATS_AF_SPEC] = AF_MAX - 1,
69};
70
71static_assert(ARRAY_SIZE(ipstats_stat_ifla_max) == IFLA_STATS_MAX + 1,
72 "An IFLA_STATS attribute is missing from the ifla_max table");
73
74static int
75ipstats_stat_show_attrs_alloc_tb(struct ipstats_stat_show_attrs *attrs,
76 unsigned int group)
77{
78 unsigned int ifla_max;
79 int err;
80
81 assert(group < ARRAY_SIZE(ipstats_stat_ifla_max));
82 assert(group < ARRAY_SIZE(attrs->tbs));
83 ifla_max = ipstats_stat_ifla_max[group];
84 assert(ifla_max != 0);
85
86 if (attrs->tbs[group])
87 return 0;
88
89 attrs->tbs[group] = calloc(ifla_max + 1, sizeof(*attrs->tbs[group]));
90 if (attrs->tbs[group] == NULL) {
91 fprintf(stderr, "Error parsing netlink answer: %s\n",
92 strerror(errno));
93 return -errno;
94 }
95
96 if (group == 0)
97 err = parse_rtattr(attrs->tbs[group], ifla_max,
98 IFLA_STATS_RTA(attrs->ifsm), attrs->len);
99 else
100 err = parse_rtattr_nested(attrs->tbs[group], ifla_max,
101 attrs->tbs[0][group]);
102
103 if (err != 0) {
104 free(attrs->tbs[group]);
105 attrs->tbs[group] = NULL;
106 }
107 return err;
108}
109
110static const struct rtattr *
111ipstats_stat_show_get_attr(struct ipstats_stat_show_attrs *attrs,
112 int group, int subgroup, int *err)
113{
114 int tmp_err;
115
116 if (err == NULL)
117 err = &tmp_err;
118
119 *err = 0;
120 if (subgroup == 0)
121 return attrs->tbs[0][group];
122
123 if (attrs->tbs[0][group] == NULL)
124 return NULL;
125
126 *err = ipstats_stat_show_attrs_alloc_tb(attrs, group);
127 if (*err != 0)
128 return NULL;
129
130 return attrs->tbs[group][subgroup];
131}
132
133static void
134ipstats_stat_show_attrs_free(struct ipstats_stat_show_attrs *attrs)
135{
136 size_t i;
137
138 for (i = 0; i < ARRAY_SIZE(attrs->tbs); i++)
139 free(attrs->tbs[i]);
140}
141
142#define IPSTATS_RTA_PAYLOAD(VAR, AT) \
143 do { \
144 const struct rtattr *__at = (AT); \
145 size_t __at_sz = __at->rta_len - RTA_LENGTH(0); \
146 size_t __var_sz = sizeof(VAR); \
147 typeof(VAR) *__dest = &VAR; \
148 \
149 memset(__dest, 0, __var_sz); \
150 memcpy(__dest, RTA_DATA(__at), MIN(__at_sz, __var_sz)); \
151 } while (0)
152
153static int ipstats_show_64(struct ipstats_stat_show_attrs *attrs,
154 unsigned int group, unsigned int subgroup)
155{
156 struct rtnl_link_stats64 stats;
157 const struct rtattr *at;
158 int err;
159
160 at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
161 if (at == NULL)
162 return err;
163
164 IPSTATS_RTA_PAYLOAD(stats, at);
165
166 open_json_object("stats64");
167 print_stats64(stdout, &stats, NULL, NULL);
168 close_json_object();
169 return 0;
170}
171
172static void print_hw_stats64(FILE *fp, struct rtnl_hw_stats64 *s)
173{
174 unsigned int cols[] = {
175 strlen("*X: bytes"),
176 strlen("packets"),
177 strlen("errors"),
178 strlen("dropped"),
179 strlen("overrun"),
180 };
181
182 if (is_json_context()) {
183
184 open_json_object("rx");
185 print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes);
186 print_u64(PRINT_JSON, "packets", NULL, s->rx_packets);
187 print_u64(PRINT_JSON, "errors", NULL, s->rx_errors);
188 print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped);
189 print_u64(PRINT_JSON, "multicast", NULL, s->multicast);
190 close_json_object();
191
192
193 open_json_object("tx");
194 print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes);
195 print_u64(PRINT_JSON, "packets", NULL, s->tx_packets);
196 print_u64(PRINT_JSON, "errors", NULL, s->tx_errors);
197 print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped);
198 close_json_object();
199 } else {
200 size_columns(cols, ARRAY_SIZE(cols),
201 s->rx_bytes, s->rx_packets, s->rx_errors,
202 s->rx_dropped, s->multicast);
203 size_columns(cols, ARRAY_SIZE(cols),
204 s->tx_bytes, s->tx_packets, s->tx_errors,
205 s->tx_dropped, 0);
206
207
208 fprintf(fp, " RX: %*s %*s %*s %*s %*s%s",
209 cols[0] - 4, "bytes", cols[1], "packets",
210 cols[2], "errors", cols[3], "dropped",
211 cols[4], "mcast", _SL_);
212
213 fprintf(fp, " ");
214 print_num(fp, cols[0], s->rx_bytes);
215 print_num(fp, cols[1], s->rx_packets);
216 print_num(fp, cols[2], s->rx_errors);
217 print_num(fp, cols[3], s->rx_dropped);
218 print_num(fp, cols[4], s->multicast);
219 fprintf(fp, "%s", _SL_);
220
221
222 fprintf(fp, " TX: %*s %*s %*s %*s%s",
223 cols[0] - 4, "bytes", cols[1], "packets",
224 cols[2], "errors", cols[3], "dropped", _SL_);
225
226 fprintf(fp, " ");
227 print_num(fp, cols[0], s->tx_bytes);
228 print_num(fp, cols[1], s->tx_packets);
229 print_num(fp, cols[2], s->tx_errors);
230 print_num(fp, cols[3], s->tx_dropped);
231 }
232}
233
234static int ipstats_show_hw64(const struct rtattr *at)
235{
236 struct rtnl_hw_stats64 stats;
237
238 IPSTATS_RTA_PAYLOAD(stats, at);
239 print_hw_stats64(stdout, &stats);
240 return 0;
241}
242
243enum ipstats_maybe_on_off {
244 IPSTATS_MOO_OFF = -1,
245 IPSTATS_MOO_INVALID,
246 IPSTATS_MOO_ON,
247};
248
249static bool ipstats_moo_to_bool(enum ipstats_maybe_on_off moo)
250{
251 assert(moo != IPSTATS_MOO_INVALID);
252 return moo + 1;
253}
254
255static int ipstats_print_moo(enum output_type t, const char *key,
256 const char *fmt, enum ipstats_maybe_on_off moo)
257{
258 if (!moo)
259 return 0;
260 return print_on_off(t, key, fmt, ipstats_moo_to_bool(moo));
261}
262
263struct ipstats_hw_s_info_one {
264 enum ipstats_maybe_on_off request;
265 enum ipstats_maybe_on_off used;
266};
267
268enum ipstats_hw_s_info_idx {
269 IPSTATS_HW_S_INFO_IDX_L3_STATS,
270 IPSTATS_HW_S_INFO_IDX_COUNT
271};
272
273static const char *const ipstats_hw_s_info_name[] = {
274 "l3_stats",
275};
276
277static_assert(ARRAY_SIZE(ipstats_hw_s_info_name) ==
278 IPSTATS_HW_S_INFO_IDX_COUNT,
279 "mismatch: enum ipstats_hw_s_info_idx x ipstats_hw_s_info_name");
280
281struct ipstats_hw_s_info {
282
283 struct ipstats_hw_s_info_one *infos[IPSTATS_HW_S_INFO_IDX_COUNT];
284};
285
286static enum ipstats_maybe_on_off ipstats_dissect_01(int value, const char *what)
287{
288 switch (value) {
289 case 0:
290 return IPSTATS_MOO_OFF;
291 case 1:
292 return IPSTATS_MOO_ON;
293 default:
294 fprintf(stderr, "Invalid value for %s: expected 0 or 1, got %d.\n",
295 what, value);
296 return IPSTATS_MOO_INVALID;
297 }
298}
299
300static int ipstats_dissect_hw_s_info_one(const struct rtattr *at,
301 struct ipstats_hw_s_info_one *p_hwsio,
302 const char *what)
303{
304 int attr_id_request = IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST;
305 struct rtattr *tb[IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX + 1];
306 int attr_id_used = IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED;
307 struct ipstats_hw_s_info_one hwsio = {};
308 int err;
309 int v;
310
311 err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX, at);
312 if (err)
313 return err;
314
315 if (tb[attr_id_request]) {
316 v = rta_getattr_u8(tb[attr_id_request]);
317 hwsio.request = ipstats_dissect_01(v, "request");
318
319
320 if (!hwsio.request)
321 return -EINVAL;
322 }
323
324 if (tb[attr_id_used]) {
325 v = rta_getattr_u8(tb[attr_id_used]);
326 hwsio.used = ipstats_dissect_01(v, "used");
327 }
328
329 *p_hwsio = hwsio;
330 return 0;
331}
332
333static int ipstats_dissect_hw_s_info(const struct rtattr *at,
334 struct ipstats_hw_s_info *hwsi)
335{
336 struct rtattr *tb[IFLA_OFFLOAD_XSTATS_MAX + 1];
337 int attr_id_l3 = IFLA_OFFLOAD_XSTATS_L3_STATS;
338 struct ipstats_hw_s_info_one *hwsio = NULL;
339 int err;
340
341 err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_MAX, at);
342 if (err)
343 return err;
344
345 *hwsi = (struct ipstats_hw_s_info){};
346
347 if (tb[attr_id_l3]) {
348 hwsio = malloc(sizeof(*hwsio));
349 if (!hwsio) {
350 err = -ENOMEM;
351 goto out;
352 }
353
354 err = ipstats_dissect_hw_s_info_one(tb[attr_id_l3], hwsio, "l3");
355 if (err)
356 goto out;
357
358 hwsi->infos[IPSTATS_HW_S_INFO_IDX_L3_STATS] = hwsio;
359 hwsio = NULL;
360 }
361
362 return 0;
363
364out:
365 free(hwsio);
366 return err;
367}
368
369static void ipstats_fini_hw_s_info(struct ipstats_hw_s_info *hwsi)
370{
371 int i;
372
373 for (i = 0; i < IPSTATS_HW_S_INFO_IDX_COUNT; i++)
374 free(hwsi->infos[i]);
375}
376
377static void
378__ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info_one *hwsio)
379{
380 if (hwsio == NULL)
381 return;
382
383 ipstats_print_moo(PRINT_ANY, "request", " %s", hwsio->request);
384 ipstats_print_moo(PRINT_ANY, "used", " used %s", hwsio->used);
385}
386
387static void
388ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info *hwsi,
389 enum ipstats_hw_s_info_idx idx)
390{
391 const struct ipstats_hw_s_info_one *hwsio = hwsi->infos[idx];
392 const char *name = ipstats_hw_s_info_name[idx];
393
394 if (hwsio == NULL)
395 return;
396
397 print_string(PRINT_FP, NULL, " %s", name);
398 open_json_object(name);
399 __ipstats_show_hw_s_info_one(hwsio);
400 close_json_object();
401}
402
403static int __ipstats_show_hw_s_info(const struct rtattr *at)
404{
405 struct ipstats_hw_s_info hwsi = {};
406 int err;
407
408 err = ipstats_dissect_hw_s_info(at, &hwsi);
409 if (err)
410 return err;
411
412 open_json_object("info");
413 ipstats_show_hw_s_info_one(&hwsi, IPSTATS_HW_S_INFO_IDX_L3_STATS);
414 close_json_object();
415
416 ipstats_fini_hw_s_info(&hwsi);
417 return 0;
418}
419
420static int ipstats_show_hw_s_info(struct ipstats_stat_show_attrs *attrs,
421 unsigned int group, unsigned int subgroup)
422{
423 const struct rtattr *at;
424 int err;
425
426 at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
427 if (at == NULL)
428 return err;
429
430 print_nl();
431 return __ipstats_show_hw_s_info(at);
432}
433
434static int __ipstats_show_hw_stats(const struct rtattr *at_hwsi,
435 const struct rtattr *at_stats,
436 enum ipstats_hw_s_info_idx idx)
437{
438 int err = 0;
439
440 if (at_hwsi != NULL) {
441 struct ipstats_hw_s_info hwsi = {};
442
443 err = ipstats_dissect_hw_s_info(at_hwsi, &hwsi);
444 if (err)
445 return err;
446
447 open_json_object("info");
448 __ipstats_show_hw_s_info_one(hwsi.infos[idx]);
449 close_json_object();
450
451 ipstats_fini_hw_s_info(&hwsi);
452 }
453
454 if (at_stats != NULL) {
455 print_nl();
456 open_json_object("stats64");
457 err = ipstats_show_hw64(at_stats);
458 close_json_object();
459 }
460
461 return err;
462}
463
464static int ipstats_show_hw_stats(struct ipstats_stat_show_attrs *attrs,
465 unsigned int group,
466 unsigned int hw_s_info,
467 unsigned int hw_stats,
468 enum ipstats_hw_s_info_idx idx)
469{
470 const struct rtattr *at_stats;
471 const struct rtattr *at_hwsi;
472 int err = 0;
473
474 at_hwsi = ipstats_stat_show_get_attr(attrs, group, hw_s_info, &err);
475 if (at_hwsi == NULL)
476 return err;
477
478 at_stats = ipstats_stat_show_get_attr(attrs, group, hw_stats, &err);
479 if (at_stats == NULL && err != 0)
480 return err;
481
482 return __ipstats_show_hw_stats(at_hwsi, at_stats, idx);
483}
484
485static void
486ipstats_stat_desc_pack_cpu_hit(struct ipstats_stat_dump_filters *filters,
487 const struct ipstats_stat_desc *desc)
488{
489 ipstats_stat_desc_enable_bit(filters,
490 IFLA_STATS_LINK_OFFLOAD_XSTATS,
491 IFLA_OFFLOAD_XSTATS_CPU_HIT);
492}
493
494static int ipstats_stat_desc_show_cpu_hit(struct ipstats_stat_show_attrs *attrs,
495 const struct ipstats_stat_desc *desc)
496{
497 print_nl();
498 return ipstats_show_64(attrs,
499 IFLA_STATS_LINK_OFFLOAD_XSTATS,
500 IFLA_OFFLOAD_XSTATS_CPU_HIT);
501}
502
503static const struct ipstats_stat_desc ipstats_stat_desc_offload_cpu_hit = {
504 .name = "cpu_hit",
505 .kind = IPSTATS_STAT_DESC_KIND_LEAF,
506 .pack = &ipstats_stat_desc_pack_cpu_hit,
507 .show = &ipstats_stat_desc_show_cpu_hit,
508};
509
510static void
511ipstats_stat_desc_pack_hw_stats_info(struct ipstats_stat_dump_filters *filters,
512 const struct ipstats_stat_desc *desc)
513{
514 ipstats_stat_desc_enable_bit(filters,
515 IFLA_STATS_LINK_OFFLOAD_XSTATS,
516 IFLA_OFFLOAD_XSTATS_HW_S_INFO);
517}
518
519static int
520ipstats_stat_desc_show_hw_stats_info(struct ipstats_stat_show_attrs *attrs,
521 const struct ipstats_stat_desc *desc)
522{
523 return ipstats_show_hw_s_info(attrs,
524 IFLA_STATS_LINK_OFFLOAD_XSTATS,
525 IFLA_OFFLOAD_XSTATS_HW_S_INFO);
526}
527
528static const struct ipstats_stat_desc ipstats_stat_desc_offload_hw_s_info = {
529 .name = "hw_stats_info",
530 .kind = IPSTATS_STAT_DESC_KIND_LEAF,
531 .pack = &ipstats_stat_desc_pack_hw_stats_info,
532 .show = &ipstats_stat_desc_show_hw_stats_info,
533};
534
535static void
536ipstats_stat_desc_pack_l3_stats(struct ipstats_stat_dump_filters *filters,
537 const struct ipstats_stat_desc *desc)
538{
539 ipstats_stat_desc_enable_bit(filters,
540 IFLA_STATS_LINK_OFFLOAD_XSTATS,
541 IFLA_OFFLOAD_XSTATS_L3_STATS);
542 ipstats_stat_desc_enable_bit(filters,
543 IFLA_STATS_LINK_OFFLOAD_XSTATS,
544 IFLA_OFFLOAD_XSTATS_HW_S_INFO);
545}
546
547static int
548ipstats_stat_desc_show_l3_stats(struct ipstats_stat_show_attrs *attrs,
549 const struct ipstats_stat_desc *desc)
550{
551 return ipstats_show_hw_stats(attrs,
552 IFLA_STATS_LINK_OFFLOAD_XSTATS,
553 IFLA_OFFLOAD_XSTATS_HW_S_INFO,
554 IFLA_OFFLOAD_XSTATS_L3_STATS,
555 IPSTATS_HW_S_INFO_IDX_L3_STATS);
556}
557
558static const struct ipstats_stat_desc ipstats_stat_desc_offload_l3_stats = {
559 .name = "l3_stats",
560 .kind = IPSTATS_STAT_DESC_KIND_LEAF,
561 .pack = &ipstats_stat_desc_pack_l3_stats,
562 .show = &ipstats_stat_desc_show_l3_stats,
563};
564
565static const struct ipstats_stat_desc *ipstats_stat_desc_offload_subs[] = {
566 &ipstats_stat_desc_offload_cpu_hit,
567 &ipstats_stat_desc_offload_hw_s_info,
568 &ipstats_stat_desc_offload_l3_stats,
569};
570
571static const struct ipstats_stat_desc ipstats_stat_desc_offload_group = {
572 .name = "offload",
573 .kind = IPSTATS_STAT_DESC_KIND_GROUP,
574 .subs = ipstats_stat_desc_offload_subs,
575 .nsubs = ARRAY_SIZE(ipstats_stat_desc_offload_subs),
576};
577
578void ipstats_stat_desc_pack_xstats(struct ipstats_stat_dump_filters *filters,
579 const struct ipstats_stat_desc *desc)
580{
581 struct ipstats_stat_desc_xstats *xdesc;
582
583 xdesc = container_of(desc, struct ipstats_stat_desc_xstats, desc);
584 ipstats_stat_desc_enable_bit(filters, xdesc->xstats_at, 0);
585}
586
587int ipstats_stat_desc_show_xstats(struct ipstats_stat_show_attrs *attrs,
588 const struct ipstats_stat_desc *desc)
589{
590 struct ipstats_stat_desc_xstats *xdesc;
591 const struct rtattr *at;
592 const struct rtattr *i;
593 int err;
594
595 xdesc = container_of(desc, struct ipstats_stat_desc_xstats, desc);
596 at = ipstats_stat_show_get_attr(attrs,
597 xdesc->xstats_at,
598 xdesc->link_type_at, &err);
599 if (at == NULL)
600 return err;
601
602 rtattr_for_each_nested(i, at) {
603 if (i->rta_type == xdesc->inner_at) {
604 print_nl();
605 xdesc->show_cb(i);
606 }
607 }
608
609 return 0;
610}
611
612static const struct ipstats_stat_desc *ipstats_stat_desc_xstats_subs[] = {
613 &ipstats_stat_desc_xstats_bridge_group,
614 &ipstats_stat_desc_xstats_bond_group,
615};
616
617static const struct ipstats_stat_desc ipstats_stat_desc_xstats_group = {
618 .name = "xstats",
619 .kind = IPSTATS_STAT_DESC_KIND_GROUP,
620 .subs = ipstats_stat_desc_xstats_subs,
621 .nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_subs),
622};
623
624static const struct ipstats_stat_desc *ipstats_stat_desc_xstats_slave_subs[] = {
625 &ipstats_stat_desc_xstats_slave_bridge_group,
626 &ipstats_stat_desc_xstats_slave_bond_group,
627};
628
629static const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_group = {
630 .name = "xstats_slave",
631 .kind = IPSTATS_STAT_DESC_KIND_GROUP,
632 .subs = ipstats_stat_desc_xstats_slave_subs,
633 .nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_slave_subs),
634};
635
636static void
637ipstats_stat_desc_pack_link(struct ipstats_stat_dump_filters *filters,
638 const struct ipstats_stat_desc *desc)
639{
640 ipstats_stat_desc_enable_bit(filters,
641 IFLA_STATS_LINK_64, 0);
642}
643
644static int
645ipstats_stat_desc_show_link(struct ipstats_stat_show_attrs *attrs,
646 const struct ipstats_stat_desc *desc)
647{
648 print_nl();
649 return ipstats_show_64(attrs, IFLA_STATS_LINK_64, 0);
650}
651
652static const struct ipstats_stat_desc ipstats_stat_desc_toplev_link = {
653 .name = "link",
654 .kind = IPSTATS_STAT_DESC_KIND_LEAF,
655 .pack = &ipstats_stat_desc_pack_link,
656 .show = &ipstats_stat_desc_show_link,
657};
658
659static const struct ipstats_stat_desc ipstats_stat_desc_afstats_group;
660
661static void
662ipstats_stat_desc_pack_afstats(struct ipstats_stat_dump_filters *filters,
663 const struct ipstats_stat_desc *desc)
664{
665 ipstats_stat_desc_enable_bit(filters, IFLA_STATS_AF_SPEC, 0);
666}
667
668static int
669ipstats_stat_desc_show_afstats_mpls(struct ipstats_stat_show_attrs *attrs,
670 const struct ipstats_stat_desc *desc)
671{
672 struct rtattr *mrtb[MPLS_STATS_MAX+1];
673 struct mpls_link_stats stats;
674 const struct rtattr *at;
675 int err;
676
677 at = ipstats_stat_show_get_attr(attrs, IFLA_STATS_AF_SPEC,
678 AF_MPLS, &err);
679 if (at == NULL)
680 return err;
681
682 parse_rtattr_nested(mrtb, MPLS_STATS_MAX, at);
683 if (mrtb[MPLS_STATS_LINK] == NULL)
684 return -ENOENT;
685
686 IPSTATS_RTA_PAYLOAD(stats, mrtb[MPLS_STATS_LINK]);
687
688 print_nl();
689 open_json_object("mpls_stats");
690 print_mpls_link_stats(stdout, &stats, " ");
691 close_json_object();
692 return 0;
693}
694
695static const struct ipstats_stat_desc ipstats_stat_desc_afstats_mpls = {
696 .name = "mpls",
697 .kind = IPSTATS_STAT_DESC_KIND_LEAF,
698 .pack = &ipstats_stat_desc_pack_afstats,
699 .show = &ipstats_stat_desc_show_afstats_mpls,
700};
701
702static const struct ipstats_stat_desc *ipstats_stat_desc_afstats_subs[] = {
703 &ipstats_stat_desc_afstats_mpls,
704};
705
706static const struct ipstats_stat_desc ipstats_stat_desc_afstats_group = {
707 .name = "afstats",
708 .kind = IPSTATS_STAT_DESC_KIND_GROUP,
709 .subs = ipstats_stat_desc_afstats_subs,
710 .nsubs = ARRAY_SIZE(ipstats_stat_desc_afstats_subs),
711};
712static const struct ipstats_stat_desc *ipstats_stat_desc_toplev_subs[] = {
713 &ipstats_stat_desc_toplev_link,
714 &ipstats_stat_desc_xstats_group,
715 &ipstats_stat_desc_xstats_slave_group,
716 &ipstats_stat_desc_offload_group,
717 &ipstats_stat_desc_afstats_group,
718};
719
720static const struct ipstats_stat_desc ipstats_stat_desc_toplev_group = {
721 .name = "top-level",
722 .kind = IPSTATS_STAT_DESC_KIND_GROUP,
723 .subs = ipstats_stat_desc_toplev_subs,
724 .nsubs = ARRAY_SIZE(ipstats_stat_desc_toplev_subs),
725};
726
727static void ipstats_show_group(const struct ipstats_sel *sel)
728{
729 int i;
730
731 for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
732 if (sel->sel[i] == NULL)
733 break;
734 print_string(PRINT_JSON, ipstats_levels[i], NULL, sel->sel[i]);
735 print_string(PRINT_FP, NULL, " %s ", ipstats_levels[i]);
736 print_string(PRINT_FP, NULL, "%s", sel->sel[i]);
737 }
738}
739
740static int
741ipstats_process_ifsm(FILE *fp, struct nlmsghdr *answer,
742 struct ipstats_stat_enabled *enabled)
743{
744 struct ipstats_stat_show_attrs show_attrs = {};
745 const char *dev;
746 int err = 0;
747 int i;
748
749 show_attrs.ifsm = NLMSG_DATA(answer);
750 show_attrs.len = (answer->nlmsg_len -
751 NLMSG_LENGTH(sizeof(*show_attrs.ifsm)));
752 if (show_attrs.len < 0) {
753 fprintf(stderr, "BUG: wrong nlmsg len %d\n", show_attrs.len);
754 return -EINVAL;
755 }
756
757 err = ipstats_stat_show_attrs_alloc_tb(&show_attrs, 0);
758 if (err)
759 return err;
760
761 dev = ll_index_to_name(show_attrs.ifsm->ifindex);
762
763 print_headers(fp, "[STATS]");
764
765 for (i = 0; i < enabled->nenabled; i++) {
766 const struct ipstats_stat_desc *desc = enabled->enabled[i].desc;
767
768 open_json_object(NULL);
769 print_int(PRINT_ANY, "ifindex", "%d:",
770 show_attrs.ifsm->ifindex);
771 print_color_string(PRINT_ANY, COLOR_IFNAME,
772 "ifname", " %s:", dev);
773 ipstats_show_group(&enabled->enabled[i].sel);
774 err = desc->show(&show_attrs, desc);
775 if (err != 0)
776 goto out;
777 close_json_object();
778 print_nl();
779 }
780
781out:
782 ipstats_stat_show_attrs_free(&show_attrs);
783 return err;
784}
785
786static bool
787ipstats_req_should_filter_at(struct ipstats_stat_dump_filters *filters, int at)
788{
789 return filters->mask[at] != 0 &&
790 filters->mask[at] != (1 << ipstats_stat_ifla_max[at]) - 1;
791}
792
793static int
794ipstats_req_add_filters(struct ipstats_req *req, void *data)
795{
796 struct ipstats_stat_dump_filters dump_filters = {};
797 struct ipstats_stat_enabled *enabled = data;
798 bool get_filters = false;
799 int i;
800
801 for (i = 0; i < enabled->nenabled; i++)
802 enabled->enabled[i].desc->pack(&dump_filters,
803 enabled->enabled[i].desc);
804
805 for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
806 if (ipstats_req_should_filter_at(&dump_filters, i)) {
807 get_filters = true;
808 break;
809 }
810 }
811
812 req->ifsm.filter_mask = dump_filters.mask[0];
813 if (get_filters) {
814 struct rtattr *nest;
815
816 nest = addattr_nest(&req->nlh, sizeof(*req),
817 IFLA_STATS_GET_FILTERS | NLA_F_NESTED);
818
819 for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
820 if (ipstats_req_should_filter_at(&dump_filters, i))
821 addattr32(&req->nlh, sizeof(*req), i,
822 dump_filters.mask[i]);
823 }
824
825 addattr_nest_end(&req->nlh, nest);
826 }
827
828 return 0;
829}
830
831static int
832ipstats_show_one(int ifindex, struct ipstats_stat_enabled *enabled)
833{
834 struct ipstats_req req = {
835 .nlh.nlmsg_flags = NLM_F_REQUEST,
836 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
837 .nlh.nlmsg_type = RTM_GETSTATS,
838 .ifsm.family = PF_UNSPEC,
839 .ifsm.ifindex = ifindex,
840 };
841 struct nlmsghdr *answer;
842 int err = 0;
843
844 ipstats_req_add_filters(&req, enabled);
845 if (rtnl_talk(&rth, &req.nlh, &answer) < 0)
846 return -2;
847 err = ipstats_process_ifsm(stdout, answer, enabled);
848 free(answer);
849
850 return err;
851}
852
853static int ipstats_dump_one(struct nlmsghdr *n, void *arg)
854{
855 struct ipstats_stat_enabled *enabled = arg;
856 int rc;
857
858 rc = ipstats_process_ifsm(stdout, n, enabled);
859 if (rc)
860 return rc;
861
862 print_nl();
863 return 0;
864}
865
866static int ipstats_dump(struct ipstats_stat_enabled *enabled)
867{
868 int rc = 0;
869
870 if (rtnl_statsdump_req_filter(&rth, PF_UNSPEC, 0,
871 ipstats_req_add_filters,
872 enabled) < 0) {
873 perror("Cannot send dump request");
874 return -2;
875 }
876
877 if (rtnl_dump_filter(&rth, ipstats_dump_one, enabled) < 0) {
878 fprintf(stderr, "Dump terminated\n");
879 rc = -2;
880 }
881
882 fflush(stdout);
883 return rc;
884}
885
886static int
887ipstats_show_do(int ifindex, struct ipstats_stat_enabled *enabled)
888{
889 int rc;
890
891 new_json_obj(json);
892 if (ifindex)
893 rc = ipstats_show_one(ifindex, enabled);
894 else
895 rc = ipstats_dump(enabled);
896 delete_json_obj();
897
898 return rc;
899}
900
901static int ipstats_add_enabled(struct ipstats_stat_enabled_one ens[],
902 size_t nens,
903 struct ipstats_stat_enabled *enabled)
904{
905 struct ipstats_stat_enabled_one *new_en;
906
907 new_en = realloc(enabled->enabled,
908 sizeof(*new_en) * (enabled->nenabled + nens));
909 if (new_en == NULL)
910 return -ENOMEM;
911
912 enabled->enabled = new_en;
913 while (nens-- > 0)
914 enabled->enabled[enabled->nenabled++] = *ens++;
915 return 0;
916}
917
918static void ipstats_select_push(struct ipstats_sel *sel, const char *name)
919{
920 int i;
921
922 for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
923 if (sel->sel[i] == NULL) {
924 sel->sel[i] = name;
925 return;
926 }
927
928 assert(false);
929}
930
931static int
932ipstats_enable_recursively(const struct ipstats_stat_desc *desc,
933 struct ipstats_stat_enabled *enabled,
934 const struct ipstats_sel *sel)
935{
936 bool found = false;
937 size_t i;
938 int err;
939
940 if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
941 struct ipstats_stat_enabled_one en[] = {{
942 .desc = desc,
943 .sel = *sel,
944 }};
945
946 return ipstats_add_enabled(en, ARRAY_SIZE(en), enabled);
947 }
948
949 for (i = 0; i < desc->nsubs; i++) {
950 struct ipstats_sel subsel = *sel;
951
952 ipstats_select_push(&subsel, desc->subs[i]->name);
953 err = ipstats_enable_recursively(desc->subs[i], enabled,
954 &subsel);
955 if (err == -ENOENT)
956 continue;
957 if (err != 0)
958 return err;
959 found = true;
960 }
961
962 return found ? 0 : -ENOENT;
963}
964
965static int ipstats_comp_enabled(const void *a, const void *b)
966{
967 const struct ipstats_stat_enabled_one *en_a = a;
968 const struct ipstats_stat_enabled_one *en_b = b;
969
970 if (en_a->desc < en_b->desc)
971 return -1;
972 if (en_a->desc > en_b->desc)
973 return 1;
974
975 return 0;
976}
977
978static void ipstats_enabled_free(struct ipstats_stat_enabled *enabled)
979{
980 free(enabled->enabled);
981}
982
983static const struct ipstats_stat_desc *
984ipstats_stat_desc_find(const struct ipstats_stat_desc *desc,
985 const char *name)
986{
987 size_t i;
988
989 assert(desc->kind == IPSTATS_STAT_DESC_KIND_GROUP);
990 for (i = 0; i < desc->nsubs; i++) {
991 const struct ipstats_stat_desc *sub = desc->subs[i];
992
993 if (strcmp(sub->name, name) == 0)
994 return sub;
995 }
996
997 return NULL;
998}
999
1000static const struct ipstats_stat_desc *
1001ipstats_enable_find_stat_desc(struct ipstats_sel *sel)
1002{
1003 const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
1004 const struct ipstats_stat_desc *desc = toplev;
1005 int i;
1006
1007 for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
1008 const struct ipstats_stat_desc *next_desc;
1009
1010 if (sel->sel[i] == NULL)
1011 break;
1012 if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
1013 fprintf(stderr, "Error: %s %s requested inside leaf %s %s\n",
1014 ipstats_levels[i], sel->sel[i],
1015 ipstats_levels[i - 1], desc->name);
1016 return NULL;
1017 }
1018
1019 next_desc = ipstats_stat_desc_find(desc, sel->sel[i]);
1020 if (next_desc == NULL) {
1021 fprintf(stderr, "Error: no %s named %s found inside %s\n",
1022 ipstats_levels[i], sel->sel[i], desc->name);
1023 return NULL;
1024 }
1025
1026 desc = next_desc;
1027 }
1028
1029 return desc;
1030}
1031
1032static int ipstats_enable(struct ipstats_sel *sel,
1033 struct ipstats_stat_enabled *enabled)
1034{
1035 struct ipstats_stat_enabled new_enabled = {};
1036 const struct ipstats_stat_desc *desc;
1037 size_t i, j;
1038 int err = 0;
1039
1040 desc = ipstats_enable_find_stat_desc(sel);
1041 if (desc == NULL)
1042 return -EINVAL;
1043
1044 err = ipstats_enable_recursively(desc, &new_enabled, sel);
1045 if (err != 0)
1046 return err;
1047
1048 err = ipstats_add_enabled(new_enabled.enabled, new_enabled.nenabled,
1049 enabled);
1050 if (err != 0)
1051 goto out;
1052
1053 qsort(enabled->enabled, enabled->nenabled, sizeof(*enabled->enabled),
1054 ipstats_comp_enabled);
1055
1056 for (i = 1, j = 1; i < enabled->nenabled; i++) {
1057 if (enabled->enabled[i].desc != enabled->enabled[j - 1].desc)
1058 enabled->enabled[j++] = enabled->enabled[i];
1059 }
1060 enabled->nenabled = j;
1061
1062out:
1063 ipstats_enabled_free(&new_enabled);
1064 return err;
1065}
1066
1067static int ipstats_enable_check(struct ipstats_sel *sel,
1068 struct ipstats_stat_enabled *enabled)
1069{
1070 int err;
1071 int i;
1072
1073 err = ipstats_enable(sel, enabled);
1074 if (err == -ENOENT) {
1075 fprintf(stderr, "The request for");
1076 for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
1077 if (sel->sel[i] != NULL)
1078 fprintf(stderr, " %s %s",
1079 ipstats_levels[i], sel->sel[i]);
1080 else
1081 break;
1082 fprintf(stderr, " did not match any known stats.\n");
1083 }
1084
1085 return err;
1086}
1087
1088static int do_help(void)
1089{
1090 const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
1091 int i;
1092
1093 fprintf(stderr,
1094 "Usage: ip stats help\n"
1095 " ip stats show [ dev DEV ] [ group GROUP [ subgroup SUBGROUP [ suite SUITE ] ... ] ... ] ...\n"
1096 " ip stats set dev DEV l3_stats { on | off }\n"
1097 );
1098
1099 for (i = 0; i < toplev->nsubs; i++) {
1100 const struct ipstats_stat_desc *desc = toplev->subs[i];
1101
1102 if (i == 0)
1103 fprintf(stderr, "GROUP := { %s", desc->name);
1104 else
1105 fprintf(stderr, " | %s", desc->name);
1106 }
1107 if (i > 0)
1108 fprintf(stderr, " }\n");
1109
1110 for (i = 0; i < toplev->nsubs; i++) {
1111 const struct ipstats_stat_desc *desc = toplev->subs[i];
1112 bool opened = false;
1113 size_t j;
1114
1115 if (desc->kind != IPSTATS_STAT_DESC_KIND_GROUP)
1116 continue;
1117
1118 for (j = 0; j < desc->nsubs; j++) {
1119 size_t k;
1120
1121 if (j == 0)
1122 fprintf(stderr, "%s SUBGROUP := {", desc->name);
1123 else
1124 fprintf(stderr, " |");
1125 fprintf(stderr, " %s", desc->subs[j]->name);
1126 opened = true;
1127
1128 if (desc->subs[j]->kind != IPSTATS_STAT_DESC_KIND_GROUP)
1129 continue;
1130
1131 for (k = 0; k < desc->subs[j]->nsubs; k++)
1132 fprintf(stderr, " [ suite %s ]",
1133 desc->subs[j]->subs[k]->name);
1134 }
1135 if (opened)
1136 fprintf(stderr, " }\n");
1137 }
1138
1139 return 0;
1140}
1141
1142static int ipstats_select(struct ipstats_sel *old_sel,
1143 const char *new_sel, int level,
1144 struct ipstats_stat_enabled *enabled)
1145{
1146 int err;
1147 int i;
1148
1149 for (i = 0; i < level; i++) {
1150 if (old_sel->sel[i] == NULL) {
1151 fprintf(stderr, "Error: %s %s requested without selecting a %s first\n",
1152 ipstats_levels[level], new_sel,
1153 ipstats_levels[i]);
1154 return -EINVAL;
1155 }
1156 }
1157
1158 for (i = level; i < IPSTATS_LEVELS_COUNT; i++) {
1159 if (old_sel->sel[i] != NULL) {
1160 err = ipstats_enable_check(old_sel, enabled);
1161 if (err)
1162 return err;
1163 break;
1164 }
1165 }
1166
1167 old_sel->sel[level] = new_sel;
1168 for (i = level + 1; i < IPSTATS_LEVELS_COUNT; i++)
1169 old_sel->sel[i] = NULL;
1170
1171 return 0;
1172}
1173
1174static int ipstats_show(int argc, char **argv)
1175{
1176 struct ipstats_stat_enabled enabled = {};
1177 struct ipstats_sel sel = {};
1178 const char *dev = NULL;
1179 int ifindex;
1180 int err;
1181 int i;
1182
1183 while (argc > 0) {
1184 if (strcmp(*argv, "dev") == 0) {
1185 NEXT_ARG();
1186 if (dev != NULL)
1187 duparg2("dev", *argv);
1188 if (check_ifname(*argv))
1189 invarg("\"dev\" not a valid ifname", *argv);
1190 dev = *argv;
1191 } else if (strcmp(*argv, "help") == 0) {
1192 do_help();
1193 return 0;
1194 } else {
1195 bool found_level = false;
1196
1197 for (i = 0; i < ARRAY_SIZE(ipstats_levels); i++) {
1198 if (strcmp(*argv, ipstats_levels[i]) == 0) {
1199 NEXT_ARG();
1200 err = ipstats_select(&sel, *argv, i,
1201 &enabled);
1202 if (err)
1203 goto err;
1204
1205 found_level = true;
1206 }
1207 }
1208
1209 if (!found_level) {
1210 fprintf(stderr, "What is \"%s\"?\n", *argv);
1211 do_help();
1212 err = -EINVAL;
1213 goto err;
1214 }
1215 }
1216
1217 NEXT_ARG_FWD();
1218 }
1219
1220
1221 err = ipstats_enable_check(&sel, &enabled);
1222 if (err)
1223 goto err;
1224
1225 if (dev) {
1226 ifindex = ll_name_to_index(dev);
1227 if (!ifindex) {
1228 err = nodev(dev);
1229 goto err;
1230 }
1231 } else {
1232 ifindex = 0;
1233 }
1234
1235
1236 err = ipstats_show_do(ifindex, &enabled);
1237
1238err:
1239 ipstats_enabled_free(&enabled);
1240 return err;
1241}
1242
1243static int ipstats_set_do(int ifindex, int at, bool enable)
1244{
1245 struct ipstats_req req = {
1246 .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
1247 .nlh.nlmsg_flags = NLM_F_REQUEST,
1248 .nlh.nlmsg_type = RTM_SETSTATS,
1249 .ifsm.family = PF_UNSPEC,
1250 .ifsm.ifindex = ifindex,
1251 };
1252
1253 addattr8(&req.nlh, sizeof(req), at, enable);
1254
1255 if (rtnl_talk(&rth, &req.nlh, NULL) < 0)
1256 return -2;
1257 return 0;
1258}
1259
1260static int ipstats_set(int argc, char **argv)
1261{
1262 const char *dev = NULL;
1263 bool enable = false;
1264 int ifindex;
1265 int at = 0;
1266
1267 while (argc > 0) {
1268 if (strcmp(*argv, "dev") == 0) {
1269 NEXT_ARG();
1270 if (dev)
1271 duparg2("dev", *argv);
1272 if (check_ifname(*argv))
1273 invarg("\"dev\" not a valid ifname", *argv);
1274 dev = *argv;
1275 } else if (strcmp(*argv, "l3_stats") == 0) {
1276 int err;
1277
1278 NEXT_ARG();
1279 if (at) {
1280 fprintf(stderr, "A statistics suite to toggle was already given.\n");
1281 return -EINVAL;
1282 }
1283 at = IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS;
1284 enable = parse_on_off("l3_stats", *argv, &err);
1285 if (err)
1286 return err;
1287 } else if (strcmp(*argv, "help") == 0) {
1288 do_help();
1289 return 0;
1290 } else {
1291 fprintf(stderr, "What is \"%s\"?\n", *argv);
1292 do_help();
1293 return -EINVAL;
1294 }
1295
1296 NEXT_ARG_FWD();
1297 }
1298
1299 if (!dev) {
1300 fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
1301 exit(-1);
1302 }
1303
1304 if (!at) {
1305 fprintf(stderr, "Not enough information: stat type to toggle is required.\n");
1306 exit(-1);
1307 }
1308
1309 ifindex = ll_name_to_index(dev);
1310 if (!ifindex)
1311 return nodev(dev);
1312
1313 return ipstats_set_do(ifindex, at, enable);
1314}
1315
1316int do_ipstats(int argc, char **argv)
1317{
1318 int rc;
1319
1320 if (argc == 0) {
1321 rc = ipstats_show(0, NULL);
1322 } else if (strcmp(*argv, "help") == 0) {
1323 do_help();
1324 rc = 0;
1325 } else if (strcmp(*argv, "show") == 0) {
1326
1327
1328
1329 show_stats += show_details + 1;
1330 rc = ipstats_show(argc-1, argv+1);
1331 } else if (strcmp(*argv, "set") == 0) {
1332 rc = ipstats_set(argc-1, argv+1);
1333 } else {
1334 fprintf(stderr, "Command \"%s\" is unknown, try \"ip stats help\".\n",
1335 *argv);
1336 rc = -1;
1337 }
1338
1339 return rc;
1340}
1341
1342int ipstats_print(struct nlmsghdr *n, void *arg)
1343{
1344 struct ipstats_stat_enabled_one one = {
1345 .desc = &ipstats_stat_desc_offload_hw_s_info,
1346 };
1347 struct ipstats_stat_enabled enabled = {
1348 .enabled = &one,
1349 .nenabled = 1,
1350 };
1351 FILE *fp = arg;
1352 int rc;
1353
1354 rc = ipstats_process_ifsm(fp, n, &enabled);
1355 if (rc)
1356 return rc;
1357
1358 fflush(fp);
1359 return 0;
1360}
1361