(root)/
strace-6.5/
tests/
nlattr_ifstats.c
       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  }