[PATCH v2 1/2] rtnl_link: decode ifinfomsg netlink attributes
Dmitry V. Levin
ldv at altlinux.org
Sat Aug 19 22:33:53 UTC 2017
On Sat, Aug 19, 2017 at 09:50:10AM +0800, JingPiao Chen wrote:
> * configure.ac (AC_CHECK_HEADERS): Add linux/if_link.h.
> (AC_CHECK_TYPES): Check for struct rtnl_link_stats64 in linux/if_link.h.
> (AC_CHECK_MEMBERS): Check for rx_nohandler field
> in struct rtnl_link_stats and struct rtnl_link_stats64.
> * rtnl_link.c: Include <arpa/inet.h>, <linux/if_arp.h>,
> <linux/if_link.h> and <linux/netdevice.h>.
> (min_ifla_address_len, ifla_address_default_decoder,
> ifla_address_type_specific_decoder,
> decode_ifla_address, decode_rtnl_link_stats,
> decode_rtnl_link_ifmap, decode_rtnl_link_stats64,
> print_item_id, decode_ifla_phys_item_id): New functions.
> (decode_ifla_phys_item_id): New array.
How could decode_ifla_phys_item_id be both a function and an array?
> (decode_ifinfomsg): Use it.
> ---
> configure.ac | 6 ++
> rtnl_link.c | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 303 insertions(+), 1 deletion(-)
>
> diff --git a/configure.ac b/configure.ac
> index cb398fb..dacd67f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -382,6 +382,7 @@ AC_CHECK_HEADERS(m4_normalize([
> linux/genetlink.h
> linux/hiddev.h
> linux/if_addr.h
> + linux/if_link.h
> linux/ip_vs.h
> linux/ipc.h
> linux/mmtimer.h
> @@ -447,6 +448,11 @@ AC_CHECK_TYPES([struct dcbmsg],,, [#include <linux/dcbnl.h>])
> AC_CHECK_TYPES([struct ifaddrlblmsg],,, [#include <linux/if_addrlabel.h>])
> AC_CHECK_TYPES([struct netconfmsg],,, [#include <linux/netconf.h>])
>
> +AC_CHECK_TYPES([struct rtnl_link_stats64], [
> + AC_CHECK_MEMBERS([struct rtnl_link_stats64.rx_nohandler],,, [#include <linux/if_link.h>])
> +],, [#include <linux/if_link.h>])
> +AC_CHECK_MEMBERS([struct rtnl_link_stats.rx_nohandler],,, [#include <linux/if_link.h>])
> +
> AC_CHECK_TYPES([struct statfs], [
> AC_CHECK_MEMBERS([struct statfs.f_frsize],,, [#include <linux/types.h>
> #include <asm/statfs.h>])
> diff --git a/rtnl_link.c b/rtnl_link.c
> index 18897f3..29c2e19 100644
> --- a/rtnl_link.c
> +++ b/rtnl_link.c
> @@ -32,11 +32,305 @@
> #include "nlattr.h"
> #include "print_fields.h"
>
> +#include <arpa/inet.h>
> +
> +#include <linux/if_arp.h>
> +#ifdef HAVE_LINUX_IF_LINK_H
> +# include <linux/if_link.h>
> +#endif
> +#include <linux/netdevice.h>
> #include "netlink.h"
> #include <linux/rtnetlink.h>
>
> #include "xlat/rtnl_link_attrs.h"
>
> +static int
> +min_ifla_address_len(const unsigned short type)
> +{
> + switch (type) {
> + case ARPHRD_TUNNEL:
> + case ARPHRD_SIT:
> + case ARPHRD_IPGRE:
> + return sizeof(struct in_addr);
> + case ARPHRD_TUNNEL6:
> + return sizeof(struct in6_addr);
> + default:
> + return 0;
> + }
> +}
The same switch on ifi_type value is repeated twice: first in
min_ifla_address_len, second in ifla_address_type_specific_decoder.
In the previous version of this patch there was just one switch
on ifi_type value.
> +
> +static bool
> +ifla_address_default_decoder(struct tcb *const tcp,
> + const kernel_ulong_t addr,
> + const unsigned int len)
> +{
> + const unsigned int addr_len =
> + len < MAX_ADDR_LEN ? len : MAX_ADDR_LEN;
> + size_t i;
> +
> + for (i = 0; i < addr_len; i++) {
> + uint8_t buf;
> + if (umove(tcp, addr + i, &buf) < 0)
> + break;
> + if (i)
> + tprints(":");
> + tprintf("%02x", buf);
> + }
> + if (i < addr_len || addr_len < len)
> + tprints(": ...");
> +
> + return true;
> +}
ifla_address_default_decoder inefficiently fetches one byte at a time
instead of the whole address. Why umoven_or_printaddr is not used here?
> +
> +static bool
> +ifla_address_type_specific_decoder(const char *const buf,
> + const unsigned short type)
> +{
> + switch (type) {
> + case ARPHRD_TUNNEL:
> + case ARPHRD_SIT:
> + case ARPHRD_IPGRE: {
> + char str[INET_ADDRSTRLEN];
> +
> + if (!inet_ntop(AF_INET, buf, str, sizeof(str)))
> + return false;
> + tprintf("%s", str);
> + break;
> + }
> + case ARPHRD_TUNNEL6: {
> + char str[INET6_ADDRSTRLEN];
> +
> + if (!inet_ntop(AF_INET6, buf, str, sizeof(str)))
> + return false;
> + tprintf("%s", str);
> + break;
> + }
> + default:
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool
> +decode_ifla_address(struct tcb *const tcp,
> + const kernel_ulong_t addr,
> + const unsigned int len,
> + const void *const opaque_data)
> +{
> + const unsigned short type =
> + ((struct ifinfomsg *) opaque_data)->ifi_type;
> + size_t size = min_ifla_address_len(type);
> + char buf[256];
> +
> + if (!size || len < size)
> + return ifla_address_default_decoder(tcp, addr, len);
> + else if (!umoven_or_printaddr(tcp, addr, size, buf))
> + return ifla_address_type_specific_decoder(buf, type);
> +
> + return true;
> +}
> +
> +static bool
> +decode_rtnl_link_stats(struct tcb *const tcp,
> + const kernel_ulong_t addr,
> + const unsigned int len,
> + const void *const opaque_data)
> +{
> + struct rtnl_link_stats st;
> +
> + if (len < sizeof(st))
> + return false;
The kernel may not transfer struct rtnl_link_stats.rx_nohandler despite
the latter being defined by linux/if_link.h, e.g. if the kernel uses
an older version of struct rtnl_link_stats.
The minimal size is therefore not sizeof(struct rtnl_link_stats)
but offsetofend(struct rtnl_link_stats, tx_compressed).
Likewise, with struct rtnl_link_stats64.rx_nohandler.
> + else if (!umove_or_printaddr(tcp, addr, &st)) {
> + PRINT_FIELD_U("{", st, rx_packets);
> + PRINT_FIELD_U(", ", st, tx_packets);
> + PRINT_FIELD_U(", ", st, rx_bytes);
> + PRINT_FIELD_U(", ", st, tx_bytes);
> + PRINT_FIELD_U(", ", st, rx_errors);
> + PRINT_FIELD_U(", ", st, tx_errors);
> + PRINT_FIELD_U(", ", st, rx_dropped);
> + PRINT_FIELD_U(", ", st, tx_dropped);
> + PRINT_FIELD_U(", ", st, multicast);
> + PRINT_FIELD_U(", ", st, collisions);
> +
> + PRINT_FIELD_U(", ", st, rx_length_errors);
> + PRINT_FIELD_U(", ", st, rx_over_errors);
> + PRINT_FIELD_U(", ", st, rx_crc_errors);
> + PRINT_FIELD_U(", ", st, rx_frame_errors);
> + PRINT_FIELD_U(", ", st, rx_fifo_errors);
> + PRINT_FIELD_U(", ", st, rx_missed_errors);
> +
> + PRINT_FIELD_U(", ", st, tx_aborted_errors);
> + PRINT_FIELD_U(", ", st, tx_carrier_errors);
> + PRINT_FIELD_U(", ", st, tx_fifo_errors);
> + PRINT_FIELD_U(", ", st, tx_heartbeat_errors);
> + PRINT_FIELD_U(", ", st, tx_window_errors);
> +
> + PRINT_FIELD_U(", ", st, rx_compressed);
> + PRINT_FIELD_U(", ", st, tx_compressed);
> +#ifdef HAVE_STRUCT_RTNL_LINK_STATS_RX_NOHANDLER
> + PRINT_FIELD_U(", ", st, rx_nohandler);
> +#endif
> + tprints("}");
> + }
> +
> + return true;
> +}
> +
> +static bool
> +decode_rtnl_link_ifmap(struct tcb *const tcp,
> + const kernel_ulong_t addr,
> + const unsigned int len,
> + const void *const opaque_data)
> +{
> + struct rtnl_link_ifmap map;
> + const unsigned int sizeof_ifmap =
> + offsetofend(struct rtnl_link_ifmap, port);
> +
> + if (len < sizeof_ifmap)
> + return false;
> + else if (!umoven_or_printaddr(tcp, addr, sizeof_ifmap, &map)) {
> + PRINT_FIELD_X("{", map, mem_start);
> + PRINT_FIELD_X(", ", map, mem_end);
> + PRINT_FIELD_X(", ", map, base_addr);
> + PRINT_FIELD_U(", ", map, irq);
> + PRINT_FIELD_U(", ", map, dma);
> + PRINT_FIELD_U(", ", map, port);
> + tprints("}");
> + }
> +
> + 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)
> +{
> +#ifdef HAVE_STRUCT_RTNL_LINK_STATS64
> + struct rtnl_link_stats64 st;
> +
> + if (len < sizeof(st))
> + return false;
> + else if (!umove_or_printaddr(tcp, addr, &st)) {
> + PRINT_FIELD_U("{", st, rx_packets);
> + PRINT_FIELD_U(", ", st, tx_packets);
> + PRINT_FIELD_U(", ", st, rx_bytes);
> + PRINT_FIELD_U(", ", st, tx_bytes);
> + PRINT_FIELD_U(", ", st, rx_errors);
> + PRINT_FIELD_U(", ", st, tx_errors);
> + PRINT_FIELD_U(", ", st, rx_dropped);
> + PRINT_FIELD_U(", ", st, tx_dropped);
> + PRINT_FIELD_U(", ", st, multicast);
> + PRINT_FIELD_U(", ", st, collisions);
> +
> + PRINT_FIELD_U(", ", st, rx_length_errors);
> + PRINT_FIELD_U(", ", st, rx_over_errors);
> + PRINT_FIELD_U(", ", st, rx_crc_errors);
> + PRINT_FIELD_U(", ", st, rx_frame_errors);
> + PRINT_FIELD_U(", ", st, rx_fifo_errors);
> + PRINT_FIELD_U(", ", st, rx_missed_errors);
> +
> + PRINT_FIELD_U(", ", st, tx_aborted_errors);
> + PRINT_FIELD_U(", ", st, tx_carrier_errors);
> + PRINT_FIELD_U(", ", st, tx_fifo_errors);
> + PRINT_FIELD_U(", ", st, tx_heartbeat_errors);
> + PRINT_FIELD_U(", ", st, tx_window_errors);
> +
> + PRINT_FIELD_U(", ", st, rx_compressed);
> + PRINT_FIELD_U(", ", st, tx_compressed);
> +#ifdef HAVE_STRUCT_RTNL_LINK_STATS64_RX_NOHANDLER
> + PRINT_FIELD_U(", ", st, rx_nohandler);
> +#endif
> + tprints("}");
> + }
> +
> + return true;
> +#else
> + return false;
> +#endif
> +}
> +
> +static bool
> +print_item_id(struct tcb *const tcp, void *const elem_buf,
> + const size_t elem_size, void *const opaque_data)
> +{
> + unsigned int *const count = opaque_data;
> +
> + /* MAX_PHYS_ITEM_ID_LEN = 32 */
> + if ((*count)++ >= 32) {
> + tprints("...");
> + return false;
> + }
> +
> + tprintf("%" PRIu8, *(uint8_t *) elem_buf);
> +
> + return true;
> +}
> +
> +static bool
> +decode_ifla_phys_item_id(struct tcb *const tcp,
> + const kernel_ulong_t addr,
> + const unsigned int len,
> + const void *const opaque_data)
> +{
> + uint8_t id;
> + unsigned int count = 0;
> +
> + print_array(tcp, addr, len, &id, sizeof(id),
> + umoven_or_printaddr, print_item_id, &count);
I'm not sure it's the best way to decode struct netdev_phys_item_id.id.
> +
> + return true;
> +}
> +
> +static const nla_decoder_t ifinfomsg_nla_decoders[] = {
> + [IFLA_ADDRESS] = decode_ifla_address,
> + [IFLA_BROADCAST] = decode_ifla_address,
> + [IFLA_IFNAME] = decode_nla_str,
> + [IFLA_MTU] = decode_nla_u32,
> + [IFLA_LINK] = decode_nla_u32,
> + [IFLA_QDISC] = decode_nla_str,
> + [IFLA_STATS] = decode_rtnl_link_stats,
> + [IFLA_COST] = NULL,
> + [IFLA_PRIORITY] = NULL,
> + [IFLA_MASTER] = decode_nla_u32,
> + [IFLA_WIRELESS] = NULL,
Here a parser of struct iw_event is expected.
> + [IFLA_PROTINFO] = NULL,
This one seems to be used in the kernel.
> + [IFLA_TXQLEN] = decode_nla_u32,
> + [IFLA_MAP] = decode_rtnl_link_ifmap,
> + [IFLA_WEIGHT] = decode_nla_u32,
> + [IFLA_OPERSTATE] = decode_nla_u8,
> + [IFLA_LINKMODE] = decode_nla_u8,
> + [IFLA_LINKINFO] = NULL,
This one also seems to be used in the kernel.
> + [IFLA_NET_NS_PID] = decode_nla_u32,
> + [IFLA_IFALIAS] = decode_nla_str,
> + [IFLA_NUM_VF] = decode_nla_u32,
> + [IFLA_VFINFO_LIST] = NULL,
Likewise.
> + [IFLA_STATS64] = decode_rtnl_link_stats64,
> + [IFLA_VF_PORTS] = NULL,
Likewise.
> + [IFLA_PORT_SELF] = NULL,
Likewise.
> + [IFLA_AF_SPEC] = NULL,
Likewise.
> + [IFLA_GROUP] = decode_nla_u32,
> + [IFLA_NET_NS_FD] = decode_nla_u32,
> + [IFLA_EXT_MASK] = decode_nla_u32,
> + [IFLA_PROMISCUITY] = decode_nla_u32,
> + [IFLA_NUM_TX_QUEUES] = decode_nla_u32,
> + [IFLA_NUM_RX_QUEUES] = decode_nla_u32,
> + [IFLA_CARRIER] = decode_nla_u8,
> + [IFLA_PHYS_PORT_ID] = decode_ifla_phys_item_id,
> + [IFLA_CARRIER_CHANGES] = decode_nla_u32,
> + [IFLA_PHYS_SWITCH_ID] = decode_ifla_phys_item_id,
> + [IFLA_LINK_NETNSID] = decode_nla_s32,
> + [IFLA_PHYS_PORT_NAME] = decode_nla_str,
> + [IFLA_PROTO_DOWN] = decode_nla_u8,
> + [IFLA_GSO_MAX_SEGS] = decode_nla_u32,
> + [IFLA_GSO_MAX_SIZE] = decode_nla_u32,
> + [IFLA_PAD] = NULL,
> + [IFLA_XDP] = NULL,
Likewise.
--
ldv
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.strace.io/pipermail/strace-devel/attachments/20170820/9fe462e1/attachment.bin>
More information about the Strace-devel
mailing list