diff options
author | Eugene Syromyatnikov <evgsyr@gmail.com> | 2018-08-19 13:34:41 +0200 |
---|---|---|
committer | Dmitry V. Levin <ldv@strace.io> | 2021-11-07 08:00:00 +0000 |
commit | a1813471957f97e85df7098453598c57aa0bfda5 (patch) | |
tree | 2a280b596b41e881700e00a66c0658d853b53a61 | |
parent | e8fb4de6d960c7a6374dbb59f20fbd3689e88df5 (diff) | |
download | strace-a1813471957f97e85df7098453598c57aa0bfda5.tar.gz |
netlink_route: implement RTM_NEWSTATS and RTM_GETSTATS message decoding
* src/xlat/ifstats_af_spec_mpls_attrs.in: New file.
* src/xlat/ifstats_attr_flags.in: Likewise.
* src/xlat/ifstats_attrs.in: Likewise.
* src/xlat/ifstats_offload_attrs.in: Likewise.
* src/xlat/ifstats_xstats_bond_3ad_attrs.in: Likewise.
* src/xlat/ifstats_xstats_bond_attrs.in: Likewise.
* src/xlat/ifstats_xstats_bridge_attrs.in: Likewise.
* src/xlat/ifstats_xstats_bridge_mcast_indices.in: Likewise.
* src/xlat/ifstats_xstats_type_attrs.in: Likewise.
* src/xlat/nl_bridge_vlan_flags.in: Likewise.
* bundled/linux/include/uapi/linux/if_bonding.h: New file, copied from
headers_install'ed Linux kernel v5.15.
* bundled/linux/include/uapi/linux/mpls.h: Likewise.
* bundled/Makefile.am (EXTRA_DIST): Add them.
* src/rtnl_stats.c: New file.
* src/Makefile.am (strace_SOURCES): Add it.
* src/netlink_route.c (route_decoders) <[RTM_NEWSTATS - RTM_BASE],
[RTM_GETSTATS - RTM_BASE]>: New decoder, call decode_ifstatsmsg.
* src/netlink_route.h (decode_ifstatsmsg): New declaration.
* src/nlattr.h (DECL_NLA(rtnl_link_stats64)): Likewise.
* src/rtnl_link.c (decode_nla_rtnl_link_stats64): Rename from
decode_rtnl_link_stats64.
(ifinfomsg_nla_decoders) <[IFLA_STATS64]>: Use
decode_nla_rtnl_link_stats64.
* src/print_fields.h (PRINT_FIELD_ARRAY_INDEXED): New macro.
* tests/nlattr_ifstats.c: New file.
* tests/nlattr_ifstats-Xabbrev.c: Likewise.
* tests/nlattr_ifstats-Xraw.c: Likewise.
* tests/nlattr_ifstats-Xverbose.c: Likewise.
* tests/.gitignore: Add nlattr_ifstats, nlattr_ifstats-Xabbrev,
nlattr_ifstats-Xraw, and nlattr_ifstats-Xverbose.
* tests/pure_executables.list: Likewise.
* tests/gen_tests.in (nlattr_ifstats, nlattr_ifstats-Xabbrev,
nlattr_ifstats-Xraw, nlattr_ifstats-Xverbose): New tests.
* tests/netlink_route.c: Update/add checks for the RTM_*STATS message
types.
* NEWS: Mention it.
29 files changed, 1614 insertions, 16 deletions
@@ -9,8 +9,8 @@ Noteworthy changes in release ?.?? (????-??-??) * Implemented decoding of SECCOMP_GET_NOTIF_SIZES operation of seccomp syscall. * Implemented decoding of SECCOMP_* ioctl commands. - * Implemented decoding of RTM_{NEW,DEL,GET}NEXTHOP NETLINK_ROUTE netlink - messages. + * Implemented decoding of RTM_{NEW,DEL,GET}NEXTHOP and RTM_{NEW,GET}STATS + NETLINK_ROUTE netlink messages. * Enhanced decoding of io_uring_register and times syscalls. * Enhanced AF_IPX socket address decoding. * Updated lists of BTRFS_*, DM_*, FAN_REPORT_*, IORING_*, MPOL_*, PACKET_*, diff --git a/bundled/Makefile.am b/bundled/Makefile.am index b3da1b23a..388e05dc5 100644 --- a/bundled/Makefile.am +++ b/bundled/Makefile.am @@ -43,6 +43,7 @@ EXTRA_DIST = \ linux/include/uapi/linux/hiddev.h \ linux/include/uapi/linux/if_addr.h \ linux/include/uapi/linux/if_addrlabel.h \ + linux/include/uapi/linux/if_bonding.h \ linux/include/uapi/linux/if_bridge.h \ linux/include/uapi/linux/if_link.h \ linux/include/uapi/linux/in6.h \ @@ -60,6 +61,7 @@ EXTRA_DIST = \ linux/include/uapi/linux/memfd.h \ linux/include/uapi/linux/mmtimer.h \ linux/include/uapi/linux/mount.h \ + linux/include/uapi/linux/mpls.h \ linux/include/uapi/linux/mqueue.h \ linux/include/uapi/linux/neighbour.h \ linux/include/uapi/linux/netconf.h \ diff --git a/bundled/linux/include/uapi/linux/if_bonding.h b/bundled/linux/include/uapi/linux/if_bonding.h new file mode 100644 index 000000000..d174914a8 --- /dev/null +++ b/bundled/linux/include/uapi/linux/if_bonding.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-1.0+ WITH Linux-syscall-note */ +/* + * Bond several ethernet interfaces into a Cisco, running 'Etherchannel'. + * + * + * Portions are (c) Copyright 1995 Simon "Guru Aleph-Null" Janes + * NCM: Network and Communications Management, Inc. + * + * BUT, I'm the one who modified it for ethernet, so: + * (c) Copyright 1999, Thomas Davis, tadavis@lbl.gov + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * 2003/03/18 - Amir Noam <amir.noam at intel dot com> + * - Added support for getting slave's speed and duplex via ethtool. + * Needed for 802.3ad and other future modes. + * + * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and + * Shmulik Hen <shmulik.hen at intel dot com> + * - Enable support of modes that need to use the unique mac address of + * each slave. + * + * 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and + * Amir Noam <amir.noam at intel dot com> + * - Moved driver's private data types to bonding.h + * + * 2003/03/18 - Amir Noam <amir.noam at intel dot com>, + * Tsippy Mendelson <tsippy.mendelson at intel dot com> and + * Shmulik Hen <shmulik.hen at intel dot com> + * - Added support for IEEE 802.3ad Dynamic link aggregation mode. + * + * 2003/05/01 - Amir Noam <amir.noam at intel dot com> + * - Added ABI version control to restore compatibility between + * new/old ifenslave and new/old bonding. + * + * 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com> + * - Code cleanup and style changes + * + * 2005/05/05 - Jason Gabler <jygabler at lbl dot gov> + * - added definitions for various XOR hashing policies + */ + +#ifndef _LINUX_IF_BONDING_H +#define _LINUX_IF_BONDING_H + +#include <linux/if.h> +#include <linux/types.h> +#include <linux/if_ether.h> + +/* userland - kernel ABI version (2003/05/08) */ +#define BOND_ABI_VERSION 2 + +/* + * We can remove these ioctl definitions in 2.5. People should use the + * SIOC*** versions of them instead + */ +#define BOND_ENSLAVE_OLD (SIOCDEVPRIVATE) +#define BOND_RELEASE_OLD (SIOCDEVPRIVATE + 1) +#define BOND_SETHWADDR_OLD (SIOCDEVPRIVATE + 2) +#define BOND_SLAVE_INFO_QUERY_OLD (SIOCDEVPRIVATE + 11) +#define BOND_INFO_QUERY_OLD (SIOCDEVPRIVATE + 12) +#define BOND_CHANGE_ACTIVE_OLD (SIOCDEVPRIVATE + 13) + +#define BOND_CHECK_MII_STATUS (SIOCGMIIPHY) + +#define BOND_MODE_ROUNDROBIN 0 +#define BOND_MODE_ACTIVEBACKUP 1 +#define BOND_MODE_XOR 2 +#define BOND_MODE_BROADCAST 3 +#define BOND_MODE_8023AD 4 +#define BOND_MODE_TLB 5 +#define BOND_MODE_ALB 6 /* TLB + RLB (receive load balancing) */ + +/* each slave's link has 4 states */ +#define BOND_LINK_UP 0 /* link is up and running */ +#define BOND_LINK_FAIL 1 /* link has just gone down */ +#define BOND_LINK_DOWN 2 /* link has been down for too long time */ +#define BOND_LINK_BACK 3 /* link is going back */ + +/* each slave has several states */ +#define BOND_STATE_ACTIVE 0 /* link is active */ +#define BOND_STATE_BACKUP 1 /* link is backup */ + +#define BOND_DEFAULT_MAX_BONDS 1 /* Default maximum number of devices to support */ + +#define BOND_DEFAULT_TX_QUEUES 16 /* Default number of tx queues per device */ + +#define BOND_DEFAULT_RESEND_IGMP 1 /* Default number of IGMP membership reports */ + +/* hashing types */ +#define BOND_XMIT_POLICY_LAYER2 0 /* layer 2 (MAC only), default */ +#define BOND_XMIT_POLICY_LAYER34 1 /* layer 3+4 (IP ^ (TCP || UDP)) */ +#define BOND_XMIT_POLICY_LAYER23 2 /* layer 2+3 (IP ^ MAC) */ +#define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */ +#define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */ +#define BOND_XMIT_POLICY_VLAN_SRCMAC 5 /* vlan + source MAC */ + +/* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */ +#define LACP_STATE_LACP_ACTIVITY 0x1 +#define LACP_STATE_LACP_TIMEOUT 0x2 +#define LACP_STATE_AGGREGATION 0x4 +#define LACP_STATE_SYNCHRONIZATION 0x8 +#define LACP_STATE_COLLECTING 0x10 +#define LACP_STATE_DISTRIBUTING 0x20 +#define LACP_STATE_DEFAULTED 0x40 +#define LACP_STATE_EXPIRED 0x80 + +typedef struct ifbond { + __s32 bond_mode; + __s32 num_slaves; + __s32 miimon; +} ifbond; + +typedef struct ifslave { + __s32 slave_id; /* Used as an IN param to the BOND_SLAVE_INFO_QUERY ioctl */ + char slave_name[IFNAMSIZ]; + __s8 link; + __s8 state; + __u32 link_failure_count; +} ifslave; + +struct ad_info { + __u16 aggregator_id; + __u16 ports; + __u16 actor_key; + __u16 partner_key; + __u8 partner_system[ETH_ALEN]; +}; + +/* Embedded inside LINK_XSTATS_TYPE_BOND */ +enum { + BOND_XSTATS_UNSPEC, + BOND_XSTATS_3AD, + __BOND_XSTATS_MAX +}; +#define BOND_XSTATS_MAX (__BOND_XSTATS_MAX - 1) + +/* Embedded inside BOND_XSTATS_3AD */ +enum { + BOND_3AD_STAT_LACPDU_RX, + BOND_3AD_STAT_LACPDU_TX, + BOND_3AD_STAT_LACPDU_UNKNOWN_RX, + BOND_3AD_STAT_LACPDU_ILLEGAL_RX, + BOND_3AD_STAT_MARKER_RX, + BOND_3AD_STAT_MARKER_TX, + BOND_3AD_STAT_MARKER_RESP_RX, + BOND_3AD_STAT_MARKER_RESP_TX, + BOND_3AD_STAT_MARKER_UNKNOWN_RX, + BOND_3AD_STAT_PAD, + __BOND_3AD_STAT_MAX +}; +#define BOND_3AD_STAT_MAX (__BOND_3AD_STAT_MAX - 1) + +#endif /* _LINUX_IF_BONDING_H */ diff --git a/bundled/linux/include/uapi/linux/mpls.h b/bundled/linux/include/uapi/linux/mpls.h new file mode 100644 index 000000000..9effbf99d --- /dev/null +++ b/bundled/linux/include/uapi/linux/mpls.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _MPLS_H +#define _MPLS_H + +#include <linux/types.h> +#include <asm/byteorder.h> + +/* Reference: RFC 5462, RFC 3032 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Label | TC |S| TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Label: Label Value, 20 bits + * TC: Traffic Class field, 3 bits + * S: Bottom of Stack, 1 bit + * TTL: Time to Live, 8 bits + */ + +struct mpls_label { + __be32 entry; +}; + +#define MPLS_LS_LABEL_MASK 0xFFFFF000 +#define MPLS_LS_LABEL_SHIFT 12 +#define MPLS_LS_TC_MASK 0x00000E00 +#define MPLS_LS_TC_SHIFT 9 +#define MPLS_LS_S_MASK 0x00000100 +#define MPLS_LS_S_SHIFT 8 +#define MPLS_LS_TTL_MASK 0x000000FF +#define MPLS_LS_TTL_SHIFT 0 + +/* Reserved labels */ +#define MPLS_LABEL_IPV4NULL 0 /* RFC3032 */ +#define MPLS_LABEL_RTALERT 1 /* RFC3032 */ +#define MPLS_LABEL_IPV6NULL 2 /* RFC3032 */ +#define MPLS_LABEL_IMPLNULL 3 /* RFC3032 */ +#define MPLS_LABEL_ENTROPY 7 /* RFC6790 */ +#define MPLS_LABEL_GAL 13 /* RFC5586 */ +#define MPLS_LABEL_OAMALERT 14 /* RFC3429 */ +#define MPLS_LABEL_EXTENSION 15 /* RFC7274 */ + +#define MPLS_LABEL_FIRST_UNRESERVED 16 /* RFC3032 */ + +/* These are embedded into IFLA_STATS_AF_SPEC: + * [IFLA_STATS_AF_SPEC] + * -> [AF_MPLS] + * -> [MPLS_STATS_xxx] + * + * Attributes: + * [MPLS_STATS_LINK] = { + * struct mpls_link_stats + * } + */ +enum { + MPLS_STATS_UNSPEC, /* also used as 64bit pad attribute */ + MPLS_STATS_LINK, + __MPLS_STATS_MAX, +}; + +#define MPLS_STATS_MAX (__MPLS_STATS_MAX - 1) + +struct mpls_link_stats { + __u64 rx_packets; /* total packets received */ + __u64 tx_packets; /* total packets transmitted */ + __u64 rx_bytes; /* total bytes received */ + __u64 tx_bytes; /* total bytes transmitted */ + __u64 rx_errors; /* bad packets received */ + __u64 tx_errors; /* packet transmit problems */ + __u64 rx_dropped; /* packet dropped on receive */ + __u64 tx_dropped; /* packet dropped on transmit */ + __u64 rx_noroute; /* no route for packet dest */ +}; + +#endif /* _MPLS_H */ diff --git a/src/Makefile.am b/src/Makefile.am index a02fe98fd..d002df787 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -308,6 +308,7 @@ libstrace_a_SOURCES = \ rtnl_nsid.c \ rtnl_route.c \ rtnl_rule.c \ + rtnl_stats.c \ rtnl_tc.c \ rtnl_tc_action.c \ s390.c \ diff --git a/src/netlink_route.c b/src/netlink_route.c index e4b037183..45ae9b9d6 100644 --- a/src/netlink_route.c +++ b/src/netlink_route.c @@ -102,8 +102,8 @@ static const netlink_route_decoder_t route_decoders[] = { [RTM_DELNSID - RTM_BASE] = decode_rtgenmsg, [RTM_GETNSID - RTM_BASE] = decode_rtgenmsg, - /* RTM_NEWSTATS */ - /* RTM_GETSTATS */ + [RTM_NEWSTATS - RTM_BASE] = decode_ifstatsmsg, + [RTM_GETSTATS - RTM_BASE] = decode_ifstatsmsg, /* RTM_NEWCACHEREPORT */ diff --git a/src/netlink_route.h b/src/netlink_route.h index 3a63d1f38..7ae573100 100644 --- a/src/netlink_route.h +++ b/src/netlink_route.h @@ -25,6 +25,7 @@ extern DECL_NETLINK_ROUTE_DECODER(decode_fib_rule_hdr); extern DECL_NETLINK_ROUTE_DECODER(decode_ifaddrlblmsg); extern DECL_NETLINK_ROUTE_DECODER(decode_ifaddrmsg); extern DECL_NETLINK_ROUTE_DECODER(decode_ifinfomsg); +extern DECL_NETLINK_ROUTE_DECODER(decode_ifstatsmsg); extern DECL_NETLINK_ROUTE_DECODER(decode_ndmsg); extern DECL_NETLINK_ROUTE_DECODER(decode_ndtmsg); extern DECL_NETLINK_ROUTE_DECODER(decode_netconfmsg); diff --git a/src/nlattr.h b/src/nlattr.h index 4610ba891..a7e29dc6e 100644 --- a/src/nlattr.h +++ b/src/nlattr.h @@ -78,6 +78,7 @@ DECL_NLA(uid); DECL_NLA(gid); DECL_NLA(clock_t); DECL_NLA(ifindex); +DECL_NLA(ifla_af_spec); DECL_NLA(ether_proto); DECL_NLA(ip_proto); DECL_NLA(in_addr); @@ -86,6 +87,7 @@ DECL_NLA(lwt_encap_type); DECL_NLA(meminfo); DECL_NLA(rt_class); DECL_NLA(rt_proto); +DECL_NLA(rtnl_link_stats64); DECL_NLA(tc_stats); # define NLA_HWADDR_FAMILY_OFFSET 1024 diff --git a/src/print_fields.h b/src/print_fields.h index f0d33f629..f4b915be2 100644 --- a/src/print_fields.h +++ b/src/print_fields.h @@ -523,6 +523,18 @@ tprints_arg_begin(const char *name) (print_func_)); \ } while (0) +# define PRINT_FIELD_ARRAY_INDEXED(where_, field_, tcp_, print_func_, \ + ind_xlat_, ind_dflt_) \ + do { \ + tprints_field_name(#field_); \ + print_local_array_ex((tcp_), (where_).field_, \ + ARRAY_SIZE((where_).field_), \ + sizeof(((where_).field_)[0]), \ + (print_func_), \ + NULL, PAF_PRINT_INDICES | XLAT_STYLE_FMT_U, \ + (ind_xlat_), (ind_dflt_)); \ + } while (0) + # define PRINT_FIELD_ARRAY_UPTO(where_, field_, \ upto_, tcp_, print_func_) \ do { \ diff --git a/src/rtnl_link.c b/src/rtnl_link.c index 2c405601c..d8a7431a5 100644 --- a/src/rtnl_link.c +++ b/src/rtnl_link.c @@ -465,11 +465,11 @@ decode_ifla_linkinfo(struct tcb *const tcp, return true; } -static bool -decode_rtnl_link_stats64(struct tcb *const tcp, - const kernel_ulong_t addr, - const unsigned int len, - const void *const opaque_data) +bool +decode_nla_rtnl_link_stats64(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) { struct rtnl_link_stats64 st; const unsigned int min_size = @@ -970,7 +970,7 @@ static const nla_decoder_t ifinfomsg_nla_decoders[] = { [IFLA_IFALIAS] = decode_nla_str, [IFLA_NUM_VF] = decode_nla_u32, [IFLA_VFINFO_LIST] = NULL, /* unimplemented */ - [IFLA_STATS64] = decode_rtnl_link_stats64, + [IFLA_STATS64] = decode_nla_rtnl_link_stats64, [IFLA_VF_PORTS] = decode_ifla_vf_ports, [IFLA_PORT_SELF] = decode_ifla_port, [IFLA_AF_SPEC] = decode_ifla_af_spec, diff --git a/src/rtnl_stats.c b/src/rtnl_stats.c new file mode 100644 index 000000000..5e2ca7e3f --- /dev/null +++ b/src/rtnl_stats.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2018-2021 Eugene Syromyatnikov <evgsyr@gmail.com> + * All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "defs.h" +#include "netlink_route.h" +#include "nlattr.h" +#include "print_fields.h" + +#include "netlink.h" + +#include <netinet/in.h> + +#include <linux/if_bonding.h> +#include <linux/if_bridge.h> +#include <linux/if_link.h> +#include <linux/mpls.h> +#include <linux/rtnetlink.h> + +#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" + +#define XLAT_MACROS_ONLY +# include "xlat/addrfams.h" /* AF_MPLS */ +#undef XLAT_MACROS_ONLY + +static bool +decode_ifstats_link_xstats_bridge_vlan(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + struct bridge_vlan_xstats st; + + if (len < sizeof(st)) + return false; + + if (umove_or_printaddr(tcp, addr, &st)) + return true; + + tprint_struct_begin(); + PRINT_FIELD_U(st, rx_bytes); + tprint_struct_next(); + PRINT_FIELD_U(st, rx_packets); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_bytes); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_packets); + tprint_struct_next(); + PRINT_FIELD_U(st, vid); + tprint_struct_next(); + PRINT_FIELD_FLAGS(st, flags, nl_bridge_vlan_flags, + "BRIDGE_VLAN_INFO_???"); + if (st.pad2) { + tprint_struct_next(); + PRINT_FIELD_X(st, pad2); + } + tprint_struct_end(); + + if (len > sizeof(st)) { + tprint_array_next(); + printstr_ex(tcp, addr + sizeof(st), len - sizeof(st), + QUOTE_FORCE_HEX); + } + + return true; +} + +static bool +decode_ifstats_link_xstats_bridge_mcast(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + struct br_mcast_stats st; + + if (len < sizeof(st)) + return false; + + if (umove_or_printaddr(tcp, addr, &st)) + return true; + +#define PRINT_FIELD_MCAST_ARRAY_(where_, field_) \ + PRINT_FIELD_ARRAY_INDEXED(where_, field_, \ + tcp, print_uint_array_member, \ + ifstats_xstats_bridge_mcast_indices, \ + NULL); + + tprint_struct_begin(); + PRINT_FIELD_MCAST_ARRAY_(st, igmp_v1queries); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, igmp_v2queries); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, igmp_v3queries); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, igmp_leaves); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, igmp_v1reports); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, igmp_v2reports); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, igmp_v3reports); + tprint_struct_next(); + PRINT_FIELD_U(st, igmp_parse_errors); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, mld_v1queries); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, mld_v2queries); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, mld_leaves); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, mld_v1reports); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, mld_v2reports); + tprint_struct_next(); + PRINT_FIELD_U(st, mld_parse_errors); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, mcast_bytes); + tprint_struct_next(); + PRINT_FIELD_MCAST_ARRAY_(st, mcast_packets); + tprint_struct_end(); + +#undef PRINT_FIELD_MCAST_ARRAY_ + + if (len > sizeof(st)) { + tprint_array_next(); + printstr_ex(tcp, addr + sizeof(st), len - sizeof(st), + QUOTE_FORCE_HEX); + } + + return true; +} + +static bool +decode_ifstats_link_xstats_bridge_stp(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + struct bridge_stp_xstats st; + + if (len < sizeof(st)) + return false; + + if (umove_or_printaddr(tcp, addr, &st)) + return true; + + tprint_struct_begin(); + PRINT_FIELD_U(st, transition_blk); + tprint_struct_next(); + PRINT_FIELD_U(st, transition_fwd); + tprint_struct_next(); + PRINT_FIELD_U(st, rx_bpdu); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_bpdu); + tprint_struct_next(); + PRINT_FIELD_U(st, rx_tcn); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_tcn); + tprint_struct_end(); + + if (len > sizeof(st)) { + tprint_array_next(); + printstr_ex(tcp, addr + sizeof(st), len - sizeof(st), + QUOTE_FORCE_HEX); + } + + return true; +} + +static const nla_decoder_t ifstats_xstats_bridge_decoders[] = { + [BRIDGE_XSTATS_UNSPEC] = NULL, + [BRIDGE_XSTATS_VLAN] = decode_ifstats_link_xstats_bridge_vlan, + [BRIDGE_XSTATS_MCAST] = decode_ifstats_link_xstats_bridge_mcast, + [BRIDGE_XSTATS_PAD] = NULL, + [BRIDGE_XSTATS_STP] = decode_ifstats_link_xstats_bridge_stp, +}; + +static bool +decode_ifstats_link_xstats_bridge(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + decode_nlattr(tcp, addr, len, ifstats_xstats_bridge_attrs, + "BRIDGE_XSTATS_???", + ARRSZ_PAIR(ifstats_xstats_bridge_decoders), + opaque_data); + + return true; +} + +static const nla_decoder_t ifstats_xstats_bond_3ad_decoders[] = { + [BOND_3AD_STAT_LACPDU_RX] = decode_nla_u64, + [BOND_3AD_STAT_LACPDU_TX] = decode_nla_u64, + [BOND_3AD_STAT_LACPDU_UNKNOWN_RX] = decode_nla_u64, + [BOND_3AD_STAT_LACPDU_ILLEGAL_RX] = decode_nla_u64, + [BOND_3AD_STAT_MARKER_RX] = decode_nla_u64, + [BOND_3AD_STAT_MARKER_TX] = decode_nla_u64, + [BOND_3AD_STAT_MARKER_RESP_RX] = decode_nla_u64, + [BOND_3AD_STAT_MARKER_RESP_TX] = decode_nla_u64, + [BOND_3AD_STAT_MARKER_UNKNOWN_RX] = decode_nla_u64, + [BOND_3AD_STAT_PAD] = NULL, +}; + +static bool +decode_ifstats_link_xstats_bond_3ad(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + decode_nlattr(tcp, addr, len, ifstats_xstats_bond_3ad_attrs, + "BOND_XSTATS_???", + ARRSZ_PAIR(ifstats_xstats_bond_3ad_decoders), + opaque_data); + + return true; +} + +static const nla_decoder_t ifstats_xstats_bond_decoders[] = { + [BOND_XSTATS_UNSPEC] = NULL, + [BOND_XSTATS_3AD] = decode_ifstats_link_xstats_bond_3ad, +}; + +static bool +decode_ifstats_link_xstats_bond(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + decode_nlattr(tcp, addr, len, ifstats_xstats_bond_attrs, + "BOND_XSTATS_???", + ARRSZ_PAIR(ifstats_xstats_bond_decoders), + opaque_data); + + return true; +} + +static const nla_decoder_t ifstats_xstats_decoders[] = { + [LINK_XSTATS_TYPE_UNSPEC] = NULL, + [LINK_XSTATS_TYPE_BRIDGE] = decode_ifstats_link_xstats_bridge, + [LINK_XSTATS_TYPE_BOND] = decode_ifstats_link_xstats_bond, +}; + +static bool +decode_ifstats_link_xstats(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + decode_nlattr(tcp, addr, len, ifstats_xstats_type_attrs, + "LINK_XSTATS_TYPE_???", + ARRSZ_PAIR(ifstats_xstats_decoders), + opaque_data); + + return true; +} + +static const nla_decoder_t ifstats_offload_xstats_decoders[] = { + [IFLA_OFFLOAD_XSTATS_UNSPEC] = NULL, + [IFLA_OFFLOAD_XSTATS_CPU_HIT] = decode_nla_rtnl_link_stats64, +}; + +static bool +decode_ifstats_link_offload_xstats(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + decode_nlattr(tcp, addr, len, ifstats_offload_attrs, + "IFLA_OFFLOAD_XSTATS_???", + ARRSZ_PAIR(ifstats_offload_xstats_decoders), + opaque_data); + + return true; +} + +static bool +decode_ifstats_af_mpls_stats_link(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + struct mpls_link_stats st; + + if (len < sizeof(st)) + return false; + + if (umove_or_printaddr(tcp, addr, &st)) + return true; + + tprint_struct_begin(); + PRINT_FIELD_U(st, rx_packets); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_packets); + tprint_struct_next(); + PRINT_FIELD_U(st, rx_bytes); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_bytes); + tprint_struct_next(); + PRINT_FIELD_U(st, rx_errors); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_errors); + tprint_struct_next(); + PRINT_FIELD_U(st, rx_dropped); + tprint_struct_next(); + PRINT_FIELD_U(st, tx_dropped); + tprint_struct_next(); + PRINT_FIELD_U(st, rx_noroute); + tprint_struct_end(); + + if (len > sizeof(st)) { + tprint_array_next(); + printstr_ex(tcp, addr + sizeof(st), len - sizeof(st), + QUOTE_FORCE_HEX); + } + + return true; +} + +static const nla_decoder_t ifla_stats_mpls_nla_decoders[] = { + [MPLS_STATS_UNSPEC] = NULL, + [MPLS_STATS_LINK] = decode_ifstats_af_mpls_stats_link, +}; + +static bool +decode_ifstats_af(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + static const struct decoder_desc { + uint8_t af; + const struct xlat *xlat; + const char *dflt; + const nla_decoder_t *table; + size_t size; + } protos[] = { + { AF_MPLS, ifstats_af_spec_mpls_attrs, "MPLS_STATS_???", + ARRSZ_PAIR(ifla_stats_mpls_nla_decoders) }, + }; + + uintptr_t proto = (uintptr_t) opaque_data; + const struct decoder_desc *desc = NULL; + + for (size_t i = 0; i < ARRAY_SIZE(protos); i++) { + if (protos[i].af == proto) { + desc = protos + i; + break; + } + } + + if (!desc) + return false; + + decode_nlattr(tcp, addr, len, + desc->xlat, desc->dflt, desc->table, desc->size, NULL); + + return true; +} + +static bool +decode_ifstats_af_spec(struct tcb *const tcp, + const kernel_ulong_t addr, + const unsigned int len, + const void *const opaque_data) +{ + static const nla_decoder_t af_spec_decoder = &decode_ifstats_af; + + decode_nlattr(tcp, addr, len, addrfams, "AF_???", + &af_spec_decoder, 0, 0); + + return true; +} + + +static const nla_decoder_t ifstatsmsg_nla_decoders[] = { + [IFLA_STATS_UNSPEC] = NULL, + [IFLA_STATS_LINK_64] = decode_nla_rtnl_link_stats64, + [IFLA_STATS_LINK_XSTATS] = decode_ifstats_link_xstats, + [IFLA_STATS_LINK_XSTATS_SLAVE] = decode_ifstats_link_xstats, + [IFLA_STATS_LINK_OFFLOAD_XSTATS] = decode_ifstats_link_offload_xstats, + [IFLA_STATS_AF_SPEC] = decode_ifstats_af_spec, +}; + +DECL_NETLINK_ROUTE_DECODER(decode_ifstatsmsg) +{ + struct if_stats_msg ifstats = { .family = family }; + size_t offset = sizeof(ifstats.family); + bool decode_nla = false; + + tprint_struct_begin(); + PRINT_FIELD_XVAL(ifstats, family, addrfams, "AF_???"); + tprint_struct_next(); + + if (len >= sizeof(ifstats)) { + if (!umoven_or_printaddr(tcp, addr + offset, + sizeof(ifstats) - offset, + (char *) &ifstats + offset)) { + if (ifstats.pad1) { + PRINT_FIELD_X(ifstats, pad1); + tprint_struct_next(); + } + if (ifstats.pad2) { + PRINT_FIELD_X(ifstats, pad2); + tprint_struct_next(); + } + PRINT_FIELD_IFINDEX(ifstats, ifindex); + tprint_struct_next(); + PRINT_FIELD_FLAGS(ifstats, filter_mask, + ifstats_attr_flags, + "1<<IFLA_STATS_???"); + decode_nla = true; + } + } else { + tprint_more_data_follows(); + } + tprint_struct_end(); + + offset = NLMSG_ALIGN(sizeof(ifstats)); + if (decode_nla && len > offset) { + tprint_array_next(); + decode_nlattr(tcp, addr + offset, len - offset, + ifstats_attrs, "IFLA_STATS_???", + ARRSZ_PAIR(ifstatsmsg_nla_decoders), &ifstats); + } +} diff --git a/src/xlat/ifstats_af_spec_mpls_attrs.in b/src/xlat/ifstats_af_spec_mpls_attrs.in new file mode 100644 index 000000000..4b43e0f2c --- /dev/null +++ b/src/xlat/ifstats_af_spec_mpls_attrs.in @@ -0,0 +1,4 @@ +#unconditional +#value_indexed +MPLS_STATS_UNSPEC 0 +MPLS_STATS_LINK 1 diff --git a/src/xlat/ifstats_attr_flags.in b/src/xlat/ifstats_attr_flags.in new file mode 100644 index 000000000..8c9650bbd --- /dev/null +++ b/src/xlat/ifstats_attr_flags.in @@ -0,0 +1,7 @@ +#unconditional +1<<IFLA_STATS_UNSPEC +1<<IFLA_STATS_LINK_64 +1<<IFLA_STATS_LINK_XSTATS +1<<IFLA_STATS_LINK_XSTATS_SLAVE +1<<IFLA_STATS_LINK_OFFLOAD_XSTATS +1<<IFLA_STATS_AF_SPEC diff --git a/src/xlat/ifstats_attrs.in b/src/xlat/ifstats_attrs.in new file mode 100644 index 000000000..c4d4c4e47 --- /dev/null +++ b/src/xlat/ifstats_attrs.in @@ -0,0 +1,8 @@ +#unconditional +#value_indexed +IFLA_STATS_UNSPEC 0 +IFLA_STATS_LINK_64 1 +IFLA_STATS_LINK_XSTATS 2 +IFLA_STATS_LINK_XSTATS_SLAVE 3 +IFLA_STATS_LINK_OFFLOAD_XSTATS 4 +IFLA_STATS_AF_SPEC 5 diff --git a/src/xlat/ifstats_offload_attrs.in b/src/xlat/ifstats_offload_attrs.in new file mode 100644 index 000000000..34159078d --- /dev/null +++ b/src/xlat/ifstats_offload_attrs.in @@ -0,0 +1,4 @@ +#unconditional +#value_indexed +IFLA_OFFLOAD_XSTATS_UNSPEC 0 +IFLA_OFFLOAD_XSTATS_CPU_HIT 1 diff --git a/src/xlat/ifstats_xstats_bond_3ad_attrs.in b/src/xlat/ifstats_xstats_bond_3ad_attrs.in new file mode 100644 index 000000000..c857c6a7a --- /dev/null +++ b/src/xlat/ifstats_xstats_bond_3ad_attrs.in @@ -0,0 +1,12 @@ +#unconditional +#value_indexed +BOND_3AD_STAT_LACPDU_RX 0 +BOND_3AD_STAT_LACPDU_TX 1 +BOND_3AD_STAT_LACPDU_UNKNOWN_RX 2 +BOND_3AD_STAT_LACPDU_ILLEGAL_RX 3 +BOND_3AD_STAT_MARKER_RX 4 +BOND_3AD_STAT_MARKER_TX 5 +BOND_3AD_STAT_MARKER_RESP_RX 6 +BOND_3AD_STAT_MARKER_RESP_TX 7 +BOND_3AD_STAT_MARKER_UNKNOWN_RX 8 +BOND_3AD_STAT_PAD 9 diff --git a/src/xlat/ifstats_xstats_bond_attrs.in b/src/xlat/ifstats_xstats_bond_attrs.in new file mode 100644 index 000000000..db27e50a2 --- /dev/null +++ b/src/xlat/ifstats_xstats_bond_attrs.in @@ -0,0 +1,4 @@ +#unconditional +#value_indexed +BOND_XSTATS_UNSPEC 0 +BOND_XSTATS_3AD 1 diff --git a/src/xlat/ifstats_xstats_bridge_attrs.in b/src/xlat/ifstats_xstats_bridge_attrs.in new file mode 100644 index 000000000..8f80bfe6a --- /dev/null +++ b/src/xlat/ifstats_xstats_bridge_attrs.in @@ -0,0 +1,7 @@ +#unconditional +#value_indexed +BRIDGE_XSTATS_UNSPEC 0 +BRIDGE_XSTATS_VLAN 1 +BRIDGE_XSTATS_MCAST 2 +BRIDGE_XSTATS_PAD 3 +BRIDGE_XSTATS_STP 4 diff --git a/src/xlat/ifstats_xstats_bridge_mcast_indices.in b/src/xlat/ifstats_xstats_bridge_mcast_indices.in new file mode 100644 index 000000000..97116d120 --- /dev/null +++ b/src/xlat/ifstats_xstats_bridge_mcast_indices.in @@ -0,0 +1,4 @@ +#unconditional +#value_indexed +BR_MCAST_DIR_RX 0 +BR_MCAST_DIR_TX 1 diff --git a/src/xlat/ifstats_xstats_type_attrs.in b/src/xlat/ifstats_xstats_type_attrs.in new file mode 100644 index 000000000..2905dfe1d --- /dev/null +++ b/src/xlat/ifstats_xstats_type_attrs.in @@ -0,0 +1,5 @@ +#unconditional +#value_indexed +LINK_XSTATS_TYPE_UNSPEC 0 +LINK_XSTATS_TYPE_BRIDGE 1 +LINK_XSTATS_TYPE_BOND 2 diff --git a/src/xlat/nl_bridge_vlan_flags.in b/src/xlat/nl_bridge_vlan_flags.in new file mode 100644 index 000000000..041ef5a8d --- /dev/null +++ b/src/xlat/nl_bridge_vlan_flags.in @@ -0,0 +1,8 @@ +#unconditional +BRIDGE_VLAN_INFO_MASTER (1<<0) +BRIDGE_VLAN_INFO_PVID (1<<1) +BRIDGE_VLAN_INFO_UNTAGGED (1<<2) +BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) +BRIDGE_VLAN_INFO_RANGE_END (1<<4) +BRIDGE_VLAN_INFO_BRENTRY (1<<5) +BRIDGE_VLAN_INFO_ONLY_OPTS (1<<6) diff --git a/tests/.gitignore b/tests/.gitignore index b957dfc5c..c2f498178 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -547,6 +547,10 @@ nlattr_ifla_xdp-y nlattr_inet_diag_msg nlattr_inet_diag_req_compat nlattr_inet_diag_req_v2 +nlattr_ifstats +nlattr_ifstats-Xabbrev +nlattr_ifstats-Xraw +nlattr_ifstats-Xverbose nlattr_mdba_mdb_entry nlattr_mdba_router_port nlattr_ndmsg diff --git a/tests/gen_tests.in b/tests/gen_tests.in index 588fe24bf..9b4122301 100644 --- a/tests/gen_tests.in +++ b/tests/gen_tests.in @@ -522,6 +522,10 @@ nlattr_ifla_xdp-y +netlink_sock_diag.test -y; exec 9</dev/full nlattr_inet_diag_msg +netlink_sock_diag.test nlattr_inet_diag_req_compat +netlink_sock_diag.test nlattr_inet_diag_req_v2 +netlink_sock_diag.test +nlattr_ifstats +netlink_sock_diag.test +nlattr_ifstats-Xabbrev +netlink_sock_diag.test -Xabbrev +nlattr_ifstats-Xraw +netlink_sock_diag.test -Xraw +nlattr_ifstats-Xverbose +netlink_sock_diag.test -Xverbose nlattr_mdba_mdb_entry +netlink_sock_diag.test nlattr_mdba_router_port +netlink_sock_diag.test nlattr_ndmsg +netlink_sock_diag.test diff --git a/tests/netlink_route.c b/tests/netlink_route.c index 17f168ee1..96b22d48c 100644 --- a/tests/netlink_route.c +++ b/tests/netlink_route.c @@ -19,6 +19,7 @@ #include <linux/if_addrlabel.h> #include <linux/if_arp.h> #include <linux/if_bridge.h> +#include <linux/if_link.h> #include <linux/ip.h> #include <linux/neighbour.h> #include <linux/netconf.h> @@ -640,6 +641,55 @@ test_rtnl_nexthop(const int fd) test_rtnl_unknown_msg(fd, RTM_NEWNEXTHOP + 3); } +static void +test_rtnl_ifstats(const int fd) +{ + static const struct strval32 types[] = { + { ARG_STR(RTM_NEWSTATS) }, + { ARG_STR(RTM_GETSTATS) }, + }; + const struct { + struct if_stats_msg msg; + const char *af_str; + const char *rest_str; + } msgs[] = { + { { .family = AF_UNIX, .pad1 = 0, .pad2 = 0, + .ifindex = ifindex_lo(), .filter_mask = 0, }, + "{family=AF_UNIX", ", ifindex=" IFINDEX_LO_STR + ", filter_mask=0}" }, + { { .family = 44, .pad1 = 0, .pad2 = 0xdead, + .ifindex = 0xdeadbeef, .filter_mask = 1, }, + "{family=AF_XDP", ", pad2=0xdead, ifindex=3735928559" + ", filter_mask=1<<IFLA_STATS_UNSPEC}" }, + { { .family = 45, .pad1 = 0xca, .pad2 = 0, + .ifindex = ifindex_lo(), .filter_mask = 0xff, }, + "{family=0x2d /* AF_??? */", ", pad1=0xca" + ", ifindex=" IFINDEX_LO_STR + ", filter_mask=1<<IFLA_STATS_UNSPEC|1<<IFLA_STATS_LINK_64" + "|1<<IFLA_STATS_LINK_XSTATS|1<<IFLA_STATS_LINK_XSTATS_SLAVE" + "|1<<IFLA_STATS_LINK_OFFLOAD_XSTATS|1<<IFLA_STATS_AF_SPEC" + "|0xc0}" }, + { { .family = 255, .pad1 = 0xde, .pad2 = 0xbeef, + .ifindex = ifindex_lo(), .filter_mask = 0xdec0dec0, }, + "{family=0xff /* AF_??? */", ", pad1=0xde" + ", pad2=0xbeef, ifindex=" IFINDEX_LO_STR + ", filter_mask=0xdec0dec0 /* 1<<IFLA_STATS_??? */}" }, + }; + void *const nlh0 = midtail_alloc(NLMSG_HDRLEN, sizeof(msgs[0].msg)); + + for (size_t i = 0; i < ARRAY_SIZE(types); i++) { + for (size_t j = 0; j < ARRAY_SIZE(msgs); j++) { + TEST_NL_ROUTE_(fd, nlh0, types[i].val, types[i].str, + msgs[j].msg, + printf("%s", msgs[j].af_str), + printf("%s", msgs[j].rest_str)); + } + } + + test_rtnl_unknown_msg(fd, RTM_NEWSTATS + 1); + test_rtnl_unknown_msg(fd, RTM_NEWSTATS + 3); +} + int main(void) { skip_if_unavailable("/proc/self/fd/"); @@ -678,12 +728,7 @@ int main(void) test_rtnl_netconf(fd); /* 80 */ test_rtnl_mdb(fd); /* 84 */ test_rtnl_nsid(fd); /* 88 */ - - /* stats */ /* 92 */ - test_rtnl_unsupported_msg(fd, ARG_STR(RTM_NEWSTATS)); - test_rtnl_unknown_msg(fd, RTM_NEWSTATS + 1); - test_rtnl_unsupported_msg(fd, ARG_STR(RTM_GETSTATS)); - test_rtnl_unknown_msg(fd, RTM_NEWSTATS + 3); + test_rtnl_ifstats(fd); /* 92 */ /* cachereport */ /* 96 */ test_rtnl_unsupported_msg(fd, ARG_STR(RTM_NEWCACHEREPORT)); diff --git a/tests/nlattr_ifstats-Xabbrev.c b/tests/nlattr_ifstats-Xabbrev.c new file mode 100644 index 000000000..d9da303d8 --- /dev/null +++ b/tests/nlattr_ifstats-Xabbrev.c @@ -0,0 +1,2 @@ +#define XLAT_ABBREV 1 +#include "nlattr_ifstats.c" diff --git a/tests/nlattr_ifstats-Xraw.c b/tests/nlattr_ifstats-Xraw.c new file mode 100644 index 000000000..0e5c5ab07 --- /dev/null +++ b/tests/nlattr_ifstats-Xraw.c @@ -0,0 +1,2 @@ +#define XLAT_RAW 1 +#include "nlattr_ifstats.c" diff --git a/tests/nlattr_ifstats-Xverbose.c b/tests/nlattr_ifstats-Xverbose.c new file mode 100644 index 000000000..a2129cd26 --- /dev/null +++ b/tests/nlattr_ifstats-Xverbose.c @@ -0,0 +1,2 @@ +#define XLAT_VERBOSE 1 +#include "nlattr_ifstats.c" diff --git a/tests/nlattr_ifstats.c b/tests/nlattr_ifstats.c new file mode 100644 index 000000000..c42374e76 --- /dev/null +++ b/tests/nlattr_ifstats.c @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2019-2021 Eugene Syromyatnikov <evgsyr@gmail.com> + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#include <arpa/inet.h> +#include <inttypes.h> +#include <linux/ip.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> + +#include "test_nlattr.h" + +#include <linux/if_link.h> +#include <linux/if_bonding.h> +#include <linux/if_bridge.h> +#include <linux/mpls.h> + +#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<<IFLA_STATS_LINK_64|1<<IFLA_STATS_AF_SPEC)); +} + +/* + * NB: these functions use global variables to control which top-level + * netlink attribute is to be used. + */ +#define DEF_NLATTR_FUNCS_NESTED(sfx_, attr_var_, attr_str_var_, \ + parent_sfx_, lvl_) \ + static void \ + init_ifstats_##sfx_(struct nlmsghdr *const nlh, \ + const unsigned int msg_len) \ + { \ + (init_##parent_sfx_)(nlh, msg_len); \ + struct nlattr *nla = NLMSG_ATTR(nlh, \ + sizeof(struct if_stats_msg) \ + + ((lvl_) - 1) * NLA_HDRLEN); \ + SET_STRUCT(struct nlattr, nla, \ + .nla_len = msg_len \ + - NLMSG_SPACE(sizeof(struct if_stats_msg)) \ + - ((lvl_) - 1) * NLA_HDRLEN, \ + .nla_type = (attr_var_), \ + ); \ + } \ + \ + static void \ + print_ifstats_##sfx_(const unsigned int msg_len) \ + { \ + (print_##parent_sfx_)(msg_len); \ + printf(", [{nla_len=%u, nla_type=%s}", \ + (unsigned int) (msg_len - NLMSG_HDRLEN \ + - NLMSG_ALIGN(sizeof(struct \ + if_stats_msg)) \ + - ((lvl_) - 1) * NLA_HDRLEN), \ + attr_str_var_); \ + } \ + /* End of DEF_NLATTR_FUNCS_NESTED */ + +static uint16_t l1_attr; +static char l1_attr_str[256]; + +static uint16_t l2_attr; +static char l2_attr_str[256]; + +static uint16_t l3_attr; +static char l3_attr_str[256]; + +DEF_NLATTR_FUNCS_NESTED(l1, l1_attr, l1_attr_str, ifstats, 1) +DEF_NLATTR_FUNCS_NESTED(l2, l2_attr, l2_attr_str, ifstats_l1, 2) +DEF_NLATTR_FUNCS_NESTED(l3, l3_attr, l3_attr_str, ifstats_l2, 3) + + +static void +print_stats_64(struct rtnl_link_stats64 *st, size_t sz) +{ + printf("{"); PRINT_FIELD_U(*st, rx_packets); + printf(", "); PRINT_FIELD_U(*st, tx_packets); + printf(", "); PRINT_FIELD_U(*st, rx_bytes); + printf(", "); PRINT_FIELD_U(*st, tx_bytes); + printf(", "); PRINT_FIELD_U(*st, rx_errors); + printf(", "); PRINT_FIELD_U(*st, tx_errors); + printf(", "); PRINT_FIELD_U(*st, rx_dropped); + printf(", "); PRINT_FIELD_U(*st, tx_dropped); + printf(", "); PRINT_FIELD_U(*st, multicast); + printf(", "); PRINT_FIELD_U(*st, collisions); + printf(", "); PRINT_FIELD_U(*st, rx_length_errors); + printf(", "); PRINT_FIELD_U(*st, rx_over_errors); + printf(", "); PRINT_FIELD_U(*st, rx_crc_errors); + printf(", "); PRINT_FIELD_U(*st, rx_frame_errors); + printf(", "); PRINT_FIELD_U(*st, rx_fifo_errors); + printf(", "); PRINT_FIELD_U(*st, rx_missed_errors); + printf(", "); PRINT_FIELD_U(*st, tx_aborted_errors); + printf(", "); PRINT_FIELD_U(*st, tx_carrier_errors); + printf(", "); PRINT_FIELD_U(*st, tx_fifo_errors); + printf(", "); PRINT_FIELD_U(*st, tx_heartbeat_errors); + printf(", "); PRINT_FIELD_U(*st, tx_window_errors); + printf(", "); PRINT_FIELD_U(*st, rx_compressed); + printf(", "); PRINT_FIELD_U(*st, tx_compressed); + if (sz > 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; +} diff --git a/tests/pure_executables.list b/tests/pure_executables.list index 73d23a336..284209d6b 100755 --- a/tests/pure_executables.list +++ b/tests/pure_executables.list @@ -394,6 +394,10 @@ nlattr_ifla_xdp nlattr_inet_diag_msg nlattr_inet_diag_req_compat nlattr_inet_diag_req_v2 +nlattr_ifstats +nlattr_ifstats-Xabbrev +nlattr_ifstats-Xraw +nlattr_ifstats-Xverbose nlattr_mdba_mdb_entry nlattr_mdba_router_port nlattr_ndmsg |