1 /*
2 * Copyright (c) 2018-2021 Eugene Syromyatnikov <evgsyr@gmail.com>
3 * Copyright (c) 2018-2022 The strace developers.
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "tests.h"
10
11 #include <arpa/inet.h>
12 #include <inttypes.h>
13 #include <linux/ip.h>
14 #include <netinet/in.h>
15 #include <stdbool.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <unistd.h>
19
20 #include "test_nlattr.h"
21
22 #include <linux/if_link.h>
23 #include <linux/if_bonding.h>
24 #include <linux/if_bridge.h>
25 #include <linux/mpls.h>
26
27 #include "xlat.h"
28 #include "xlat/addrfams.h"
29 #define XLAT_MACROS_ONLY
30 # include "xlat/ifstats_af_spec_mpls_attrs.h"
31 # include "xlat/ifstats_attrs.h"
32 # include "xlat/ifstats_attr_flags.h"
33 # include "xlat/ifstats_offload_attrs.h"
34 # include "xlat/ifstats_xstats_bond_attrs.h"
35 # include "xlat/ifstats_xstats_bond_3ad_attrs.h"
36 # include "xlat/ifstats_xstats_bridge_attrs.h"
37 # include "xlat/ifstats_xstats_bridge_mcast_indices.h"
38 # include "xlat/ifstats_xstats_type_attrs.h"
39 # include "xlat/nl_bridge_vlan_flags.h"
40 #undef XLAT_MACROS_ONLY
41
42 static const unsigned int hdrlen = sizeof(struct if_stats_msg);
43 static char pattern[4096];
44 static char nla_type_str[256];
45
46 static void
47 init_ifstats(struct nlmsghdr *const nlh, const unsigned int msg_len)
48 {
49 SET_STRUCT(struct nlmsghdr, nlh,
50 .nlmsg_len = msg_len,
51 .nlmsg_type = RTM_GETSTATS,
52 .nlmsg_flags = NLM_F_DUMP,
53 );
54
55 struct if_stats_msg *const msg = NLMSG_DATA(nlh);
56 SET_STRUCT(struct if_stats_msg, msg,
57 .family = AF_UNIX,
58 .ifindex = ifindex_lo(),
59 .filter_mask = 0x22,
60 );
61 }
62
63 static void
64 print_ifstats(const unsigned int msg_len)
65 {
66 printf("{nlmsg_len=%u, nlmsg_type=" XLAT_FMT ", nlmsg_flags=" XLAT_FMT
67 ", nlmsg_seq=0, nlmsg_pid=0}, {family=" XLAT_FMT ", ifindex="
68 XLAT_FMT_U ", filter_mask=" XLAT_FMT "}",
69 msg_len, XLAT_ARGS(RTM_GETSTATS), XLAT_ARGS(NLM_F_DUMP),
70 XLAT_ARGS(AF_UNIX), XLAT_SEL(ifindex_lo(), IFINDEX_LO_STR),
71 XLAT_ARGS(1<<IFLA_STATS_LINK_64|1<<IFLA_STATS_AF_SPEC));
72 }
73
74 /*
75 * NB: these functions use global variables to control which top-level
76 * netlink attribute is to be used.
77 */
78 #define DEF_NLATTR_FUNCS_NESTED(sfx_, attr_var_, attr_str_var_, \
79 parent_sfx_, lvl_) \
80 static void \
81 init_ifstats_##sfx_(struct nlmsghdr *const nlh, \
82 const unsigned int msg_len) \
83 { \
84 (init_##parent_sfx_)(nlh, msg_len); \
85 struct nlattr *nla = NLMSG_ATTR(nlh, \
86 sizeof(struct if_stats_msg) \
87 + ((lvl_) - 1) * NLA_HDRLEN); \
88 SET_STRUCT(struct nlattr, nla, \
89 .nla_len = msg_len \
90 - NLMSG_SPACE(sizeof(struct if_stats_msg)) \
91 - ((lvl_) - 1) * NLA_HDRLEN, \
92 .nla_type = (attr_var_), \
93 ); \
94 } \
95 \
96 static void \
97 print_ifstats_##sfx_(const unsigned int msg_len) \
98 { \
99 (print_##parent_sfx_)(msg_len); \
100 printf(", [{nla_len=%u, nla_type=%s}", \
101 (unsigned int) (msg_len - NLMSG_HDRLEN \
102 - NLMSG_ALIGN(sizeof(struct \
103 if_stats_msg)) \
104 - ((lvl_) - 1) * NLA_HDRLEN), \
105 attr_str_var_); \
106 } \
107 /* End of DEF_NLATTR_FUNCS_NESTED */
108
109 static uint16_t l1_attr;
110 static char l1_attr_str[256];
111
112 static uint16_t l2_attr;
113 static char l2_attr_str[256];
114
115 static uint16_t l3_attr;
116 static char l3_attr_str[256];
117
118 DEF_NLATTR_FUNCS_NESTED(l1, l1_attr, l1_attr_str, ifstats, 1)
119 DEF_NLATTR_FUNCS_NESTED(l2, l2_attr, l2_attr_str, ifstats_l1, 2)
120 DEF_NLATTR_FUNCS_NESTED(l3, l3_attr, l3_attr_str, ifstats_l2, 3)
121
122
123 static void
124 print_stats_64(struct rtnl_link_stats64 *st, size_t sz)
125 {
126 printf("{"); PRINT_FIELD_U(*st, rx_packets);
127 printf(", "); PRINT_FIELD_U(*st, tx_packets);
128 printf(", "); PRINT_FIELD_U(*st, rx_bytes);
129 printf(", "); PRINT_FIELD_U(*st, tx_bytes);
130 printf(", "); PRINT_FIELD_U(*st, rx_errors);
131 printf(", "); PRINT_FIELD_U(*st, tx_errors);
132 printf(", "); PRINT_FIELD_U(*st, rx_dropped);
133 printf(", "); PRINT_FIELD_U(*st, tx_dropped);
134 printf(", "); PRINT_FIELD_U(*st, multicast);
135 printf(", "); PRINT_FIELD_U(*st, collisions);
136 printf(", "); PRINT_FIELD_U(*st, rx_length_errors);
137 printf(", "); PRINT_FIELD_U(*st, rx_over_errors);
138 printf(", "); PRINT_FIELD_U(*st, rx_crc_errors);
139 printf(", "); PRINT_FIELD_U(*st, rx_frame_errors);
140 printf(", "); PRINT_FIELD_U(*st, rx_fifo_errors);
141 printf(", "); PRINT_FIELD_U(*st, rx_missed_errors);
142 printf(", "); PRINT_FIELD_U(*st, tx_aborted_errors);
143 printf(", "); PRINT_FIELD_U(*st, tx_carrier_errors);
144 printf(", "); PRINT_FIELD_U(*st, tx_fifo_errors);
145 printf(", "); PRINT_FIELD_U(*st, tx_heartbeat_errors);
146 printf(", "); PRINT_FIELD_U(*st, tx_window_errors);
147 printf(", "); PRINT_FIELD_U(*st, rx_compressed);
148 printf(", "); PRINT_FIELD_U(*st, tx_compressed);
149 if (sz >= offsetofend(struct rtnl_link_stats64, rx_nohandler)) {
150 printf(", ");
151 PRINT_FIELD_U(*st, rx_nohandler);
152 }
153 if (sz >= offsetofend(struct rtnl_link_stats64, rx_otherhost_dropped)) {
154 printf(", ");
155 PRINT_FIELD_U(*st, rx_otherhost_dropped);
156 }
157 printf("}");
158 }
159
160 static void
161 check_stats_64(const int fd, unsigned int cmd, const char *cmd_str, bool nest)
162 {
163 static const size_t minsz = offsetofend(struct rtnl_link_stats64,
164 tx_compressed);
165
166 struct rtnl_link_stats64 st;
167 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen),
168 (!!nest + 1) * NLA_HDRLEN + sizeof(st));
169
170 snprintf(nla_type_str, sizeof(nla_type_str), XLAT_FMT,
171 XLAT_SEL(cmd, cmd_str));
172 fill_memory(&st, sizeof(st));
173
174 TEST_NESTED_NLATTR_OBJECT_EX_MINSZ_(fd, nlh0, hdrlen,
175 nest ? init_ifstats_l1
176 : init_ifstats,
177 nest ? print_ifstats_l1
178 : print_ifstats,
179 cmd, nla_type_str,
180 pattern, st, minsz,
181 print_quoted_hex, (unsigned) !!nest,
182 print_stats_64(&st, sizeof(st)));
183
184 TEST_NLATTR_(fd, nlh0 - !!nest * NLA_HDRLEN,
185 hdrlen + !!nest * NLA_HDRLEN,
186 nest ? init_ifstats_l1 : init_ifstats,
187 nest ? print_ifstats_l1 : print_ifstats,
188 cmd, nla_type_str, minsz, &st, minsz,
189 print_stats_64(&st, minsz);
190 for (size_t i = 0; i < (unsigned) !!nest; i++)
191 printf("]"));
192 }
193
194 static void
195 fmt_str(char *dst, size_t dst_sz, uint32_t cmd, const char *s, const char *dflt)
196 {
197 if (s) {
198 snprintf(dst, dst_sz, XLAT_FMT, XLAT_SEL(cmd, s));
199 } else {
200 snprintf(dst, dst_sz, "%#x" NRAW(" /* %s */"),
201 cmd NRAW(, dflt));
202 }
203 }
204
205 static void
206 print_mcast_stats(struct br_mcast_stats *br_xst_mc)
207 {
208 #define PR_FIELD_(pfx_, field_) \
209 printf(pfx_ #field_ "=[[" XLAT_KNOWN(0, "BR_MCAST_DIR_RX") \
210 "]=%llu, [" XLAT_KNOWN(1, "BR_MCAST_DIR_TX") "]=%llu]", \
211 (unsigned long long) br_xst_mc->field_[0], \
212 (unsigned long long) br_xst_mc->field_[1])
213
214 PR_FIELD_("{", igmp_v1queries);
215 PR_FIELD_(", ", igmp_v2queries);
216 PR_FIELD_(", ", igmp_v3queries);
217 PR_FIELD_(", ", igmp_leaves);
218 PR_FIELD_(", ", igmp_v1reports);
219 PR_FIELD_(", ", igmp_v2reports);
220 PR_FIELD_(", ", igmp_v3reports);
221 printf(", igmp_parse_errors=%llu",
222 (unsigned long long) br_xst_mc->igmp_parse_errors);
223 PR_FIELD_(", ", mld_v1queries);
224 PR_FIELD_(", ", mld_v2queries);
225 PR_FIELD_(", ", mld_leaves);
226 PR_FIELD_(", ", mld_v1reports);
227 PR_FIELD_(", ", mld_v2reports);
228 printf(", mld_parse_errors=%llu",
229 (unsigned long long) br_xst_mc->mld_parse_errors);
230 PR_FIELD_(", ", mcast_bytes);
231 PR_FIELD_(", ", mcast_packets);
232 printf("}");
233
234 #undef PR_FIELD_
235 }
236
237 static void
238 check_xstats(const int fd, unsigned int cmd, const char *cmd_str)
239 {
240 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen) + 3 * NLA_HDRLEN,
241 NLA_HDRLEN + 256);
242
243 l1_attr = cmd;
244 snprintf(l1_attr_str, sizeof(l1_attr_str), XLAT_FMT,
245 XLAT_SEL(cmd, cmd_str));
246
247 /* Unknown, unimplemented, no semantics. */
248 static const struct strval32 undec_types[] = {
249 { ARG_STR(LINK_XSTATS_TYPE_UNSPEC) },
250 { 0x3 },
251 { 0xbad },
252 };
253 for (size_t i = 0; i < ARRAY_SIZE(undec_types); i++) {
254 fmt_str(nla_type_str, sizeof(nla_type_str),
255 undec_types[i].val, undec_types[i].str,
256 "LINK_XSTATS_TYPE_???");
257 TEST_NLATTR_(fd, nlh0, hdrlen + NLA_HDRLEN,
258 init_ifstats_l1, print_ifstats_l1,
259 undec_types[i].val, nla_type_str,
260 37, pattern, 37,
261 print_quoted_hex(pattern, 32);
262 printf("...]"));
263 }
264
265 /* LINK_XSTATS_TYPE_BRIDGE */
266 l2_attr = LINK_XSTATS_TYPE_BRIDGE;
267 snprintf(l2_attr_str, sizeof(l2_attr_str), XLAT_FMT,
268 XLAT_ARGS(LINK_XSTATS_TYPE_BRIDGE));
269
270 /* LINK_XSTATS_TYPE_BRIDGE: Unknown, unimplemented, no semantics. */
271 static const struct strval32 undec_br_types[] = {
272 { ARG_STR(BRIDGE_XSTATS_UNSPEC) },
273 { ARG_STR(BRIDGE_XSTATS_PAD) },
274 { 0x5 },
275 { 0xbad },
276 };
277 for (size_t i = 0; i < ARRAY_SIZE(undec_br_types); i++) {
278 fmt_str(nla_type_str, sizeof(nla_type_str),
279 undec_br_types[i].val, undec_br_types[i].str,
280 "BRIDGE_XSTATS_???");
281 TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN,
282 init_ifstats_l2, print_ifstats_l2,
283 undec_br_types[i].val, nla_type_str,
284 37, pattern, 37,
285 print_quoted_hex(pattern, 32);
286 printf("...]]"));
287 }
288
289 /* LINK_XSTATS_TYPE_BRIDGE: BRIDGE_XSTATS_VLAN */
290 static const struct {
291 struct bridge_vlan_xstats val;
292 const char *str;
293 } br_xst_vlan_vecs[] = {
294 { { .rx_bytes=0, .rx_packets=0xdeadfacebeeffeedULL,
295 .tx_bytes=0x8090a0b0c0d0e0f0ULL, .tx_packets=0,
296 .vid=0xdead, .flags=2 },
297 "{rx_bytes=0, rx_packets=16045756813264551661"
298 ", tx_bytes=9264081114510713072, tx_packets=0"
299 ", vid=57005, flags="
300 XLAT_KNOWN(0x2, "BRIDGE_VLAN_INFO_PVID") "}" },
301 { { .rx_bytes=12345678901234567890ULL, .rx_packets=0,
302 .tx_bytes=0, .tx_packets=9876543210987654321ULL,
303 .vid=0, .flags=0, .pad2=0xbadc0ded },
304 "{rx_bytes=12345678901234567890, rx_packets=0"
305 ", tx_bytes=0, tx_packets=9876543210987654321"
306 ", vid=0, flags=0, pad2=0xbadc0ded}" },
307 { { .flags=0xdeed },
308 "{rx_bytes=0, rx_packets=0, tx_bytes=0, tx_packets=0, vid=0"
309 ", flags=" XLAT_KNOWN(0xdeed, "BRIDGE_VLAN_INFO_MASTER"
310 "|BRIDGE_VLAN_INFO_UNTAGGED"
311 "|BRIDGE_VLAN_INFO_RANGE_BEGIN"
312 "|BRIDGE_VLAN_INFO_BRENTRY"
313 "|BRIDGE_VLAN_INFO_ONLY_OPTS"
314 "|0xde80") "}" },
315 { { .rx_bytes=0xdefaceddecaffeedULL,
316 .rx_packets=0xbeeffacedeadbabeULL,
317 .tx_bytes=0xbeeffeeddadfacedULL,
318 .tx_packets=0xbeeffadeeffaceedULL,
319 .vid=0xcafe, .flags=0xfa80, .pad2=0xdeadabba },
320 "{rx_bytes=16067382073151717101"
321 ", rx_packets=13758491153046289086"
322 ", tx_bytes=13758495684172950765"
323 ", tx_packets=13758491222056029933"
324 ", vid=51966, flags=0xfa80"
325 NRAW(" /* BRIDGE_VLAN_INFO_??? */")
326 ", pad2=0xdeadabba}" },
327 };
328 void *nlh_vlan = midtail_alloc(NLMSG_SPACE(hdrlen),
329 2 * NLA_HDRLEN
330 + sizeof(struct bridge_vlan_xstats));
331
332 for (size_t i = 0; i < ARRAY_SIZE(br_xst_vlan_vecs); i++) {
333 TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_vlan, hdrlen,
334 init_ifstats_l2, print_ifstats_l2,
335 BRIDGE_XSTATS_VLAN,
336 XLAT_KNOWN(0x1,
337 "BRIDGE_XSTATS_VLAN"),
338 pattern, br_xst_vlan_vecs[i].val,
339 print_quoted_hex, 2,
340 printf("%s",
341 br_xst_vlan_vecs[i].str));
342
343 char buf[sizeof(br_xst_vlan_vecs[0].val) + 42];
344 fill_memory(buf, sizeof(buf));
345 memcpy(buf, &br_xst_vlan_vecs[i].val,
346 sizeof(br_xst_vlan_vecs[i].val));
347 TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN,
348 init_ifstats_l2, print_ifstats_l2,
349 BRIDGE_XSTATS_VLAN,
350 XLAT_KNOWN(0x1, "BRIDGE_XSTATS_VLAN"),
351 sizeof(buf), buf, sizeof(buf),
352 printf("%s", br_xst_vlan_vecs[i].str);
353 printf(", ");
354 print_quoted_hex(buf
355 + sizeof(br_xst_vlan_vecs[0].val),
356 32);
357 printf("...]]"));
358 }
359
360 /* LINK_XSTATS_TYPE_BRIDGE: BRIDGE_XSTATS_MCAST */
361 struct br_mcast_stats br_xst_mc;
362 void *nlh_mc = midtail_alloc(NLMSG_SPACE(hdrlen),
363 2 * NLA_HDRLEN + sizeof(br_xst_mc));
364
365 #define FIELD_STR_(field_) \
366 #field_ "=[[" XLAT_KNOWN(0, "BR_MCAST_DIR_RX") "]=0, [" \
367 XLAT_KNOWN(1, "BR_MCAST_DIR_TX") "]=0]"
368
369 memset(&br_xst_mc, 0, sizeof(br_xst_mc));
370 TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mc, hdrlen,
371 init_ifstats_l2, print_ifstats_l2,
372 BRIDGE_XSTATS_MCAST,
373 XLAT_KNOWN(0x2, "BRIDGE_XSTATS_MCAST"),
374 pattern, br_xst_mc,
375 print_quoted_hex, 2,
376 printf("{" FIELD_STR_(igmp_v1queries)
377 ", " FIELD_STR_(igmp_v2queries)
378 ", " FIELD_STR_(igmp_v3queries)
379 ", " FIELD_STR_(igmp_leaves)
380 ", " FIELD_STR_(igmp_v1reports)
381 ", " FIELD_STR_(igmp_v2reports)
382 ", " FIELD_STR_(igmp_v3reports)
383 ", igmp_parse_errors=0"
384 ", " FIELD_STR_(mld_v1queries)
385 ", " FIELD_STR_(mld_v2queries)
386 ", " FIELD_STR_(mld_leaves)
387 ", " FIELD_STR_(mld_v1reports)
388 ", " FIELD_STR_(mld_v2reports)
389 ", mld_parse_errors=0"
390 ", " FIELD_STR_(mcast_bytes)
391 ", " FIELD_STR_(mcast_packets)
392 "}"));
393 #undef FIELD_STR_
394
395 fill_memory64(&br_xst_mc, sizeof(br_xst_mc));
396 TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mc, hdrlen,
397 init_ifstats_l2, print_ifstats_l2,
398 BRIDGE_XSTATS_MCAST,
399 XLAT_KNOWN(0x2, "BRIDGE_XSTATS_MCAST"),
400 pattern, br_xst_mc,
401 print_quoted_hex, 2,
402 print_mcast_stats(&br_xst_mc));
403
404 char mc_buf[sizeof(br_xst_mc) + 8];
405 fill_memory(mc_buf, sizeof(mc_buf));
406 memcpy(mc_buf, &br_xst_mc, sizeof(br_xst_mc));
407 TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN,
408 init_ifstats_l2, print_ifstats_l2,
409 BRIDGE_XSTATS_MCAST,
410 XLAT_KNOWN(0x2, "BRIDGE_XSTATS_MCAST"),
411 sizeof(mc_buf), mc_buf, sizeof(mc_buf),
412 print_mcast_stats(&br_xst_mc);
413 printf(", ");
414 print_quoted_hex(mc_buf + sizeof(br_xst_mc), 8);
415 printf("]]"));
416
417 /* LINK_XSTATS_TYPE_BRIDGE: BRIDGE_XSTATS_STP */
418 static const struct {
419 struct bridge_stp_xstats val;
420 const char *str;
421 } br_xst_stp_vecs[] = {
422 { { .transition_blk=0, .transition_fwd=0, .rx_bpdu=0,
423 .tx_bpdu=0, .rx_tcn=0, .tx_tcn=0, },
424 "{transition_blk=0, transition_fwd=0, rx_bpdu=0, tx_bpdu=0"
425 ", rx_tcn=0, tx_tcn=0}" },
426 { { .transition_blk=0x8090a0b0c0d0e0f0ULL,
427 .transition_fwd=0x8191a1b1c1d1e1f1ULL,
428 .rx_bpdu=0x8292a2b2c2d2e2f2ULL,
429 .tx_bpdu=0x8393a3b3c3d3e3f3ULL,
430 .rx_tcn=0x8494a4b4c4d4e4f4ULL,
431 .tx_tcn=0x8595a5b5c5d5e5f5ULL, },
432 "{transition_blk=9264081114510713072"
433 ", transition_fwd=9336421287348789745"
434 ", rx_bpdu=9408761460186866418, tx_bpdu=9481101633024943091"
435 ", rx_tcn=9553441805863019764, tx_tcn=9625781978701096437}" },
436 };
437 void *nlh_stp = midtail_alloc(NLMSG_SPACE(hdrlen),
438 2 * NLA_HDRLEN
439 + sizeof(struct bridge_stp_xstats));
440
441 for (size_t i = 0; i < ARRAY_SIZE(br_xst_stp_vecs); i++) {
442 TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_stp, hdrlen,
443 init_ifstats_l2, print_ifstats_l2,
444 BRIDGE_XSTATS_STP,
445 XLAT_KNOWN(0x4,
446 "BRIDGE_XSTATS_STP"),
447 pattern, br_xst_stp_vecs[i].val,
448 print_quoted_hex, 2,
449 printf("%s",
450 br_xst_stp_vecs[i].str));
451
452 char buf[sizeof(br_xst_stp_vecs[0].val) + 33];
453 fill_memory(buf, sizeof(buf));
454 memcpy(buf, &br_xst_stp_vecs[i].val,
455 sizeof(br_xst_stp_vecs[i].val));
456 TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN,
457 init_ifstats_l2, print_ifstats_l2,
458 BRIDGE_XSTATS_STP,
459 XLAT_KNOWN(0x4, "BRIDGE_XSTATS_STP"),
460 sizeof(buf), buf, sizeof(buf),
461 printf("%s", br_xst_stp_vecs[i].str);
462 printf(", ");
463 print_quoted_hex(buf
464 + sizeof(br_xst_stp_vecs[0].val),
465 32);
466 printf("...]]"));
467 }
468
469
470
471 /* LINK_XSTATS_TYPE_BOND */
472 l2_attr = LINK_XSTATS_TYPE_BOND;
473 snprintf(l2_attr_str, sizeof(l2_attr_str), XLAT_FMT,
474 XLAT_ARGS(LINK_XSTATS_TYPE_BOND));
475
476 /* LINK_XSTATS_TYPE_BOND: Unknown, unimplemented, no semantics. */
477 static const struct strval32 undec_bd_types[] = {
478 { ARG_STR(BOND_XSTATS_UNSPEC) },
479 { 0x2 },
480 { 0xbad },
481 };
482 for (size_t i = 0; i < ARRAY_SIZE(undec_bd_types); i++) {
483 fmt_str(nla_type_str, sizeof(nla_type_str),
484 undec_bd_types[i].val, undec_bd_types[i].str,
485 "BOND_XSTATS_???");
486 TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN,
487 init_ifstats_l2, print_ifstats_l2,
488 undec_bd_types[i].val, nla_type_str,
489 37, pattern, 37,
490 print_quoted_hex(pattern, 32);
491 printf("...]]"));
492 }
493
494 /* LINK_XSTATS_TYPE_BOND: BOND_XSTATS_3AD */
495 l3_attr = BOND_XSTATS_3AD;
496 snprintf(l3_attr_str, sizeof(l3_attr_str), XLAT_FMT,
497 XLAT_ARGS(BOND_XSTATS_3AD));
498
499 /* BOND_XSTATS_3AD: Unknown, unimplemented, no semantics. */
500 static const struct strval32 undec_3ad_types[] = {
501 { ARG_STR(BOND_3AD_STAT_PAD) },
502 { 0xa },
503 { 0xbad },
504 };
505 for (size_t i = 0; i < ARRAY_SIZE(undec_3ad_types); i++) {
506 fmt_str(nla_type_str, sizeof(nla_type_str),
507 undec_3ad_types[i].val, undec_3ad_types[i].str,
508 "BOND_XSTATS_???");
509 TEST_NLATTR_(fd, nlh0, hdrlen + 3 * NLA_HDRLEN,
510 init_ifstats_l3, print_ifstats_l3,
511 undec_3ad_types[i].val, nla_type_str,
512 37, pattern, 37,
513 print_quoted_hex(pattern, 32);
514 printf("...]]]"));
515 }
516
517 /* BOND_XSTATS_3AD: u64 args */
518 static const struct strval32 u64_3ad_types[] = {
519 { ARG_STR(BOND_3AD_STAT_LACPDU_RX) },
520 { ARG_STR(BOND_3AD_STAT_LACPDU_TX) },
521 { ARG_STR(BOND_3AD_STAT_LACPDU_UNKNOWN_RX) },
522 { ARG_STR(BOND_3AD_STAT_LACPDU_ILLEGAL_RX) },
523 { ARG_STR(BOND_3AD_STAT_MARKER_RX) },
524 { ARG_STR(BOND_3AD_STAT_MARKER_TX) },
525 { ARG_STR(BOND_3AD_STAT_MARKER_RESP_RX) },
526 { ARG_STR(BOND_3AD_STAT_MARKER_RESP_TX) },
527 { ARG_STR(BOND_3AD_STAT_MARKER_UNKNOWN_RX) },
528 };
529 void *nlh_3ad_u64 = midtail_alloc(NLMSG_SPACE(hdrlen),
530 3 * NLA_HDRLEN + sizeof(uint64_t));
531 for (size_t i = 0; i < ARRAY_SIZE(u64_3ad_types); i++) {
532 snprintf(nla_type_str, sizeof(nla_type_str), XLAT_FMT,
533 XLAT_SEL(u64_3ad_types[i].val, u64_3ad_types[i].str));
534 check_u64_nlattr(fd, nlh_3ad_u64, hdrlen,
535 init_ifstats_l3, print_ifstats_l3,
536 u64_3ad_types[i].val, nla_type_str,
537 pattern, 3);
538 }
539 }
540
541 static void
542 check_stats_offload(const int fd)
543 {
544 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen) + 3 * NLA_HDRLEN,
545 NLA_HDRLEN + 128);
546
547 /* IFLA_STATS_LINK_OFFLOAD_XSTATS */
548 l1_attr = IFLA_STATS_LINK_OFFLOAD_XSTATS;
549 snprintf(l1_attr_str, sizeof(l1_attr_str), XLAT_FMT,
550 XLAT_ARGS(IFLA_STATS_LINK_OFFLOAD_XSTATS));
551
552 /* Unknown, unimplemented, no semantics. */
553 static const struct strval32 undec_types[] = {
554 { ARG_STR(IFLA_OFFLOAD_XSTATS_UNSPEC) },
555 { 0x2 },
556 { 0xbad },
557 };
558 for (size_t i = 0; i < ARRAY_SIZE(undec_types); i++) {
559 fmt_str(nla_type_str, sizeof(nla_type_str),
560 undec_types[i].val, undec_types[i].str,
561 "IFLA_OFFLOAD_XSTATS_???");
562 TEST_NLATTR_(fd, nlh0, hdrlen + NLA_HDRLEN,
563 init_ifstats_l1, print_ifstats_l1,
564 undec_types[i].val, nla_type_str,
565 37, pattern, 37,
566 print_quoted_hex(pattern, 32);
567 printf("...]"));
568 }
569
570 /* IFLA_OFFLOAD_XSTATS_CPU_HIT */
571 check_stats_64(fd, ARG_STR(IFLA_OFFLOAD_XSTATS_CPU_HIT), true);
572 }
573
574 /*
575 * skip_af is expected to be sorted
576 *
577 * [RTM_GETSTATS] -> struct if_stats_msg
578 * [cmd]
579 * [AF_*]
580 * [0] -> u32
581 * [1] -> u64
582 */
583 static void
584 check_stats_af_generic(const int fd, unsigned int cmd, const char *cmd_str,
585 const uint8_t * const skip_af, const size_t skip_af_cnt)
586 {
587 enum { ATTR_SZ = NLA_HDRLEN + 2 * NLA_HDRLEN + 4 + 8 };
588
589 /*
590 * The payload is designed like this so if a decoder for a new AF_*
591 * is implemented, this check will likely fail.
592 */
593 struct {
594 struct nlattr hdr;
595
596 struct nlattr nested_hdr1;
597 uint32_t nested_data1;
598
599 struct nlattr nested_hdr2;
600 uint64_t nested_data2;
601 } dummy_data = {
602 { ATTR_SZ, 0 /* AF_* */ },
603 { NLA_HDRLEN + sizeof(uint32_t), 0 }, 0xdeadc0de,
604 { NLA_HDRLEN + sizeof(uint64_t), 1 }, 0xbadda7adeadfacedULL,
605 };
606 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen), NLA_HDRLEN + ATTR_SZ);
607 size_t skip_pos = 0;
608
609 static_assert(ATTR_SZ == sizeof(dummy_data),
610 "Dummy nlattr payload size mismatch");
611
612 snprintf(nla_type_str, sizeof(nla_type_str), XLAT_FMT,
613 XLAT_SEL(cmd, cmd_str));
614
615 for (size_t i = 0; i < 256; i++) {
616 if (skip_pos < skip_af_cnt && i == skip_af[skip_pos]) {
617 skip_pos++;
618 continue;
619 }
620
621 dummy_data.hdr.nla_type = i;
622 TEST_NLATTR_(fd, nlh0, hdrlen, init_ifstats, print_ifstats,
623 cmd, nla_type_str, ATTR_SZ, &dummy_data, ATTR_SZ,
624 printf("[{nla_len=%u, nla_type=", ATTR_SZ);
625 printxval(addrfams, i, "AF_???");
626 printf("}, ");
627 print_quoted_hex(&dummy_data.nested_hdr1,
628 sizeof(dummy_data) - NLA_HDRLEN);
629 printf("]"));
630 }
631 }
632
633 static void
634 check_stats_af_mpls(const int fd)
635 {
636 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen) + 3 * NLA_HDRLEN,
637 NLA_HDRLEN + 128);
638
639 /* l1: IFLA_STATS_AF_SPEC */
640 l1_attr = IFLA_STATS_AF_SPEC;
641 snprintf(l1_attr_str, sizeof(l1_attr_str),
642 XLAT_KNOWN(0x5, "IFLA_STATS_AF_SPEC"));
643
644 /* l2: AF_MPLS */
645 l2_attr = AF_MPLS;
646 snprintf(l2_attr_str, sizeof(l2_attr_str), XLAT_FMT,
647 XLAT_ARGS(AF_MPLS));
648
649 /* Unknown, unimplemented, no semantics. */
650 static const struct strval32 undec_types[] = {
651 { ARG_STR(MPLS_STATS_UNSPEC) },
652 { 0x2 },
653 { 0xbad },
654 };
655 for (size_t i = 0; i < ARRAY_SIZE(undec_types); i++) {
656 fmt_str(nla_type_str, sizeof(nla_type_str),
657 undec_types[i].val, undec_types[i].str,
658 "MPLS_STATS_???");
659 TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN,
660 init_ifstats_l2, print_ifstats_l2,
661 undec_types[i].val, nla_type_str,
662 37, pattern, 37,
663 print_quoted_hex(pattern, 32);
664 printf("...]]"));
665 }
666
667 /* MPLS_STATS_LINK */
668 struct mpls_link_stats mls;
669 void *nlh_mls = midtail_alloc(NLMSG_SPACE(hdrlen),
670 2 * NLA_HDRLEN + sizeof(mls));
671
672 memset(&mls, 0, sizeof(mls));
673 TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mls, hdrlen,
674 init_ifstats_l2, print_ifstats_l2,
675 MPLS_STATS_LINK,
676 XLAT_KNOWN(0x1, "MPLS_STATS_LINK"),
677 pattern, mls, print_quoted_hex, 2,
678 printf("{rx_packets=0, tx_packets=0"
679 ", rx_bytes=0, tx_bytes=0"
680 ", rx_errors=0, tx_errors=0"
681 ", rx_dropped=0, tx_dropped=0"
682 ", rx_noroute=0}"));
683
684 typedef unsigned long long ullong;
685 fill_memory64(&mls, sizeof(mls));
686 TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mls, hdrlen,
687 init_ifstats_l2, print_ifstats_l2,
688 MPLS_STATS_LINK,
689 XLAT_KNOWN(0x1, "MPLS_STATS_LINK"),
690 pattern, mls, print_quoted_hex, 2,
691 printf("{rx_packets=%llu, tx_packets=%llu"
692 ", rx_bytes=%llu, tx_bytes=%llu"
693 ", rx_errors=%llu, tx_errors=%llu"
694 ", rx_dropped=%llu"
695 ", tx_dropped=%llu"
696 ", rx_noroute=%llu}",
697 (ullong) mls.rx_packets,
698 (ullong) mls.tx_packets,
699 (ullong) mls.rx_bytes,
700 (ullong) mls.tx_bytes,
701 (ullong) mls.rx_errors,
702 (ullong) mls.tx_errors,
703 (ullong) mls.rx_dropped,
704 (ullong) mls.tx_dropped,
705 (ullong) mls.rx_noroute));
706
707 char mls_buf[sizeof(mls) + 32];
708 fill_memory(mls_buf, sizeof(mls_buf));
709 memcpy(mls_buf, &mls, sizeof(mls));
710 TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN,
711 init_ifstats_l2, print_ifstats_l2,
712 MPLS_STATS_LINK, XLAT_KNOWN(0x1, "MPLS_STATS_LINK"),
713 sizeof(mls_buf), mls_buf, sizeof(mls_buf),
714 printf("{rx_packets=9264081114510713072"
715 ", tx_packets=9264081114510713073"
716 ", rx_bytes=9264081114510713074"
717 ", tx_bytes=9264081114510713075"
718 ", rx_errors=9264081114510713076"
719 ", tx_errors=9264081114510713077"
720 ", rx_dropped=9264081114510713078"
721 ", tx_dropped=9264081114510713079"
722 ", rx_noroute=9264081114510713080}, ");
723 print_quoted_hex(mls_buf + sizeof(mls), 32);
724 printf("]]"));
725 }
726
727 int
728 main(void)
729 {
730 skip_if_unavailable("/proc/self/fd/");
731
732 const int fd = create_nl_socket(NETLINK_ROUTE);
733 void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen),
734 NLA_HDRLEN + 256);
735
736 fill_memory_ex(pattern, sizeof(pattern), 'a', 'z' - 'a' + 1);
737
738 /* Unknown attrs. */
739 static const uint16_t unk_types[] = { 6, 0xffff & NLA_TYPE_MASK };
740 for (size_t i = 0; i < ARRAY_SIZE(unk_types); i++) {
741 sprintf(nla_type_str, "%#x" NRAW(" /* IFLA_STATS_??? */"),
742 unk_types[i]);
743 TEST_NLATTR_(fd, nlh0, hdrlen,
744 init_ifstats, print_ifstats,
745 unk_types[i], nla_type_str,
746 4, pattern, 4,
747 print_quoted_hex(pattern, 4));
748 }
749
750
751 /* IFLA_STATS_UNSPEC: unimplemented, no semantics. */
752 static const struct strval32 unimp_types[] = {
753 { ARG_XLAT_KNOWN(0, "IFLA_STATS_UNSPEC") },
754 };
755 for (size_t i = 0; i < ARRAY_SIZE(unimp_types); i++) {
756 TEST_NLATTR_(fd, nlh0, hdrlen,
757 init_ifstats, print_ifstats,
758 unimp_types[i].val, unimp_types[i].str,
759 42, pattern, 42,
760 print_quoted_hex(pattern, 32);
761 printf("..."));
762 }
763
764
765 /* IFLA_STATS_LINK_64 */
766 check_stats_64(fd, ARG_STR(IFLA_STATS_LINK_64), false);
767
768
769 /* IFLA_STATS_LINK_XSTATS, IFLA_STATS_LINK_XSTATS_SLAVE */
770 check_xstats(fd, ARG_STR(IFLA_STATS_LINK_XSTATS));
771 check_xstats(fd, ARG_STR(IFLA_STATS_LINK_XSTATS_SLAVE));
772
773
774 /* IFLA_STATS_LINK_OFFLOAD_STATS */
775 check_stats_offload(fd);
776
777
778 /* IFLA_STATS_AF_SPEC */
779 static const uint8_t af_spec_fams[] = { AF_MPLS };
780 check_stats_af_generic(fd, ARG_STR(IFLA_STATS_AF_SPEC),
781 ARRSZ_PAIR(af_spec_fams));
782
783 /* IFLA_STATS_AF_SPEC: AF_MPLS */
784 check_stats_af_mpls(fd);
785
786
787 puts("+++ exited with 0 +++");
788 return 0;
789 }