/* * Copyright (c) 2018-2021 Eugene Syromyatnikov * All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "tests.h" #include #include #include #include #include #include #include #include #include "test_nlattr.h" #include #include #include #include #include "xlat.h" #include "xlat/addrfams.h" #define XLAT_MACROS_ONLY # include "xlat/ifstats_af_spec_mpls_attrs.h" # include "xlat/ifstats_attrs.h" # include "xlat/ifstats_attr_flags.h" # include "xlat/ifstats_offload_attrs.h" # include "xlat/ifstats_xstats_bond_attrs.h" # include "xlat/ifstats_xstats_bond_3ad_attrs.h" # include "xlat/ifstats_xstats_bridge_attrs.h" # include "xlat/ifstats_xstats_bridge_mcast_indices.h" # include "xlat/ifstats_xstats_type_attrs.h" # include "xlat/nl_bridge_vlan_flags.h" #undef XLAT_MACROS_ONLY static const unsigned int hdrlen = sizeof(struct if_stats_msg); static char pattern[4096]; static char nla_type_str[256]; static void init_ifstats(struct nlmsghdr *const nlh, const unsigned int msg_len) { SET_STRUCT(struct nlmsghdr, nlh, .nlmsg_len = msg_len, .nlmsg_type = RTM_GETSTATS, .nlmsg_flags = NLM_F_DUMP, ); struct if_stats_msg *const msg = NLMSG_DATA(nlh); SET_STRUCT(struct if_stats_msg, msg, .family = AF_UNIX, .ifindex = ifindex_lo(), .filter_mask = 0x22, ); } static void print_ifstats(const unsigned int msg_len) { printf("{nlmsg_len=%u, nlmsg_type=" XLAT_FMT ", nlmsg_flags=" XLAT_FMT ", nlmsg_seq=0, nlmsg_pid=0}, {family=" XLAT_FMT ", ifindex=" XLAT_FMT_U ", filter_mask=" XLAT_FMT "}", msg_len, XLAT_ARGS(RTM_GETSTATS), XLAT_ARGS(NLM_F_DUMP), XLAT_ARGS(AF_UNIX), XLAT_SEL(ifindex_lo(), IFINDEX_LO_STR), XLAT_ARGS(1< offsetofend(struct rtnl_link_stats64, tx_compressed)) { printf(", "); PRINT_FIELD_U(*st, rx_nohandler); } printf("}"); } static void check_stats_64(const int fd, unsigned int cmd, const char *cmd_str, bool nest) { static const size_t minsz = offsetofend(struct rtnl_link_stats64, tx_compressed); struct rtnl_link_stats64 st; void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen), (!!nest + 1) * NLA_HDRLEN + sizeof(st)); snprintf(nla_type_str, sizeof(nla_type_str), XLAT_FMT, XLAT_SEL(cmd, cmd_str)); fill_memory(&st, sizeof(st)); TEST_NESTED_NLATTR_OBJECT_EX_MINSZ_(fd, nlh0, hdrlen, nest ? init_ifstats_l1 : init_ifstats, nest ? print_ifstats_l1 : print_ifstats, cmd, nla_type_str, pattern, st, minsz, print_quoted_hex, (unsigned) !!nest, print_stats_64(&st, sizeof(st))); TEST_NLATTR_(fd, nlh0 - !!nest * NLA_HDRLEN, hdrlen + !!nest * NLA_HDRLEN, nest ? init_ifstats_l1 : init_ifstats, nest ? print_ifstats_l1 : print_ifstats, cmd, nla_type_str, minsz, &st, minsz, print_stats_64(&st, minsz); for (size_t i = 0; i < (unsigned) !!nest; i++) printf("]")); } static void fmt_str(char *dst, size_t dst_sz, uint32_t cmd, const char *s, const char *dflt) { if (s) { snprintf(dst, dst_sz, XLAT_FMT, XLAT_SEL(cmd, s)); } else { snprintf(dst, dst_sz, "%#x" NRAW(" /* ") "%s" NRAW(" */"), cmd, NRAW(dflt)); } } static void print_mcast_stats(struct br_mcast_stats *br_xst_mc) { #define PR_FIELD_(pfx_, field_) \ printf(pfx_ #field_ "=[[" XLAT_KNOWN(0, "BR_MCAST_DIR_RX") \ "] = %llu, [" XLAT_KNOWN(1, "BR_MCAST_DIR_TX") "] = %llu]", \ (unsigned long long) br_xst_mc->field_[0], \ (unsigned long long) br_xst_mc->field_[1]) PR_FIELD_("{", igmp_v1queries); PR_FIELD_(", ", igmp_v2queries); PR_FIELD_(", ", igmp_v3queries); PR_FIELD_(", ", igmp_leaves); PR_FIELD_(", ", igmp_v1reports); PR_FIELD_(", ", igmp_v2reports); PR_FIELD_(", ", igmp_v3reports); printf(", igmp_parse_errors=%llu", (unsigned long long) br_xst_mc->igmp_parse_errors); PR_FIELD_(", ", mld_v1queries); PR_FIELD_(", ", mld_v2queries); PR_FIELD_(", ", mld_leaves); PR_FIELD_(", ", mld_v1reports); PR_FIELD_(", ", mld_v2reports); printf(", mld_parse_errors=%llu", (unsigned long long) br_xst_mc->mld_parse_errors); PR_FIELD_(", ", mcast_bytes); PR_FIELD_(", ", mcast_packets); printf("}"); #undef PR_FIELD_ } static void check_xstats(const int fd, unsigned int cmd, const char *cmd_str) { void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen) + 3 * NLA_HDRLEN, NLA_HDRLEN + 256); l1_attr = cmd; snprintf(l1_attr_str, sizeof(l1_attr_str), XLAT_FMT, XLAT_SEL(cmd, cmd_str)); /* Unknown, unimplemented, no semantics. */ static const struct strval32 undec_types[] = { { ARG_STR(LINK_XSTATS_TYPE_UNSPEC) }, { 0x3 }, { 0xbad }, }; for (size_t i = 0; i < ARRAY_SIZE(undec_types); i++) { fmt_str(nla_type_str, sizeof(nla_type_str), undec_types[i].val, undec_types[i].str, "LINK_XSTATS_TYPE_???"); TEST_NLATTR_(fd, nlh0, hdrlen + NLA_HDRLEN, init_ifstats_l1, print_ifstats_l1, undec_types[i].val, nla_type_str, 37, pattern, 37, print_quoted_hex(pattern, 32); printf("...]")); } /* LINK_XSTATS_TYPE_BRIDGE */ l2_attr = LINK_XSTATS_TYPE_BRIDGE; snprintf(l2_attr_str, sizeof(l2_attr_str), XLAT_FMT, XLAT_ARGS(LINK_XSTATS_TYPE_BRIDGE)); /* LINK_XSTATS_TYPE_BRIDGE: Unknown, unimplemented, no semantics. */ static const struct strval32 undec_br_types[] = { { ARG_STR(BRIDGE_XSTATS_UNSPEC) }, { ARG_STR(BRIDGE_XSTATS_PAD) }, { 0x5 }, { 0xbad }, }; for (size_t i = 0; i < ARRAY_SIZE(undec_br_types); i++) { fmt_str(nla_type_str, sizeof(nla_type_str), undec_br_types[i].val, undec_br_types[i].str, "BRIDGE_XSTATS_???"); TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN, init_ifstats_l2, print_ifstats_l2, undec_br_types[i].val, nla_type_str, 37, pattern, 37, print_quoted_hex(pattern, 32); printf("...]]")); } /* LINK_XSTATS_TYPE_BRIDGE: BRIDGE_XSTATS_VLAN */ static const struct { struct bridge_vlan_xstats val; const char *str; } br_xst_vlan_vecs[] = { { { .rx_bytes=0, .rx_packets=0xdeadfacebeeffeedULL, .tx_bytes=0x8090a0b0c0d0e0f0ULL, .tx_packets=0, .vid=0xdead, .flags=2 }, "{rx_bytes=0, rx_packets=16045756813264551661" ", tx_bytes=9264081114510713072, tx_packets=0" ", vid=57005, flags=" XLAT_KNOWN(0x2, "BRIDGE_VLAN_INFO_PVID") "}" }, { { .rx_bytes=12345678901234567890ULL, .rx_packets=0, .tx_bytes=0, .tx_packets=9876543210987654321ULL, .vid=0, .flags=0, .pad2=0xbadc0ded }, "{rx_bytes=12345678901234567890, rx_packets=0" ", tx_bytes=0, tx_packets=9876543210987654321" ", vid=0, flags=0, pad2=0xbadc0ded}" }, { { .flags=0xdeed }, "{rx_bytes=0, rx_packets=0, tx_bytes=0, tx_packets=0, vid=0" ", flags=" XLAT_KNOWN(0xdeed, "BRIDGE_VLAN_INFO_MASTER" "|BRIDGE_VLAN_INFO_UNTAGGED" "|BRIDGE_VLAN_INFO_RANGE_BEGIN" "|BRIDGE_VLAN_INFO_BRENTRY" "|BRIDGE_VLAN_INFO_ONLY_OPTS" "|0xde80") "}" }, { { .rx_bytes=0xdefaceddecaffeedULL, .rx_packets=0xbeeffacedeadbabeULL, .tx_bytes=0xbeeffeeddadfacedULL, .tx_packets=0xbeeffadeeffaceedULL, .vid=0xcafe, .flags=0xfa80, .pad2=0xdeadabba }, "{rx_bytes=16067382073151717101" ", rx_packets=13758491153046289086" ", tx_bytes=13758495684172950765" ", tx_packets=13758491222056029933" ", vid=51966, flags=0xfa80" NRAW(" /* BRIDGE_VLAN_INFO_??? */") ", pad2=0xdeadabba}" }, }; void *nlh_vlan = midtail_alloc(NLMSG_SPACE(hdrlen), 2 * NLA_HDRLEN + sizeof(struct bridge_vlan_xstats)); for (size_t i = 0; i < ARRAY_SIZE(br_xst_vlan_vecs); i++) { TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_vlan, hdrlen, init_ifstats_l2, print_ifstats_l2, BRIDGE_XSTATS_VLAN, XLAT_KNOWN(0x1, "BRIDGE_XSTATS_VLAN"), pattern, br_xst_vlan_vecs[i].val, print_quoted_hex, 2, printf("%s", br_xst_vlan_vecs[i].str)); char buf[sizeof(br_xst_vlan_vecs[0].val) + 42]; fill_memory(buf, sizeof(buf)); memcpy(buf, &br_xst_vlan_vecs[i].val, sizeof(br_xst_vlan_vecs[i].val)); TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN, init_ifstats_l2, print_ifstats_l2, BRIDGE_XSTATS_VLAN, XLAT_KNOWN(0x1, "BRIDGE_XSTATS_VLAN"), sizeof(buf), buf, sizeof(buf), printf("%s", br_xst_vlan_vecs[i].str); printf(", "); print_quoted_hex(buf + sizeof(br_xst_vlan_vecs[0].val), 32); printf("...]]")); } /* LINK_XSTATS_TYPE_BRIDGE: BRIDGE_XSTATS_MCAST */ struct br_mcast_stats br_xst_mc; void *nlh_mc = midtail_alloc(NLMSG_SPACE(hdrlen), 2 * NLA_HDRLEN + sizeof(br_xst_mc)); #define FIELD_STR_(field_) \ #field_ "=[[" XLAT_KNOWN(0, "BR_MCAST_DIR_RX") "] = 0, [" \ XLAT_KNOWN(1, "BR_MCAST_DIR_TX") "] = 0]" memset(&br_xst_mc, 0, sizeof(br_xst_mc)); TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mc, hdrlen, init_ifstats_l2, print_ifstats_l2, BRIDGE_XSTATS_MCAST, XLAT_KNOWN(0x2, "BRIDGE_XSTATS_MCAST"), pattern, br_xst_mc, print_quoted_hex, 2, printf("{" FIELD_STR_(igmp_v1queries) ", " FIELD_STR_(igmp_v2queries) ", " FIELD_STR_(igmp_v3queries) ", " FIELD_STR_(igmp_leaves) ", " FIELD_STR_(igmp_v1reports) ", " FIELD_STR_(igmp_v2reports) ", " FIELD_STR_(igmp_v3reports) ", igmp_parse_errors=0" ", " FIELD_STR_(mld_v1queries) ", " FIELD_STR_(mld_v2queries) ", " FIELD_STR_(mld_leaves) ", " FIELD_STR_(mld_v1reports) ", " FIELD_STR_(mld_v2reports) ", mld_parse_errors=0" ", " FIELD_STR_(mcast_bytes) ", " FIELD_STR_(mcast_packets) "}")); #undef FIELD_STR_ fill_memory64(&br_xst_mc, sizeof(br_xst_mc)); TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mc, hdrlen, init_ifstats_l2, print_ifstats_l2, BRIDGE_XSTATS_MCAST, XLAT_KNOWN(0x2, "BRIDGE_XSTATS_MCAST"), pattern, br_xst_mc, print_quoted_hex, 2, print_mcast_stats(&br_xst_mc)); char mc_buf[sizeof(br_xst_mc) + 8]; fill_memory(mc_buf, sizeof(mc_buf)); memcpy(mc_buf, &br_xst_mc, sizeof(br_xst_mc)); TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN, init_ifstats_l2, print_ifstats_l2, BRIDGE_XSTATS_MCAST, XLAT_KNOWN(0x2, "BRIDGE_XSTATS_MCAST"), sizeof(mc_buf), mc_buf, sizeof(mc_buf), print_mcast_stats(&br_xst_mc); printf(", "); print_quoted_hex(mc_buf + sizeof(br_xst_mc), 8); printf("]]")); /* LINK_XSTATS_TYPE_BRIDGE: BRIDGE_XSTATS_STP */ static const struct { struct bridge_stp_xstats val; const char *str; } br_xst_stp_vecs[] = { { { .transition_blk=0, .transition_fwd=0, .rx_bpdu=0, .tx_bpdu=0, .rx_tcn=0, .tx_tcn=0, }, "{transition_blk=0, transition_fwd=0, rx_bpdu=0, tx_bpdu=0" ", rx_tcn=0, tx_tcn=0}" }, { { .transition_blk=0x8090a0b0c0d0e0f0ULL, .transition_fwd=0x8191a1b1c1d1e1f1ULL, .rx_bpdu=0x8292a2b2c2d2e2f2ULL, .tx_bpdu=0x8393a3b3c3d3e3f3ULL, .rx_tcn=0x8494a4b4c4d4e4f4ULL, .tx_tcn=0x8595a5b5c5d5e5f5ULL, }, "{transition_blk=9264081114510713072" ", transition_fwd=9336421287348789745" ", rx_bpdu=9408761460186866418, tx_bpdu=9481101633024943091" ", rx_tcn=9553441805863019764, tx_tcn=9625781978701096437}" }, }; void *nlh_stp = midtail_alloc(NLMSG_SPACE(hdrlen), 2 * NLA_HDRLEN + sizeof(struct bridge_stp_xstats)); for (size_t i = 0; i < ARRAY_SIZE(br_xst_stp_vecs); i++) { TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_stp, hdrlen, init_ifstats_l2, print_ifstats_l2, BRIDGE_XSTATS_STP, XLAT_KNOWN(0x4, "BRIDGE_XSTATS_STP"), pattern, br_xst_stp_vecs[i].val, print_quoted_hex, 2, printf("%s", br_xst_stp_vecs[i].str)); char buf[sizeof(br_xst_stp_vecs[0].val) + 33]; fill_memory(buf, sizeof(buf)); memcpy(buf, &br_xst_stp_vecs[i].val, sizeof(br_xst_stp_vecs[i].val)); TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN, init_ifstats_l2, print_ifstats_l2, BRIDGE_XSTATS_STP, XLAT_KNOWN(0x4, "BRIDGE_XSTATS_STP"), sizeof(buf), buf, sizeof(buf), printf("%s", br_xst_stp_vecs[i].str); printf(", "); print_quoted_hex(buf + sizeof(br_xst_stp_vecs[0].val), 32); printf("...]]")); } /* LINK_XSTATS_TYPE_BOND */ l2_attr = LINK_XSTATS_TYPE_BOND; snprintf(l2_attr_str, sizeof(l2_attr_str), XLAT_FMT, XLAT_ARGS(LINK_XSTATS_TYPE_BOND)); /* LINK_XSTATS_TYPE_BOND: Unknown, unimplemented, no semantics. */ static const struct strval32 undec_bd_types[] = { { ARG_STR(BOND_XSTATS_UNSPEC) }, { 0x2 }, { 0xbad }, }; for (size_t i = 0; i < ARRAY_SIZE(undec_bd_types); i++) { fmt_str(nla_type_str, sizeof(nla_type_str), undec_bd_types[i].val, undec_bd_types[i].str, "BOND_XSTATS_???"); TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN, init_ifstats_l2, print_ifstats_l2, undec_bd_types[i].val, nla_type_str, 37, pattern, 37, print_quoted_hex(pattern, 32); printf("...]]")); } /* LINK_XSTATS_TYPE_BOND: BOND_XSTATS_3AD */ l3_attr = BOND_XSTATS_3AD; snprintf(l3_attr_str, sizeof(l3_attr_str), XLAT_FMT, XLAT_ARGS(BOND_XSTATS_3AD)); /* BOND_XSTATS_3AD: Unknown, unimplemented, no semantics. */ static const struct strval32 undec_3ad_types[] = { { ARG_STR(BOND_3AD_STAT_PAD) }, { 0xa }, { 0xbad }, }; for (size_t i = 0; i < ARRAY_SIZE(undec_3ad_types); i++) { fmt_str(nla_type_str, sizeof(nla_type_str), undec_3ad_types[i].val, undec_3ad_types[i].str, "BOND_XSTATS_???"); TEST_NLATTR_(fd, nlh0, hdrlen + 3 * NLA_HDRLEN, init_ifstats_l3, print_ifstats_l3, undec_3ad_types[i].val, nla_type_str, 37, pattern, 37, print_quoted_hex(pattern, 32); printf("...]]]")); } /* BOND_XSTATS_3AD: u64 args */ static const struct strval32 u64_3ad_types[] = { { ARG_STR(BOND_3AD_STAT_LACPDU_RX) }, { ARG_STR(BOND_3AD_STAT_LACPDU_TX) }, { ARG_STR(BOND_3AD_STAT_LACPDU_UNKNOWN_RX) }, { ARG_STR(BOND_3AD_STAT_LACPDU_ILLEGAL_RX) }, { ARG_STR(BOND_3AD_STAT_MARKER_RX) }, { ARG_STR(BOND_3AD_STAT_MARKER_TX) }, { ARG_STR(BOND_3AD_STAT_MARKER_RESP_RX) }, { ARG_STR(BOND_3AD_STAT_MARKER_RESP_TX) }, { ARG_STR(BOND_3AD_STAT_MARKER_UNKNOWN_RX) }, }; void *nlh_3ad_u64 = midtail_alloc(NLMSG_SPACE(hdrlen), 3 * NLA_HDRLEN + sizeof(uint64_t)); for (size_t i = 0; i < ARRAY_SIZE(u64_3ad_types); i++) { snprintf(nla_type_str, sizeof(nla_type_str), XLAT_FMT, XLAT_SEL(u64_3ad_types[i].val, u64_3ad_types[i].str)); check_u64_nlattr(fd, nlh_3ad_u64, hdrlen, init_ifstats_l3, print_ifstats_l3, u64_3ad_types[i].val, nla_type_str, pattern, 3); } } static void check_stats_offload(const int fd) { void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen) + 3 * NLA_HDRLEN, NLA_HDRLEN + 128); /* IFLA_STATS_LINK_OFFLOAD_XSTATS */ l1_attr = IFLA_STATS_LINK_OFFLOAD_XSTATS; snprintf(l1_attr_str, sizeof(l1_attr_str), XLAT_FMT, XLAT_ARGS(IFLA_STATS_LINK_OFFLOAD_XSTATS)); /* Unknown, unimplemented, no semantics. */ static const struct strval32 undec_types[] = { { ARG_STR(IFLA_OFFLOAD_XSTATS_UNSPEC) }, { 0x2 }, { 0xbad }, }; for (size_t i = 0; i < ARRAY_SIZE(undec_types); i++) { fmt_str(nla_type_str, sizeof(nla_type_str), undec_types[i].val, undec_types[i].str, "IFLA_OFFLOAD_XSTATS_???"); TEST_NLATTR_(fd, nlh0, hdrlen + NLA_HDRLEN, init_ifstats_l1, print_ifstats_l1, undec_types[i].val, nla_type_str, 37, pattern, 37, print_quoted_hex(pattern, 32); printf("...]")); } /* IFLA_OFFLOAD_XSTATS_CPU_HIT */ check_stats_64(fd, ARG_STR(IFLA_OFFLOAD_XSTATS_CPU_HIT), true); } /* * skip_af is expected to be sorted * * [RTM_GETSTATS] -> struct if_stats_msg * [cmd] * [AF_*] * [0] -> u32 * [1] -> u64 */ static void check_stats_af_generic(const int fd, unsigned int cmd, const char *cmd_str, const uint8_t * const skip_af, const size_t skip_af_cnt) { enum { ATTR_SZ = NLA_HDRLEN + 2 * NLA_HDRLEN + 4 + 8 }; /* * The payload is designed like this so if a decoder for a new AF_* * is implemented, this check will likely fail. */ struct { struct nlattr hdr; struct nlattr nested_hdr1; uint32_t nested_data1; struct nlattr nested_hdr2; uint64_t nested_data2; } dummy_data = { { ATTR_SZ, 0 /* AF_* */ }, { NLA_HDRLEN + sizeof(uint32_t), 0 }, 0xdeadc0de, { NLA_HDRLEN + sizeof(uint64_t), 1 }, 0xbadda7adeadfacedULL, }; void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen), NLA_HDRLEN + ATTR_SZ); size_t skip_pos = 0; static_assert(ATTR_SZ == sizeof(dummy_data), "Dummy nlattr payload size mismatch"); snprintf(nla_type_str, sizeof(nla_type_str), XLAT_FMT, XLAT_SEL(cmd, cmd_str)); for (size_t i = 0; i < 256; i++) { if (skip_pos < skip_af_cnt && i == skip_af[skip_pos]) { skip_pos++; continue; } dummy_data.hdr.nla_type = i; TEST_NLATTR_(fd, nlh0, hdrlen, init_ifstats, print_ifstats, cmd, nla_type_str, ATTR_SZ, &dummy_data, ATTR_SZ, printf("[{nla_len=%u, nla_type=", ATTR_SZ); printxval(addrfams, i, "AF_???"); printf("}, "); print_quoted_hex(&dummy_data.nested_hdr1, sizeof(dummy_data) - NLA_HDRLEN); printf("]")); } } static void check_stats_af_mpls(const int fd) { void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen) + 3 * NLA_HDRLEN, NLA_HDRLEN + 128); /* l1: IFLA_STATS_AF_SPEC */ l1_attr = IFLA_STATS_AF_SPEC; snprintf(l1_attr_str, sizeof(l1_attr_str), XLAT_KNOWN(0x5, "IFLA_STATS_AF_SPEC")); /* l2: AF_MPLS */ l2_attr = AF_MPLS; snprintf(l2_attr_str, sizeof(l2_attr_str), XLAT_FMT, XLAT_ARGS(AF_MPLS)); /* Unknown, unimplemented, no semantics. */ static const struct strval32 undec_types[] = { { ARG_STR(MPLS_STATS_UNSPEC) }, { 0x2 }, { 0xbad }, }; for (size_t i = 0; i < ARRAY_SIZE(undec_types); i++) { fmt_str(nla_type_str, sizeof(nla_type_str), undec_types[i].val, undec_types[i].str, "MPLS_STATS_???"); TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN, init_ifstats_l2, print_ifstats_l2, undec_types[i].val, nla_type_str, 37, pattern, 37, print_quoted_hex(pattern, 32); printf("...]]")); } /* MPLS_STATS_LINK */ struct mpls_link_stats mls; void *nlh_mls = midtail_alloc(NLMSG_SPACE(hdrlen), 2 * NLA_HDRLEN + sizeof(mls)); memset(&mls, 0, sizeof(mls)); TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mls, hdrlen, init_ifstats_l2, print_ifstats_l2, MPLS_STATS_LINK, XLAT_KNOWN(0x1, "MPLS_STATS_LINK"), pattern, mls, print_quoted_hex, 2, printf("{rx_packets=0, tx_packets=0" ", rx_bytes=0, tx_bytes=0" ", rx_errors=0, tx_errors=0" ", rx_dropped=0, tx_dropped=0" ", rx_noroute=0}")); typedef unsigned long long ullong; fill_memory64(&mls, sizeof(mls)); TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh_mls, hdrlen, init_ifstats_l2, print_ifstats_l2, MPLS_STATS_LINK, XLAT_KNOWN(0x1, "MPLS_STATS_LINK"), pattern, mls, print_quoted_hex, 2, printf("{rx_packets=%llu, tx_packets=%llu" ", rx_bytes=%llu, tx_bytes=%llu" ", rx_errors=%llu, tx_errors=%llu" ", rx_dropped=%llu" ", tx_dropped=%llu" ", rx_noroute=%llu}", (ullong) mls.rx_packets, (ullong) mls.tx_packets, (ullong) mls.rx_bytes, (ullong) mls.tx_bytes, (ullong) mls.rx_errors, (ullong) mls.tx_errors, (ullong) mls.rx_dropped, (ullong) mls.tx_dropped, (ullong) mls.rx_noroute)); char mls_buf[sizeof(mls) + 32]; fill_memory(mls_buf, sizeof(mls_buf)); memcpy(mls_buf, &mls, sizeof(mls)); TEST_NLATTR_(fd, nlh0, hdrlen + 2 * NLA_HDRLEN, init_ifstats_l2, print_ifstats_l2, MPLS_STATS_LINK, XLAT_KNOWN(0x1, "MPLS_STATS_LINK"), sizeof(mls_buf), mls_buf, sizeof(mls_buf), printf("{rx_packets=9264081114510713072" ", tx_packets=9264081114510713073" ", rx_bytes=9264081114510713074" ", tx_bytes=9264081114510713075" ", rx_errors=9264081114510713076" ", tx_errors=9264081114510713077" ", rx_dropped=9264081114510713078" ", tx_dropped=9264081114510713079" ", rx_noroute=9264081114510713080}, "); print_quoted_hex(mls_buf + sizeof(mls), 32); printf("]]")); } int main(void) { skip_if_unavailable("/proc/self/fd/"); const int fd = create_nl_socket(NETLINK_ROUTE); void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen), NLA_HDRLEN + 256); fill_memory_ex(pattern, sizeof(pattern), 'a', 'z' - 'a' + 1); /* Unknown attrs. */ static const uint16_t unk_types[] = { 6, 0xffff & NLA_TYPE_MASK }; for (size_t i = 0; i < ARRAY_SIZE(unk_types); i++) { sprintf(nla_type_str, "%#x" NRAW(" /* IFLA_STATS_??? */"), unk_types[i]); TEST_NLATTR_(fd, nlh0, hdrlen, init_ifstats, print_ifstats, unk_types[i], nla_type_str, 4, pattern, 4, print_quoted_hex(pattern, 4)); } /* IFLA_STATS_UNSPEC: unimplemented, no semantics. */ static const struct strval32 unimp_types[] = { { ARG_XLAT_KNOWN(0, "IFLA_STATS_UNSPEC") }, }; for (size_t i = 0; i < ARRAY_SIZE(unimp_types); i++) { TEST_NLATTR_(fd, nlh0, hdrlen, init_ifstats, print_ifstats, unimp_types[i].val, unimp_types[i].str, 42, pattern, 42, print_quoted_hex(pattern, 32); printf("...")); } /* IFLA_STATS_LINK_64 */ check_stats_64(fd, ARG_STR(IFLA_STATS_LINK_64), false); /* IFLA_STATS_LINK_XSTATS, IFLA_STATS_LINK_XSTATS_SLAVE */ check_xstats(fd, ARG_STR(IFLA_STATS_LINK_XSTATS)); check_xstats(fd, ARG_STR(IFLA_STATS_LINK_XSTATS_SLAVE)); /* IFLA_STATS_LINK_OFFLOAD_STATS */ check_stats_offload(fd); /* IFLA_STATS_AF_SPEC */ static const uint8_t af_spec_fams[] = { AF_MPLS }; check_stats_af_generic(fd, ARG_STR(IFLA_STATS_AF_SPEC), ARRSZ_PAIR(af_spec_fams)); /* IFLA_STATS_AF_SPEC: AF_MPLS */ check_stats_af_mpls(fd); puts("+++ exited with 0 +++"); return 0; }